a74d4e43f2
Implement update_access for - glusterfs_native driver - glusterfs driver with volume layout - glusterfs driver with directory layout Co-Authored-By: Csaba Henk <chenk@redhat.com> Implements bp glusterfs-update-access Change-Id: I553bc8e23442102252163a29740967d4b64af460
275 lines
9.9 KiB
Python
275 lines
9.9 KiB
Python
# Copyright (c) 2015 Red Hat, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""GlusterFS share layouts.
|
|
|
|
A share layout encapsulates a particular way of mapping GlusterFS entities
|
|
to a share and utilizing them to back the share.
|
|
"""
|
|
|
|
import abc
|
|
import errno
|
|
|
|
from oslo_config import cfg
|
|
from oslo_utils import importutils
|
|
import six
|
|
|
|
from manila import exception
|
|
from manila.i18n import _
|
|
from manila.share import driver
|
|
from manila.share.drivers.ganesha import utils as ganesha_utils
|
|
|
|
glusterfs_share_layout_opts = [
|
|
cfg.StrOpt(
|
|
'glusterfs_share_layout',
|
|
help="Specifies GlusterFS share layout, that is, "
|
|
"the method of associating backing GlusterFS "
|
|
"resources to shares."),
|
|
]
|
|
|
|
CONF = cfg.CONF
|
|
CONF.register_opts(glusterfs_share_layout_opts)
|
|
|
|
|
|
class GlusterfsShareDriverBase(driver.ShareDriver):
|
|
|
|
LAYOUT_PREFIX = 'manila.share.drivers.glusterfs'
|
|
|
|
supported_layouts = ()
|
|
supported_protocols = ()
|
|
_supported_access_types = ()
|
|
_supported_access_levels = ()
|
|
|
|
GLUSTERFS_VERSION_MIN = (0, 0)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(GlusterfsShareDriverBase, self).__init__(*args, **kwargs)
|
|
self.configuration.append_config_values(
|
|
glusterfs_share_layout_opts)
|
|
layout_name = self.configuration.glusterfs_share_layout
|
|
if not layout_name:
|
|
layout_name = self.supported_layouts[0]
|
|
if layout_name not in self.supported_layouts:
|
|
raise exception.GlusterfsException(
|
|
_('driver %(driver)s does not support %(layout)s layout') %
|
|
{'driver': type(self).__name__, 'layout': layout_name})
|
|
|
|
self.layout = importutils.import_object(
|
|
'.'.join((self.LAYOUT_PREFIX, layout_name)),
|
|
self, **kwargs)
|
|
# we determine snapshot support in our own scope, as
|
|
# 1) the calculation based on parent method
|
|
# redefinition does not work for us, as actual
|
|
# glusterfs driver classes are subclassed from
|
|
# *this* class, not from driver.ShareDriver
|
|
# and they don't need to redefine snapshot
|
|
# methods for themselves;
|
|
# 2) snapshot support depends on choice of layout.
|
|
self._snapshots_are_supported = getattr(self.layout,
|
|
'_snapshots_are_supported',
|
|
False)
|
|
|
|
def _setup_via_manager(self, share_mgr, share_mgr_parent=None):
|
|
"""Callback for layout's `create_share` and `create_share_from_snapshot`
|
|
|
|
:param share_mgr: a {'share': <share>, 'manager': <gmgr>}
|
|
dict where <share> is the share created
|
|
in `create_share` or `create_share_from_snapshot`
|
|
and <gmgr> is a GlusterManager instance
|
|
representing the GlusterFS resource
|
|
allocated for it.
|
|
:param gluster_mgr_parent: a {'share': <share>, 'manager': <gmgr>}
|
|
dict where <share> is the original share of the snapshot
|
|
used in `create_share_from_snapshot` and <gmgr> is a
|
|
GlusterManager instance representing the GlusterFS
|
|
resource allocated for it.
|
|
:returns: export location for share_mgr['share'].
|
|
"""
|
|
|
|
@property
|
|
def supported_access_levels(self):
|
|
return self._supported_access_levels
|
|
|
|
@property
|
|
def supported_access_types(self):
|
|
return self._supported_access_types
|
|
|
|
def _access_rule_validator(self, abort):
|
|
|
|
def validator(rule):
|
|
return ganesha_utils.validate_access_rule(
|
|
self.supported_access_types, self.supported_access_levels,
|
|
rule, abort)
|
|
|
|
return validator
|
|
|
|
def update_access(self, context, share, access_rules, add_rules,
|
|
delete_rules, share_server=None):
|
|
"""Update access rules for given share.
|
|
|
|
Driver supports 2 different cases in this method:
|
|
1. Recovery after error - 'access_rules' contains all access_rules,
|
|
'add_rules' and 'delete_rules' are []. Driver should clear any
|
|
existent access rules and apply all access rules for given share.
|
|
This recovery is made at driver start up.
|
|
|
|
2. Adding/Deleting of several access rules - 'access_rules' contains
|
|
all access_rules, 'add_rules' and 'delete_rules' contain rules which
|
|
should be added/deleted. Driver can ignore rules in 'access_rules' and
|
|
apply only rules from 'add_rules' and 'delete_rules'.
|
|
"""
|
|
gluster_mgr = self.layout._share_manager(share)
|
|
|
|
access_rules, add_rules, delete_rules = (
|
|
list(filter(self._access_rule_validator(abort), rules)) for (
|
|
rules, abort) in ((access_rules, True),
|
|
(add_rules, True),
|
|
(delete_rules, False)))
|
|
|
|
# Recovery mode.
|
|
if not (add_rules or delete_rules):
|
|
ruleop, recovery = (access_rules, []), True
|
|
else:
|
|
ruleop, recovery = (add_rules, delete_rules), False
|
|
|
|
self._update_access_via_manager(gluster_mgr, context, share,
|
|
*ruleop, recovery=recovery)
|
|
|
|
def _update_access_via_manager(self, gluster_mgr, context, share,
|
|
add_rules, delete_rules, recovery=False,
|
|
share_server=None):
|
|
raise NotImplementedError()
|
|
|
|
def do_setup(self, *a, **kw):
|
|
return self.layout.do_setup(*a, **kw)
|
|
|
|
@classmethod
|
|
def _check_proto(cls, share):
|
|
proto = share['share_proto'].upper()
|
|
if proto not in cls.supported_protocols:
|
|
msg = _("Share protocol %s is not supported.") % proto
|
|
raise exception.ShareBackendException(msg=msg)
|
|
|
|
def create_share(self, context, share, *a, **kw):
|
|
self._check_proto(share)
|
|
return self.layout.create_share(context, share, *a, **kw)
|
|
|
|
def create_share_from_snapshot(self, context, share, *a, **kw):
|
|
self._check_proto(share)
|
|
return self.layout.create_share_from_snapshot(context, share, *a, **kw)
|
|
|
|
def create_snapshot(self, *a, **kw):
|
|
return self.layout.create_snapshot(*a, **kw)
|
|
|
|
def delete_share(self, *a, **kw):
|
|
return self.layout.delete_share(*a, **kw)
|
|
|
|
def delete_snapshot(self, *a, **kw):
|
|
return self.layout.delete_snapshot(*a, **kw)
|
|
|
|
def ensure_share(self, *a, **kw):
|
|
return self.layout.ensure_share(*a, **kw)
|
|
|
|
def manage_existing(self, *a, **kw):
|
|
return self.layout.manage_existing(*a, **kw)
|
|
|
|
def unmanage(self, *a, **kw):
|
|
return self.layout.unmanage(*a, **kw)
|
|
|
|
def extend_share(self, *a, **kw):
|
|
return self.layout.extend_share(*a, **kw)
|
|
|
|
def shrink_share(self, *a, **kw):
|
|
return self.layout.shrink_share(*a, **kw)
|
|
|
|
def _update_share_stats(self, data={}):
|
|
try:
|
|
data.update(self.layout._update_share_stats())
|
|
except NotImplementedError:
|
|
pass
|
|
super(GlusterfsShareDriverBase, self)._update_share_stats(data)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class GlusterfsShareLayoutBase(object):
|
|
"""Base class for share layouts."""
|
|
|
|
def __init__(self, driver, *args, **kwargs):
|
|
self.driver = driver
|
|
self.configuration = kwargs.get('configuration')
|
|
|
|
def _check_mount_glusterfs(self):
|
|
"""Checks if mount.glusterfs(8) is available."""
|
|
try:
|
|
self.driver._execute('mount.glusterfs', check_exit_code=False)
|
|
except OSError as exc:
|
|
if exc.errno == errno.ENOENT:
|
|
raise exception.GlusterfsException(
|
|
_('mount.glusterfs is not installed.'))
|
|
else:
|
|
raise
|
|
|
|
@abc.abstractmethod
|
|
def _share_manager(self, share):
|
|
"""Return GlusterManager object representing share's backend."""
|
|
|
|
@abc.abstractmethod
|
|
def do_setup(self, context):
|
|
"""Any initialization the share driver does while starting."""
|
|
|
|
@abc.abstractmethod
|
|
def create_share(self, context, share, share_server=None):
|
|
"""Is called to create share."""
|
|
|
|
@abc.abstractmethod
|
|
def create_share_from_snapshot(self, context, share, snapshot,
|
|
share_server=None):
|
|
"""Is called to create share from snapshot."""
|
|
|
|
@abc.abstractmethod
|
|
def create_snapshot(self, context, snapshot, share_server=None):
|
|
"""Is called to create snapshot."""
|
|
|
|
@abc.abstractmethod
|
|
def delete_share(self, context, share, share_server=None):
|
|
"""Is called to remove share."""
|
|
|
|
@abc.abstractmethod
|
|
def delete_snapshot(self, context, snapshot, share_server=None):
|
|
"""Is called to remove snapshot."""
|
|
|
|
@abc.abstractmethod
|
|
def ensure_share(self, context, share, share_server=None):
|
|
"""Invoked to ensure that share is exported."""
|
|
|
|
@abc.abstractmethod
|
|
def manage_existing(self, share, driver_options):
|
|
"""Brings an existing share under Manila management."""
|
|
|
|
@abc.abstractmethod
|
|
def unmanage(self, share):
|
|
"""Removes the specified share from Manila management."""
|
|
|
|
@abc.abstractmethod
|
|
def extend_share(self, share, new_size, share_server=None):
|
|
"""Extends size of existing share."""
|
|
|
|
@abc.abstractmethod
|
|
def shrink_share(self, share, new_size, share_server=None):
|
|
"""Shrinks size of existing share."""
|
|
|
|
def _update_share_stats(self):
|
|
raise NotImplementedError()
|