Browse Source

Merge "[Unity] Manage/unmanage share server/share/snap"

tags/10.0.0.0rc1
Zuul 3 months ago
committed by Gerrit Code Review
parent
commit
64af75d72a
11 changed files with 832 additions and 44 deletions
  1. +1
    -1
      doc/source/admin/share_back_ends_feature_support_mapping.rst
  2. +138
    -27
      doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst
  3. +104
    -1
      manila/share/drivers/dell_emc/driver.py
  4. +172
    -13
      manila/share/drivers/dell_emc/plugins/unity/connection.py
  5. +37
    -0
      manila/share/drivers/dell_emc/plugins/unity/utils.py
  6. +42
    -1
      manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml
  7. +86
    -0
      manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml
  8. +143
    -1
      manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py
  9. +49
    -0
      manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py
  10. +56
    -0
      manila/tests/share/drivers/dell_emc/test_driver.py
  11. +4
    -0
      releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml

+ 1
- 1
doc/source/admin/share_back_ends_feature_support_mapping.rst View File

@@ -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 | \- | \- | \- |
+----------------------------------------+-----------------------+-----------------------+--------------------------+--------------------------+------------------------+----------------------------+--------------------------+--------------------+--------------------+


+ 138
- 27
doc/source/configuration/shared-file-systems/drivers/dell-emc-unity-driver.rst View File

@@ -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 = <IPv6 address>

@@ -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 = <name of NAS server in Unity system>

#. 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 <share_type_name> False
$ manila type-create ${share_type_name} False

#. Create share.

.. code-block:: console

$ manila create <share_protocol> <size> --name <share_name> --share-type <share_type_name>
$ 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
---------------------------


+ 104
- 1
manila/share/drivers/dell_emc/driver.py View File

@@ -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."""


+ 172
- 13
manila/share/drivers/dell_emc/plugins/unity/connection.py View File

@@ -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=<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=<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)

+ 37
- 0
manila/share/drivers/dell_emc/plugins/unity/utils.py View File

@@ -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']

+ 42
- 1
manila/tests/share/drivers/dell_emc/plugins/unity/mocked_manila.yaml View File

@@ -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,8 +205,27 @@ 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'
@@ -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'


+ 86
- 0
manila/tests/share/drivers/dell_emc/plugins/unity/mocked_unity.yaml View File

@@ -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


+ 143
- 1
manila/tests/share/drivers/dell_emc/plugins/unity/test_connection.py View File

@@ -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)

+ 49
- 0
manila/tests/share/drivers/dell_emc/plugins/unity/test_utils.py View File

@@ -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)

+ 56
- 0
manila/tests/share/drivers/dell_emc/test_driver.py View File

@@ -146,3 +146,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)

+ 4
- 0
releasenotes/notes/unity-manage-server-share-snapshot-support-6a0bbbed74da13c7.yaml View File

@@ -0,0 +1,4 @@
---
features:
- Dell EMC Unity Manila driver now supports manage/unmange share server,
share instance and share snapshot.

Loading…
Cancel
Save