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
« 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.
16"""GlusterFS share layouts.
18A share layout encapsulates a particular way of mapping GlusterFS entities
19to a share and utilizing them to back the share.
20"""
22import abc
23import errno
25from oslo_config import cfg
26from oslo_utils import importutils
28from manila import exception
29from manila.i18n import _
30from manila.share import driver
31from manila.share.drivers.ganesha import utils as ganesha_utils
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]
41CONF = cfg.CONF
42CONF.register_opts(glusterfs_share_layout_opts)
45class GlusterfsShareDriverBase(driver.ShareDriver):
47 LAYOUT_PREFIX = 'manila.share.drivers.glusterfs'
49 supported_layouts = ()
50 supported_protocols = ()
51 _supported_access_types = ()
52 _supported_access_levels = ()
54 GLUSTERFS_VERSION_MIN = (0, 0)
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})
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)
83 def _setup_via_manager(self, share_mgr, share_mgr_parent=None):
84 """Callback for layout's `create_share`/`create_share_from_snapshot`
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 """
100 @property
101 def supported_access_levels(self):
102 return self._supported_access_levels
104 @property
105 def supported_access_types(self):
106 return self._supported_access_types
108 def _access_rule_validator(self, abort):
110 def validator(rule):
111 return ganesha_utils.validate_access_rule(
112 self.supported_access_types, self.supported_access_levels,
113 rule, abort)
115 return validator
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.
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.
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)
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)))
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
147 self._update_access_via_manager(gluster_mgr, context, share,
148 *ruleop, recovery=recovery)
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()
155 def do_setup(self, *a, **kw):
156 return self.layout.do_setup(*a, **kw)
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)
165 def create_share(self, context, share, *a, **kw):
166 self._check_proto(share)
167 return self.layout.create_share(context, share, *a, **kw)
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)
173 def create_snapshot(self, *a, **kw):
174 return self.layout.create_snapshot(*a, **kw)
176 def delete_share(self, *a, **kw):
177 return self.layout.delete_share(*a, **kw)
179 def delete_snapshot(self, *a, **kw):
180 return self.layout.delete_snapshot(*a, **kw)
182 def ensure_share(self, *a, **kw):
183 return self.layout.ensure_share(*a, **kw)
185 def manage_existing(self, *a, **kw):
186 return self.layout.manage_existing(*a, **kw)
188 def unmanage(self, *a, **kw):
189 return self.layout.unmanage(*a, **kw)
191 def extend_share(self, *a, **kw):
192 return self.layout.extend_share(*a, **kw)
194 def shrink_share(self, *a, **kw):
195 return self.layout.shrink_share(*a, **kw)
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)
205class GlusterfsShareLayoutBase(metaclass=abc.ABCMeta):
206 """Base class for share layouts."""
208 def __init__(self, driver, *args, **kwargs):
209 self.driver = driver
210 self.configuration = kwargs.get('configuration')
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
223 @abc.abstractmethod
224 def _share_manager(self, share):
225 """Return GlusterManager object representing share's backend."""
227 @abc.abstractmethod
228 def do_setup(self, context):
229 """Any initialization the share driver does while starting."""
231 @abc.abstractmethod
232 def create_share(self, context, share, share_server=None):
233 """Is called to create share."""
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."""
240 @abc.abstractmethod
241 def create_snapshot(self, context, snapshot, share_server=None):
242 """Is called to create snapshot."""
244 @abc.abstractmethod
245 def delete_share(self, context, share, share_server=None):
246 """Is called to remove share."""
248 @abc.abstractmethod
249 def delete_snapshot(self, context, snapshot, share_server=None):
250 """Is called to remove snapshot."""
252 @abc.abstractmethod
253 def ensure_share(self, context, share, share_server=None):
254 """Invoked to ensure that share is exported."""
256 @abc.abstractmethod
257 def manage_existing(self, share, driver_options):
258 """Brings an existing share under Manila management."""
260 @abc.abstractmethod
261 def unmanage(self, share):
262 """Removes the specified share from Manila management."""
264 @abc.abstractmethod
265 def extend_share(self, share, new_size, share_server=None):
266 """Extends size of existing share."""
268 @abc.abstractmethod
269 def shrink_share(self, share, new_size, share_server=None):
270 """Shrinks size of existing share."""
272 def _update_share_stats(self):
273 raise NotImplementedError()