Add mountable snapshots support to HNAS driver
This patch adds Mountable Snapshots support in HNAS driver. Also, updates feature table in share_back_ends_feature_support_mapping.rst adding mountable snapshot column. Implements: bp hnas-mountable-snapshots Change-Id: I5e2294d218595a7ef28261333ab34d8bcde94ff1
This commit is contained in:
parent
dbd8f5dc95
commit
1f1932eb92
@ -30,61 +30,61 @@ Column value "-" means that this feature is not currently supported.
|
|||||||
Mapping of share drivers and share features support
|
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 |
|
| 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 | \- |
|
| ZFSonLinux | M | N | M | M | M | M | N | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Container | N | \- | N | \- | \- | \- | \- | \- |
|
| Container | N | \- | N | \- | \- | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Generic (Cinder as back-end) | J | K | L | L | J | J | M | \- |
|
| Generic (Cinder as back-end) | J | K | L | L | J | J | M | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| NetApp Clustered Data ONTAP | J | L | L | L | J | J | N | O |
|
| NetApp Clustered Data ONTAP | J | L | L | L | J | J | N | O | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| EMC VMAX | O | \- | O | \- | O | O | \- | \- |
|
| EMC VMAX | O | \- | O | \- | O | O | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| EMC VNX | J | \- | \- | \- | J | J | \- | \- |
|
| EMC VNX | J | \- | \- | \- | J | J | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| EMC Unity | N | \- | N | \- | N | N | \- | \- |
|
| EMC Unity | N | \- | N | \- | N | N | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| EMC Isilon | K | \- | M | \- | K | K | \- | \- |
|
| EMC Isilon | K | \- | M | \- | K | K | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Red Hat GlusterFS | J | \- | \- | \- | volume layout (L) | volume layout (L) | \- | \- |
|
| Red Hat GlusterFS | J | \- | \- | \- | volume layout (L) | volume layout (L) | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Red Hat GlusterFS-Native | J | \- | \- | \- | K | L | \- | \- |
|
| Red Hat GlusterFS-Native | J | \- | \- | \- | K | L | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| HDFS | K | \- | M | \- | K | K | \- | \- |
|
| HDFS | K | \- | M | \- | K | K | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Hitachi HNAS | L | L | L | M | L | L | O | O |
|
| Hitachi HNAS | L | L | L | M | L | L | O | O | O |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Hitachi HSP | N | N | N | N | \- | \- | \- | \- |
|
| Hitachi HSP | N | N | N | N | \- | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| HPE 3PAR | K | \- | \- | \- | K | K | \- | \- |
|
| HPE 3PAR | K | \- | \- | \- | K | K | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Huawei | K | L | L | L | K | M | \- | \- |
|
| Huawei | K | L | L | L | K | M | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| IBM GPFS | K | O | L | \- | K | K | \- | \- |
|
| IBM GPFS | K | O | L | \- | K | K | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| LVM | M | \- | M | \- | M | M | \- | O |
|
| LVM | M | \- | M | \- | M | M | \- | O | O |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Quobyte | K | \- | M | M | \- | \- | \- | \- |
|
| Quobyte | K | \- | M | M | \- | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Windows SMB | L | L | L | L | L | L | \- | \- |
|
| Windows SMB | L | L | L | L | L | L | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Oracle ZFSSA | K | N | M | M | K | K | \- | \- |
|
| Oracle ZFSSA | K | N | M | M | K | K | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| CephFS Native | M | \- | M | M | M | \- | \- | \- |
|
| CephFS Native | M | \- | M | M | M | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| Tegile | M | \- | M | M | M | M | \- | \- |
|
| Tegile | M | \- | M | M | M | M | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| NexentaStor4 | N | \- | N | \- | N | N | \- | \- |
|
| NexentaStor4 | N | \- | N | \- | N | N | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| NexentaStor5 | N | \- | N | N | N | N | \- | \- |
|
| NexentaStor5 | N | \- | N | N | N | N | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| MapRFS | O | O | O | O | O | O | O | \- |
|
| MapRFS | O | O | O | O | O | O | O | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
| QNAP | O | O | O | \- | O | O | O | \- |
|
| QNAP | O | O | O | \- | O | O | O | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+
|
||||||
|
|
||||||
Mapping of share drivers and share access rules support
|
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`
|
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 |
|
| 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 | \- |
|
| ZFSonLinux | \- | M | M | M | M | \- | \- | M | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Container | N | \- | \- | \- | \- | N | \- | \- | \- |
|
| Container | N | \- | \- | \- | \- | N | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- |
|
| Generic (Cinder as back-end) | J | K | \- | \- | \- | L | \- | J | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| NetApp Clustered Data ONTAP | J | K | M | M | M | L | \- | J | O |
|
| NetApp Clustered Data ONTAP | J | K | M | M | M | L | \- | J | O | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| EMC VMAX | O | \- | \- | \- | \- | O | \- | O | \- |
|
| EMC VMAX | O | \- | \- | \- | \- | O | \- | O | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- |
|
| EMC VNX | J | \- | \- | \- | \- | L | \- | J | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- |
|
| EMC Unity | N | \- | \- | \- | N | \- | \- | N | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- |
|
| EMC Isilon | \- | K | \- | \- | \- | L | \- | K | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Red Hat GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- |
|
| Red Hat GlusterFS | \- | J | \- | \- | \- | L | \- | volume layout (L) | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Red Hat GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- |
|
| Red Hat GlusterFS-Native | \- | J | \- | \- | \- | L | \- | L | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| HDFS | \- | K | \- | \- | \- | L | \- | K | \- |
|
| HDFS | \- | K | \- | \- | \- | L | \- | K | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O |
|
| Hitachi HNAS | \- | L | N | \- | L | \- | \- | L | O | O |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- |
|
| Hitachi HSP | \- | N | \- | \- | N | \- | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- |
|
| HPE 3PAR | L | K | L | \- | L | L | \- | K | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Huawei | M | K | L | L | L | L | M | M | \- |
|
| Huawei | M | K | L | L | L | L | M | M | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| LVM | \- | M | \- | \- | \- | M | \- | K | O |
|
| LVM | \- | M | \- | \- | \- | M | \- | K | O | O |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- |
|
| Quobyte | \- | K | \- | \- | \- | L | \- | M | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- |
|
| Windows SMB | L | L | \- | \- | \- | L | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- |
|
| IBM GPFS | \- | K | \- | \- | \- | L | \- | L | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- |
|
| Oracle ZFSSA | \- | K | \- | \- | \- | L | \- | K | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| CephFS Native | \- | M | \- | \- | \- | M | \- | \- | \- |
|
| CephFS Native | \- | M | \- | \- | \- | M | \- | \- | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| Tegile | \- | M | M | M | M | \- | \- | M | \- |
|
| Tegile | \- | M | M | M | M | \- | \- | M | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- |
|
| NexentaStor4 | \- | N | N | N | N | N | \- | N | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| NexentaStor5 | \- | N | N | N | N | N | \- | N | \- |
|
| NexentaStor5 | \- | N | N | N | N | N | \- | N | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- |
|
| MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
| QNAP | \- | O | \- | \- | O | \- | \- | O | \- |
|
| QNAP | \- | O | \- | \- | O | \- | \- | O | \- | \- |
|
||||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+
|
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -450,6 +450,10 @@ class ShareSnapshotAccessExists(InvalidInput):
|
|||||||
message = _("Share snapshot access %(access_type)s:%(access)s exists.")
|
message = _("Share snapshot access %(access_type)s:%(access)s exists.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSnapshotAccess(Invalid):
|
||||||
|
message = _("Invalid access rule: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class InvalidShareAccess(Invalid):
|
class InvalidShareAccess(Invalid):
|
||||||
message = _("Invalid access rule: %(reason)s")
|
message = _("Invalid access rule: %(reason)s")
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ from manila.common import constants
|
|||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _, _LE, _LI, _LW
|
from manila.i18n import _, _LE, _LI, _LW
|
||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
|
from manila.share import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
host_list.append(rule['access_to'] + '(' +
|
host_list.append(rule['access_to'] + '(' +
|
||||||
rule['access_level'] + ')')
|
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:
|
if host_list:
|
||||||
LOG.debug("Share %(share)s has the rules: %(rules)s",
|
LOG.debug("Share %(share)s has the rules: %(rules)s",
|
||||||
@ -223,14 +224,25 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
else:
|
else:
|
||||||
LOG.debug("Share %(share)s has no rules.", {'share': share['id']})
|
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:
|
for rule in add_rules:
|
||||||
if rule['access_type'].lower() != 'user':
|
if rule['access_type'].lower() != 'user':
|
||||||
msg = _("Only USER access type currently supported for CIFS. "
|
msg = _("Only USER access type currently supported for CIFS. "
|
||||||
"Share provided %(share)s with rule %(r_id)s type "
|
"%(entity_type)s provided %(share)s with "
|
||||||
"%(type)s allowing permission to %(to)s.") % {
|
"rule %(r_id)s type %(type)s allowing permission "
|
||||||
'share': share['id'], 'type': rule['access_type'],
|
"to %(to)s.") % {
|
||||||
'r_id': rule['id'], 'to': rule['access_to']}
|
'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)
|
raise exception.InvalidShareAccess(reason=msg)
|
||||||
|
|
||||||
if rule['access_level'] == constants.ACCESS_LEVEL_RW:
|
if rule['access_level'] == constants.ACCESS_LEVEL_RW:
|
||||||
@ -242,38 +254,54 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
|
|
||||||
formatted_user = rule['access_to'].replace('\\', '\\\\')
|
formatted_user = rule['access_to'].replace('\\', '\\\\')
|
||||||
|
|
||||||
self.hnas.cifs_allow_access(hnas_share_id, formatted_user,
|
self.hnas.cifs_allow_access(hnas_id, formatted_user,
|
||||||
permission)
|
permission, is_snapshot=is_snapshot)
|
||||||
|
|
||||||
LOG.debug("Added %(rule)s rule for user/group %(user)s to share "
|
LOG.debug("Added %(rule)s rule for user/group %(user)s "
|
||||||
"%(share)s.", {'rule': rule['access_level'],
|
"to %(entity_type)s %(share)s.",
|
||||||
|
{'rule': rule['access_level'],
|
||||||
'user': rule['access_to'],
|
'user': rule['access_to'],
|
||||||
'share': share['id']})
|
'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:
|
for rule in delete_rules:
|
||||||
if rule['access_type'].lower() != 'user':
|
if rule['access_type'].lower() != 'user':
|
||||||
LOG.warning(_LW('Only USER access type is allowed for '
|
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.'),
|
'protocol %(proto)s.'),
|
||||||
{'share': share['id'],
|
{'entity_type': entity_type.capitalize(),
|
||||||
'proto': share['share_proto']})
|
'share': share_or_snapshot['id'],
|
||||||
|
'proto': share_proto})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
formatted_user = rule['access_to'].replace('\\', '\\\\')
|
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 "
|
LOG.debug("Access denied for user/group %(user)s "
|
||||||
"%(share)s.", {'user': rule['access_to'],
|
"to %(entity_type)s %(share)s.",
|
||||||
'share': share['id']})
|
{'user': rule['access_to'],
|
||||||
|
'entity_type': entity_type,
|
||||||
|
'share': share_or_snapshot['id']})
|
||||||
|
|
||||||
def _clean_cifs_access_list(self, hnas_share_id):
|
def _clean_cifs_access_list(self, hnas_id, is_snapshot=False):
|
||||||
permission_list = self.hnas.list_cifs_permissions(hnas_share_id)
|
permission_list = self.hnas.list_cifs_permissions(hnas_id)
|
||||||
|
|
||||||
for permission in permission_list:
|
for permission in permission_list:
|
||||||
formatted_user = r'"\{1}{0}\{1}"'.format(permission[0], '"')
|
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):
|
def create_share(self, context, share, share_server=None):
|
||||||
"""Creates share.
|
"""Creates share.
|
||||||
@ -363,12 +391,15 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
{'snap_share_id': snapshot['share_id'],
|
{'snap_share_id': snapshot['share_id'],
|
||||||
'snap_id': snapshot['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."),
|
LOG.info(_LI("Snapshot %(id)s successfully created."),
|
||||||
{'id': snapshot['id']})
|
{'id': snapshot['id']})
|
||||||
|
|
||||||
return {'provider_location': os.path.join('/snapshots', hnas_share_id,
|
return {
|
||||||
snapshot['id'])}
|
'provider_location': os.path.join('/snapshots', hnas_share_id,
|
||||||
|
snapshot['id']),
|
||||||
|
'export_locations': export_locations,
|
||||||
|
}
|
||||||
|
|
||||||
def delete_snapshot(self, context, snapshot, share_server=None):
|
def delete_snapshot(self, context, snapshot, share_server=None):
|
||||||
"""Deletes snapshot.
|
"""Deletes snapshot.
|
||||||
@ -386,7 +417,8 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
{'snap_id': snapshot['id'],
|
{'snap_id': snapshot['id'],
|
||||||
'snap_share_id': snapshot['share_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."),
|
LOG.info(_LI("Snapshot %(id)s successfully deleted."),
|
||||||
{'id': snapshot['id']})
|
{'id': snapshot['id']})
|
||||||
@ -553,6 +585,7 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
'thin_provisioning': True,
|
'thin_provisioning': True,
|
||||||
'dedupe': dedupe,
|
'dedupe': dedupe,
|
||||||
'revert_to_snapshot_support': True,
|
'revert_to_snapshot_support': True,
|
||||||
|
'mount_snapshot_support': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info(_LI("HNAS Capabilities: %(data)s."),
|
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.",
|
LOG.debug("Share created with id %(shr)s, size %(size)sG.",
|
||||||
{'shr': share_id, 'size': share_size})
|
{'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:
|
try:
|
||||||
if share_proto.lower() == 'nfs':
|
if share_proto.lower() == 'nfs':
|
||||||
# Create NFS export
|
# 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.",
|
LOG.debug("NFS Export created to %(shr)s.",
|
||||||
{'shr': share_id})
|
{'shr': share_id})
|
||||||
else:
|
else:
|
||||||
# Create CIFS share with vvol path
|
# 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.",
|
LOG.debug("CIFS share created to %(shr)s.",
|
||||||
{'shr': share_id})
|
{'shr': share_id})
|
||||||
|
|
||||||
except exception.HNASBackendException as e:
|
except exception.HNASBackendException as e:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
|
if snapshot_id is None:
|
||||||
self.hnas.vvol_delete(share_id)
|
self.hnas.vvol_delete(share_id)
|
||||||
msg = six.text_type(e)
|
msg = six.text_type(e)
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
|
|
||||||
export_list = self._get_export_locations(share_proto, share_id)
|
|
||||||
return export_list
|
|
||||||
|
|
||||||
def _check_fs_mounted(self):
|
def _check_fs_mounted(self):
|
||||||
mounted = self.hnas.check_fs_mounted()
|
mounted = self.hnas.check_fs_mounted()
|
||||||
if not mounted:
|
if not mounted:
|
||||||
@ -926,15 +962,15 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
self._ensure_share(snapshot['share'], hnas_share_id)
|
self._ensure_share(snapshot['share'], hnas_share_id)
|
||||||
saved_list = []
|
saved_list = []
|
||||||
|
|
||||||
self._check_protocol(snapshot['share_id'],
|
share_proto = snapshot['share']['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)
|
saved_list = self.hnas.get_nfs_host_list(hnas_share_id)
|
||||||
new_list = []
|
new_list = []
|
||||||
for access in saved_list:
|
for access in saved_list:
|
||||||
new_list.append(access.replace('(rw)', '(ro)'))
|
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
|
else: # CIFS
|
||||||
if (self.hnas.is_cifs_in_use(hnas_share_id) and
|
if (self.hnas.is_cifs_in_use(hnas_share_id) and
|
||||||
not self.cifs_snapshot):
|
not self.cifs_snapshot):
|
||||||
@ -952,10 +988,18 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
"directory."))
|
"directory."))
|
||||||
self.hnas.create_directory(dest_path)
|
self.hnas.create_directory(dest_path)
|
||||||
finally:
|
finally:
|
||||||
if snapshot['share']['share_proto'].lower() == 'nfs':
|
if share_proto.lower() == 'nfs':
|
||||||
self.hnas.update_nfs_access_rule(hnas_share_id, saved_list)
|
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.
|
"""Deletes snapshot.
|
||||||
|
|
||||||
It receives the hnas_share_id only to join the path for 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()
|
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)
|
path = os.path.join('/snapshots', hnas_share_id, snapshot_id)
|
||||||
self.hnas.tree_delete(path)
|
self.hnas.tree_delete(path)
|
||||||
path = os.path.join('/snapshots', hnas_share_id)
|
path = os.path.join('/snapshots', hnas_share_id)
|
||||||
@ -1029,11 +1078,12 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
'proto': protocol}
|
'proto': protocol}
|
||||||
raise exception.ShareBackendException(msg=msg)
|
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 = []
|
export_list = []
|
||||||
for ip in (self.hnas_evs_ip, self.hnas_admin_network_ip):
|
for ip in (self.hnas_evs_ip, self.hnas_admin_network_ip):
|
||||||
if 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({
|
export_list.append({
|
||||||
"path": path,
|
"path": path,
|
||||||
"is_admin_only": ip == self.hnas_admin_network_ip,
|
"is_admin_only": ip == self.hnas_admin_network_ip,
|
||||||
@ -1041,12 +1091,30 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
})
|
})
|
||||||
return export_list
|
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':
|
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))
|
export = ':'.join((ip, path))
|
||||||
else:
|
else:
|
||||||
export = r'\\%s\%s' % (ip, hnas_share_id)
|
export = r'\\%s\%s' % (ip, hnas_id)
|
||||||
return export
|
return export
|
||||||
|
|
||||||
def manage_existing_snapshot(self, snapshot, driver_options):
|
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||||
@ -1111,7 +1179,11 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
{'snap_path': snapshot['provider_location'],
|
{'snap_path': snapshot['provider_location'],
|
||||||
'shr_id': snapshot['share_id'], 'snap_id': snapshot['id']})
|
'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):
|
def unmanage_snapshot(self, snapshot):
|
||||||
"""Unmanage a share snapshot
|
"""Unmanage a share snapshot
|
||||||
@ -1123,3 +1195,72 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
"However, it is not deleted and can be found in HNAS."),
|
"However, it is not deleted and can be found in HNAS."),
|
||||||
{'snap_id': snapshot['id'],
|
{'snap_id': snapshot['id'],
|
||||||
'share_id': snapshot['share_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)
|
||||||
|
@ -20,6 +20,7 @@ from oslo_utils import units
|
|||||||
import paramiko
|
import paramiko
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
@ -61,10 +62,15 @@ class HNASSSHBackend(object):
|
|||||||
available_space = fs.size - fs.used
|
available_space = fs.size - fs.used
|
||||||
return fs.size, available_space, fs.dedupe
|
return fs.size, available_space, fs.dedupe
|
||||||
|
|
||||||
def nfs_export_add(self, share_id):
|
def nfs_export_add(self, share_id, snapshot_id=None):
|
||||||
path = '/shares/' + share_id
|
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',
|
command = ['nfs-export', 'add', '-S', 'disable', '-c', '127.0.0.1',
|
||||||
path, self.fs_name, path]
|
name, self.fs_name, path]
|
||||||
try:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError:
|
except processutils.ProcessExecutionError:
|
||||||
@ -72,24 +78,36 @@ class HNASSSHBackend(object):
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.HNASBackendException(msg=msg)
|
raise exception.HNASBackendException(msg=msg)
|
||||||
|
|
||||||
def nfs_export_del(self, share_id):
|
def nfs_export_del(self, share_id=None, snapshot_id=None):
|
||||||
path = '/shares/' + share_id
|
if share_id is not None:
|
||||||
command = ['nfs-export', 'del', path]
|
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:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
if 'does not exist' in e.stderr:
|
if 'does not exist' in e.stderr:
|
||||||
LOG.warning(_LW("Export %s does not exist on "
|
LOG.warning(_LW("Export %s does not exist on "
|
||||||
"backend anymore."), path)
|
"backend anymore."), name)
|
||||||
else:
|
else:
|
||||||
msg = six.text_type(e)
|
msg = six.text_type(e)
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.HNASBackendException(msg=msg)
|
raise exception.HNASBackendException(msg=msg)
|
||||||
|
|
||||||
def cifs_share_add(self, 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
|
path = r'\\shares\\' + share_id
|
||||||
|
name = share_id
|
||||||
command = ['cifs-share', 'add', '-S', 'disable', '--enable-abe',
|
command = ['cifs-share', 'add', '-S', 'disable', '--enable-abe',
|
||||||
'--nodefaultsaa', share_id, self.fs_name, path]
|
'--nodefaultsaa', name, self.fs_name, path]
|
||||||
try:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError:
|
except processutils.ProcessExecutionError:
|
||||||
@ -97,15 +115,15 @@ class HNASSSHBackend(object):
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.HNASBackendException(msg=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,
|
command = ['cifs-share', 'del', '--target-label', self.fs_name,
|
||||||
share_id]
|
name]
|
||||||
try:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
if e.exit_code == 1:
|
if e.exit_code == 1:
|
||||||
LOG.warning(_LW("CIFS share %s does not exist on "
|
LOG.warning(_LW("CIFS share %s does not exist on "
|
||||||
"backend anymore."), share_id)
|
"backend anymore."), name)
|
||||||
else:
|
else:
|
||||||
msg = six.text_type(e)
|
msg = six.text_type(e)
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
@ -115,7 +133,16 @@ class HNASSSHBackend(object):
|
|||||||
export = self._get_share_export(share_id)
|
export = self._get_share_export(share_id)
|
||||||
return export[0].export_configuration
|
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']
|
command = ['nfs-export', 'mod', '-c']
|
||||||
|
|
||||||
if len(host_list) == 0:
|
if len(host_list) == 0:
|
||||||
@ -128,35 +155,47 @@ class HNASSSHBackend(object):
|
|||||||
string_command += '"'
|
string_command += '"'
|
||||||
command.append(string_command)
|
command.append(string_command)
|
||||||
|
|
||||||
path = '/shares/' + share_id
|
command.append(name)
|
||||||
command.append(path)
|
|
||||||
self._execute(command)
|
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,
|
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:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
if 'already listed as a user' in e.stderr:
|
if 'already listed as a user' in e.stderr:
|
||||||
LOG.debug('User %(user)s already allowed to access share '
|
LOG.debug('User %(user)s already allowed to access '
|
||||||
'%(share)s.', {'user': user, 'share': hnas_share_id})
|
'%(entity_type)s %(share)s.',
|
||||||
|
{'entity_type': entity_type, 'user': user,
|
||||||
|
'share': name})
|
||||||
else:
|
else:
|
||||||
msg = six.text_type(e)
|
msg = six.text_type(e)
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.InvalidShareAccess(reason=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,
|
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:
|
try:
|
||||||
self._execute(command)
|
self._execute(command)
|
||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
if ('not listed as a user' in e.stderr or
|
if ('not listed as a user' in e.stderr or
|
||||||
'Could not delete user/group' in e.stderr):
|
'Could not delete user/group' in e.stderr):
|
||||||
LOG.warning(_LW('User %(user)s already not allowed to access '
|
LOG.warning(_LW('User %(user)s already not allowed to access '
|
||||||
'share %(share)s.'),
|
'%(entity_type)s %(share)s.'),
|
||||||
{'user': user, 'share': hnas_share_id})
|
{'entity_type': entity_type, 'user': user,
|
||||||
|
'share': name})
|
||||||
else:
|
else:
|
||||||
msg = six.text_type(e)
|
msg = six.text_type(e)
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
@ -438,6 +477,7 @@ class HNASSSHBackend(object):
|
|||||||
|
|
||||||
def _get_share_export(self, share_id):
|
def _get_share_export(self, share_id):
|
||||||
share_id = '/shares/' + share_id
|
share_id = '/shares/' + share_id
|
||||||
|
|
||||||
command = ['nfs-export', 'list ', share_id]
|
command = ['nfs-export', 'list ', share_id]
|
||||||
export_list = []
|
export_list = []
|
||||||
try:
|
try:
|
||||||
@ -445,7 +485,7 @@ class HNASSSHBackend(object):
|
|||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
if 'does not exist' in e.stderr:
|
if 'does not exist' in e.stderr:
|
||||||
msg = _("Export %(share)s was not found in EVS "
|
msg = _("Export %(share)s was not found in EVS "
|
||||||
"%(evs_id)s") % {'share': share_id,
|
"%(evs_id)s.") % {'share': share_id,
|
||||||
'evs_id': self.evs_id}
|
'evs_id': self.evs_id}
|
||||||
raise exception.HNASItemNotFoundException(msg=msg)
|
raise exception.HNASItemNotFoundException(msg=msg)
|
||||||
else:
|
else:
|
||||||
|
@ -31,6 +31,7 @@ from manila import exception
|
|||||||
from manila.i18n import _, _LE, _LI, _LW
|
from manila.i18n import _, _LE, _LI, _LW
|
||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
from manila.share.drivers import generic
|
from manila.share.drivers import generic
|
||||||
|
from manila.share import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -428,27 +429,9 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||||||
:param share_server: None or Share server model
|
:param share_server: None or Share server model
|
||||||
"""
|
"""
|
||||||
helper = self._get_helper(snapshot['share'])
|
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)
|
access_rules, add_rules, delete_rules)
|
||||||
|
|
||||||
helper.update_access(self.share_server,
|
helper.update_access(self.share_server,
|
||||||
snapshot['name'], access_rules,
|
snapshot['name'], access_rules,
|
||||||
add_rules=add_rules, delete_rules=delete_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
|
|
||||||
|
@ -86,3 +86,21 @@ def get_active_replica(replica_list):
|
|||||||
for replica in replica_list:
|
for replica in replica_list:
|
||||||
if replica['replica_state'] == constants.REPLICA_STATE_ACTIVE:
|
if replica['replica_state'] == constants.REPLICA_STATE_ACTIVE:
|
||||||
return replica
|
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
|
||||||
|
@ -228,10 +228,9 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
self._driver.update_access('context', share_nfs, access_list, [], [])
|
self._driver.update_access('context', share_nfs, access_list, [], [])
|
||||||
|
|
||||||
ssh.HNASSSHBackend.update_nfs_access_rule.assert_called_once_with(
|
ssh.HNASSSHBackend.update_nfs_access_rule.assert_called_once_with(
|
||||||
share_nfs['id'], [access1['access_to'] + '('
|
[access1['access_to'] + '(' + access1['access_level'] +
|
||||||
+ access1['access_level'] + ',norootsquash)',
|
',norootsquash)', access2['access_to'] + '(' +
|
||||||
access2['access_to'] + '('
|
access2['access_level'] + ')', ], share_id=share_nfs['id'])
|
||||||
+ access2['access_level'] + ')'])
|
|
||||||
self.assertTrue(self.mock_log.debug.called)
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
def test_update_access_ip_exception(self):
|
def test_update_access_ip_exception(self):
|
||||||
@ -282,7 +281,7 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
access_list_allow, [])
|
access_list_allow, [])
|
||||||
|
|
||||||
ssh.HNASSSHBackend.cifs_allow_access.assert_called_once_with(
|
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)
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
def test_allow_access_cifs_invalid_type(self):
|
def test_allow_access_cifs_invalid_type(self):
|
||||||
@ -308,7 +307,7 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
access_list_deny)
|
access_list_deny)
|
||||||
|
|
||||||
ssh.HNASSSHBackend.cifs_deny_access.assert_called_once_with(
|
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)
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
def test_deny_access_cifs_unsupported_type(self):
|
def test_deny_access_cifs_unsupported_type(self):
|
||||||
@ -349,11 +348,16 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
share_cifs['id'])
|
share_cifs['id'])
|
||||||
self.assertTrue(self.mock_log.debug.called)
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
def _get_export(self, share, ip, is_admin_only):
|
def _get_export(self, id, share_proto, ip, is_admin_only,
|
||||||
if share['share_proto'].lower() == 'nfs':
|
is_snapshot=False):
|
||||||
export = ':'.join((ip, '/shares/' + share['id']))
|
if share_proto.lower() == 'nfs':
|
||||||
|
if is_snapshot:
|
||||||
|
path = '/snapshots/' + id
|
||||||
else:
|
else:
|
||||||
export = r'\\%s\%s' % (ip, share['id'])
|
path = '/shares/' + id
|
||||||
|
export = ':'.join((ip, path))
|
||||||
|
else:
|
||||||
|
export = r'\\%s\%s' % (ip, id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"path": export,
|
"path": export,
|
||||||
@ -379,17 +383,19 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
share['size'])
|
share['size'])
|
||||||
expected = [
|
expected = [
|
||||||
self._get_export(
|
self._get_export(
|
||||||
share, self._driver.hnas_evs_ip, False),
|
share['id'], share['share_proto'], self._driver.hnas_evs_ip,
|
||||||
|
False),
|
||||||
self._get_export(
|
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':
|
if share['share_proto'].lower() == 'nfs':
|
||||||
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(
|
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)
|
self.assertFalse(ssh.HNASSSHBackend.cifs_share_add.called)
|
||||||
else:
|
else:
|
||||||
ssh.HNASSSHBackend.cifs_share_add.assert_called_once_with(
|
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.assertFalse(ssh.HNASSSHBackend.nfs_export_add.called)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
@ -410,7 +416,7 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
ssh.HNASSSHBackend.quota_add.assert_called_once_with(share_nfs['id'],
|
ssh.HNASSSHBackend.quota_add.assert_called_once_with(share_nfs['id'],
|
||||||
share_nfs['size'])
|
share_nfs['size'])
|
||||||
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(
|
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'])
|
ssh.HNASSSHBackend.vvol_delete.assert_called_once_with(share_nfs['id'])
|
||||||
|
|
||||||
def test_create_share_invalid_share_protocol(self):
|
def test_create_share_invalid_share_protocol(self):
|
||||||
@ -447,8 +453,18 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
@ddt.data(snapshot_nfs, snapshot_cifs)
|
@ddt.data(snapshot_nfs, snapshot_cifs)
|
||||||
def test_create_snapshot(self, snapshot):
|
def test_create_snapshot(self, snapshot):
|
||||||
hnas_id = snapshot['share_id']
|
hnas_id = snapshot['share_id']
|
||||||
p_location = {'provider_location': '/snapshots/' + hnas_id + '/' +
|
export_locations = [
|
||||||
snapshot['id']}
|
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(
|
self.mock_object(ssh.HNASSSHBackend, "get_nfs_host_list", mock.Mock(
|
||||||
return_value=['172.24.44.200(rw)']))
|
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(
|
self.mock_object(ssh.HNASSSHBackend, "is_cifs_in_use", mock.Mock(
|
||||||
return_value=False))
|
return_value=False))
|
||||||
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock())
|
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)
|
out = self._driver.create_snapshot('context', snapshot)
|
||||||
|
|
||||||
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
|
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
|
||||||
'/shares/' + hnas_id, '/snapshots/' + hnas_id + '/' +
|
'/shares/' + hnas_id, '/snapshots/' + hnas_id + '/' +
|
||||||
snapshot['id'])
|
snapshot['id'])
|
||||||
self.assertEqual(p_location, out)
|
self.assertEqual(expected, out)
|
||||||
|
|
||||||
if snapshot['share']['share_proto'].lower() == 'nfs':
|
if snapshot['share']['share_proto'].lower() == 'nfs':
|
||||||
ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with(
|
ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with(
|
||||||
hnas_id)
|
hnas_id)
|
||||||
ssh.HNASSSHBackend.update_nfs_access_rule.assert_any_call(
|
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(
|
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:
|
else:
|
||||||
ssh.HNASSSHBackend.is_cifs_in_use.assert_called_once_with(
|
ssh.HNASSSHBackend.is_cifs_in_use.assert_called_once_with(
|
||||||
hnas_id)
|
hnas_id)
|
||||||
@ -506,39 +524,54 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock(
|
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock(
|
||||||
side_effect=exception.HNASNothingToCloneException('msg')))
|
side_effect=exception.HNASNothingToCloneException('msg')))
|
||||||
self.mock_object(ssh.HNASSSHBackend, "create_directory", mock.Mock())
|
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._driver.create_snapshot('context', snapshot_nfs)
|
||||||
|
|
||||||
self.assertTrue(self.mock_log.warning.called)
|
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(
|
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(
|
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(
|
ssh.HNASSSHBackend.create_directory.assert_called_once_with(
|
||||||
'/snapshots/' + hnas_id + '/' + snapshot_nfs['id'])
|
'/snapshots/' + hnas_id + '/' + snapshot_nfs['id'])
|
||||||
|
|
||||||
def test_delete_snapshot(self):
|
@ddt.data(snapshot_nfs, snapshot_cifs)
|
||||||
hnas_id = snapshot_nfs['share_id']
|
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(driver.HitachiHNASDriver, "_check_fs_mounted")
|
||||||
self.mock_object(ssh.HNASSSHBackend, "tree_delete", mock.Mock())
|
self.mock_object(ssh.HNASSSHBackend, "tree_delete")
|
||||||
self.mock_object(ssh.HNASSSHBackend, "delete_directory", mock.Mock())
|
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.debug.called)
|
||||||
self.assertTrue(self.mock_log.info.called)
|
self.assertTrue(self.mock_log.info.called)
|
||||||
driver.HitachiHNASDriver._check_fs_mounted.assert_called_once_with()
|
driver.HitachiHNASDriver._check_fs_mounted.assert_called_once_with()
|
||||||
ssh.HNASSSHBackend.tree_delete.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(
|
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):
|
def test_delete_managed_snapshot(self):
|
||||||
hnas_id = manage_snapshot['share_id']
|
hnas_id = manage_snapshot['share_id']
|
||||||
self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted")
|
self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted")
|
||||||
self.mock_object(ssh.HNASSSHBackend, "tree_delete")
|
self.mock_object(ssh.HNASSSHBackend, "tree_delete")
|
||||||
self.mock_object(ssh.HNASSSHBackend, "delete_directory")
|
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)
|
self._driver.delete_snapshot('context', manage_snapshot)
|
||||||
|
|
||||||
@ -559,9 +592,11 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
|
|
||||||
expected = [
|
expected = [
|
||||||
self._get_export(
|
self._get_export(
|
||||||
share, self._driver.hnas_evs_ip, False),
|
share['id'], share['share_proto'], self._driver.hnas_evs_ip,
|
||||||
|
False),
|
||||||
self._get_export(
|
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':
|
if share['share_proto'].lower() == 'nfs':
|
||||||
ssh.HNASSSHBackend.check_export.assert_called_once_with(
|
ssh.HNASSSHBackend.check_export.assert_called_once_with(
|
||||||
@ -625,9 +660,11 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
|
|
||||||
expected_exports = [
|
expected_exports = [
|
||||||
self._get_export(
|
self._get_export(
|
||||||
share, self._driver.hnas_evs_ip, False),
|
share['id'], share['share_proto'], self._driver.hnas_evs_ip,
|
||||||
|
False),
|
||||||
self._get_export(
|
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'],
|
expected_out = {'size': share['size'],
|
||||||
'export_locations': expected_exports}
|
'export_locations': expected_exports}
|
||||||
@ -724,9 +761,11 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
|
|
||||||
expected = [
|
expected = [
|
||||||
self._get_export(
|
self._get_export(
|
||||||
share, self._driver.hnas_evs_ip, False),
|
share['id'], share['share_proto'], self._driver.hnas_evs_ip,
|
||||||
|
False),
|
||||||
self._get_export(
|
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':
|
if share['share_proto'].lower() == 'nfs':
|
||||||
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(
|
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(
|
||||||
@ -752,9 +791,11 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
snapshot_nfs)
|
snapshot_nfs)
|
||||||
expected = [
|
expected = [
|
||||||
self._get_export(
|
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(
|
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.assertEqual(expected, result)
|
||||||
self.assertTrue(self.mock_log.warning.called)
|
self.assertTrue(self.mock_log.warning.called)
|
||||||
@ -837,6 +878,7 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
'thin_provisioning': True,
|
'thin_provisioning': True,
|
||||||
'dedupe': True,
|
'dedupe': True,
|
||||||
'revert_to_snapshot_support': True,
|
'revert_to_snapshot_support': True,
|
||||||
|
'mount_snapshot_support': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mock_object(ssh.HNASSSHBackend, 'get_stats', mock.Mock(
|
self.mock_object(ssh.HNASSSHBackend, 'get_stats', mock.Mock(
|
||||||
@ -933,3 +975,109 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
if exc:
|
if exc:
|
||||||
self.assertTrue(self.mock_log.warning.called)
|
self.assertTrue(self.mock_log.warning.called)
|
||||||
self.assertTrue(self.mock_log.info.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)
|
||||||
|
@ -539,13 +539,24 @@ class HNASSSHTestCase(test.TestCase):
|
|||||||
self.assertEqual(5120.0, free)
|
self.assertEqual(5120.0, free)
|
||||||
self.assertTrue(dedupe)
|
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',
|
fake_nfs_command = ['nfs-export', 'add', '-S', 'disable', '-c',
|
||||||
'127.0.0.1', '/shares/vvol_test', self.fs_name,
|
'127.0.0.1', name, self.fs_name,
|
||||||
'/shares/vvol_test']
|
path]
|
||||||
self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock())
|
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)
|
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._driver_ssh.nfs_export_add, 'vvol_test')
|
||||||
self.assertTrue(self.mock_log.exception.called)
|
self.assertTrue(self.mock_log.exception.called)
|
||||||
|
|
||||||
def test_nfs_export_del(self):
|
@ddt.data(True, False)
|
||||||
fake_nfs_command = ['nfs-export', 'del', '/shares/vvol_test']
|
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.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)
|
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)
|
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(
|
self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
|
||||||
side_effect=[putils.ProcessExecutionError(stderr='')]))
|
side_effect=[putils.ProcessExecutionError(stderr='')]))
|
||||||
|
|
||||||
@ -582,14 +605,26 @@ class HNASSSHTestCase(test.TestCase):
|
|||||||
self._driver_ssh.nfs_export_del, 'vvol_test')
|
self._driver_ssh.nfs_export_del, 'vvol_test')
|
||||||
self.assertTrue(self.mock_log.exception.called)
|
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',
|
fake_cifs_add_command = ['cifs-share', 'add', '-S', 'disable',
|
||||||
'--enable-abe', '--nodefaultsaa',
|
'--enable-abe', '--nodefaultsaa',
|
||||||
'vvol_test', self.fs_name,
|
name, self.fs_name,
|
||||||
r'\\shares\\vvol_test']
|
path]
|
||||||
self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock())
|
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)
|
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):
|
def test_update_nfs_access_rule_empty_host_list(self):
|
||||||
fake_export_command = ['nfs-export', 'mod', '-c', '127.0.0.1',
|
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.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)
|
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']
|
u'"127.0.0.1,127.0.0.2"', '/shares/fake_id']
|
||||||
self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock())
|
self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock())
|
||||||
|
|
||||||
self._driver_ssh.update_nfs_access_rule("fake_id", ['127.0.0.1',
|
self._driver_ssh.update_nfs_access_rule(['127.0.0.1', '127.0.0.2'],
|
||||||
'127.0.0.2'])
|
share_id="fake_id")
|
||||||
|
|
||||||
self._driver_ssh._execute.assert_called_with(fake_export_command)
|
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):
|
def test_cifs_allow_access(self):
|
||||||
fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label',
|
fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label',
|
||||||
self.fs_name, 'vvol_test',
|
self.fs_name, 'vvol_test',
|
||||||
@ -1163,15 +1203,20 @@ class HNASSSHTestCase(test.TestCase):
|
|||||||
self.assertEqual(1024, self._driver_ssh.get_share_usage("vvol_test"))
|
self.assertEqual(1024, self._driver_ssh.get_share_usage("vvol_test"))
|
||||||
|
|
||||||
def test__get_share_export(self):
|
def test__get_share_export(self):
|
||||||
self.mock_object(ssh.HNASSSHBackend, '_execute',
|
self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
|
||||||
mock.Mock(return_value=[HNAS_RESULT_export_ip, '']))
|
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_name)
|
||||||
self.assertEqual('/vvol_test', export_list[0].export_path)
|
self.assertEqual('/vvol_test', export_list[0].export_path)
|
||||||
self.assertEqual('fake_fs', export_list[0].file_system_label)
|
self.assertEqual('fake_fs', export_list[0].file_system_label)
|
||||||
self.assertEqual('Yes', export_list[0].mounted)
|
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):
|
def test__get_share_export_exception_not_found(self):
|
||||||
|
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user