diff --git a/doc/source/devref/share_back_ends_feature_support_mapping.rst b/doc/source/devref/share_back_ends_feature_support_mapping.rst index 9bcc526119..a6292cd745 100644 --- a/doc/source/devref/share_back_ends_feature_support_mapping.rst +++ b/doc/source/devref/share_back_ends_feature_support_mapping.rst @@ -30,61 +30,61 @@ Column value "-" means that this feature is not currently supported. Mapping of share drivers and share features support --------------------------------------------------- -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Driver name | create delete share | manage unmanage share | extend share | shrink share | create delete snapshot | create share from snapshot | manage unmanage snapshot | revert to snapshot | -+========================================+=======================+=======================+==============+==============+========================+============================+==========================+====================+ -| ZFSonLinux | M | N | M | M | M | M | N | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Container | N | \- | N | \- | \- | \- | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Generic (Cinder as back-end) | J | K | L | L | J | J | M | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| NetApp Clustered Data ONTAP | J | L | L | L | J | J | N | O | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| EMC VMAX | O | \- | O | \- | O | O | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| EMC VNX | J | \- | \- | \- | J | J | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| EMC Unity | N | \- | N | \- | N | N | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| EMC Isilon | K | \- | M | \- | K | K | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Red Hat GlusterFS | J | \- | \- | \- | volume layout (L) | volume layout (L) | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Red Hat GlusterFS-Native | J | \- | \- | \- | K | L | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| HDFS | K | \- | M | \- | K | K | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Hitachi HNAS | L | L | L | M | L | L | O | O | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Hitachi HSP | N | N | N | N | \- | \- | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| HPE 3PAR | K | \- | \- | \- | K | K | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Huawei | K | L | L | L | K | M | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| IBM GPFS | K | O | L | \- | K | K | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| LVM | M | \- | M | \- | M | M | \- | O | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Quobyte | K | \- | M | M | \- | \- | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Windows SMB | L | L | L | L | L | L | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Oracle ZFSSA | K | N | M | M | K | K | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| CephFS Native | M | \- | M | M | M | \- | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| Tegile | M | \- | M | M | M | M | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| NexentaStor4 | N | \- | N | \- | N | N | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| NexentaStor5 | N | \- | N | N | N | N | \- | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| MapRFS | O | O | O | O | O | O | O | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ -| QNAP | O | O | O | \- | O | O | O | \- | -+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+ ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Driver name | create delete share | manage unmanage share | extend share | shrink share | create delete snapshot | create share from snapshot | manage unmanage snapshot | revert to snapshot | mountable snapshot | ++========================================+=======================+=======================+==============+==============+========================+============================+==========================+====================+====================+ +| ZFSonLinux | M | N | M | M | M | M | N | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Container | N | \- | N | \- | \- | \- | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Generic (Cinder as back-end) | J | K | L | L | J | J | M | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| NetApp Clustered Data ONTAP | J | L | L | L | J | J | N | O | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| EMC VMAX | O | \- | O | \- | O | O | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| EMC VNX | J | \- | \- | \- | J | J | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| EMC Unity | N | \- | N | \- | N | N | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| EMC Isilon | K | \- | M | \- | K | K | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Red Hat GlusterFS | J | \- | \- | \- | volume layout (L) | volume layout (L) | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Red Hat GlusterFS-Native | J | \- | \- | \- | K | L | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| HDFS | K | \- | M | \- | K | K | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Hitachi HNAS | L | L | L | M | L | L | O | O | O | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Hitachi HSP | N | N | N | N | \- | \- | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| HPE 3PAR | K | \- | \- | \- | K | K | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Huawei | K | L | L | L | K | M | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| IBM GPFS | K | O | L | \- | K | K | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| LVM | M | \- | M | \- | M | M | \- | O | O | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Quobyte | K | \- | M | M | \- | \- | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Windows SMB | L | L | L | L | L | L | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Oracle ZFSSA | K | N | M | M | K | K | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| CephFS Native | M | \- | M | M | M | \- | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| Tegile | M | \- | M | M | M | M | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| NexentaStor4 | N | \- | N | \- | N | N | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| NexentaStor5 | N | \- | N | N | N | N | \- | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| MapRFS | O | O | O | O | O | O | O | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ +| QNAP | O | O | O | \- | O | O | O | \- | \- | ++----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ Mapping of share drivers and share access rules support ------------------------------------------------------- @@ -211,61 +211,61 @@ Mapping of share drivers and common capabilities More information: :ref:`capabilities_and_extra_specs` -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Driver name | DHSS=True | DHSS=False | dedupe | compression | thin_provisioning | thick_provisioning | qos | create share from snapshot | revert to snapshot | -+========================================+===========+============+========+=============+===================+====================+=====+============================+====================+ -| ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Container | N | \- | \- | \- | \- | N | \- | \- | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| NetApp Clustered Data ONTAP | J | K | M | M | M | L | \- | J | O | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| EMC VMAX | O | \- | \- | \- | \- | O | \- | O | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Red Hat GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Red Hat GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Huawei | M | K | L | L | L | L | M | M | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| LVM | \- | M | \- | \- | \- | M | \- | K | O | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| CephFS Native | \- | M | \- | \- | \- | M | \- | \- | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| Tegile | \- | M | M | M | M | \- | \- | M | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| NexentaStor5 | \- | N | N | N | N | N | \- | N | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ -| QNAP | \- | O | \- | \- | O | \- | \- | O | \- | -+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+ ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Driver name | DHSS=True | DHSS=False | dedupe | compression | thin_provisioning | thick_provisioning | qos | create share from snapshot | revert to snapshot | mountable snapshot | ++========================================+===========+============+========+=============+===================+====================+=====+============================+====================+====================+ +| ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| NetApp Clustered Data ONTAP | J | K | M | M | M | L | \- | J | O | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| EMC VMAX | O | \- | \- | \- | \- | O | \- | O | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Red Hat GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Red Hat GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Huawei | M | K | L | L | L | L | M | M | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| LVM | \- | M | \- | \- | \- | M | \- | K | O | O | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| CephFS Native | \- | M | \- | \- | \- | M | \- | \- | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| Tegile | \- | M | M | M | M | \- | \- | M | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| NexentaStor5 | \- | N | N | N | N | N | \- | N | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ +| QNAP | \- | O | \- | \- | O | \- | \- | O | \- | \- | ++----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+ .. note:: diff --git a/manila/exception.py b/manila/exception.py index 76519fe647..f2959a3b2c 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -450,6 +450,10 @@ class ShareSnapshotAccessExists(InvalidInput): message = _("Share snapshot access %(access_type)s:%(access)s exists.") +class InvalidSnapshotAccess(Invalid): + message = _("Invalid access rule: %(reason)s") + + class InvalidShareAccess(Invalid): message = _("Invalid access rule: %(reason)s") diff --git a/manila/share/drivers/hitachi/hnas/driver.py b/manila/share/drivers/hitachi/hnas/driver.py index 7c2a19bf3d..3a98d86482 100644 --- a/manila/share/drivers/hitachi/hnas/driver.py +++ b/manila/share/drivers/hitachi/hnas/driver.py @@ -25,6 +25,7 @@ from manila.common import constants from manila import exception from manila.i18n import _, _LE, _LI, _LW from manila.share import driver +from manila.share import utils LOG = log.getLogger(__name__) @@ -215,7 +216,7 @@ class HitachiHNASDriver(driver.ShareDriver): host_list.append(rule['access_to'] + '(' + rule['access_level'] + ')') - self.hnas.update_nfs_access_rule(hnas_share_id, host_list) + self.hnas.update_nfs_access_rule(host_list, share_id=hnas_share_id) if host_list: LOG.debug("Share %(share)s has the rules: %(rules)s", @@ -223,14 +224,25 @@ class HitachiHNASDriver(driver.ShareDriver): else: LOG.debug("Share %(share)s has no rules.", {'share': share['id']}) - def _cifs_allow_access(self, share, hnas_share_id, add_rules): + def _cifs_allow_access(self, share_or_snapshot, hnas_id, add_rules, + is_snapshot=False): + + entity_type = "share" + if is_snapshot: + entity_type = "snapshot" + for rule in add_rules: if rule['access_type'].lower() != 'user': msg = _("Only USER access type currently supported for CIFS. " - "Share provided %(share)s with rule %(r_id)s type " - "%(type)s allowing permission to %(to)s.") % { - 'share': share['id'], 'type': rule['access_type'], - 'r_id': rule['id'], 'to': rule['access_to']} + "%(entity_type)s provided %(share)s with " + "rule %(r_id)s type %(type)s allowing permission " + "to %(to)s.") % { + 'entity_type': entity_type.capitalize(), + 'share': share_or_snapshot['id'], + 'type': rule['access_type'], + 'r_id': rule['id'], + 'to': rule['access_to'], + } raise exception.InvalidShareAccess(reason=msg) if rule['access_level'] == constants.ACCESS_LEVEL_RW: @@ -242,38 +254,54 @@ class HitachiHNASDriver(driver.ShareDriver): formatted_user = rule['access_to'].replace('\\', '\\\\') - self.hnas.cifs_allow_access(hnas_share_id, formatted_user, - permission) + self.hnas.cifs_allow_access(hnas_id, formatted_user, + permission, is_snapshot=is_snapshot) - LOG.debug("Added %(rule)s rule for user/group %(user)s to share " - "%(share)s.", {'rule': rule['access_level'], - 'user': rule['access_to'], - 'share': share['id']}) + LOG.debug("Added %(rule)s rule for user/group %(user)s " + "to %(entity_type)s %(share)s.", + {'rule': rule['access_level'], + 'user': rule['access_to'], + 'entity_type': entity_type, + 'share': share_or_snapshot['id']}) + + def _cifs_deny_access(self, share_or_snapshot, hnas_id, delete_rules, + is_snapshot=False): + if is_snapshot: + entity_type = "snapshot" + share_proto = share_or_snapshot['share']['share_proto'] + else: + entity_type = "share" + share_proto = share_or_snapshot['share_proto'] - def _cifs_deny_access(self, share, hnas_share_id, delete_rules): for rule in delete_rules: if rule['access_type'].lower() != 'user': LOG.warning(_LW('Only USER access type is allowed for ' - 'CIFS shares. Share provided %(share)s with ' + 'CIFS. %(entity_type)s ' + 'provided %(share)s with ' 'protocol %(proto)s.'), - {'share': share['id'], - 'proto': share['share_proto']}) + {'entity_type': entity_type.capitalize(), + 'share': share_or_snapshot['id'], + 'proto': share_proto}) continue formatted_user = rule['access_to'].replace('\\', '\\\\') - self.hnas.cifs_deny_access(hnas_share_id, formatted_user) + self.hnas.cifs_deny_access(hnas_id, formatted_user, + is_snapshot=is_snapshot) - LOG.debug("Access denied for user/group %(user)s to share " - "%(share)s.", {'user': rule['access_to'], - 'share': share['id']}) + LOG.debug("Access denied for user/group %(user)s " + "to %(entity_type)s %(share)s.", + {'user': rule['access_to'], + 'entity_type': entity_type, + 'share': share_or_snapshot['id']}) - def _clean_cifs_access_list(self, hnas_share_id): - permission_list = self.hnas.list_cifs_permissions(hnas_share_id) + def _clean_cifs_access_list(self, hnas_id, is_snapshot=False): + permission_list = self.hnas.list_cifs_permissions(hnas_id) for permission in permission_list: formatted_user = r'"\{1}{0}\{1}"'.format(permission[0], '"') - self.hnas.cifs_deny_access(hnas_share_id, formatted_user) + self.hnas.cifs_deny_access(hnas_id, formatted_user, + is_snapshot=is_snapshot) def create_share(self, context, share, share_server=None): """Creates share. @@ -363,12 +391,15 @@ class HitachiHNASDriver(driver.ShareDriver): {'snap_share_id': snapshot['share_id'], 'snap_id': snapshot['id']}) - self._create_snapshot(hnas_share_id, snapshot) + export_locations = self._create_snapshot(hnas_share_id, snapshot) LOG.info(_LI("Snapshot %(id)s successfully created."), {'id': snapshot['id']}) - return {'provider_location': os.path.join('/snapshots', hnas_share_id, - snapshot['id'])} + return { + 'provider_location': os.path.join('/snapshots', hnas_share_id, + snapshot['id']), + 'export_locations': export_locations, + } def delete_snapshot(self, context, snapshot, share_server=None): """Deletes snapshot. @@ -386,7 +417,8 @@ class HitachiHNASDriver(driver.ShareDriver): {'snap_id': snapshot['id'], 'snap_share_id': snapshot['share_id']}) - self._delete_snapshot(hnas_share_id, hnas_snapshot_id) + self._delete_snapshot(snapshot['share']['share_proto'], + hnas_share_id, hnas_snapshot_id) LOG.info(_LI("Snapshot %(id)s successfully deleted."), {'id': snapshot['id']}) @@ -553,6 +585,7 @@ class HitachiHNASDriver(driver.ShareDriver): 'thin_provisioning': True, 'dedupe': dedupe, 'revert_to_snapshot_support': True, + 'mount_snapshot_support': True, } LOG.info(_LI("HNAS Capabilities: %(data)s."), @@ -783,27 +816,30 @@ class HitachiHNASDriver(driver.ShareDriver): LOG.debug("Share created with id %(shr)s, size %(size)sG.", {'shr': share_id, 'size': share_size}) + self._create_export(share_id, share_proto) + + export_list = self._get_export_locations(share_proto, share_id) + return export_list + + def _create_export(self, share_id, share_proto, snapshot_id=None): try: if share_proto.lower() == 'nfs': # Create NFS export - self.hnas.nfs_export_add(share_id) + self.hnas.nfs_export_add(share_id, snapshot_id=snapshot_id) LOG.debug("NFS Export created to %(shr)s.", {'shr': share_id}) else: # Create CIFS share with vvol path - self.hnas.cifs_share_add(share_id) + self.hnas.cifs_share_add(share_id, snapshot_id=snapshot_id) LOG.debug("CIFS share created to %(shr)s.", {'shr': share_id}) - except exception.HNASBackendException as e: with excutils.save_and_reraise_exception(): - self.hnas.vvol_delete(share_id) + if snapshot_id is None: + self.hnas.vvol_delete(share_id) msg = six.text_type(e) LOG.exception(msg) - export_list = self._get_export_locations(share_proto, share_id) - return export_list - def _check_fs_mounted(self): mounted = self.hnas.check_fs_mounted() if not mounted: @@ -926,15 +962,15 @@ class HitachiHNASDriver(driver.ShareDriver): self._ensure_share(snapshot['share'], hnas_share_id) saved_list = [] - self._check_protocol(snapshot['share_id'], - snapshot['share']['share_proto']) + share_proto = snapshot['share']['share_proto'] + self._check_protocol(snapshot['share_id'], share_proto) - if snapshot['share']['share_proto'].lower() == 'nfs': + if share_proto.lower() == 'nfs': saved_list = self.hnas.get_nfs_host_list(hnas_share_id) new_list = [] for access in saved_list: new_list.append(access.replace('(rw)', '(ro)')) - self.hnas.update_nfs_access_rule(hnas_share_id, new_list) + self.hnas.update_nfs_access_rule(new_list, share_id=hnas_share_id) else: # CIFS if (self.hnas.is_cifs_in_use(hnas_share_id) and not self.cifs_snapshot): @@ -952,10 +988,18 @@ class HitachiHNASDriver(driver.ShareDriver): "directory.")) self.hnas.create_directory(dest_path) finally: - if snapshot['share']['share_proto'].lower() == 'nfs': - self.hnas.update_nfs_access_rule(hnas_share_id, saved_list) + if share_proto.lower() == 'nfs': + self.hnas.update_nfs_access_rule(saved_list, + share_id=hnas_share_id) - def _delete_snapshot(self, hnas_share_id, snapshot_id): + self._create_export(hnas_share_id, share_proto, + snapshot_id=snapshot['id']) + export_locations = self._get_export_locations(share_proto, + snapshot['id'], + is_snapshot=True) + return export_locations + + def _delete_snapshot(self, share_proto, hnas_share_id, snapshot_id): """Deletes snapshot. It receives the hnas_share_id only to join the path for snapshot. @@ -964,6 +1008,11 @@ class HitachiHNASDriver(driver.ShareDriver): """ self._check_fs_mounted() + if share_proto.lower() == 'nfs': + self.hnas.nfs_export_del(snapshot_id=snapshot_id) + elif share_proto.lower() == 'cifs': + self.hnas.cifs_share_del(snapshot_id) + path = os.path.join('/snapshots', hnas_share_id, snapshot_id) self.hnas.tree_delete(path) path = os.path.join('/snapshots', hnas_share_id) @@ -1029,11 +1078,12 @@ class HitachiHNASDriver(driver.ShareDriver): 'proto': protocol} raise exception.ShareBackendException(msg=msg) - def _get_export_locations(self, share_proto, hnas_share_id): + def _get_export_locations(self, share_proto, hnas_id, is_snapshot=False): export_list = [] for ip in (self.hnas_evs_ip, self.hnas_admin_network_ip): if ip: - path = self._get_export_path(ip, share_proto, hnas_share_id) + path = self._get_export_path(ip, share_proto, hnas_id, + is_snapshot) export_list.append({ "path": path, "is_admin_only": ip == self.hnas_admin_network_ip, @@ -1041,12 +1091,30 @@ class HitachiHNASDriver(driver.ShareDriver): }) return export_list - def _get_export_path(self, ip, share_proto, hnas_share_id): + def _get_export_path(self, ip, share_proto, hnas_id, is_snapshot): + """Gets and returns export path. + + :param ip: IP from HNAS EVS configured. + :param share_proto: Share or snapshot protocol (NFS or CIFS). + :param hnas_id: Entity ID in HNAS, it can be the ID from a share or + a snapshot. + :param is_snapshot: Boolean to determine if export is related to a + share or a snapshot. + :return: Complete export path, for example: + - In NFS: + SHARE: 172.24.44.10:/shares/id + SNAPSHOT: 172.24.44.10:/snapshots/id + - In CIFS: + SHARE and SNAPSHOT: \\172.24.44.10\id + """ if share_proto.lower() == 'nfs': - path = os.path.join('/shares', hnas_share_id) + if is_snapshot: + path = os.path.join('/snapshots', hnas_id) + else: + path = os.path.join('/shares', hnas_id) export = ':'.join((ip, path)) else: - export = r'\\%s\%s' % (ip, hnas_share_id) + export = r'\\%s\%s' % (ip, hnas_id) return export def manage_existing_snapshot(self, snapshot, driver_options): @@ -1111,7 +1179,11 @@ class HitachiHNASDriver(driver.ShareDriver): {'snap_path': snapshot['provider_location'], 'shr_id': snapshot['share_id'], 'snap_id': snapshot['id']}) - return {'size': snapshot_size} + export_locations = self._get_export_locations( + snapshot['share']['share_proto'], hnas_snapshot_id, + is_snapshot=True) + + return {'size': snapshot_size, 'export_locations': export_locations} def unmanage_snapshot(self, snapshot): """Unmanage a share snapshot @@ -1123,3 +1195,72 @@ class HitachiHNASDriver(driver.ShareDriver): "However, it is not deleted and can be found in HNAS."), {'snap_id': snapshot['id'], 'share_id': snapshot['share_id']}) + + def snapshot_update_access(self, context, snapshot, access_rules, + add_rules, delete_rules, share_server=None): + """Update access rules for given snapshot. + + Drivers should support 2 different cases in this method: + 1. Recovery after error - 'access_rules' contains all access rules, + 'add_rules' and 'delete_rules' shall be empty. Driver should clear any + existent access rules and apply all access rules for given snapshot. + 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'. All snapshots + rules should be read only. + + :param context: Current context + :param snapshot: Snapshot model with snapshot data. + :param access_rules: All access rules for given snapshot + :param add_rules: Empty List or List of access rules which should be + added. access_rules already contains these rules. + :param delete_rules: Empty List or List of access rules which should be + removed. access_rules doesn't contain these rules. + :param share_server: None or Share server model + """ + hnas_snapshot_id = self._get_hnas_snapshot_id(snapshot) + + self._check_protocol(snapshot['share']['id'], + snapshot['share']['share_proto']) + + access_rules, add_rules, delete_rules = utils.change_rules_to_readonly( + access_rules, add_rules, delete_rules) + + if snapshot['share']['share_proto'].lower() == 'nfs': + host_list = [] + + for rule in access_rules: + if rule['access_type'].lower() != 'ip': + msg = _("Only IP access type currently supported for NFS. " + "Snapshot provided %(snapshot)s with rule type " + "%(type)s.") % {'snapshot': snapshot['id'], + 'type': rule['access_type']} + raise exception.InvalidSnapshotAccess(reason=msg) + + host_list.append(rule['access_to'] + '(ro)') + + self.hnas.update_nfs_access_rule(host_list, + snapshot_id=hnas_snapshot_id) + + if host_list: + LOG.debug("Snapshot %(snapshot)s has the rules: %(rules)s", + {'snapshot': snapshot['id'], + 'rules': ', '.join(host_list)}) + else: + LOG.debug("Snapshot %(snapshot)s has no rules.", + {'snapshot': snapshot['id']}) + else: + if not (add_rules or delete_rules): + # cifs recovery mode + self._clean_cifs_access_list(hnas_snapshot_id, + is_snapshot=True) + self._cifs_allow_access(snapshot, hnas_snapshot_id, + access_rules, is_snapshot=True) + else: + self._cifs_deny_access(snapshot, hnas_snapshot_id, + delete_rules, is_snapshot=True) + self._cifs_allow_access(snapshot, hnas_snapshot_id, + add_rules, is_snapshot=True) diff --git a/manila/share/drivers/hitachi/hnas/ssh.py b/manila/share/drivers/hitachi/hnas/ssh.py index 8c92dfe730..b96ad345fa 100644 --- a/manila/share/drivers/hitachi/hnas/ssh.py +++ b/manila/share/drivers/hitachi/hnas/ssh.py @@ -20,6 +20,7 @@ from oslo_utils import units import paramiko import six +import os import time from manila import exception @@ -61,10 +62,15 @@ class HNASSSHBackend(object): available_space = fs.size - fs.used return fs.size, available_space, fs.dedupe - def nfs_export_add(self, share_id): - path = '/shares/' + share_id + def nfs_export_add(self, share_id, snapshot_id=None): + if snapshot_id is not None: + path = os.path.join('/snapshots', share_id, snapshot_id) + name = os.path.join('/snapshots', snapshot_id) + else: + path = name = os.path.join('/shares', share_id) + command = ['nfs-export', 'add', '-S', 'disable', '-c', '127.0.0.1', - path, self.fs_name, path] + name, self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError: @@ -72,24 +78,36 @@ class HNASSSHBackend(object): LOG.exception(msg) raise exception.HNASBackendException(msg=msg) - def nfs_export_del(self, share_id): - path = '/shares/' + share_id - command = ['nfs-export', 'del', path] + def nfs_export_del(self, share_id=None, snapshot_id=None): + if share_id is not None: + name = os.path.join('/shares', share_id) + elif snapshot_id is not None: + name = os.path.join('/snapshots', snapshot_id) + else: + msg = _("NFS export not specified to delete.") + raise exception.HNASBackendException(msg=msg) + + command = ['nfs-export', 'del', name] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'does not exist' in e.stderr: LOG.warning(_LW("Export %s does not exist on " - "backend anymore."), path) + "backend anymore."), name) else: msg = six.text_type(e) LOG.exception(msg) raise exception.HNASBackendException(msg=msg) - def cifs_share_add(self, share_id): - path = r'\\shares\\' + share_id + def cifs_share_add(self, share_id, snapshot_id=None): + if snapshot_id is not None: + path = r'\\snapshots\\' + share_id + r'\\' + snapshot_id + name = snapshot_id + else: + path = r'\\shares\\' + share_id + name = share_id command = ['cifs-share', 'add', '-S', 'disable', '--enable-abe', - '--nodefaultsaa', share_id, self.fs_name, path] + '--nodefaultsaa', name, self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError: @@ -97,15 +115,15 @@ class HNASSSHBackend(object): LOG.exception(msg) raise exception.HNASBackendException(msg=msg) - def cifs_share_del(self, share_id): + def cifs_share_del(self, name): command = ['cifs-share', 'del', '--target-label', self.fs_name, - share_id] + name] try: self._execute(command) except processutils.ProcessExecutionError as e: if e.exit_code == 1: LOG.warning(_LW("CIFS share %s does not exist on " - "backend anymore."), share_id) + "backend anymore."), name) else: msg = six.text_type(e) LOG.exception(msg) @@ -115,7 +133,16 @@ class HNASSSHBackend(object): export = self._get_share_export(share_id) return export[0].export_configuration - def update_nfs_access_rule(self, share_id, host_list): + def update_nfs_access_rule(self, host_list, share_id=None, + snapshot_id=None): + if share_id is not None: + name = os.path.join('/shares', share_id) + elif snapshot_id is not None: + name = os.path.join('/snapshots', snapshot_id) + else: + msg = _("No share/snapshot provided to update NFS rules.") + raise exception.HNASBackendException(msg=msg) + command = ['nfs-export', 'mod', '-c'] if len(host_list) == 0: @@ -128,35 +155,47 @@ class HNASSSHBackend(object): string_command += '"' command.append(string_command) - path = '/shares/' + share_id - command.append(path) + command.append(name) self._execute(command) - def cifs_allow_access(self, hnas_share_id, user, permission): + def cifs_allow_access(self, name, user, permission, is_snapshot=False): command = ['cifs-saa', 'add', '--target-label', self.fs_name, - hnas_share_id, user, permission] + name, user, permission] + + entity_type = "share" + if is_snapshot: + entity_type = "snapshot" + try: self._execute(command) except processutils.ProcessExecutionError as e: if 'already listed as a user' in e.stderr: - LOG.debug('User %(user)s already allowed to access share ' - '%(share)s.', {'user': user, 'share': hnas_share_id}) + LOG.debug('User %(user)s already allowed to access ' + '%(entity_type)s %(share)s.', + {'entity_type': entity_type, 'user': user, + 'share': name}) else: msg = six.text_type(e) LOG.exception(msg) raise exception.InvalidShareAccess(reason=msg) - def cifs_deny_access(self, hnas_share_id, user): + def cifs_deny_access(self, name, user, is_snapshot=False): command = ['cifs-saa', 'delete', '--target-label', self.fs_name, - hnas_share_id, user] + name, user] + + entity_type = "share" + if is_snapshot: + entity_type = "snapshot" + try: self._execute(command) except processutils.ProcessExecutionError as e: if ('not listed as a user' in e.stderr or 'Could not delete user/group' in e.stderr): LOG.warning(_LW('User %(user)s already not allowed to access ' - 'share %(share)s.'), - {'user': user, 'share': hnas_share_id}) + '%(entity_type)s %(share)s.'), + {'entity_type': entity_type, 'user': user, + 'share': name}) else: msg = six.text_type(e) LOG.exception(msg) @@ -438,6 +477,7 @@ class HNASSSHBackend(object): def _get_share_export(self, share_id): share_id = '/shares/' + share_id + command = ['nfs-export', 'list ', share_id] export_list = [] try: @@ -445,8 +485,8 @@ class HNASSSHBackend(object): except processutils.ProcessExecutionError as e: if 'does not exist' in e.stderr: msg = _("Export %(share)s was not found in EVS " - "%(evs_id)s") % {'share': share_id, - 'evs_id': self.evs_id} + "%(evs_id)s.") % {'share': share_id, + 'evs_id': self.evs_id} raise exception.HNASItemNotFoundException(msg=msg) else: raise diff --git a/manila/share/drivers/lvm.py b/manila/share/drivers/lvm.py index 0e450b27e2..f4be76c173 100644 --- a/manila/share/drivers/lvm.py +++ b/manila/share/drivers/lvm.py @@ -31,6 +31,7 @@ from manila import exception from manila.i18n import _, _LE, _LI, _LW from manila.share import driver from manila.share.drivers import generic +from manila.share import utils LOG = log.getLogger(__name__) @@ -428,27 +429,9 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver): :param share_server: None or Share server model """ helper = self._get_helper(snapshot['share']) - access_rules, add_rules, delete_rules = change_rules_to_readonly( + access_rules, add_rules, delete_rules = utils.change_rules_to_readonly( access_rules, add_rules, delete_rules) helper.update_access(self.share_server, snapshot['name'], access_rules, add_rules=add_rules, delete_rules=delete_rules) - - -def change_rules_to_readonly(access_rules, add_rules, delete_rules): - dict_access_rules = cast_access_object_to_dict_in_readonly(access_rules) - dict_add_rules = cast_access_object_to_dict_in_readonly(add_rules) - dict_delete_rules = cast_access_object_to_dict_in_readonly(delete_rules) - return dict_access_rules, dict_add_rules, dict_delete_rules - - -def cast_access_object_to_dict_in_readonly(rules): - dict_rules = [] - for rule in rules: - dict_rules.append({ - 'access_level': 'ro', - 'access_type': rule['access_type'], - 'access_to': rule['access_to'] - }) - return dict_rules diff --git a/manila/share/utils.py b/manila/share/utils.py index 7db1d63423..164bf3041d 100644 --- a/manila/share/utils.py +++ b/manila/share/utils.py @@ -86,3 +86,21 @@ def get_active_replica(replica_list): for replica in replica_list: if replica['replica_state'] == constants.REPLICA_STATE_ACTIVE: return replica + + +def change_rules_to_readonly(access_rules, add_rules, delete_rules): + dict_access_rules = cast_access_object_to_dict_in_readonly(access_rules) + dict_add_rules = cast_access_object_to_dict_in_readonly(add_rules) + dict_delete_rules = cast_access_object_to_dict_in_readonly(delete_rules) + return dict_access_rules, dict_add_rules, dict_delete_rules + + +def cast_access_object_to_dict_in_readonly(rules): + dict_rules = [] + for rule in rules: + dict_rules.append({ + 'access_level': constants.ACCESS_LEVEL_RO, + 'access_type': rule['access_type'], + 'access_to': rule['access_to'] + }) + return dict_rules diff --git a/manila/tests/share/drivers/hitachi/hnas/test_driver.py b/manila/tests/share/drivers/hitachi/hnas/test_driver.py index 491bf3863e..cd8eada84a 100644 --- a/manila/tests/share/drivers/hitachi/hnas/test_driver.py +++ b/manila/tests/share/drivers/hitachi/hnas/test_driver.py @@ -228,10 +228,9 @@ class HitachiHNASTestCase(test.TestCase): self._driver.update_access('context', share_nfs, access_list, [], []) ssh.HNASSSHBackend.update_nfs_access_rule.assert_called_once_with( - share_nfs['id'], [access1['access_to'] + '(' - + access1['access_level'] + ',norootsquash)', - access2['access_to'] + '(' - + access2['access_level'] + ')']) + [access1['access_to'] + '(' + access1['access_level'] + + ',norootsquash)', access2['access_to'] + '(' + + access2['access_level'] + ')', ], share_id=share_nfs['id']) self.assertTrue(self.mock_log.debug.called) def test_update_access_ip_exception(self): @@ -282,7 +281,7 @@ class HitachiHNASTestCase(test.TestCase): access_list_allow, []) ssh.HNASSSHBackend.cifs_allow_access.assert_called_once_with( - share_cifs['id'], 'fake_user', permission) + share_cifs['id'], 'fake_user', permission, is_snapshot=False) self.assertTrue(self.mock_log.debug.called) def test_allow_access_cifs_invalid_type(self): @@ -308,7 +307,7 @@ class HitachiHNASTestCase(test.TestCase): access_list_deny) ssh.HNASSSHBackend.cifs_deny_access.assert_called_once_with( - share_cifs['id'], 'fake_user') + share_cifs['id'], 'fake_user', is_snapshot=False) self.assertTrue(self.mock_log.debug.called) def test_deny_access_cifs_unsupported_type(self): @@ -349,11 +348,16 @@ class HitachiHNASTestCase(test.TestCase): share_cifs['id']) self.assertTrue(self.mock_log.debug.called) - def _get_export(self, share, ip, is_admin_only): - if share['share_proto'].lower() == 'nfs': - export = ':'.join((ip, '/shares/' + share['id'])) + def _get_export(self, id, share_proto, ip, is_admin_only, + is_snapshot=False): + if share_proto.lower() == 'nfs': + if is_snapshot: + path = '/snapshots/' + id + else: + path = '/shares/' + id + export = ':'.join((ip, path)) else: - export = r'\\%s\%s' % (ip, share['id']) + export = r'\\%s\%s' % (ip, id) return { "path": export, @@ -379,17 +383,19 @@ class HitachiHNASTestCase(test.TestCase): share['size']) expected = [ self._get_export( - share, self._driver.hnas_evs_ip, False), + share['id'], share['share_proto'], self._driver.hnas_evs_ip, + False), self._get_export( - share, self._driver.hnas_admin_network_ip, True)] + share['id'], share['share_proto'], + self._driver.hnas_admin_network_ip, True)] if share['share_proto'].lower() == 'nfs': ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with( - share_nfs['id']) + share_nfs['id'], snapshot_id=None) self.assertFalse(ssh.HNASSSHBackend.cifs_share_add.called) else: ssh.HNASSSHBackend.cifs_share_add.assert_called_once_with( - share_cifs['id']) + share_cifs['id'], snapshot_id=None) self.assertFalse(ssh.HNASSSHBackend.nfs_export_add.called) self.assertEqual(expected, result) @@ -410,7 +416,7 @@ class HitachiHNASTestCase(test.TestCase): ssh.HNASSSHBackend.quota_add.assert_called_once_with(share_nfs['id'], share_nfs['size']) ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with( - share_nfs['id']) + share_nfs['id'], snapshot_id=None) ssh.HNASSSHBackend.vvol_delete.assert_called_once_with(share_nfs['id']) def test_create_share_invalid_share_protocol(self): @@ -447,8 +453,18 @@ class HitachiHNASTestCase(test.TestCase): @ddt.data(snapshot_nfs, snapshot_cifs) def test_create_snapshot(self, snapshot): hnas_id = snapshot['share_id'] - p_location = {'provider_location': '/snapshots/' + hnas_id + '/' + - snapshot['id']} + export_locations = [ + self._get_export( + snapshot['id'], snapshot['share']['share_proto'], + self._driver.hnas_evs_ip, False, is_snapshot=True), + self._get_export( + snapshot['id'], snapshot['share']['share_proto'], + self._driver.hnas_admin_network_ip, True, is_snapshot=True)] + expected = { + 'provider_location': '/snapshots/' + hnas_id + '/' + + snapshot['id'], + 'export_locations': export_locations, + } self.mock_object(ssh.HNASSSHBackend, "get_nfs_host_list", mock.Mock( return_value=['172.24.44.200(rw)'])) @@ -457,21 +473,23 @@ class HitachiHNASTestCase(test.TestCase): self.mock_object(ssh.HNASSSHBackend, "is_cifs_in_use", mock.Mock( return_value=False)) self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock()) + self.mock_object(ssh.HNASSSHBackend, "nfs_export_add") + self.mock_object(ssh.HNASSSHBackend, "cifs_share_add") out = self._driver.create_snapshot('context', snapshot) ssh.HNASSSHBackend.tree_clone.assert_called_once_with( '/shares/' + hnas_id, '/snapshots/' + hnas_id + '/' + snapshot['id']) - self.assertEqual(p_location, out) + self.assertEqual(expected, out) if snapshot['share']['share_proto'].lower() == 'nfs': ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with( hnas_id) ssh.HNASSSHBackend.update_nfs_access_rule.assert_any_call( - hnas_id, ['172.24.44.200(ro)']) + ['172.24.44.200(ro)'], share_id=hnas_id) ssh.HNASSSHBackend.update_nfs_access_rule.assert_any_call( - hnas_id, ['172.24.44.200(rw)']) + ['172.24.44.200(rw)'], share_id=hnas_id) else: ssh.HNASSSHBackend.is_cifs_in_use.assert_called_once_with( hnas_id) @@ -506,39 +524,54 @@ class HitachiHNASTestCase(test.TestCase): self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock( side_effect=exception.HNASNothingToCloneException('msg'))) self.mock_object(ssh.HNASSSHBackend, "create_directory", mock.Mock()) + self.mock_object(ssh.HNASSSHBackend, "nfs_export_add") + self.mock_object(ssh.HNASSSHBackend, "cifs_share_add") self._driver.create_snapshot('context', snapshot_nfs) self.assertTrue(self.mock_log.warning.called) - ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with(hnas_id) + ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with( + hnas_id) ssh.HNASSSHBackend.update_nfs_access_rule.assert_any_call( - hnas_id, ['172.24.44.200(ro)']) + ['172.24.44.200(ro)'], share_id=hnas_id) ssh.HNASSSHBackend.update_nfs_access_rule.assert_any_call( - hnas_id, ['172.24.44.200(rw)']) + ['172.24.44.200(rw)'], share_id=hnas_id) ssh.HNASSSHBackend.create_directory.assert_called_once_with( '/snapshots/' + hnas_id + '/' + snapshot_nfs['id']) - def test_delete_snapshot(self): - hnas_id = snapshot_nfs['share_id'] + @ddt.data(snapshot_nfs, snapshot_cifs) + def test_delete_snapshot(self, snapshot): + hnas_share_id = snapshot['share_id'] + hnas_snapshot_id = snapshot['id'] self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted") - self.mock_object(ssh.HNASSSHBackend, "tree_delete", mock.Mock()) - self.mock_object(ssh.HNASSSHBackend, "delete_directory", mock.Mock()) + self.mock_object(ssh.HNASSSHBackend, "tree_delete") + self.mock_object(ssh.HNASSSHBackend, "delete_directory") + self.mock_object(ssh.HNASSSHBackend, "nfs_export_del") + self.mock_object(ssh.HNASSSHBackend, "cifs_share_del") - self._driver.delete_snapshot('context', snapshot_nfs) + self._driver.delete_snapshot('context', snapshot) self.assertTrue(self.mock_log.debug.called) self.assertTrue(self.mock_log.info.called) driver.HitachiHNASDriver._check_fs_mounted.assert_called_once_with() ssh.HNASSSHBackend.tree_delete.assert_called_once_with( - '/snapshots/' + hnas_id + '/' + snapshot_nfs['id']) + '/snapshots/' + hnas_share_id + '/' + snapshot['id']) ssh.HNASSSHBackend.delete_directory.assert_called_once_with( - '/snapshots/' + hnas_id) + '/snapshots/' + hnas_share_id) + if snapshot['share']['share_proto'].lower() == 'nfs': + ssh.HNASSSHBackend.nfs_export_del.assert_called_once_with( + snapshot_id=hnas_snapshot_id) + else: + ssh.HNASSSHBackend.cifs_share_del.assert_called_once_with( + hnas_snapshot_id) def test_delete_managed_snapshot(self): hnas_id = manage_snapshot['share_id'] self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted") self.mock_object(ssh.HNASSSHBackend, "tree_delete") self.mock_object(ssh.HNASSSHBackend, "delete_directory") + self.mock_object(ssh.HNASSSHBackend, "nfs_export_del") + self.mock_object(ssh.HNASSSHBackend, "cifs_share_del") self._driver.delete_snapshot('context', manage_snapshot) @@ -559,9 +592,11 @@ class HitachiHNASTestCase(test.TestCase): expected = [ self._get_export( - share, self._driver.hnas_evs_ip, False), + share['id'], share['share_proto'], self._driver.hnas_evs_ip, + False), self._get_export( - share, self._driver.hnas_admin_network_ip, True)] + share['id'], share['share_proto'], + self._driver.hnas_admin_network_ip, True)] if share['share_proto'].lower() == 'nfs': ssh.HNASSSHBackend.check_export.assert_called_once_with( @@ -625,9 +660,11 @@ class HitachiHNASTestCase(test.TestCase): expected_exports = [ self._get_export( - share, self._driver.hnas_evs_ip, False), + share['id'], share['share_proto'], self._driver.hnas_evs_ip, + False), self._get_export( - share, self._driver.hnas_admin_network_ip, True)] + share['id'], share['share_proto'], + self._driver.hnas_admin_network_ip, True)] expected_out = {'size': share['size'], 'export_locations': expected_exports} @@ -724,9 +761,11 @@ class HitachiHNASTestCase(test.TestCase): expected = [ self._get_export( - share, self._driver.hnas_evs_ip, False), + share['id'], share['share_proto'], self._driver.hnas_evs_ip, + False), self._get_export( - share, self._driver.hnas_admin_network_ip, True)] + share['id'], share['share_proto'], + self._driver.hnas_admin_network_ip, True)] if share['share_proto'].lower() == 'nfs': ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with( @@ -752,9 +791,11 @@ class HitachiHNASTestCase(test.TestCase): snapshot_nfs) expected = [ self._get_export( - share_nfs, self._driver.hnas_evs_ip, False), + share_nfs['id'], share_nfs['share_proto'], + self._driver.hnas_evs_ip, False), self._get_export( - share_nfs, self._driver.hnas_admin_network_ip, True)] + share_nfs['id'], share_nfs['share_proto'], + self._driver.hnas_admin_network_ip, True)] self.assertEqual(expected, result) self.assertTrue(self.mock_log.warning.called) @@ -837,6 +878,7 @@ class HitachiHNASTestCase(test.TestCase): 'thin_provisioning': True, 'dedupe': True, 'revert_to_snapshot_support': True, + 'mount_snapshot_support': True, } self.mock_object(ssh.HNASSSHBackend, 'get_stats', mock.Mock( @@ -933,3 +975,109 @@ class HitachiHNASTestCase(test.TestCase): if exc: self.assertTrue(self.mock_log.warning.called) self.assertTrue(self.mock_log.info.called) + + def test_nfs_snapshot_update_access_allow(self): + access1 = { + 'access_type': 'ip', + 'access_to': '172.24.10.10', + } + access2 = { + 'access_type': 'ip', + 'access_to': '172.31.20.20', + } + access_list = [access1, access2] + + self.mock_object(ssh.HNASSSHBackend, "update_nfs_access_rule") + + self._driver.snapshot_update_access('ctxt', snapshot_nfs, access_list, + access_list, []) + + ssh.HNASSSHBackend.update_nfs_access_rule.assert_called_once_with( + [access1['access_to'] + '(ro)', access2['access_to'] + '(ro)'], + snapshot_id=snapshot_nfs['id']) + self.assertTrue(self.mock_log.debug.called) + + def test_nfs_snapshot_update_access_deny(self): + access1 = { + 'access_type': 'ip', + 'access_to': '172.24.10.10', + } + + self.mock_object(ssh.HNASSSHBackend, "update_nfs_access_rule") + + self._driver.snapshot_update_access('ctxt', snapshot_nfs, [], + [], [access1]) + + ssh.HNASSSHBackend.update_nfs_access_rule.assert_called_once_with( + [], snapshot_id=snapshot_nfs['id']) + self.assertTrue(self.mock_log.debug.called) + + def test_nfs_snapshot_update_access_invalid_access_type(self): + access1 = { + 'access_type': 'user', + 'access_to': 'user1', + } + + self.assertRaises(exception.InvalidSnapshotAccess, + self._driver.snapshot_update_access, 'ctxt', + snapshot_nfs, [access1], [], []) + + def test_cifs_snapshot_update_access_allow(self): + access1 = { + 'access_type': 'user', + 'access_to': 'fake_user1', + } + + self.mock_object(ssh.HNASSSHBackend, 'cifs_allow_access') + + self._driver.snapshot_update_access('ctxt', snapshot_cifs, [access1], + [access1], []) + + ssh.HNASSSHBackend.cifs_allow_access.assert_called_with( + snapshot_cifs['id'], access1['access_to'], 'ar', is_snapshot=True) + self.assertTrue(self.mock_log.debug.called) + + def test_cifs_snapshot_update_access_deny(self): + access1 = { + 'access_type': 'user', + 'access_to': 'fake_user1', + } + + self.mock_object(ssh.HNASSSHBackend, 'cifs_deny_access') + + self._driver.snapshot_update_access('ctxt', snapshot_cifs, [], [], + [access1]) + + ssh.HNASSSHBackend.cifs_deny_access.assert_called_with( + snapshot_cifs['id'], access1['access_to'], is_snapshot=True) + self.assertTrue(self.mock_log.debug.called) + + def test_cifs_snapshot_update_access_recovery_mode(self): + access1 = { + 'access_type': 'user', + 'access_to': 'fake_user1', + } + access2 = { + 'access_type': 'user', + 'access_to': 'HDS\\fake_user2', + } + access_list = [access1, access2] + permission_list = [('fake_user1', 'ar'), ('HDS\\fake_user2', 'ar')] + formatted_user = r'"\{1}{0}\{1}"'.format(access2['access_to'], '"') + + self.mock_object(ssh.HNASSSHBackend, 'list_cifs_permissions', + mock.Mock(return_value=permission_list)) + self.mock_object(ssh.HNASSSHBackend, 'cifs_deny_access') + self.mock_object(ssh.HNASSSHBackend, 'cifs_allow_access') + + self._driver.snapshot_update_access('ctxt', snapshot_cifs, access_list, + [], []) + + ssh.HNASSSHBackend.list_cifs_permissions.assert_called_once_with( + snapshot_cifs['id']) + ssh.HNASSSHBackend.cifs_deny_access.assert_called_with( + snapshot_cifs['id'], formatted_user, is_snapshot=True) + ssh.HNASSSHBackend.cifs_allow_access.assert_called_with( + snapshot_cifs['id'], access2['access_to'].replace('\\', '\\\\'), + 'ar', is_snapshot=True) + self.assertTrue(self.mock_log.debug.called) diff --git a/manila/tests/share/drivers/hitachi/hnas/test_ssh.py b/manila/tests/share/drivers/hitachi/hnas/test_ssh.py index bcf26cf895..255bc419d7 100644 --- a/manila/tests/share/drivers/hitachi/hnas/test_ssh.py +++ b/manila/tests/share/drivers/hitachi/hnas/test_ssh.py @@ -539,13 +539,24 @@ class HNASSSHTestCase(test.TestCase): self.assertEqual(5120.0, free) self.assertTrue(dedupe) - def test_nfs_export_add(self): + @ddt.data(True, False) + def test_nfs_export_add(self, is_snapshot): + if is_snapshot: + name = '/snapshots/fake_snap' + path = '/snapshots/fake_share/fake_snap' + else: + name = path = '/shares/fake_share' + fake_nfs_command = ['nfs-export', 'add', '-S', 'disable', '-c', - '127.0.0.1', '/shares/vvol_test', self.fs_name, - '/shares/vvol_test'] + '127.0.0.1', name, self.fs_name, + path] self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock()) - self._driver_ssh.nfs_export_add('vvol_test') + if is_snapshot: + self._driver_ssh.nfs_export_add('fake_share', + snapshot_id='fake_snap') + else: + self._driver_ssh.nfs_export_add('fake_share') self._driver_ssh._execute.assert_called_with(fake_nfs_command) @@ -557,11 +568,19 @@ class HNASSSHTestCase(test.TestCase): self._driver_ssh.nfs_export_add, 'vvol_test') self.assertTrue(self.mock_log.exception.called) - def test_nfs_export_del(self): - fake_nfs_command = ['nfs-export', 'del', '/shares/vvol_test'] + @ddt.data(True, False) + def test_nfs_export_del(self, is_snapshot): + if is_snapshot: + name = '/snapshots/vvol_test' + args = {'snapshot_id': 'vvol_test'} + else: + name = '/shares/vvol_test' + args = {'share_id': 'vvol_test'} + + fake_nfs_command = ['nfs-export', 'del', name] self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock()) - self._driver_ssh.nfs_export_del('vvol_test') + self._driver_ssh.nfs_export_del(**args) self._driver_ssh._execute.assert_called_with(fake_nfs_command) @@ -574,7 +593,11 @@ class HNASSSHTestCase(test.TestCase): self.assertTrue(self.mock_log.warning.called) - def test_nfs_export_del_error(self): + def test_nfs_export_del_exception(self): + self.assertRaises(exception.HNASBackendException, + self._driver_ssh.nfs_export_del) + + def test_nfs_export_del_execute_error(self): self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( side_effect=[putils.ProcessExecutionError(stderr='')])) @@ -582,14 +605,26 @@ class HNASSSHTestCase(test.TestCase): self._driver_ssh.nfs_export_del, 'vvol_test') self.assertTrue(self.mock_log.exception.called) - def test_cifs_share_add(self): + @ddt.data(True, False) + def test_cifs_share_add(self, is_snapshot): + if is_snapshot: + name = 'fake_snap' + path = r'\\snapshots\\fake_share\\fake_snap' + else: + name = 'fake_share' + path = r'\\shares\\fake_share' + fake_cifs_add_command = ['cifs-share', 'add', '-S', 'disable', '--enable-abe', '--nodefaultsaa', - 'vvol_test', self.fs_name, - r'\\shares\\vvol_test'] + name, self.fs_name, + path] self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock()) - self._driver_ssh.cifs_share_add('vvol_test') + if is_snapshot: + self._driver_ssh.cifs_share_add('fake_share', + snapshot_id='fake_snap') + else: + self._driver_ssh.cifs_share_add('fake_share') self._driver_ssh._execute.assert_called_with(fake_cifs_add_command) @@ -642,10 +677,10 @@ class HNASSSHTestCase(test.TestCase): def test_update_nfs_access_rule_empty_host_list(self): fake_export_command = ['nfs-export', 'mod', '-c', '127.0.0.1', - '/shares/fake_id'] + '/snapshots/fake_id'] self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock()) - self._driver_ssh.update_nfs_access_rule("fake_id", []) + self._driver_ssh.update_nfs_access_rule([], snapshot_id="fake_id") self._driver_ssh._execute.assert_called_with(fake_export_command) @@ -654,11 +689,16 @@ class HNASSSHTestCase(test.TestCase): u'"127.0.0.1,127.0.0.2"', '/shares/fake_id'] self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock()) - self._driver_ssh.update_nfs_access_rule("fake_id", ['127.0.0.1', - '127.0.0.2']) + self._driver_ssh.update_nfs_access_rule(['127.0.0.1', '127.0.0.2'], + share_id="fake_id") self._driver_ssh._execute.assert_called_with(fake_export_command) + def test_update_nfs_access_rule_exception(self): + self.assertRaises(exception.HNASBackendException, + self._driver_ssh.update_nfs_access_rule, + ['127.0.0.1']) + def test_cifs_allow_access(self): fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label', self.fs_name, 'vvol_test', @@ -1163,15 +1203,20 @@ class HNASSSHTestCase(test.TestCase): self.assertEqual(1024, self._driver_ssh.get_share_usage("vvol_test")) def test__get_share_export(self): - self.mock_object(ssh.HNASSSHBackend, '_execute', - mock.Mock(return_value=[HNAS_RESULT_export_ip, ''])) + self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( + return_value=[HNAS_RESULT_export_ip, ''])) - export_list = self._driver_ssh._get_share_export('fake_id') + export_list = self._driver_ssh._get_share_export(share_id='share_id') + path = '/shares/share_id' + command = ['nfs-export', 'list ', path] + + self._driver_ssh._execute.assert_called_with(command) self.assertEqual('vvol_test', export_list[0].export_name) self.assertEqual('/vvol_test', export_list[0].export_path) self.assertEqual('fake_fs', export_list[0].file_system_label) self.assertEqual('Yes', export_list[0].mounted) + self.assertIn('rw', export_list[0].export_configuration[0]) def test__get_share_export_exception_not_found(self): diff --git a/releasenotes/notes/hnas-mountable-snapshots-4fbffa05656112c4.yaml b/releasenotes/notes/hnas-mountable-snapshots-4fbffa05656112c4.yaml new file mode 100644 index 0000000000..0a77afcded --- /dev/null +++ b/releasenotes/notes/hnas-mountable-snapshots-4fbffa05656112c4.yaml @@ -0,0 +1,9 @@ +--- +features: + - Added Mountable Snapshots support to HNAS driver. +upgrade: + - If using existing share types with the HNAS back end, + set the 'mount_snapshot_support' extra-spec to allow + creating shares that support mountable snapshots. + This modification will not affect existing shares of + such types.