Coverage for manila/share/drivers/glusterfs/layout.py: 100%

121 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2015 Red Hat, Inc. 

2# All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16"""GlusterFS share layouts. 

17 

18A share layout encapsulates a particular way of mapping GlusterFS entities 

19to a share and utilizing them to back the share. 

20""" 

21 

22import abc 

23import errno 

24 

25from oslo_config import cfg 

26from oslo_utils import importutils 

27 

28from manila import exception 

29from manila.i18n import _ 

30from manila.share import driver 

31from manila.share.drivers.ganesha import utils as ganesha_utils 

32 

33glusterfs_share_layout_opts = [ 

34 cfg.StrOpt( 

35 'glusterfs_share_layout', 

36 help="Specifies GlusterFS share layout, that is, " 

37 "the method of associating backing GlusterFS " 

38 "resources to shares."), 

39] 

40 

41CONF = cfg.CONF 

42CONF.register_opts(glusterfs_share_layout_opts) 

43 

44 

45class GlusterfsShareDriverBase(driver.ShareDriver): 

46 

47 LAYOUT_PREFIX = 'manila.share.drivers.glusterfs' 

48 

49 supported_layouts = () 

50 supported_protocols = () 

51 _supported_access_types = () 

52 _supported_access_levels = () 

53 

54 GLUSTERFS_VERSION_MIN = (0, 0) 

55 

56 def __init__(self, *args, **kwargs): 

57 super(GlusterfsShareDriverBase, self).__init__(*args, **kwargs) 

58 self.configuration.append_config_values( 

59 glusterfs_share_layout_opts) 

60 layout_name = self.configuration.glusterfs_share_layout 

61 if not layout_name: 

62 layout_name = self.supported_layouts[0] 

63 if layout_name not in self.supported_layouts: 

64 raise exception.GlusterfsException( 

65 _('driver %(driver)s does not support %(layout)s layout') % 

66 {'driver': type(self).__name__, 'layout': layout_name}) 

67 

68 self.layout = importutils.import_object( 

69 '.'.join((self.LAYOUT_PREFIX, layout_name)), 

70 self, **kwargs) 

71 # we determine snapshot support in our own scope, as 

72 # 1) the calculation based on parent method 

73 # redefinition does not work for us, as actual 

74 # glusterfs driver classes are subclassed from 

75 # *this* class, not from driver.ShareDriver 

76 # and they don't need to redefine snapshot 

77 # methods for themselves; 

78 # 2) snapshot support depends on choice of layout. 

79 self._snapshots_are_supported = getattr(self.layout, 

80 '_snapshots_are_supported', 

81 False) 

82 

83 def _setup_via_manager(self, share_mgr, share_mgr_parent=None): 

84 """Callback for layout's `create_share`/`create_share_from_snapshot` 

85 

86 :param share_mgr: a {'share': <share>, 'manager': <gmgr>} 

87 dict where <share> is the share created 

88 in `create_share` or `create_share_from_snapshot` 

89 and <gmgr> is a GlusterManager instance 

90 representing the GlusterFS resource 

91 allocated for it. 

92 :param gluster_mgr_parent: a {'share': <share>, 'manager': <gmgr>} 

93 dict where <share> is the original share of the snapshot 

94 used in `create_share_from_snapshot` and <gmgr> is a 

95 GlusterManager instance representing the GlusterFS 

96 resource allocated for it. 

97 :returns: export location for share_mgr['share']. 

98 """ 

99 

100 @property 

101 def supported_access_levels(self): 

102 return self._supported_access_levels 

103 

104 @property 

105 def supported_access_types(self): 

106 return self._supported_access_types 

107 

108 def _access_rule_validator(self, abort): 

109 

110 def validator(rule): 

111 return ganesha_utils.validate_access_rule( 

112 self.supported_access_types, self.supported_access_levels, 

113 rule, abort) 

114 

115 return validator 

116 

117 def update_access(self, context, share, access_rules, add_rules, 

118 delete_rules, update_rules, share_server=None): 

119 """Update access rules for given share. 

120 

121 Driver supports 2 different cases in this method: 

122 1. Recovery after error - 'access_rules' contains all access_rules, 

123 'add_rules' and 'delete_rules' are []. Driver should clear any 

124 existent access rules and apply all access rules for given share. 

125 This recovery is made at driver start up. 

126 

127 2. Adding/Deleting of several access rules - 'access_rules' contains 

128 all access_rules, 'add_rules' and 'delete_rules' and 'update_rules' 

129 contain rules which should be added/deleted/updated. Driver can 

130 ignore rules in 'access_rules' and apply only rules from 'add_rules', 

131 'delete_rules' and 'update_rules'. 

