manila/manila/share/drivers/glusterfs/layout.py

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, parent_share=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()