From 252a7e811807903b3011081859ea0f9ab42cfe8e Mon Sep 17 00:00:00 2001 From: dingd Date: Sat, 12 Oct 2019 11:29:28 +0800 Subject: [PATCH] [Unity] Manage/unmanage share server/share/snap 1. Try parse share_backend_id from export path in case it is managed share. 2. Try to take snapshot id from 'provider_location' in case this is managed snapshot 3. Try to get share server name from property 'identifier' first in case this is managed share server 4. Add property 'provider_location' when creating snapshot Implements: blueprint emc-unity-manage-unmange Change-Id: I676475d0500ecce31a1a863926accec8585ac83a Signed-off-by: Goutham Pacha Ravi --- ...hare_back_ends_feature_support_mapping.rst | 2 +- .../drivers/dell-emc-unity-driver.rst | 165 +++++++++++++--- manila/share/drivers/dell_emc/driver.py | 105 +++++++++- .../dell_emc/plugins/unity/connection.py | 185 ++++++++++++++++-- .../drivers/dell_emc/plugins/unity/utils.py | 37 ++++ .../dell_emc/plugins/unity/mocked_manila.yaml | 43 +++- .../dell_emc/plugins/unity/mocked_unity.yaml | 86 ++++++++ .../dell_emc/plugins/unity/test_connection.py | 144 +++++++++++++- .../dell_emc/plugins/unity/test_utils.py | 49 +++++ .../share/drivers/dell_emc/test_driver.py | 56 ++++++ ...are-snapshot-support-6a0bbbed74da13c7.yaml | 4 + 11 files changed, 832 insertions(+), 44 deletions(-) create mode 100644 releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml diff --git a/doc/source/admin/share_back_ends_feature_support_mapping.rst b/doc/source/admin/share_back_ends_feature_support_mapping.rst index c45342156b..a2078b2b95 100644 --- a/doc/source/admin/share_back_ends_feature_support_mapping.rst +++ b/doc/source/admin/share_back_ends_feature_support_mapping.rst @@ -45,7 +45,7 @@ Mapping of share drivers and share features support +----------------------------------------+-----------------------+-----------------------+--------------------------+--------------------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ | EMC VNX | J | \- | \- | \- | J | J | \- | \- | \- | +----------------------------------------+-----------------------+-----------------------+--------------------------+--------------------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ -| EMC Unity | N | \- | N | S | N | N | \- | S | \- | +| EMC Unity | N | U | N | S | N | N | U | S | \- | +----------------------------------------+-----------------------+-----------------------+--------------------------+--------------------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ | EMC Isilon | K | \- | M | \- | K | K | \- | \- | \- | +----------------------------------------+-----------------------+-----------------------+--------------------------+--------------------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+ diff --git a/doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst b/doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst index 85e6b46d16..f2950cfa73 100644 --- a/doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst +++ b/doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst @@ -44,6 +44,9 @@ Storage Systems. * Create/Delete snapshot of a share. * Create a new share from snapshot. * Revert a share to a snapshot. +* Manage/Unmanage a share server. +* Manage/Unmanage a share. +* Manage/Unmanage a snapshot. Supported Network Topologies @@ -82,7 +85,7 @@ You may need root privilege to install python libraries. .. code-block:: console - pip install storops + $ pip install storops On Unity System @@ -189,8 +192,8 @@ for the Unity driver. One of NAS server names in Unity, it is used for share creation when the driver is in `DHSS=False` mode. -Restart of :term:`manila-share` service is needed for the configuration changes to take -effect. +Restart of :term:`manila-share` service is needed for the configuration +changes to take effect. Supported MTU size ------------------ @@ -208,11 +211,12 @@ of share network during share server creation. IPv6 support ------------ -IPv6 support for Unity driver is introduced in Queens release. The feature is divided -into two parts: +IPv6 support for Unity driver is introduced in Queens release. The feature +is divided into two parts: #. The driver is able to manage share or snapshot in the Neutron IPv6 network. -#. The driver is able to connect Unity management interface using its IPv6 address. +#. The driver is able to connect Unity management interface using its IPv6 + address. Pre-Configurations for IPv6 support ----------------------------------- @@ -224,9 +228,10 @@ for the Unity driver: - `network_plugin_ipv6_enabled` indicates IPv6 is enabled. -If you want to connect Unity using IPv6 address, you should configure IPv6 address -by `/net/if/mgmt` uemcli command, `mgmtInterfaceSettings` RESTful api or the system -settings of Unity GUI for Unity and specify the address in `/etc/manila/manila.conf`: +If you want to connect Unity using IPv6 address, you should configure IPv6 +address by `/net/if/mgmt` uemcli command, `mgmtInterfaceSettings` RESTful api +or the system settings of Unity GUI for Unity and specify the address in +`/etc/manila/manila.conf`: emc_nas_server = @@ -236,37 +241,43 @@ Supported share creation in mode that driver does not create and destroy share s To create a file share in this mode, you need to: #. Create NAS server with network interface in Unity system. -#. Set 'driver_handles_share_servers=False' and 'unity_share_server' in ``/etc/manila/manila.conf``: +#. Set 'driver_handles_share_servers=False' and 'unity_share_server' in + ``/etc/manila/manila.conf``: .. code-block:: ini driver_handles_share_servers = False unity_share_server = -#. Specify the share type with driver_handles_share_servers = False extra specification: +#. Specify the share type with driver_handles_share_servers = False extra + specification: .. code-block:: console - $ manila type-create False + $ manila type-create ${share_type_name} False #. Create share. .. code-block:: console - $ manila create --name --share-type + $ manila create ${share_protocol} ${size} --name ${share_name} --share-type ${share_type_name} .. note:: - Do not specify the share network in share creation command because no share servers will be created. + Do not specify the share network in share creation command because + no share servers will be created. Driver will use the unity_share_server specified for share creation. Snapshot support ---------------- -In the Mitaka and Newton release of OpenStack, Snapshot support is enabled by default for a newly created share type. -Starting with the Ocata release, the snapshot_support extra spec must be set to True in order to allow snapshots for -a share type. If the 'snapshot_support' extra_spec is omitted or if it is set to False, users would not be able to -create snapshots on shares of this share type. The feature is divided into two parts: +In the Mitaka and Newton release of OpenStack, Snapshot support is enabled by +default for a newly created share type. +Starting with the Ocata release, the snapshot_support extra spec must be set +to True in order to allow snapshots for a share type. If the 'snapshot_support' +extra_spec is omitted or if it is set to False, users would not be able to +create snapshots on shares of this share type. The feature is divided into +two parts: 1. The driver is able to create/delete snapshot of share. 2. The driver is able to create share from snapshot. @@ -279,35 +290,135 @@ The following extra specifications need to be configured with share type. - snapshot_support = True - create_share_from_snapshot_support = True -For new share type, these extra specifications can be set directly when creating share type: +For new share type, these extra specifications can be set directly when +creating share type: .. code-block:: console - manila type-create --snapshot_support True --create_share_from_snapshot_support True ${share_type_name} True + $ manila type-create --snapshot_support True --create_share_from_snapshot_support True ${share_type_name} True Or you can update already existing share type with command: .. code-block:: console - manila type-key ${share_type_name} set snapshot_support=True - manila type-key ${share_type_name} set create_share_from_snapshot_support=True + $ manila type-key ${share_type_name} set snapshot_support=True + $ manila type-key ${share_type_name} set create_share_from_snapshot_support=True To snapshot a share and create share from the snapshot ------------------------------------------------------ -Firstly, you need create a share from share type that has extra specifications(snapshot_support=True, create_share_from_snapshot_support=True). +Firstly, you need create a share from share type that has extra specifications +(snapshot_support=True, create_share_from_snapshot_support=True). Then snapshot the share with command: .. code-block:: console - manila snapshot-create ${source_share_name} --name ${target_snapshot_name} --description " " + $ manila snapshot-create ${source_share_name} --name ${target_snapshot_name} --description " " -After creating the snapshot from previous step, you can create share from that snapshot. -Use command: +After creating the snapshot from previous step, you can create share from that +snapshot. Use command: .. code-block:: console - manila create nfs 1 --name ${target_share_name} --metadata source=snapshot --description " " --snapshot-id ${source_snapshot_id} + $ manila create nfs 1 --name ${target_share_name} --metadata source=snapshot --description " " --snapshot-id ${source_snapshot_id} + +To manage an existing share server +---------------------------------- + +To manage a share server existing in Unity System, you need to: + +#. Create network, subnet, port (ip address of nas server in Unity system) and + share network in OpenStack. + + .. code-block:: console + + $ openstack network create ${network_name} --provider-network-type ${network_type} + $ openstack subnet create ${subnet_name} --network ${network_name} --subnet-range ${subnet_range} + $ openstack port create --network ${network_name} --fixed-ip subnet=${subnet_name},ip-address=${ip address} \ + ${port_name} --device-owner=manila:share + $ manila share-network-create --name ${share_network_name} --neutron-net-id ${network_name} \ + --neutron-subnet-id ${subnet_name} + +#. Manage the share server in OpenStack: + + .. code-block:: console + + $ manila share-server-manage ${host} ${share_network_name} ${identifier} + + .. note:: + + '${identifier}' is the nas server name in Unity system. + +To un-manage a Manila share server +---------------------------------- +To unmanage a share server existing in OpenStack: + + .. code-block:: console + + $ manila share-server-unmanage ${share_server_id} + +To manage an existing share +--------------------------- + +To manage a share existing in Unity System: + +- In DHSS=True mode + + Need make sure the related share server is existing in OpenStack, otherwise + need to manage share server first (check the step of 'Supported Manage share + server'). + + .. code-block:: console + + $ manila manage ${service_host} ${protocol} '${export_path}' --name ${share_name} --driver_options size=${share_size} \ + --share_type ${share_type} --share_server_id ${share_server_id} + + .. note:: + + '${share_server_id}' is the id of share server in OpenStack. + '${share_type}' should have the property 'driver_handles_share_servers=True'. + +- In DHSS=False mode + + .. code-block:: console + + $ manila manage ${service_host} ${protocol} '${export_path}' --name ${share_name} --driver_options size=${share_size} \ + --share_type ${share_type} + + .. note:: + + '${share_type}' should have the property 'driver_handles_share_servers=False'. + +To un-manage a Manila share +--------------------------- +To unmanage a share existing in OpenStack: + + .. code-block:: console + + $ manila unmanage ${share_id} + +To manage an existing share snapshot +------------------------------------ +To manage a snapshot existing in Unity System, you need make sure the related +share instance is existing in OpenStack, otherwise need to manage share first +(check the step of 'Supported Manage share'). + + .. code-block:: console + + $ manila snapshot-manage --name ${name} ${share_name} ${provider_location} --driver_options size=${snapshot_size} + + .. note:: + + '${provider_location}' is the snapshot name in Unity system. + '${share_name}' is the share name or id in OpenStack. + +To un-manage a Manila share snapshot +------------------------------------ +To unmanage a snapshot existing in OpenStack: + + .. code-block:: console + + $ manila snapshot-unmanage ${snapshot_id} Supported security services --------------------------- diff --git a/manila/share/drivers/dell_emc/driver.py b/manila/share/drivers/dell_emc/driver.py index 5867d0153f..158c8db359 100644 --- a/manila/share/drivers/dell_emc/driver.py +++ b/manila/share/drivers/dell_emc/driver.py @@ -100,6 +100,109 @@ class EMCShareDriver(driver.ShareDriver): else: self.shrink_share_support = False + if hasattr(self.plugin, 'manage_existing_support'): + self.manage_existing_support = self.plugin.manage_existing_support + else: + self.manage_existing_support = False + + if hasattr(self.plugin, 'manage_existing_with_server_support'): + self.manage_existing_with_server_support = ( + self.plugin.manage_existing_with_server_support) + else: + self.manage_existing_with_server_support = False + + if hasattr(self.plugin, 'manage_existing_snapshot_support'): + self.manage_existing_snapshot_support = ( + self.plugin.manage_existing_snapshot_support) + else: + self.manage_existing_snapshot_support = False + + if hasattr(self.plugin, 'manage_snapshot_with_server_support'): + self.manage_snapshot_with_server_support = ( + self.plugin.manage_snapshot_with_server_support) + else: + self.manage_snapshot_with_server_support = False + + if hasattr(self.plugin, 'manage_server_support'): + self.manage_server_support = self.plugin.manage_server_support + else: + self.manage_server_support = False + + if hasattr(self.plugin, 'get_share_server_network_info_support'): + self.get_share_server_network_info_support = ( + self.plugin.get_share_server_network_info_support) + else: + self.get_share_server_network_info_support = False + + def manage_existing(self, share, driver_options): + """manage an existing share""" + if self.manage_existing_support: + return self.plugin.manage_existing(share, driver_options) + else: + return NotImplementedError() + + def manage_existing_with_server(self, share, driver_options, + share_server=None): + """manage an existing share""" + if self.manage_existing_with_server_support: + return self.plugin.manage_existing_with_server( + share, driver_options, share_server) + else: + return NotImplementedError() + + def manage_existing_snapshot(self, snapshot, driver_options): + """manage an existing share snapshot""" + if self.manage_existing_snapshot_support: + return self.plugin.manage_existing_snapshot(snapshot, + driver_options) + else: + return NotImplementedError() + + def manage_existing_snapshot_with_server(self, snapshot, driver_options, + share_server=None): + """manage an existing share snapshot""" + if self.manage_snapshot_with_server_support: + return self.plugin.manage_existing_snapshot_with_server( + snapshot, driver_options, share_server=None) + else: + return NotImplementedError() + + def manage_server(self, context, share_server, identifier, + driver_options): + if self.manage_server_support: + return self.plugin.manage_server(context, share_server, + identifier, driver_options) + else: + return NotImplementedError() + + def get_share_server_network_info( + self, context, share_server, identifier, driver_options): + if self.get_share_server_network_info_support: + return self.plugin.get_share_server_network_info( + context, share_server, identifier, driver_options) + else: + return NotImplementedError() + + def unmanage_server(self, server_details, security_services=None): + LOG.info('Dell EMC driver will unmanage share server: %s out of ' + 'OpenStack.', server_details.get('server_id')) + + def unmanage(self, share): + LOG.info('Dell EMC driver will unmanage share: %s out of ' + 'OpenStack.', share.get('id')) + + def unmanage_with_server(self, share, share_server=None): + LOG.info('Dell EMC driver will unmanage share: %s out of ' + 'OpenStack.', share.get('id')) + + def unmanage_snapshot(self, snapshot): + LOG.info('Dell EMC driver will unmanage snapshot: %s out of ' + 'OpenStack.', snapshot.get('id')) + + def unmanage_snapshot_with_server(self, snapshot, share_server=None): + LOG.info('Dell EMC driver will unmanage snapshot: %s out of ' + 'OpenStack.', snapshot.get('id')) + def create_share(self, context, share, share_server=None): """Is called to create share.""" location = self.plugin.create_share(context, share, share_server) @@ -127,7 +230,7 @@ class EMCShareDriver(driver.ShareDriver): def create_snapshot(self, context, snapshot, share_server=None): """Is called to create snapshot.""" - self.plugin.create_snapshot(context, snapshot, share_server) + return self.plugin.create_snapshot(context, snapshot, share_server) def delete_share(self, context, share, share_server=None): """Is called to remove share.""" diff --git a/manila/share/drivers/dell_emc/plugins/unity/connection.py b/manila/share/drivers/dell_emc/plugins/unity/connection.py index 9775ece73e..e3b4293f3f 100644 --- a/manila/share/drivers/dell_emc/plugins/unity/connection.py +++ b/manila/share/drivers/dell_emc/plugins/unity/connection.py @@ -41,9 +41,10 @@ from manila import utils 7.0.0 - Supports DHSS=False mode 7.0.1 - Fix parsing management IPv6 address 7.0.2 - Bugfix: failed to delete CIFS share if wrong access was set + 8.0.0 - Supports manage/unmanage share server/share/snapshot """ -VERSION = "7.0.2" +VERSION = "8.0.0" LOG = log.getLogger(__name__) SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan') @@ -100,6 +101,12 @@ class UnityStorageConnection(driver.StorageConnection): self.ipv6_implemented = True self.revert_to_snap_support = True self.shrink_share_support = True + self.manage_existing_support = True + self.manage_existing_with_server_support = True + self.manage_existing_snapshot_support = True + self.manage_snapshot_with_server_support = True + self.manage_server_support = True + self.get_share_server_network_info_support = True # props from super class. self.driver_handles_share_servers = (True, False) @@ -180,6 +187,151 @@ class UnityStorageConnection(driver.StorageConnection): def check_for_setup_error(self): """Check for setup error.""" + def manage_existing(self, share, driver_options, share_server=None): + """Manages a share that exists on backend. + + :param share: Share that will be managed. + :param driver_options: Driver-specific options provided by admin. + :param share_server: Share server name provided by admin in DHSS=True. + :returns: Returns a dict with share size and export location. + """ + export_locations = share['export_locations'] + if not export_locations: + message = ("Failed to manage existing share: %s, missing " + "export locations." % share['id']) + raise exception.ManageInvalidShare(reason=message) + + try: + share_size = int(driver_options.get("size", 0)) + except (ValueError, TypeError): + msg = _("The driver options' size to manage the share " + "%(share_id)s, should be an integer, in format " + "driver-options size=. Value specified: " + "%(size)s.") % {'share_id': share['id'], + 'size': driver_options.get("size")} + raise exception.ManageInvalidShare(reason=msg) + + if not share_size: + msg = _("Share %(share_id)s has no specified size. " + "Using default value 1, set size in driver options if you " + "want.") % {'share_id': share['id']} + LOG.warning(msg) + share_size = 1 + + share_id = unity_utils.get_share_backend_id(share) + backend_share = self.client.get_share(share_id, + share['share_proto']) + if not backend_share: + message = ("Could not find the share in backend, please make sure " + "the export location is right.") + raise exception.ManageInvalidShare(reason=message) + + # Check the share server when in DHSS=true mode + if share_server: + backend_share_server = self._get_server_name(share_server) + if not backend_share_server: + message = ("Could not find the backend share server: %s, " + "please make sure that share server with the " + "specified name exists in the backend.", + share_server) + raise exception.BadConfigurationException(message) + LOG.info("Share %(shr_path)s is being managed with ID " + "%(shr_id)s.", + {'shr_path': share['export_locations'][0]['path'], + 'shr_id': share['id']}) + # export_locations was not changed, return original value + return {"size": share_size, 'export_locations': { + 'path': share['export_locations'][0]['path']}} + + def manage_existing_with_server(self, share, driver_options, share_server): + return self.manage_existing(share, driver_options, share_server) + + def manage_existing_snapshot(self, snapshot, driver_options, + share_server=None): + """Brings an existing snapshot under Manila management.""" + try: + snapshot_size = int(driver_options.get("size", 0)) + except (ValueError, TypeError): + msg = _("The size in driver options to manage snapshot " + "%(snap_id)s should be an integer, in format " + "driver-options size=. Value passed: " + "%(size)s.") % {'snap_id': snapshot['id'], + 'size': driver_options.get("size")} + raise exception.ManageInvalidShareSnapshot(reason=msg) + + if not snapshot_size: + msg = _("Snapshot %(snap_id)s has no specified size. " + "Use default value 1, set size in driver options if you " + "want.") % {'snap_id': snapshot['id']} + LOG.info(msg) + snapshot_size = 1 + provider_location = snapshot.get('provider_location') + snap = self.client.get_snapshot(provider_location) + if not snap: + message = ("Could not find a snapshot in the backend with " + "provider_location: %s, please make sure " + "the snapshot exists in the backend." + % provider_location) + raise exception.ManageInvalidShareSnapshot(reason=message) + + LOG.info("Snapshot %(provider_location)s in Unity will be managed " + "with ID %(snapshot_id)s.", + {'provider_location': snapshot.get('provider_location'), + 'snapshot_id': snapshot['id']}) + return {"size": snapshot_size, "provider_location": provider_location} + + def manage_existing_snapshot_with_server(self, snapshot, driver_options, + share_server): + return self.manage_existing_snapshot(snapshot, driver_options, + share_server) + + def manage_server(self, context, share_server, identifier, driver_options): + """Manage the share server and return compiled back end details. + + :param context: Current context. + :param share_server: Share server model. + :param identifier: A driver-specific share server identifier + :param driver_options: Dictionary of driver options to assist managing + the share server + :return: Identifier and dictionary with back end details to be saved + in the database. + + Example:: + + 'my_new_server_identifier',{'server_name': 'my_old_server'} + + """ + nas_server = self.client.get_nas_server(identifier) + if not nas_server: + message = ("Could not find the backend share server by server " + "name: %s, please make sure the share server is " + "existing in the backend." % identifier) + raise exception.ManageInvalidShare(reason=message) + return identifier, driver_options + + def get_share_server_network_info( + self, context, share_server, identifier, driver_options): + """Obtain network allocations used by share server. + + :param context: Current context. + :param share_server: Share server model. + :param identifier: A driver-specific share server identifier + :param driver_options: Dictionary of driver options to assist managing + the share server + :return: The containing IP address allocated in the backend, Unity + only supports single IP address + Example:: + + ['10.10.10.10'] or ['fd11::2000'] + + """ + containing_ips = [] + nas_server = self.client.get_nas_server(identifier) + if nas_server: + for file_interface in nas_server.file_interface: + containing_ips.append(file_interface.ip_address) + return containing_ips + def create_share(self, context, share, share_server=None): """Create a share and export it based on protocol used.""" share_name = share['id'] @@ -243,8 +395,8 @@ class UnityStorageConnection(driver.StorageConnection): {'server': server_name, 'share': share_name}) LOG.exception(message) raise exception.EMCUnityError(err=message) - - backend_snap = self.client.create_snap_of_snap(snapshot['id'], + snapshot_id = unity_utils.get_snapshot_id(snapshot) + backend_snap = self.client.create_snap_of_snap(snapshot_id, share_name) locations = None @@ -263,7 +415,7 @@ class UnityStorageConnection(driver.StorageConnection): def delete_share(self, context, share, share_server=None): """Delete a share.""" - share_name = share['id'] + share_name = unity_utils.get_share_backend_id(share) try: backend_share = self.client.get_share(share_name, share['share_proto']) @@ -284,7 +436,8 @@ class UnityStorageConnection(driver.StorageConnection): self.client.delete_filesystem(filesystem) def extend_share(self, share, new_size, share_server=None): - backend_share = self.client.get_share(share['id'], + share_id = unity_utils.get_share_backend_id(share) + backend_share = self.client.get_share(share_id, share['share_proto']) if not self._is_share_from_snapshot(backend_share): @@ -305,7 +458,7 @@ class UnityStorageConnection(driver.StorageConnection): :param share_server: Data structure with share server information. Not used by this driver. """ - share_id = share['id'] + share_id = unity_utils.get_share_backend_id(share) backend_share = self.client.get_share(share_id, share['share_proto']) if self._is_share_from_snapshot(backend_share): @@ -322,7 +475,9 @@ class UnityStorageConnection(driver.StorageConnection): def create_snapshot(self, context, snapshot, share_server=None): """Create snapshot from share.""" - share_name = snapshot['share_id'] + share = snapshot['share'] + share_name = unity_utils.get_share_backend_id( + share) if share else snapshot['share_id'] share_proto = snapshot['share']['share_proto'] backend_share = self.client.get_share(share_name, share_proto) @@ -332,10 +487,12 @@ class UnityStorageConnection(driver.StorageConnection): else: self.client.create_snapshot(backend_share.filesystem, snapshot_name) + return {'provider_location': snapshot_name} def delete_snapshot(self, context, snapshot, share_server=None): """Delete a snapshot.""" - snap = self.client.get_snapshot(snapshot['id']) + snapshot_id = unity_utils.get_snapshot_id(snapshot) + snap = self.client.get_snapshot(snapshot_id) self.client.delete_snapshot(snap) def update_access(self, context, share, access_rules, add_rules, @@ -360,7 +517,7 @@ class UnityStorageConnection(driver.StorageConnection): def clear_access(self, share, white_list=None): share_proto = share['share_proto'].upper() - share_name = share['id'] + share_name = unity_utils.get_share_backend_id(share) if share_proto == 'CIFS': self.client.cifs_clear_access(share_name, white_list) elif share_proto == 'NFS': @@ -396,7 +553,7 @@ class UnityStorageConnection(driver.StorageConnection): def ensure_share(self, context, share, share_server): """Ensure that the share is exported.""" - share_name = share['id'] + share_name = unity_utils.get_share_backend_id(share) share_proto = share['share_proto'] backend_share = self.client.get_share(share_name, share_proto) @@ -641,8 +798,9 @@ class UnityStorageConnection(driver.StorageConnection): if not share_server: msg = _('Share server not provided.') raise exception.InvalidInput(reason=msg) - - server_name = share_server.get( + # Try to get share server name from property 'identifier' first in + # case this is managed share server. + server_name = share_server.get('identifier') or share_server.get( 'backend_details', {}).get('share_server_name') if server_name is None: @@ -734,4 +892,5 @@ class UnityStorageConnection(driver.StorageConnection): def revert_to_snapshot(self, context, snapshot, share_access_rules, snapshot_access_rules, share_server=None): """Reverts a share (in place) to the specified snapshot.""" - return self.client.restore_snapshot(snapshot['id']) + snapshot_id = unity_utils.get_snapshot_id(snapshot) + return self.client.restore_snapshot(snapshot_id) diff --git a/manila/share/drivers/dell_emc/plugins/unity/utils.py b/manila/share/drivers/dell_emc/plugins/unity/utils.py index 11e7f4bffc..564fa543d7 100644 --- a/manila/share/drivers/dell_emc/plugins/unity/utils.py +++ b/manila/share/drivers/dell_emc/plugins/unity/utils.py @@ -14,12 +14,15 @@ # under the License. """ Utility module for EMC Unity Manila Driver """ +from oslo_log import log from oslo_utils import fnmatch from oslo_utils import units from manila import exception from manila.i18n import _ +LOG = log.getLogger(__name__) + def do_match(full, matcher_list): matched = set() @@ -78,3 +81,37 @@ def find_ports_by_mtu(all_ports, port_ids_conf, mtu): def gib_to_byte(size_gib): return size_gib * units.Gi + + +def get_share_backend_id(share): + """Get backend share id. + + Try to get backend share id from path in case this is managed share, + use share['id'] when path is empty. + """ + + backend_share_id = None + try: + export_locations = share['export_locations'][0] + path = export_locations['path'] + if share['share_proto'].lower() == 'nfs': + # 10.0.0.1:/example_share_name + backend_share_id = path.split(':/')[-1] + if share['share_proto'].lower() == 'cifs': + # \\10.0.0.1\example_share_name + backend_share_id = path.split('\\')[-1] + except Exception as e: + LOG.warning('Cannot get share name from path, make sure the path ' + 'is right. Error details: %s', e) + if backend_share_id and (backend_share_id != share['id']): + return backend_share_id + else: + return share['id'] + + +def get_snapshot_id(snapshot): + """Get backend snapshot id. + + Take the id from provider_location in case this is managed snapshot. + """ + return snapshot['provider_location'] or snapshot['id'] diff --git a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml index 6ce19e6ffb..4205e3967b 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml +++ b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml @@ -138,6 +138,7 @@ share_server: share_server_name: '78fd845f-8e7d-487f-bfde-051d83e78103' network_allocations: *network_allocations_prop id: '78fd845f-8e7d-487f-bfde-051d83e78103' + identifier: 'c2e48947-98ed-4eae-999b-fa0b83731dfd' share_server__no_share_server_name: _type: 'share_server' @@ -170,6 +171,25 @@ cifs_share: export_locations: [] is_public: False +managed_cifs_share: + _type: 'share' + _properties: &managed_cifs_share_share_prop + share_id: '708e753c-aacb-411f-9c8a-8b8175da4e73' + availability_zone_id: 'de628fb6-1c99-41f6-a06a-adb61ff693b5' + share_network_id: '232d8218-2743-41d1-832b-4194626e691e' + share_server_id: '78fd845f-8e7d-487f-bfde-051d83e78103' + id: '716100cc-e0b4-416b-ac27-d38dd019330d' + size: 10 + user_id: '19bbda71b578471a93363653dcb4c61d' + status: 'creating' + share_type_id: '57679eab-3e67-4052-b180-62b609670e93' + host: 'openstack@VNX#Pool_2' + display_name: 'cifs_share' + share_proto: 'CIFS' + export_locations: [path: '\\10.0.0.1\bd23121f-hg4e-432c-12cd2c5-bb93dfghe212'] + is_public: False + snapshot_support: False + nfs_share: _type: 'share' _properties: &nfs_share_prop @@ -185,9 +205,28 @@ nfs_share: host: 'openstack@VNX#Pool_2' display_name: 'nfs_share' share_proto: 'NFS' - export_locations: [] + export_locations: null is_public: False +managed_nfs_share: + _type: 'share' + _properties: &managed_nfs_share_prop + share_id: '12eb3777-7008-4721-8243-422507db8f9d' + availability_zone_id: 'de628fb6-1c99-41f6-a06a-adb61ff693b5' + share_network_id: '232d8218-2743-41d1-832b-4194626e691e' + share_server_id: '78fd845f-8e7d-487f-bfde-051d83e78103' + id: 'cb532599-8dc6-4c3e-bb21-74ea54be566c' + size: 9 + user_id: '19bbda71b578471a93363653dcb4c61d' + status: 'creating' + share_type_id: '57679eab-3e67-4052-b180-62b609670e93' + host: 'openstack@VNX#Pool_2' + display_name: 'nfs_share' + share_proto: 'NFS' + export_locations: [path: '172.168.201.201:/ad1caddf-097e-462c-8ac6-5592ed6fe22f'] + is_public: False + snapshot_support: False + dhss_false_cifs_share: _type: 'share' _properties: &dhss_false_cifs_share_prop @@ -285,10 +324,12 @@ snapshot: share_instance_id: '27e4625e-c336-4749-85bc-634216755fbc' share: share_proto: 'CIFS' + id: 's24r-3fgw2-g039ef-j029f0-nrver' snapshot_id: '345476cc-32ab-4565-ba88-e4733b7ffa0e' progress: '0%' id: 'ab411797-b1cf-4035-bf14-8771a7bf1805' share_id: '27e4625e-c336-4749-85bc-634216755fbc' + provider_location: '23047-ef2344-4563cvw-r4323cwed' cifs_rw_access: _type: 'access' diff --git a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml index 6185ba9ae9..695ab9e518 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml +++ b/manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml @@ -1252,3 +1252,89 @@ test_restore_snapshot: unity: _methods: get_snap: *snapshot_1 + + +test_manage_cifs_share_with_server: + filesystem: &filesystem__test_manage_cifs_share_with_server + _properties: &filesystem_prop__test_manage_cifs_share_with_server + <<: *filesystem_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + size_total: 5368709120 + _methods: + shrink: + + cifs_share: &cifs_share__test_manage_cifs_share_with_server + _properties: + <<: *cifs_share_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + filesystem: *filesystem__test_manage_cifs_share_with_server + + unity: + _methods: + <<: *unity_base_method + get_cifs_share: *cifs_share__test_manage_cifs_share_with_server + +test_manage_cifs_share: + filesystem: &filesystem__test_manage_cifs_share + _properties: &filesystem_prop__test_manage_cifs_share + <<: *filesystem_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + size_total: 5368709120 + _methods: + shrink: + + cifs_share: &cifs_share__test_manage_cifs_share + _properties: + <<: *cifs_share_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + filesystem: *filesystem__test_manage_cifs_share + + unity: + _methods: + <<: *unity_base_method + get_cifs_share: *cifs_share__test_manage_cifs_share + +test_manage_nfs_share_with_server: + filesystem: &filesystem__test_manage_nfs_share_with_server + _properties: &filesystem_prop__test_manage_nfs_share_with_server + <<: *filesystem_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + size_total: 5368709120 + _methods: + extend: + + nfs_share: &nfs_share__test_manage_nfs_share_with_server + _properties: + <<: *nfs_share_base_prop + name: '716100cc-e0b4-416b-ac27-d38dd019330d' + filesystem: *filesystem__test_manage_nfs_share_with_server + + unity: + _methods: + <<: *unity_base_method + get_nfs_share: *nfs_share__test_manage_nfs_share_with_server + +test_manage_nfs_share: + filesystem: &filesystem__test_manage_nfs_share + _properties: &filesystem_prop__test_manage_nfs_share + <<: *filesystem_base_prop + size_total: 5368709120 + _methods: + shrink: + + nfs_share: &nfs_share__test_manage_nfs_share + _properties: + <<: *nfs_share_base_prop + filesystem: *filesystem__test_manage_nfs_share + + unity: + _methods: + <<: *unity_base_method + get_nfs_share: *nfs_share__test_manage_nfs_share + +test_get_share_server_network_info: + unity: + _methods: + <<: *unity_base_method + get_nas_server: *nas_server + diff --git a/manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py b/manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py index 4c055596b3..d193556b14 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py +++ b/manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py @@ -116,6 +116,7 @@ class TestConnection(test.TestCase): share_server = { 'backend_details': {'share_server_name': None}, 'id': 'test', + 'identifier': '', } self.assertRaises(exception.InvalidInput, @@ -246,7 +247,9 @@ class TestConnection(test.TestCase): snapshot = mocked_input['snapshot'] share_server = mocked_input['share_server'] - connection.create_snapshot(None, snapshot, share_server) + result = connection.create_snapshot(None, snapshot, share_server) + self.assertEqual('ab411797-b1cf-4035-bf14-8771a7bf1805', + result['provider_location']) @res_mock.mock_manila_input @res_mock.patch_connection @@ -739,3 +742,142 @@ class TestConnection(test.TestCase): exp_location = sorted(exp_location, key=lambda x: sorted(x['path'])) location = sorted(location, key=lambda x: sorted(x['path'])) self.assertEqual(exp_location, location) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_get_share_server_id(self, connection, mocked_input): + share_server = mocked_input['share_server'] + result = connection._get_server_name(share_server) + expected = 'c2e48947-98ed-4eae-999b-fa0b83731dfd' + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_snapshot(self, connection, mocked_input): + snapshot = mocked_input['snapshot'] + driver_options = {'size': 8} + result = connection.manage_existing_snapshot(snapshot, + driver_options, None) + expected = {'provider_location': '23047-ef2344-4563cvw-r4323cwed', + 'size': 8} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_snapshot_wrong_size_type(self, connection, mocked_input): + snapshot = mocked_input['snapshot'] + driver_options = {'size': 'str_size'} + self.assertRaises(exception.ManageInvalidShareSnapshot, + connection.manage_existing_snapshot, + snapshot, driver_options, None) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_snapshot_with_server(self, connection, mocked_input): + share_server = mocked_input['share_server'] + snapshot = mocked_input['snapshot'] + driver_options = {} + result = connection.manage_existing_snapshot_with_server( + snapshot, driver_options, share_server) + expected = {'provider_location': '23047-ef2344-4563cvw-r4323cwed', + 'size': 1} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_get_share_server_network_info(self, connection, mocked_input): + share_server = mocked_input['share_server'] + identifier = 'test_manage_nas_server' + result = connection.get_share_server_network_info(None, share_server, + identifier, None) + expected = ['fake_ip_addr_1', 'fake_ip_addr_2'] + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_server(self, connection, mocked_input): + share_server = mocked_input['share_server'] + identifier = 'test_manage_nas_server' + result = connection.manage_server(None, share_server, identifier, None) + expected = (identifier, None) + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_nfs_share(self, connection, mocked_input): + share = mocked_input['managed_nfs_share'] + driver_options = {'size': 3} + result = connection.manage_existing(share, driver_options) + path = '172.168.201.201:/ad1caddf-097e-462c-8ac6-5592ed6fe22f' + expected = {'export_locations': {'path': path}, 'size': 3} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_nfs_share_with_server(self, connection, mocked_input): + share = mocked_input['managed_nfs_share'] + share_server = mocked_input['share_server'] + driver_options = {'size': 8} + result = connection.manage_existing_with_server(share, driver_options, + share_server) + path = '172.168.201.201:/ad1caddf-097e-462c-8ac6-5592ed6fe22f' + expected = {'export_locations': {'path': path}, 'size': 8} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_cifs_share(self, connection, mocked_input): + share = mocked_input['managed_cifs_share'] + driver_options = {'size': 3} + result = connection.manage_existing(share, driver_options) + path = '\\\\10.0.0.1\\bd23121f-hg4e-432c-12cd2c5-bb93dfghe212' + expected = {'export_locations': {'path': path}, 'size': 3} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_cifs_share_with_server(self, connection, mocked_input): + connection.client.create_interface = mock.Mock(return_value=None) + share = mocked_input['managed_cifs_share'] + share_server = mocked_input['share_server'] + driver_options = {'size': 3} + result = connection.manage_existing_with_server(share, driver_options, + share_server) + path = '\\\\10.0.0.1\\bd23121f-hg4e-432c-12cd2c5-bb93dfghe212' + expected = {'export_locations': {'path': path}, 'size': 3} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_with_wrong_size_data_type(self, connection, mocked_input): + connection.client.create_interface = mock.Mock(return_value=None) + share = mocked_input['managed_nfs_share'] + share_server = mocked_input['share_server'] + driver_options = {'size': 'str_size'} + self.assertRaises(exception.ManageInvalidShare, + connection.manage_existing_with_server, + share, driver_options, share_server) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_without_size(self, connection, mocked_input): + connection.client.create_interface = mock.Mock(return_value=None) + share = mocked_input['managed_nfs_share'] + share_server = mocked_input['share_server'] + driver_options = {'size': 0} + result = connection.manage_existing_with_server(share, driver_options, + share_server) + path = '172.168.201.201:/ad1caddf-097e-462c-8ac6-5592ed6fe22f' + expected = {'export_locations': {'path': path}, 'size': 1} + self.assertEqual(expected, result) + + @res_mock.mock_manila_input + @res_mock.patch_connection + def test_manage_without_export_locations(self, connection, mocked_input): + connection.client.create_interface = mock.Mock(return_value=None) + share = mocked_input['nfs_share'] + share_server = mocked_input['share_server'] + driver_options = {'size': 3} + self.assertRaises(exception.ManageInvalidShare, + connection.manage_existing_with_server, + share, driver_options, share_server) diff --git a/manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py b/manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py index 51258d98d5..709481300b 100644 --- a/manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py +++ b/manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py @@ -115,3 +115,52 @@ class TestUtils(test.TestCase): def test_gb_to_byte(self): self.assertEqual(3 * units.Gi, utils.gib_to_byte(3)) + + def test_get_snapshot_id(self): + snapshot = {'provider_location': '23047-ef2344-4563cvw-r4323cwed', + 'id': 'test_id'} + result = utils.get_snapshot_id(snapshot) + expected = '23047-ef2344-4563cvw-r4323cwed' + self.assertEqual(expected, result) + + def test_get_snapshot_id_without_pl(self): + snapshot = {'provider_location': '', 'id': 'test_id'} + result = utils.get_snapshot_id(snapshot) + expected = 'test_id' + self.assertEqual(expected, result) + + def test_get_nfs_share_id(self): + nfs_share = {'export_locations': + [{'path': '10.10.1.12:/addf-97e-46c-8ac6-55922f', + 'share_instance_id': 'e24-457e-47-12c6-gf345'}], + 'share_proto': 'NFS', 'id': 'test_nfs_id'} + result = utils.get_share_backend_id(nfs_share) + expected = 'addf-97e-46c-8ac6-55922f' + self.assertEqual(expected, result) + + def test_get_nfs_share_id_without_path(self): + nfs_share = {'export_locations': + [{'path': '', + 'share_instance_id': 'ev24-7e-4-12c6-g45245'}], + 'share_proto': 'NFS', 'id': 'test_nfs_id'} + result = utils.get_share_backend_id(nfs_share) + expected = 'test_nfs_id' + self.assertEqual(expected, result) + + def test_get_cifs_share_id(self): + cifs_share = {'export_locations': + [{'path': '\\\\17.66.5.3\\bdf-h4e-42c-122c5-b212', + 'share_instance_id': 'ev4-47e-48-126-gfbh452'}], + 'share_proto': 'CIFS', 'id': 'test_cifs_id'} + result = utils.get_share_backend_id(cifs_share) + expected = 'bdf-h4e-42c-122c5-b212' + self.assertEqual(expected, result) + + def test_get_cifs_share_id_without_path(self): + cifs_share = {'export_locations': + [{'path': '', + 'share_instance_id': 'ef4-47e-48-12c6-gf452'}], + 'share_proto': 'CIFS', 'id': 'test_cifs_id'} + result = utils.get_share_backend_id(cifs_share) + expected = 'test_cifs_id' + self.assertEqual(expected, result) diff --git a/manila/tests/share/drivers/dell_emc/test_driver.py b/manila/tests/share/drivers/dell_emc/test_driver.py index 8d1e0a6557..0a528d1947 100644 --- a/manila/tests/share/drivers/dell_emc/test_driver.py +++ b/manila/tests/share/drivers/dell_emc/test_driver.py @@ -145,3 +145,59 @@ class EMCShareFrameworkTestCase(test.TestCase): elif value == 'driver_handles_share_servers': return True return None + + def test_support_manage(self): + share = mock.Mock() + driver_options = mock.Mock() + share_server = mock.Mock() + snapshot = mock.Mock() + context = mock.Mock() + identifier = mock.Mock() + self.driver.plugin = mock.Mock() + self.driver.manage_existing_support = True + self.driver.manage_existing_with_server_support = True + self.driver.manage_existing_snapshot_support = True + self.driver.manage_snapshot_with_server_support = True + self.driver.manage_server_support = True + self.driver.manage_existing(share, driver_options) + self.driver.manage_existing_with_server(share, driver_options, + share_server) + self.driver.manage_existing_snapshot(snapshot, driver_options) + self.driver.manage_existing_snapshot_with_server(snapshot, + driver_options, + share_server) + self.driver.manage_server(context, share_server, identifier, + driver_options) + + def test_not_support_manage(self): + share = mock.Mock() + driver_options = {} + share_server = mock.Mock() + snapshot = mock.Mock() + identifier = mock.Mock() + self.driver.plugin = mock.Mock() + result = self.driver.manage_existing(share, driver_options) + self.assertIsInstance(result, NotImplementedError) + result = self.driver.manage_existing_with_server( + share, driver_options, share_server) + self.assertIsInstance(result, NotImplementedError) + result = self.driver.manage_existing_snapshot(snapshot, driver_options) + self.assertIsInstance(result, NotImplementedError) + result = self.driver.manage_existing_snapshot_with_server( + snapshot, driver_options, share_server) + self.assertIsInstance(result, NotImplementedError) + result = self.driver.manage_server(None, share_server, identifier, + driver_options) + self.assertIsInstance(result, NotImplementedError) + + def unmanage_manage(self): + share = mock.Mock() + server_details = {} + share_server = mock.Mock() + snapshot = mock.Mock() + self.driver.plugin = mock.Mock(share) + self.driver.unmanage(share) + self.driver.unmanage_with_server(share, share_server) + self.driver.unmanage_snapshot(snapshot) + self.driver.unmanage_snapshot_with_server(snapshot, share_server) + self.driver.unmanage_server(server_details) diff --git a/releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml b/releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml new file mode 100644 index 0000000000..faf66a6e96 --- /dev/null +++ b/releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml @@ -0,0 +1,4 @@ +--- +features: + - Dell EMC Unity Manila driver now supports manage/unmange share server, + share instance and share snapshot.