132 """ 

133 gluster_mgr = self.layout._share_manager(share) 

134 

135 access_rules, add_rules, delete_rules = ( 

136 list(filter(self._access_rule_validator(abort), rules)) for ( 

137 rules, abort) in ((access_rules, True), 

138 (add_rules, True), 

139 (delete_rules, False))) 

140 

141 # Recovery mode. 

142 if not (add_rules or delete_rules): 

143 ruleop, recovery = (access_rules, []), True 

144 else: 

145 ruleop, recovery = (add_rules, delete_rules), False 

146 

147 self._update_access_via_manager(gluster_mgr, context, share, 

148 *ruleop, recovery=recovery) 

149 

150 def _update_access_via_manager(self, gluster_mgr, context, share, 

151 add_rules, delete_rules, recovery=False, 

152 share_server=None): 

153 raise NotImplementedError() 

154 

155 def do_setup(self, *a, **kw): 

156 return self.layout.do_setup(*a, **kw) 

157 

158 @classmethod 

159 def _check_proto(cls, share): 

160 proto = share['share_proto'].upper() 

161 if proto not in cls.supported_protocols: 

162 msg = _("Share protocol %s is not supported.") % proto 

163 raise exception.ShareBackendException(msg=msg) 

164 

165 def create_share(self, context, share, *a, **kw): 

166 self._check_proto(share) 

167 return self.layout.create_share(context, share, *a, **kw) 

168 

169 def create_share_from_snapshot(self, context, share, *a, **kw): 

170 self._check_proto(share) 

171 return self.layout.create_share_from_snapshot(context, share, *a, **kw) 

172 

173 def create_snapshot(self, *a, **kw): 

174 return self.layout.create_snapshot(*a, **kw) 

175 

176 def delete_share(self, *a, **kw): 

177 return self.layout.delete_share(*a, **kw) 

178 

179 def delete_snapshot(self, *a, **kw): 

180 return self.layout.delete_snapshot(*a, **kw) 

181 

182 def ensure_share(self, *a, **kw): 

183 return self.layout.ensure_share(*a, **kw) 

184 

185 def manage_existing(self, *a, **kw): 

186 return self.layout.manage_existing(*a, **kw) 

187 

188 def unmanage(self, *a, **kw): 

189 return self.layout.unmanage(*a, **kw) 

190 

191 def extend_share(self, *a, **kw): 

192 return self.layout.extend_share(*a, **kw) 

193 

194 def shrink_share(self, *a, **kw): 

195 return self.layout.shrink_share(*a, **kw) 

196 

197 def _update_share_stats(self, data={}): 

198 try: 

199 data.update(self.layout._update_share_stats()) 

200 except NotImplementedError: 

201 pass 

202 super(GlusterfsShareDriverBase, self)._update_share_stats(data) 

203 

204 

205class GlusterfsShareLayoutBase(metaclass=abc.ABCMeta): 

206 """Base class for share layouts.""" 

207 

208 def __init__(self, driver, *args, **kwargs): 

209 self.driver = driver 

210 self.configuration = kwargs.get('configuration') 

211 

212 def _check_mount_glusterfs(self): 

213 """Checks if mount.glusterfs(8) is available.""" 

214 try: 

215 self.driver._execute('mount.glusterfs', check_exit_code=False) 

216 except OSError as exc: 

217 if exc.errno == errno.ENOENT: 

218 raise exception.GlusterfsException( 

219 _('mount.glusterfs is not installed.')) 

220 else: 

221 raise 

222 

223 @abc.abstractmethod 

224 def _share_manager(self, share): 

225 """Return GlusterManager object representing share's backend.""" 

226 

227 @abc.abstractmethod 

228 def do_setup(self, context): 

229 """Any initialization the share driver does while starting.""" 

230 

231 @abc.abstractmethod 

232 def create_share(self, context, share, share_server=None): 

233 """Is called to create share.""" 

234 

235 @abc.abstractmethod 

236 def create_share_from_snapshot(self, context, share, snapshot, 

237 share_server=None, parent_share=None): 

238 """Is called to create share from snapshot.""" 

239 

240 @abc.abstractmethod 

241 def create_snapshot(self, context, snapshot, share_server=None): 

242 """Is called to create snapshot.""" 

243 

244 @abc.abstractmethod 

245 def delete_share(self, context, share, share_server=None): 

246 """Is called to remove share.""" 

247 

248 @abc.abstractmethod 

249 def delete_snapshot(self, context, snapshot, share_server=None): 

250 """Is called to remove snapshot.""" 

251 

252 @abc.abstractmethod 

253 def ensure_share(self, context, share, share_server=None): 

254 """Invoked to ensure that share is exported.""" 

255 

256 @abc.abstractmethod 

257 def manage_existing(self, share, driver_options): 

258 """Brings an existing share under Manila management.""" 

259 

260 @abc.abstractmethod 

261 def unmanage(self, share): 

262 """Removes the specified share from Manila management.""" 

263 

264 @abc.abstractmethod 

265 def extend_share(self, share, new_size, share_server=None): 

266 """Extends size of existing share.""" 

267 

268 @abc.abstractmethod 

269 def shrink_share(self, share, new_size, share_server=None): 

270 """Shrinks size of existing share.""" 

271 

272 def _update_share_stats(self): 

273 raise NotImplementedError()