Merge "Add EMC Unity Driver for Manila"
This commit is contained in:
commit
367d54335e
126
doc/source/devref/emc_unity_driver.rst
Normal file
126
doc/source/devref/emc_unity_driver.rst
Normal file
@ -0,0 +1,126 @@
|
||||
..
|
||||
Copyright (c) 2014 EMC Corporation
|
||||
All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Unity Driver
|
||||
============
|
||||
|
||||
EMC manila driver framework (EMCShareDriver) utilizes the EMC storage products
|
||||
to provide the shared filesystems to OpenStack. The EMC manila driver is a
|
||||
plugin based driver which is designed to use different plugins to manage
|
||||
different EMC storage products.
|
||||
|
||||
Unity plugin is the plugin which manages the Unity Storage System to provide
|
||||
shared filesystems. EMC driver framework with Unity plugin is referred to as
|
||||
Unity driver in this document.
|
||||
|
||||
This driver performs the operations on Unity by REST API. Each backend manages
|
||||
one Unity Storage System. Multiple manila backends need to be configured to
|
||||
manage multiple Unity Storage Systems.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- Unity OE 4.0.1 or higher.
|
||||
- StorOps 0.2.17 or higher is installed on Manila node.
|
||||
- Following licenses are activated on Unity:
|
||||
* CIFS/SMB Support
|
||||
* Network File System (NFS)
|
||||
* Thin Provisioning
|
||||
* Fiber Channel (FC)
|
||||
* Internet Small Computer System Interface (iSCSI)
|
||||
|
||||
|
||||
Supported Operations
|
||||
--------------------
|
||||
|
||||
In detail, users are allowed to do following operation with EMC Unity
|
||||
Storage Systems.
|
||||
|
||||
* Create/delete a NFS share.
|
||||
* Create/delete a CIFS share.
|
||||
* Extend the size of a share.
|
||||
* Modify the host access privilege of a NFS share.
|
||||
* Modify the user access privilege of a CIFS share.
|
||||
* Take/Delete snapshot of a share.
|
||||
* Create a new share from snapshot.
|
||||
|
||||
|
||||
Supported Network Topologies
|
||||
----------------------------
|
||||
|
||||
flat, VLAN
|
||||
|
||||
|
||||
Pre-Configurations
|
||||
------------------
|
||||
|
||||
On Manila Node
|
||||
``````````````
|
||||
|
||||
StorOps library is required to run Unity driver.
|
||||
Please install it with the pip command.
|
||||
You may need root privilege to install python libraries.
|
||||
|
||||
::
|
||||
|
||||
pip install storops
|
||||
|
||||
|
||||
Configurations
|
||||
--------------
|
||||
|
||||
Following configurations are introduced for the Unity plugin.
|
||||
|
||||
* emc_interface_ports: White list of the ports to be used for connection.
|
||||
Wild card character is supported.
|
||||
Examples: spa_eth1, spa_*, *
|
||||
* emc_nas_server_pool: The pool used to persist the meta-data of created
|
||||
NAS servers. Wild card character is supported.
|
||||
Examples: pool_1, pool_*, *
|
||||
|
||||
|
||||
API Implementations
|
||||
-------------------
|
||||
|
||||
Following driver features are implemented in the plugin.
|
||||
|
||||
* create_share: Create a share and export it based on the protocol used
|
||||
(NFS or CIFS).
|
||||
* create_share_from_snapshot: Create a share from a snapshot - clone a
|
||||
snapshot.
|
||||
* delete_share: Delete a share.
|
||||
* extend_share: Extend the maximum size of a share.
|
||||
* create_snapshot: Create a snapshot for the specified share.
|
||||
* delete_snapshot: Delete the snapshot of the share.
|
||||
* update_access: recover, add or delete user/host access to a share.
|
||||
* allow_access: Allow access (read write/read only) of a user to a
|
||||
CIFS share. Allow access (read write/read only) of a host to a NFS
|
||||
share.
|
||||
* deny_access: Remove access (read write/read only) of a user from
|
||||
a CIFS share. Remove access (read write/read only) of a host from a
|
||||
NFS share.
|
||||
* ensure_share: Check whether share exists or not.
|
||||
* update_share_stats: Retrieve share related statistics from Unity.
|
||||
* get_network_allocations_number: Returns number of network allocations for
|
||||
creating VIFs.
|
||||
* setup_server: Set up and configures share server with given network
|
||||
parameters.
|
||||
* teardown_server: Tear down the share server.
|
||||
|
||||
Restrictions
|
||||
------------
|
||||
|
||||
* EMC Unity does not support the same IP in different VLANs.
|
@ -108,6 +108,7 @@ Share backends
|
||||
netapp_cluster_mode_driver
|
||||
emc_isilon_driver
|
||||
emc_vnx_driver
|
||||
emc_unity_driver
|
||||
generic_driver
|
||||
glusterfs_driver
|
||||
glusterfs_native_driver
|
||||
|
@ -41,6 +41,8 @@ Mapping of share drivers and share features support
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| EMC VNX | J | \- | \- | \- | J | J | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| EMC Unity | N | \- | N | \- | N | N | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| EMC Isilon | K | \- | M | \- | K | K | \- |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| Red Hat GlusterFS | J | \- | \- | \- | volume layout (L) | volume layout (L) | \- |
|
||||
@ -86,6 +88,8 @@ Mapping of share drivers and share access rules support
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+--------------+----------------+------------+------------+
|
||||
| EMC VNX | NFS (J) | CIFS (J) | \- | \- | NFS (L) | CIFS (L) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+--------------+----------------+------------+------------+
|
||||
| EMC Unity | NFS (N) | CIFS (N) | \- | \- | NFS (N) | CIFS (N) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+--------------+----------------+------------+------------+
|
||||
| EMC Isilon | NFS,CIFS (K) | CIFS (M) | \- | \- | NFS (M) | CIFS (M) | \- | \- |
|
||||
+----------------------------------------+--------------+----------------+------------+--------------+--------------+----------------+------------+------------+
|
||||
| Red Hat GlusterFS | NFS (J) | \- | \- | \- | \- | \- | \- | \- |
|
||||
@ -129,6 +133,8 @@ Mapping of share drivers and security services support
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| EMC VNX | J | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| EMC Unity | N | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| EMC Isilon | \- | \- | \- |
|
||||
+----------------------------------------+------------------+-----------------+------------------+
|
||||
| Red Hat GlusterFS | \- | \- | \- |
|
||||
@ -172,6 +178,8 @@ Mapping of share drivers and common capabilities
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+
|
||||
| EMC VNX | J | \- | \- | \- | \- | L | \- |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+
|
||||
| EMC Unity | N | \- | \- | \- | N | \- | \- |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+
|
||||
| EMC Isilon | \- | K | \- | \- | \- | L | \- |
|
||||
+----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+
|
||||
| Red Hat GlusterFS | \- | J | \- | \- | \- | L | \- |
|
||||
|
@ -554,6 +554,10 @@ class ShareTypeInUse(ManilaException):
|
||||
"shares present with the type.")
|
||||
|
||||
|
||||
class IPAddressInUse(InUse):
|
||||
message = _("IP address %(ip)s is already used.")
|
||||
|
||||
|
||||
class ShareTypeExists(ManilaException):
|
||||
message = _("Share Type %(id)s already exists.")
|
||||
|
||||
@ -645,6 +649,10 @@ class EMCVnxInvalidMoverID(ManilaException):
|
||||
message = _("Invalid mover or vdm %(id)s.")
|
||||
|
||||
|
||||
class EMCUnityError(ShareBackendException):
|
||||
message = _("%(err)s")
|
||||
|
||||
|
||||
class HPE3ParInvalidClient(Invalid):
|
||||
message = _("%(err)s")
|
||||
|
||||
|
@ -26,10 +26,8 @@ from oslo_log import log
|
||||
from manila.share import driver
|
||||
from manila.share.drivers.emc import plugin_manager as manager
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
EMC_NAS_OPTS = [
|
||||
cfg.StrOpt('emc_nas_login',
|
||||
help='User name for the EMC server.'),
|
||||
@ -44,15 +42,18 @@ EMC_NAS_OPTS = [
|
||||
default=True,
|
||||
help='Use secure connection to server.'),
|
||||
cfg.StrOpt('emc_share_backend',
|
||||
ignore_case=True,
|
||||
choices=['isilon', 'vnx', 'unity'],
|
||||
help='Share backend.'),
|
||||
cfg.StrOpt('emc_nas_server_container',
|
||||
default='server_2',
|
||||
help='Container of share servers.'),
|
||||
cfg.StrOpt('emc_nas_pool_names',
|
||||
deprecated_name='emc_nas_pool_name',
|
||||
help='EMC pool names.'),
|
||||
cfg.ListOpt('emc_nas_pool_names',
|
||||
deprecated_name='emc_nas_pool_name',
|
||||
help='EMC pool names.'),
|
||||
cfg.StrOpt('emc_nas_root_dir',
|
||||
help='The root directory where shares will be located.'),
|
||||
cfg.StrOpt('emc_nas_server_pool',
|
||||
help='Pool to persist the meta-data of NAS server.'),
|
||||
cfg.ListOpt('emc_interface_ports',
|
||||
help='Comma separated list specifying the ports that can be '
|
||||
'used for share server interfaces. Members of the list '
|
||||
@ -65,6 +66,7 @@ CONF.register_opts(EMC_NAS_OPTS)
|
||||
|
||||
class EMCShareDriver(driver.ShareDriver):
|
||||
"""EMC specific NAS driver. Allows for NFS and CIFS NAS storage usage."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
if self.configuration:
|
||||
|
0
manila/share/drivers/emc/plugins/unity/__init__.py
Normal file
0
manila/share/drivers/emc/plugins/unity/__init__.py
Normal file
261
manila/share/drivers/emc/plugins/unity/client.py
Normal file
261
manila/share/drivers/emc/plugins/unity/client.py
Normal file
@ -0,0 +1,261 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import random
|
||||
import six
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import importutils
|
||||
|
||||
storops = importutils.try_import('storops')
|
||||
if storops:
|
||||
from storops import exception as storops_ex
|
||||
from storops.unity import enums
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _, _LI, _LE
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class UnityClient(object):
|
||||
def __init__(self, host, username, password):
|
||||
if storops is None:
|
||||
LOG.error(_LE('StorOps is required to run EMC Unity driver.'))
|
||||
self.system = storops.UnitySystem(host, username, password)
|
||||
|
||||
def create_cifs_share(self, resource, share_name):
|
||||
"""Create CIFS share from the resource.
|
||||
|
||||
:param resource: either UnityFilesystem or UnitySnap object
|
||||
:param share_name: CIFS share name
|
||||
:return: UnityCifsShare object
|
||||
"""
|
||||
try:
|
||||
share = resource.create_cifs_share(share_name)
|
||||
try:
|
||||
# bug on unity: the enable ace API has bug for snap
|
||||
# based share. Log the internal error if it happens.
|
||||
share.enable_ace()
|
||||
except storops_ex.UnityException:
|
||||
msg = _LE('Failed to enabled ACE for share: {}.')
|
||||
LOG.exception(msg.format(share_name))
|
||||
return share
|
||||
except storops_ex.UnitySmbShareNameExistedError:
|
||||
return self.get_share(share_name, 'CIFS')
|
||||
|
||||
def create_nfs_share(self, resource, share_name):
|
||||
"""Create NFS share from the resource.
|
||||
|
||||
:param resource: either UnityFilesystem or UnitySnap object
|
||||
:param share_name: NFS share name
|
||||
:return: UnityNfsShare object
|
||||
"""
|
||||
try:
|
||||
return resource.create_nfs_share(share_name)
|
||||
except storops_ex.UnityNfsShareNameExistedError:
|
||||
return self.get_share(share_name, 'NFS')
|
||||
|
||||
def get_share(self, name, share_proto):
|
||||
# Validate the share protocol
|
||||
proto = share_proto.upper()
|
||||
|
||||
if proto == 'CIFS':
|
||||
return self.system.get_cifs_share(name=name)
|
||||
elif proto == 'NFS':
|
||||
return self.system.get_nfs_share(name=name)
|
||||
else:
|
||||
raise exception.BadConfigurationException(
|
||||
reason=_('Invalid NAS protocol supplied: %s.') % share_proto)
|
||||
|
||||
@staticmethod
|
||||
def delete_share(share):
|
||||
share.delete()
|
||||
|
||||
def create_filesystem(self, pool, nas_server, share_name, size, proto):
|
||||
try:
|
||||
return pool.create_filesystem(nas_server,
|
||||
share_name,
|
||||
size,
|
||||
proto=proto)
|
||||
except storops_ex.UnityFileSystemNameAlreadyExisted:
|
||||
LOG.debug('Filesystem %s already exists, '
|
||||
'ignoring filesystem creation.', share_name)
|
||||
return self.system.get_filesystem(name=share_name)
|
||||
|
||||
@staticmethod
|
||||
def delete_filesystem(filesystem):
|
||||
try:
|
||||
filesystem.delete()
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
LOG.info(_LI('Filesystem %s is already removed.'), filesystem.name)
|
||||
|
||||
def create_nas_server(self, name, sp, pool):
|
||||
try:
|
||||
return self.system.create_nas_server(name, sp, pool)
|
||||
except storops_ex.UnityNasServerNameUsedError:
|
||||
LOG.info(_LI('Share server %s already exists, ignoring share '
|
||||
'server creation.'), name)
|
||||
return self.get_nas_server(name)
|
||||
|
||||
def get_nas_server(self, name):
|
||||
try:
|
||||
return self.system.get_nas_server(name=name)
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
LOG.info(_LI('NAS server %s not found.'), name)
|
||||
raise
|
||||
|
||||
def delete_nas_server(self, name, username=None, password=None):
|
||||
try:
|
||||
nas_server = self.get_nas_server(name=name)
|
||||
nas_server.delete(username=username, password=password)
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
LOG.info(_LI('NAS server %s not found.'), name)
|
||||
|
||||
@staticmethod
|
||||
def create_dns_server(nas_server, domain, dns_ip):
|
||||
try:
|
||||
nas_server.create_dns_server(domain, dns_ip)
|
||||
except storops_ex.UnityOneDnsPerNasServerError:
|
||||
LOG.info(_LI('DNS server %s already exists, '
|
||||
'ignoring DNS server creation.'), domain)
|
||||
|
||||
@staticmethod
|
||||
def create_interface(nas_server, ip_addr, netmask, gateway, ports,
|
||||
vlan_id=None):
|
||||
port_list = list(ports)
|
||||
random.shuffle(port_list)
|
||||
try:
|
||||
nas_server.create_file_interface(port_list[0],
|
||||
ip_addr,
|
||||
netmask=netmask,
|
||||
gateway=gateway,
|
||||
vlan_id=vlan_id)
|
||||
except storops_ex.UnityIpAddressUsedError:
|
||||
raise exception.IPAddressInUse(ip=ip_addr)
|
||||
|
||||
@staticmethod
|
||||
def enable_cifs_service(nas_server, domain, username, password):
|
||||
try:
|
||||
nas_server.enable_cifs_service(
|
||||
nas_server.file_interface,
|
||||
domain=domain,
|
||||
domain_username=username,
|
||||
domain_password=password)
|
||||
except storops_ex.UnitySmbNameInUseError:
|
||||
LOG.info(_LI('CIFS service on NAS server %s is '
|
||||
'already enabled.'), nas_server.name)
|
||||
|
||||
@staticmethod
|
||||
def enable_nfs_service(nas_server):
|
||||
try:
|
||||
nas_server.enable_nfs_service()
|
||||
except storops_ex.UnityNfsAlreadyEnabledError:
|
||||
LOG.info(_LI('NFS service on NAS server %s is '
|
||||
'already enabled.'), nas_server.name)
|
||||
|
||||
@staticmethod
|
||||
def create_snapshot(filesystem, name):
|
||||
access_type = enums.FilesystemSnapAccessTypeEnum.CHECKPOINT
|
||||
try:
|
||||
return filesystem.create_snap(name, fs_access_type=access_type)
|
||||
except storops_ex.UnitySnapNameInUseError:
|
||||
LOG.info(_LI('Snapshot %(snap)s on Filesystem %(fs)s already '
|
||||
'exists.'), {'snap': name, 'fs': filesystem.name})
|
||||
|
||||
def create_snap_of_snap(self, src_snap, dst_snap_name, snap_type):
|
||||
access_type = enums.FilesystemSnapAccessTypeEnum.PROTOCOL
|
||||
if snap_type == 'checkpoint':
|
||||
access_type = enums.FilesystemSnapAccessTypeEnum.CHECKPOINT
|
||||
|
||||
if isinstance(src_snap, six.string_types):
|
||||
snap = self.get_snapshot(name=src_snap)
|
||||
else:
|
||||
snap = src_snap
|
||||
|
||||
try:
|
||||
return snap.create_snap(dst_snap_name, fs_access_type=access_type)
|
||||
except storops_ex.UnitySnapNameInUseError:
|
||||
return self.get_snapshot(dst_snap_name)
|
||||
|
||||
def get_snapshot(self, name):
|
||||
return self.system.get_snap(name=name)
|
||||
|
||||
@staticmethod
|
||||
def delete_snapshot(snap):
|
||||
try:
|
||||
snap.delete()
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
LOG.info(_LI('Snapshot %s is already removed.'), snap.name)
|
||||
|
||||
def get_pool(self, name=None):
|
||||
return self.system.get_pool(name=name)
|
||||
|
||||
def get_storage_processor(self, sp_id):
|
||||
sp = self.system.get_sp(sp_id)
|
||||
return sp if sp.existed else None
|
||||
|
||||
def cifs_clear_access(self, share_name, white_list=None):
|
||||
share = self.system.get_cifs_share(name=share_name)
|
||||
share.clear_access(white_list)
|
||||
|
||||
def nfs_clear_access(self, share_name, white_list=None):
|
||||
share = self.system.get_nfs_share(name=share_name)
|
||||
share.clear_access(white_list, force_create_host=True)
|
||||
|
||||
def cifs_allow_access(self, share_name, user_name, access_level):
|
||||
share = self.system.get_cifs_share(name=share_name)
|
||||
|
||||
if access_level == const.ACCESS_LEVEL_RW:
|
||||
cifs_access = enums.ACEAccessLevelEnum.WRITE
|
||||
else:
|
||||
cifs_access = enums.ACEAccessLevelEnum.READ
|
||||
|
||||
share.add_ace(user=user_name, access_level=cifs_access)
|
||||
|
||||
def nfs_allow_access(self, share_name, host_ip, access_level):
|
||||
share = self.system.get_nfs_share(name=share_name)
|
||||
if access_level == const.ACCESS_LEVEL_RW:
|
||||
share.allow_read_write_access(host_ip, force_create_host=True)
|
||||
share.allow_root_access(host_ip, force_create_host=True)
|
||||
else:
|
||||
share.allow_read_only_access(host_ip, force_create_host=True)
|
||||
|
||||
def cifs_deny_access(self, share_name, user_name):
|
||||
share = self.system.get_cifs_share(name=share_name)
|
||||
|
||||
share.delete_ace(user=user_name)
|
||||
|
||||
def nfs_deny_access(self, share_name, host_ip):
|
||||
share = self.system.get_nfs_share(name=share_name)
|
||||
|
||||
try:
|
||||
share.delete_access(host_ip)
|
||||
except storops_ex.UnityHostNotFoundException:
|
||||
LOG.info(_LI('%(host)s access to %(share)s is already removed.'),
|
||||
{'host': host_ip, 'share': share_name})
|
||||
|
||||
def get_ip_ports(self, sp=None):
|
||||
ports = self.system.get_ip_port()
|
||||
link_up_ports = []
|
||||
for port in ports:
|
||||
if port.is_link_up and 'eth' in port.id:
|
||||
if sp and port.sp.id != sp.id:
|
||||
continue
|
||||
|
||||
link_up_ports.append(port)
|
||||
|
||||
return link_up_ports
|
632
manila/share/drivers/emc/plugins/unity/connection.py
Normal file
632
manila/share/drivers/emc/plugins/unity/connection.py
Normal file
@ -0,0 +1,632 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Unity backend for the EMC Manila driver."""
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import units
|
||||
|
||||
storops = importutils.try_import('storops')
|
||||
if storops:
|
||||
from storops import exception as storops_ex
|
||||
from storops.unity import enums
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _, _LE, _LW, _LI
|
||||
from manila.share.drivers.emc.plugins import base as driver
|
||||
from manila.share.drivers.emc.plugins.unity import client
|
||||
from manila.share.drivers.emc.plugins.unity import utils as unity_utils
|
||||
from manila.share.drivers.emc.plugins.vnx import utils as emc_utils
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils
|
||||
|
||||
VERSION = "1.0.0"
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan')
|
||||
|
||||
|
||||
@emc_utils.decorate_all_methods(emc_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
class UnityStorageConnection(driver.StorageConnection):
|
||||
"""Implements Unity specific functionality for EMC Manila driver."""
|
||||
|
||||
IP_ALLOCATIONS = 2
|
||||
|
||||
@emc_utils.log_enter_exit
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnityStorageConnection, self).__init__(*args, **kwargs)
|
||||
self.client = None
|
||||
self.pool_set = None
|
||||
self.port_set = None
|
||||
self.nas_server_pool = None
|
||||
self.storage_processor = None
|
||||
self.reserved_percentage = None
|
||||
self.max_over_subscription_ratio = None
|
||||
|
||||
# props from super class.
|
||||
self.driver_handles_share_servers = True
|
||||
|
||||
def connect(self, emc_share_driver, context):
|
||||
"""Connect to Unity storage."""
|
||||
config = emc_share_driver.configuration
|
||||
storage_ip = config.emc_nas_server
|
||||
username = config.emc_nas_login
|
||||
password = config.emc_nas_password
|
||||
sp_name = config.emc_nas_server_container
|
||||
self.client = client.UnityClient(storage_ip, username, password)
|
||||
|
||||
pool_conf = config.safe_get(
|
||||
'emc_nas_pool_names')
|
||||
self.pool_set = self._get_managed_pools(pool_conf)
|
||||
|
||||
self.reserved_percentage = config.safe_get(
|
||||
'reserved_share_percentage')
|
||||
if self.reserved_percentage is None:
|
||||
self.reserved_percentage = 0
|
||||
|
||||
self.max_over_subscription_ratio = config.safe_get(
|
||||
'max_over_subscription_ratio')
|
||||
|
||||
self._config_sp(sp_name)
|
||||
|
||||
port_conf = config.safe_get(
|
||||
'emc_interface_ports')
|
||||
self.port_set = self._get_managed_ports(
|
||||
port_conf, self.storage_processor)
|
||||
|
||||
pool_name = config.emc_nas_server_pool
|
||||
self._config_pool(pool_name)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Check for setup error."""
|
||||
|
||||
def create_share(self, context, share, share_server=None):
|
||||
"""Create a share and export it based on protocol used."""
|
||||
share_name = share['id']
|
||||
size = share['size'] * units.Gi
|
||||
|
||||
# Check share's protocol.
|
||||
# Throw an exception immediately if it is an invalid protocol.
|
||||
share_proto = share['share_proto'].upper()
|
||||
proto_enum = self._get_proto_enum(share_proto)
|
||||
|
||||
# Get pool name from share host field
|
||||
pool_name = self._get_pool_name_from_host(share['host'])
|
||||
# Get share server name from share server
|
||||
server_name = self._get_server_name(share_server)
|
||||
|
||||
pool = self.client.get_pool(pool_name)
|
||||
try:
|
||||
nas_server = self.client.get_nas_server(server_name)
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
message = (_LE("Failed to get NAS server %(server)s when "
|
||||
"creating the share %(share)s.") %
|
||||
{'server': server_name, 'share': share_name})
|
||||
LOG.error(message)
|
||||
raise exception.EMCUnityError(err=message)
|
||||
|
||||
filesystem = self.client.create_filesystem(
|
||||
pool, nas_server, share_name, size, proto=proto_enum)
|
||||
|
||||
locations = None
|
||||
if share_proto == 'CIFS':
|
||||
self.client.create_cifs_share(filesystem, share_name)
|
||||
|
||||
locations = self._get_cifs_location(
|
||||
nas_server.file_interface, share_name)
|
||||
elif share_proto == 'NFS':
|
||||
self.client.create_nfs_share(filesystem, share_name)
|
||||
|
||||
locations = self._get_nfs_location(
|
||||
nas_server.file_interface, share_name)
|
||||
|
||||
return locations
|
||||
|
||||
def create_share_from_snapshot(self, context, share, snapshot,
|
||||
share_server=None):
|
||||
"""Create a share from a snapshot - clone a snapshot."""
|
||||
share_name = share['id']
|
||||
|
||||
# Check share's protocol.
|
||||
# Throw an exception immediately if it is an invalid protocol.
|
||||
share_proto = share['share_proto'].upper()
|
||||
self._validate_share_protocol(share_proto)
|
||||
|
||||
# Get share server name from share server
|
||||
server_name = self._get_server_name(share_server)
|
||||
|
||||
try:
|
||||
nas_server = self.client.get_nas_server(server_name)
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
message = (_LE("Failed to get NAS server %(server)s when "
|
||||
"creating the share %(share)s.") %
|
||||
{'server': server_name, 'share': share_name})
|
||||
LOG.error(message)
|
||||
raise exception.EMCUnityError(err=message)
|
||||
|
||||
backend_snap = self.client.create_snap_of_snap(snapshot['id'],
|
||||
share_name,
|
||||
snap_type='snapshot')
|
||||
|
||||
locations = None
|
||||
if share_proto == 'CIFS':
|
||||
self.client.create_cifs_share(backend_snap, share_name)
|
||||
|
||||
locations = self._get_cifs_location(
|
||||
nas_server.file_interface, share_name)
|
||||
elif share_proto == 'NFS':
|
||||
self.client.create_nfs_share(backend_snap, share_name)
|
||||
|
||||
locations = self._get_nfs_location(
|
||||
nas_server.file_interface, share_name)
|
||||
|
||||
return locations
|
||||
|
||||
def delete_share(self, context, share, share_server=None):
|
||||
"""Delete a share."""
|
||||
share_name = share['id']
|
||||
try:
|
||||
backend_share = self.client.get_share(share_name,
|
||||
share['share_proto'])
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
LOG.warning(_LW("Share %s is not found when deleting the share"),
|
||||
share_name)
|
||||
return
|
||||
|
||||
# Share created by the API create_share_from_snapshot()
|
||||
if self._is_share_from_snapshot(backend_share):
|
||||
filesystem = backend_share.snap.filesystem
|
||||
self.client.delete_snapshot(backend_share.snap)
|
||||
else:
|
||||
filesystem = backend_share.filesystem
|
||||
self.client.delete_share(backend_share)
|
||||
|
||||
if self._is_isolated_filesystem(filesystem):
|
||||
self.client.delete_filesystem(filesystem)
|
||||
|
||||
def extend_share(self, share, new_size, share_server=None):
|
||||
backend_share = self.client.get_share(share['id'],
|
||||
share['share_proto'])
|
||||
|
||||
if not self._is_share_from_snapshot(backend_share):
|
||||
backend_share.filesystem.extend(new_size * units.Gi)
|
||||
else:
|
||||
share_id = share['id']
|
||||
reason = _LE("Driver does not support extending a "
|
||||
"snapshot based share.")
|
||||
raise exception.ShareExtendingError(share_id=share_id,
|
||||
reason=reason)
|
||||
|
||||
def create_snapshot(self, context, snapshot, share_server=None):
|
||||
"""Create snapshot from share."""
|
||||
share_name = snapshot['share_id']
|
||||
share_proto = snapshot['share']['share_proto']
|
||||
backend_share = self.client.get_share(share_name, share_proto)
|
||||
|
||||
snapshot_name = snapshot['id']
|
||||
if self._is_share_from_snapshot(backend_share):
|
||||
self.client.create_snap_of_snap(backend_share.snap,
|
||||
snapshot_name,
|
||||
snap_type='checkpoint')
|
||||
else:
|
||||
self.client.create_snapshot(backend_share.filesystem,
|
||||
snapshot_name)
|
||||
|
||||
def delete_snapshot(self, context, snapshot, share_server=None):
|
||||
"""Delete a snapshot."""
|
||||
snap = self.client.get_snapshot(snapshot['id'])
|
||||
self.client.delete_snapshot(snap)
|
||||
|
||||
def update_access(self, context, share, access_rules, add_rules,
|
||||
delete_rules, share_server=None):
|
||||
# adding rules
|
||||
if add_rules:
|
||||
for rule in add_rules:
|
||||
self.allow_access(context, share, rule, share_server)
|
||||
|
||||
# deleting rules
|
||||
if delete_rules:
|
||||
for rule in delete_rules:
|
||||
self.deny_access(context, share, rule, share_server)
|
||||
|
||||
# recovery mode
|
||||
if not (add_rules or delete_rules):
|
||||
white_list = []
|
||||
for rule in access_rules:
|
||||
self.allow_access(context, share, rule, share_server)
|
||||
white_list.append(rule['access_to'])
|
||||
self.clear_access(share, white_list)
|
||||
|
||||
def clear_access(self, share, white_list=None):
|
||||
share_proto = share['share_proto'].upper()
|
||||
share_name = share['id']
|
||||
if share_proto == 'CIFS':
|
||||
self.client.cifs_clear_access(share_name, white_list)
|
||||
elif share_proto == 'NFS':
|
||||
self.client.nfs_clear_access(share_name, white_list)
|
||||
|
||||
def allow_access(self, context, share, access, share_server=None):
|
||||
"""Allow access to a share."""
|
||||
access_level = access['access_level']
|
||||
if access_level not in const.ACCESS_LEVELS:
|
||||
raise exception.InvalidShareAccessLevel(level=access_level)
|
||||
|
||||
share_proto = share['share_proto'].upper()
|
||||
|
||||
self._validate_share_protocol(share_proto)
|
||||
self._validate_share_access_type(share, access)
|
||||
|
||||
if share_proto == 'CIFS':
|
||||
self._cifs_allow_access(share, access)
|
||||
elif share_proto == 'NFS':
|
||||
self._nfs_allow_access(share, access)
|
||||
|
||||
def deny_access(self, context, share, access, share_server):
|
||||
"""Deny access to a share."""
|
||||
share_proto = share['share_proto'].upper()
|
||||
|
||||
self._validate_share_protocol(share_proto)
|
||||
self._validate_share_access_type(share, access)
|
||||
|
||||
if share_proto == 'CIFS':
|
||||
self._cifs_deny_access(share, access)
|
||||
elif share_proto == 'NFS':
|
||||
self._nfs_deny_access(share, access)
|
||||
|
||||
def ensure_share(self, context, share, share_server):
|
||||
"""Ensure that the share is exported."""
|
||||
share_name = share['id']
|
||||
share_proto = share['share_proto']
|
||||
|
||||
backend_share = self.client.get_share(share_name, share_proto)
|
||||
if not backend_share.existed:
|
||||
raise exception.ShareNotFound(share_id=share_name)
|
||||
|
||||
def update_share_stats(self, stats_dict):
|
||||
"""Communicate with EMCNASClient to get the stats."""
|
||||
stats_dict['driver_version'] = VERSION
|
||||
stats_dict['pools'] = []
|
||||
|
||||
for pool in self.client.get_pool():
|
||||
if pool.name in self.pool_set:
|
||||
# the unit of following numbers are GB
|
||||
total_size = float(pool.size_total)
|
||||
used_size = float(pool.size_used)
|
||||
|
||||
pool_stat = {
|
||||
'pool_name': pool.name,
|
||||
'thin_provisioning': True,
|
||||
'total_capacity_gb': total_size,
|
||||
'free_capacity_gb': total_size - used_size,
|
||||
'allocated_capacity_gb': used_size,
|
||||
'provisioned_capacity_gb': float(pool.size_subscribed),
|
||||
'qos': False,
|
||||
'reserved_percentage': self.reserved_percentage,
|
||||
'max_over_subscription_ratio':
|
||||
self.max_over_subscription_ratio,
|
||||
}
|
||||
stats_dict['pools'].append(pool_stat)
|
||||
|
||||
if not stats_dict.get('pools'):
|
||||
message = _LE("Failed to update storage pool.")
|
||||
LOG.error(message)
|
||||
raise exception.EMCUnityError(err=message)
|
||||
|
||||
def get_pool(self, share):
|
||||
"""Get the pool name of the share."""
|
||||
backend_share = self.client.get_share(
|
||||
share['id'], share['share_proto'])
|
||||
|
||||
return backend_share.filesystem.pool.name
|
||||
|
||||
def get_network_allocations_number(self):
|
||||
"""Returns number of network allocations for creating VIFs."""
|
||||
return self.IP_ALLOCATIONS
|
||||
|
||||
def setup_server(self, network_info, metadata=None):
|
||||
"""Set up and configures share server with given network parameters."""
|
||||
server_name = network_info['server_id']
|
||||
nas_server = self.client.create_nas_server(server_name,
|
||||
self.storage_processor,
|
||||
self.nas_server_pool)
|
||||
|
||||
try:
|
||||
for network in network_info['network_allocations']:
|
||||
self._create_network_interface(nas_server, network)
|
||||
|
||||
self._handle_security_services(
|
||||
nas_server, network_info['security_services'])
|
||||
|
||||
return {'share_server_name': server_name}
|
||||
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Could not setup server. Reason: %s.'), ex)
|
||||
server_details = {'share_server_name': server_name}
|
||||
self.teardown_server(
|
||||
server_details, network_info['security_services'])
|
||||
|
||||
def teardown_server(self, server_details, security_services=None):
|
||||
"""Teardown share server."""
|
||||
if not server_details:
|
||||
LOG.debug('Server details are empty.')
|
||||
return
|
||||
|
||||
server_name = server_details.get('share_server_name')
|
||||
if not server_name:
|
||||
LOG.debug('No share server found for server %s.',
|
||||
server_details.get('instance_id'))
|
||||
return
|
||||
|
||||
username = None
|
||||
password = None
|
||||
for security_service in security_services:
|
||||
if security_service['type'] == 'active_directory':
|
||||
username = security_service['user']
|
||||
password = security_service['password']
|
||||
break
|
||||
|
||||
self.client.delete_nas_server(server_name, username, password)
|
||||
|
||||
def _cifs_allow_access(self, share, access):
|
||||
"""Allow access to CIFS share."""
|
||||
self.client.cifs_allow_access(
|
||||
share['id'], access['access_to'], access['access_level'])
|
||||
|
||||
def _cifs_deny_access(self, share, access):
|
||||
"""Deny access to CIFS share."""
|
||||
self.client.cifs_deny_access(share['id'], access['access_to'])
|
||||
|
||||
def _config_pool(self, pool_name):
|
||||
try:
|
||||
self.nas_server_pool = self.client.get_pool(pool_name)
|
||||
except storops_ex.UnityResourceNotFoundError:
|
||||
message = (_LE("The storage pools %s to store NAS server "
|
||||
"configuration do not exist.") % pool_name)
|
||||
LOG.error(message)
|
||||
raise exception.BadConfigurationException(reason=message)
|
||||
|
||||
def _config_sp(self, sp_name):
|
||||
self.storage_processor = self.client.get_storage_processor(
|
||||
sp_name.lower())
|
||||
if self.storage_processor is None:
|
||||
message = (_LE("The storage processor %s does not exist or "
|
||||
"is unavailable. Please reconfigure it in "
|
||||
"manila.conf.") % sp_name)
|
||||
LOG.error(message)
|
||||
raise exception.BadConfigurationException(reason=message)
|
||||
|
||||
def _create_network_interface(self, nas_server, network):
|
||||
ip_addr = network['ip_address']
|
||||
netmask = utils.cidr_to_netmask(network['cidr'])
|
||||
gateway = network['gateway']
|
||||
vlan_id = network['segmentation_id']
|
||||
if network['network_type'] not in SUPPORTED_NETWORK_TYPES:
|
||||
msg = _('The specified network type %s is unsupported by '
|
||||
'the EMC Unity driver')
|
||||
raise exception.NetworkBadConfigurationException(
|
||||
reason=msg % network['network_type'])
|
||||
|
||||
# Create the interfaces on NAS server
|
||||
self.client.create_interface(nas_server,
|
||||
ip_addr,
|
||||
netmask,
|
||||
gateway,
|
||||
ports=self.port_set,
|
||||
vlan_id=vlan_id)
|
||||
|
||||
@staticmethod
|
||||
def _get_cifs_location(file_interfaces, share_name):
|
||||
return [
|
||||
{'path': r'\\%(interface)s\%(share_name)s' % {
|
||||
'interface': interface.ip_address,
|
||||
'share_name': share_name}
|
||||
}
|
||||
for interface in file_interfaces
|
||||
]
|
||||
|
||||
def _get_managed_pools(self, pool_conf):
|
||||
# Get the real pools from the backend storage
|
||||
real_pools = set([pool.name for pool in self.client.get_pool()])
|
||||
|
||||
if not pool_conf:
|
||||
LOG.debug("No storage pool is specified, so all pools in storage "
|
||||
"system will be managed.")
|
||||
return real_pools
|
||||
|
||||
matched_pools, unmanaged_pools = unity_utils.do_match(real_pools,
|
||||
pool_conf)
|
||||
|
||||
if not matched_pools:
|
||||
msg = (_("All the specified storage pools to be managed "
|
||||
"do not exist. Please check your configuration "
|
||||
"emc_nas_pool_names in manila.conf. "
|
||||
"The available pools in the backend are %s") %
|
||||
",".join(real_pools))
|
||||
raise exception.BadConfigurationException(reason=msg)
|
||||
|
||||
if unmanaged_pools:
|
||||
LOG.info(_LI("The following specified storage pools "
|
||||
"are not managed by the backend: "
|
||||
"%(un_managed)s. This host will only manage "
|
||||
"the storage pools: %(exist)s"),
|
||||
{'un_managed': ",".join(unmanaged_pools),
|
||||
'exist': ",".join(matched_pools)})
|
||||
else:
|
||||
LOG.debug("Storage pools: %s will be managed.",
|
||||
",".join(matched_pools))
|
||||
|
||||
return matched_pools
|
||||
|
||||
def _get_managed_ports(self, port_conf, sp):
|
||||
# Get the real ports from the backend storage
|
||||
real_ports = set([port.id for port in self.client.get_ip_ports(sp)])
|
||||
|
||||
if not port_conf:
|
||||
LOG.debug("No ports are specified, so all ports in storage "
|
||||
"system will be managed.")
|
||||
return real_ports
|
||||
|
||||
matched_ports, unmanaged_ports = unity_utils.do_match(real_ports,
|
||||
port_conf)
|
||||
|
||||
if not matched_ports:
|
||||
msg = (_("All the specified storage ports to be managed "
|
||||
"do not exist. Please check your configuration "
|
||||
"emc_interface_ports in manila.conf. "
|
||||
"The available ports in the backend are %s") %
|
||||
",".join(real_ports))
|
||||
raise exception.BadConfigurationException(reason=msg)
|
||||
|
||||
if unmanaged_ports:
|
||||
LOG.info(_LI("The following specified ports "
|
||||
"are not managed by the backend: "
|
||||
"%(un_managed)s. This host will only manage "
|
||||
"the storage ports: %(exist)s"),
|
||||
{'un_managed': ",".join(unmanaged_ports),
|
||||
'exist': ",".join(matched_ports)})
|
||||
else:
|
||||
LOG.debug("Ports: %s will be managed.",
|
||||
",".join(matched_ports))
|
||||
|
||||
return matched_ports
|
||||
|
||||
@staticmethod
|
||||
def _get_nfs_location(file_interfaces, share_name):
|
||||
return [
|
||||
{'path': '%(interface)s:/%(share_name)s' % {
|
||||
'interface': interface.ip_address,
|
||||
'share_name': share_name}
|
||||
}
|
||||
for interface in file_interfaces
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _get_pool_name_from_host(host):
|
||||
pool_name = share_utils.extract_host(host, level='pool')
|
||||
if not pool_name:
|
||||
message = (_("Pool is not available in the share host %s.") %
|
||||
host)
|
||||
raise exception.InvalidHost(reason=message)
|
||||
|
||||
return pool_name
|
||||
|
||||
@staticmethod
|
||||
def _get_proto_enum(share_proto):
|
||||
share_proto = share_proto.upper()
|
||||
UnityStorageConnection._validate_share_protocol(share_proto)
|
||||
|
||||
if share_proto == 'CIFS':
|
||||
return enums.FSSupportedProtocolEnum.CIFS
|
||||
elif share_proto == 'NFS':
|
||||
return enums.FSSupportedProtocolEnum.NFS
|
||||
|
||||
@staticmethod
|
||||
def _get_server_name(share_server):
|
||||
if not share_server:
|
||||
msg = _('Share server not provided.')
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
server_name = share_server.get(
|
||||
'backend_details', {}).get('share_server_name')
|
||||
|
||||
if server_name is None:
|
||||
msg = (_LE("Name of the share server %s not found.")
|
||||
% share_server['id'])
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
return server_name
|
||||
|
||||
def _handle_security_services(self, nas_server, security_services):
|
||||
kerberos_enabled = False
|
||||
# Support 'active_directory' and 'kerberos'
|
||||
for security_service in security_services:
|
||||
service_type = security_service['type']
|
||||
if service_type == 'active_directory':
|
||||
# Create DNS server for NAS server
|
||||
domain = security_service['domain']
|
||||
dns_ip = security_service['dns_ip']
|
||||
self.client.create_dns_server(nas_server,
|
||||
domain,
|
||||
dns_ip)
|
||||
|
||||
# Enable CIFS service
|
||||
username = security_service['user']
|
||||
password = security_service['password']
|
||||
self.client.enable_cifs_service(nas_server,
|
||||
domain=domain,
|
||||
username=username,
|
||||
password=password)
|
||||
elif service_type == 'kerberos':
|
||||
# Enable NFS service with kerberos
|
||||
kerberos_enabled = True
|
||||
# TODO(jay.xu): enable nfs service with kerberos
|
||||
LOG.warning(_LW('Kerberos is not supported by '
|
||||
'EMC Unity manila driver plugin.'))
|
||||
elif service_type == 'ldap':
|
||||
LOG.warning(_LW('LDAP is not supported by '
|
||||
'EMC Unity manila driver plugin.'))
|
||||
else:
|
||||
LOG.warning(_LW('Unknown security service type: %s.'),
|
||||
service_type)
|
||||
|
||||
if not kerberos_enabled:
|
||||
# Enable NFS service without kerberos
|
||||
self.client.enable_nfs_service(nas_server)
|
||||
|
||||
def _nfs_allow_access(self, share, access):
|
||||
"""Allow access to NFS share."""
|
||||
self.client.nfs_allow_access(
|
||||
share['id'], access['access_to'], access['access_level'])
|
||||
|
||||
def _nfs_deny_access(self, share, access):
|
||||
"""Deny access to NFS share."""
|
||||
self.client.nfs_deny_access(share['id'], access['access_to'])
|
||||
|
||||
@staticmethod
|
||||
def _is_isolated_filesystem(filesystem):
|
||||
filesystem.update()
|
||||
return (
|
||||
not filesystem.has_snap() and
|
||||
not (filesystem.cifs_share or filesystem.nfs_share)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_share_from_snapshot(share):
|
||||
return True if share.snap else False
|
||||
|
||||
@staticmethod
|
||||
def _validate_share_access_type(share, access):
|
||||
reason = None
|
||||
share_proto = share['share_proto'].upper()
|
||||
|
||||
if share_proto == 'CIFS' and access['access_type'] != 'user':
|
||||
reason = _('Only user access type allowed for CIFS share.')
|
||||
elif share_proto == 'NFS' and access['access_type'] != 'ip':
|
||||
reason = _('Only IP access type allowed for NFS share.')
|
||||
|
||||
if reason:
|
||||
raise exception.InvalidShareAccess(reason=reason)
|
||||
|
||||
@staticmethod
|
||||
def _validate_share_protocol(share_proto):
|
||||
if share_proto not in ('NFS', 'CIFS'):
|
||||
raise exception.InvalidShare(
|
||||
reason=(_('Invalid NAS protocol supplied: %s.') %
|
||||
share_proto))
|
34
manila/share/drivers/emc/plugins/unity/utils.py
Normal file
34
manila/share/drivers/emc/plugins/unity/utils.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
""" Utility module for EMC Unity Manila Driver """
|
||||
|
||||
import fnmatch
|
||||
|
||||
|
||||
def do_match(full, matcher_list):
|
||||
matched = set()
|
||||
|
||||
full = set([item.strip() for item in full])
|
||||
if matcher_list is None:
|
||||
# default to all
|
||||
matcher_list = set('*')
|
||||
else:
|
||||
matcher_list = set([item.strip() for item in matcher_list])
|
||||
|
||||
for item in full:
|
||||
for matcher in matcher_list:
|
||||
if fnmatch.fnmatchcase(item, matcher):
|
||||
matched.add(item)
|
||||
return matched, full - matched
|
@ -459,7 +459,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
||||
raise exception.EMCVnxXMLAPIError(err=message)
|
||||
|
||||
real_pools = set([item for item in backend_pools])
|
||||
conf_pools = set([item.strip() for item in pools.split(",")])
|
||||
conf_pools = set([item.strip() for item in pools])
|
||||
matched_pools, unmatched_pools = vnx_utils.do_match_any(
|
||||
real_pools, conf_pools)
|
||||
|
||||
|
20
manila/tests/share/drivers/emc/plugins/unity/__init__.py
Normal file
20
manila/tests/share/drivers/emc/plugins/unity/__init__.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import sys
|
||||
|
||||
sys.modules['storops'] = mock.Mock()
|
||||
sys.modules['storops.unity'] = mock.Mock()
|
@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class UnityFakeException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnityException(UnityFakeException):
|
||||
pass
|
||||
|
||||
|
||||
class UnitySmbShareNameExistedError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityFileSystemNameAlreadyExisted(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityNasServerNameUsedError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityNfsShareNameExistedError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnitySnapNameInUseError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityIpAddressUsedError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityResourceNotFoundError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityOneDnsPerNasServerError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnitySmbNameInUseError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityNfsAlreadyEnabledError(UnityException):
|
||||
pass
|
||||
|
||||
|
||||
class UnityHostNotFoundException(UnityException):
|
||||
pass
|
235
manila/tests/share/drivers/emc/plugins/unity/mocked_manila.yaml
Normal file
235
manila/tests/share/drivers/emc/plugins/unity/mocked_manila.yaml
Normal file
@ -0,0 +1,235 @@
|
||||
network_allocations:
|
||||
_type: 'network_allocations'
|
||||
_properties: &network_allocations_prop
|
||||
- id: '04ac4c27-9cf7-4406-809c-13edc93e4849'
|
||||
ip_address: 'fake_ip_addr_1'
|
||||
cidr: '192.168.1.0/24'
|
||||
segmentation_id: null
|
||||
gateway: '192.168.1.1'
|
||||
network_type: flat
|
||||
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
||||
ip_address: 'fake_ip_addr_2'
|
||||
cidr: '192.168.1.0/24'
|
||||
segmentation_id: null
|
||||
gateway: '192.168.1.1'
|
||||
network_type: flat
|
||||
|
||||
network_allocations_vlan:
|
||||
_type: 'network_allocations'
|
||||
_properties: &network_allocations_vlan_prop
|
||||
- id: '04ac4c27-9cf7-4406-809c-13edc93e4849'
|
||||
ip_address: 'fake_ip_addr_1'
|
||||
cidr: '192.168.1.0/24'
|
||||
segmentation_id: 160
|
||||
gateway: '192.168.1.1'
|
||||
network_type: vlan
|
||||
- id: '0cf87de7-5c65-4036-8b6a-e8176c356958'
|
||||
ip_address: 'fake_ip_addr_2'
|
||||
cidr: '192.168.1.0/24'
|
||||
segmentation_id: 160
|
||||
gateway: '192.168.1.1'
|
||||
network_type: vlan
|
||||
|
||||
network_allocations_vxlan:
|
||||
_type: 'network_allocations'
|
||||
_properties: &network_allocations_vxlan_prop
|
||||
- id: '04ac4c27-9cf7-4406-809c-13edc93e4849'
|
||||
ip_address: 'fake_ip_addr_1'
|
||||
cidr: '192.168.1.0/24'
|
||||
segmentation_id: 123
|
||||
gateway: '192.168.1.1'
|
||||
network_type: vxlan
|
||||
|
||||
active_directory:
|
||||
_type: 'security_service'
|
||||
_properties: &active_directory_prop
|
||||
type: 'active_directory'
|
||||
domain: 'fake_domain_name'
|
||||
dns_ip: 'fake_dns_ip'
|
||||
user: 'fake_user'
|
||||
password: 'fake_password'
|
||||
|
||||
kerberos:
|
||||
_type: 'security_service'
|
||||
_properties: &kerberos_prop
|
||||
<<: *active_directory_prop
|
||||
type: 'kerberos'
|
||||
server: 'fake_server'
|
||||
|
||||
security_services:
|
||||
_type: 'security_services'
|
||||
_properties: &security_services_prop
|
||||
services: [*active_directory_prop, *kerberos_prop]
|
||||
|
||||
|
||||
network_info__flat:
|
||||
_type: 'network_info'
|
||||
_properties: &network_info_flat_prop
|
||||
name: 'share_network'
|
||||
neutron_subnet_id: 'a3f3eeac-0b16-4932-8c03-0a37003644ff'
|
||||
network_type: 'flat'
|
||||
neutron_net_id: 'e6c96730-2bcf-4ce3-86fa-7cb7740086cb'
|
||||
ip_version: 4
|
||||
id: '232d8218-2743-41d1-832b-4194626e691e'
|
||||
network_allocations: *network_allocations_prop
|
||||
server_id: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
security_services: []
|
||||
|
||||
network_info__vlan:
|
||||
_type: 'network_info'
|
||||
_properties: &network_info__vlan_prop
|
||||
<<: *network_info_flat_prop
|
||||
network_type: 'vlan'
|
||||
network_allocations: *network_allocations_vlan_prop
|
||||
|
||||
network_info__vxlan:
|
||||
_type: 'network_info'
|
||||
_properties: &network_info__vxlan_prop
|
||||
<<: *network_info_flat_prop
|
||||
network_type: 'vxlan'
|
||||
network_allocations: *network_allocations_vxlan_prop
|
||||
|
||||
network_info__active_directory:
|
||||
_type: 'network_info'
|
||||
_properties:
|
||||
<<: *network_info__vlan_prop
|
||||
security_services: [*active_directory_prop]
|
||||
|
||||
network_info__kerberos:
|
||||
_type: 'network_info'
|
||||
_properties:
|
||||
<<: *network_info_flat_prop
|
||||
security_services: [*kerberos_prop]
|
||||
|
||||
share_server:
|
||||
_type: 'share_server'
|
||||
_properties: &share_server_prop
|
||||
status: 'active'
|
||||
share_network: *network_info_flat_prop
|
||||
share_network_id: '232d8218-2743-41d1-832b-4194626e691e'
|
||||
host: 'openstack@VNX'
|
||||
backend_details:
|
||||
share_server_name: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
network_allocations: *network_allocations_prop
|
||||
id: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
|
||||
share_server__no_share_server_name:
|
||||
_type: 'share_server'
|
||||
_properties:
|
||||
<<: *share_server_prop
|
||||
backend_details:
|
||||
share_server_name: None
|
||||
id: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
|
||||
server_detail:
|
||||
_type: 'server_detail'
|
||||
_properties: &server_detail_prop
|
||||
share_server_name: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
|
||||
cifs_share:
|
||||
_type: 'share'
|
||||
_properties: &cifs_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: 1
|
||||
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: []
|
||||
is_public: False
|
||||
|
||||
nfs_share:
|
||||
_type: 'share'
|
||||
_properties: &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: 1
|
||||
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: []
|
||||
is_public: False
|
||||
|
||||
invalid_share:
|
||||
_type: 'share'
|
||||
_properties: &invalid_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: 1
|
||||
user_id: '19bbda71b578471a93363653dcb4c61d'
|
||||
status: 'creating'
|
||||
share_type_id: '57679eab-3e67-4052-b180-62b609670e93'
|
||||
host: 'openstack@VNX#Pool_2'
|
||||
display_name: 'nfs_share'
|
||||
share_proto: 'fake_proto'
|
||||
export_locations: []
|
||||
is_public: False
|
||||
|
||||
snapshot:
|
||||
_type: 'snapshot'
|
||||
_properties: &snapshot_prop
|
||||
status: 'creating'
|
||||
share_instance_id: '27e4625e-c336-4749-85bc-634216755fbc'
|
||||
share:
|
||||
share_proto: 'CIFS'
|
||||
snapshot_id: '345476cc-32ab-4565-ba88-e4733b7ffa0e'
|
||||
progress: '0%'
|
||||
id: 'ab411797-b1cf-4035-bf14-8771a7bf1805'
|
||||
share_id: '27e4625e-c336-4749-85bc-634216755fbc'
|
||||
|
||||
cifs_rw_access:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'rw'
|
||||
access_to: 'administrator'
|
||||
access_type: 'user'
|
||||
|
||||
cifs_ro_access:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'ro'
|
||||
access_to: 'administrator'
|
||||
access_type: 'user'
|
||||
|
||||
nfs_rw_access:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'rw'
|
||||
access_to: '192.168.1.1'
|
||||
access_type: 'ip'
|
||||
|
||||
nfs_rw_access_cidr:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'rw'
|
||||
access_to: '192.168.1.0/24'
|
||||
access_type: 'ip'
|
||||
|
||||
nfs_ro_access:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'ro'
|
||||
access_to: '192.168.1.1'
|
||||
access_type: 'ip'
|
||||
|
||||
invalid_access:
|
||||
_type: 'access'
|
||||
_properties:
|
||||
access_level: 'fake_access_level'
|
||||
access_to: 'fake_access_to'
|
||||
access_type: 'fake_type'
|
958
manila/tests/share/drivers/emc/plugins/unity/mocked_unity.yaml
Normal file
958
manila/tests/share/drivers/emc/plugins/unity/mocked_unity.yaml
Normal file
@ -0,0 +1,958 @@
|
||||
sp_a: &sp_a
|
||||
_properties:
|
||||
name: 'SPA'
|
||||
id: 'SPA'
|
||||
existed: true
|
||||
|
||||
sp_b: &sp_b
|
||||
_properties:
|
||||
name: 'SPB'
|
||||
id: 'SPB'
|
||||
existed: true
|
||||
|
||||
sp_c: &sp_invalid
|
||||
_properties:
|
||||
id: 'SPC'
|
||||
existed: false
|
||||
|
||||
interface_1: &interface_1
|
||||
_properties:
|
||||
ip_address: 'fake_ip_addr_1'
|
||||
|
||||
interface_2: &interface_2
|
||||
_properties:
|
||||
ip_address: 'fake_ip_addr_2'
|
||||
|
||||
nas_server: &nas_server
|
||||
_properties: &nas_server_prop
|
||||
name: '78fd845f-8e7d-487f-bfde-051d83e78103'
|
||||
file_interface: [*interface_1, *interface_2]
|
||||
current_sp: *sp_a
|
||||
|
||||
filesystem_base: &filesystem_base
|
||||
_properties: &filesystem_base_prop
|
||||
name: 'fake_filesystem_name'
|
||||
id: 'fake_filesystem_id'
|
||||
size_total: 50000000000
|
||||
is_thin_enabled: true
|
||||
pool: null
|
||||
nas_server: null
|
||||
cifs_share: []
|
||||
nfs_share: []
|
||||
_methods:
|
||||
has_snap: False
|
||||
|
||||
snap_base:
|
||||
_properties: &snap_base_prop
|
||||
name: 'fake_snap_name'
|
||||
id: 'fake_snap_id'
|
||||
size: 50000000000
|
||||
filesystem: *filesystem_base
|
||||
|
||||
share_base:
|
||||
_properties: &share_base_prop
|
||||
name: 'fake_share_name'
|
||||
id: 'fake_share_id'
|
||||
filesystem: null
|
||||
snap: null
|
||||
|
||||
cifs_share_base: &cifs_share_base
|
||||
_properties: &cifs_share_base_prop
|
||||
<<: *share_base_prop
|
||||
|
||||
nfs_share_base: &nfs_share_base
|
||||
_properties: &nfs_share_base_prop
|
||||
<<: *share_base_prop
|
||||
|
||||
pool_base:
|
||||
_properties: &pool_base_prop
|
||||
name: 'fake_pool_name'
|
||||
pool_id: 0
|
||||
state: Ready
|
||||
user_capacity_gbs: 1311
|
||||
total_subscribed_capacity_gbs: 131
|
||||
available_capacity_gbs: 132
|
||||
percent_full_threshold: 70
|
||||
fast_cache: True
|
||||
|
||||
|
||||
pool_1: &pool_1
|
||||
_properties: &pool_1_prop
|
||||
<<: *pool_base_prop
|
||||
name: 'pool_1'
|
||||
size_total: 500000
|
||||
size_used: 10000
|
||||
size_subscribed: 30000
|
||||
|
||||
pool_2: &pool_2
|
||||
_properties: &pool_2_prop
|
||||
<<: *pool_base_prop
|
||||
name: 'pool_2'
|
||||
size_total: 600000
|
||||
size_used: 20000
|
||||
size_subscribed: 40000
|
||||
|
||||
nas_server_pool: &nas_server_pool
|
||||
_properties:
|
||||
<<: *pool_base_prop
|
||||
name: 'nas_server_pool'
|
||||
|
||||
port_base:
|
||||
_properties: &port_base_prop
|
||||
is_link_up: true
|
||||
id: 'fake_name'
|
||||
sp: *sp_a
|
||||
|
||||
port_1: &port_1
|
||||
_properties:
|
||||
<<: *port_base_prop
|
||||
is_link_up: true
|
||||
id: 'spa_eth1'
|
||||
sp: *sp_a
|
||||
|
||||
port_2: &port_2
|
||||
_properties:
|
||||
<<: *port_base_prop
|
||||
is_link_up: true
|
||||
id: 'spa_eth2'
|
||||
sp: *sp_a
|
||||
|
||||
port_3: &port_internal_port
|
||||
_properties:
|
||||
<<: *port_base_prop
|
||||
is_link_up: true
|
||||
id: 'internal_port'
|
||||
sp: *sp_a
|
||||
|
||||
unity_base: &unity_base
|
||||
_methods: &unity_base_method
|
||||
get_sp: *sp_a
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool]
|
||||
get_ip_port: [*port_1, *port_2]
|
||||
|
||||
test_connect: &test_connect
|
||||
unity: *unity_base
|
||||
|
||||
test_connect__invalid_sp_configuration:
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_sp: *sp_invalid
|
||||
|
||||
test_connect__invalid_pool_configuration: *test_connect
|
||||
|
||||
test_create_nfs_share:
|
||||
nfs_share: &nfs_share__test_create_nfs_share
|
||||
_properties:
|
||||
<<: *nfs_share_base_prop
|
||||
name: 'cb532599-8dc6-4c3e-bb21-74ea54be566c'
|
||||
|
||||
filesystem: &filesystem__test_create_nfs_share
|
||||
_properties: &filesystem_prop__test_create_nfs_share
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
create_nfs_share: *nfs_share__test_create_nfs_share
|
||||
|
||||
pool: &pool__test_create_nfs_share
|
||||
_properties:
|
||||
<<: *pool_base_prop
|
||||
name: 'Pool_2'
|
||||
_methods:
|
||||
create_filesystem: *filesystem__test_create_nfs_share
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool],
|
||||
*nas_server_pool, *pool__test_create_nfs_share]
|
||||
get_nas_server: *nas_server
|
||||
|
||||
test_create_cifs_share:
|
||||
cifs_share: &cifs_share__test_create_cifs_share
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
enable_ace:
|
||||
|
||||
filesystem: &filesystem__test_create_cifs_share
|
||||
_properties: &filesystem_prop__test_create_cifs_share
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
create_cifs_share: *cifs_share__test_create_cifs_share
|
||||
|
||||
pool: &pool__test_create_cifs_share
|
||||
_properties:
|
||||
<<: *pool_base_prop
|
||||
name: 'Pool_2'
|
||||
_methods:
|
||||
create_filesystem: *filesystem__test_create_cifs_share
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
|
||||
*pool__test_create_cifs_share]
|
||||
get_nas_server: *nas_server
|
||||
|
||||
test_create_share_with_invalid_share_server:
|
||||
pool: &pool__test_create_share_with_invalid_share_server
|
||||
_properties:
|
||||
<<: *pool_base_prop
|
||||
name: 'Pool_2'
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2, *nas_server_pool], *nas_server_pool,
|
||||
*pool__test_create_share_with_invalid_share_server]
|
||||
get_nas_server:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'Failed to get NAS server.'
|
||||
|
||||
|
||||
test_delete_share:
|
||||
filesystem: &filesystem__test_delete_share
|
||||
_properties: &filesystem_prop__test_delete_share
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
delete:
|
||||
update:
|
||||
has_snap: False
|
||||
|
||||
cifs_share: &cifs_share__test_delete_share
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_delete_share
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_delete_share
|
||||
|
||||
test_delete_share__with_invalid_share:
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'Failed to get CIFS share.'
|
||||
|
||||
test_delete_share__create_from_snap:
|
||||
filesystem: &filesystem__test_delete_share__create_from_snap
|
||||
_properties: &filesystem_prop__test_delete_share__create_from_snap
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
delete:
|
||||
update:
|
||||
has_snap: False
|
||||
|
||||
snap: &snap__test_delete_share__create_from_snap
|
||||
_properties: &snap_prop__test_delete_share__create_from_snap
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_delete_share__create_from_snap
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
cifs_share: &cifs_share__test_delete_share__create_from_snap
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
snap: *snap__test_delete_share__create_from_snap
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_delete_share__create_from_snap
|
||||
get_snap: *snap__test_delete_share__create_from_snap
|
||||
|
||||
test_delete_share__create_from_snap_but_not_isolated:
|
||||
filesystem: &filesystem__test_delete_share__create_from_snap_but_not_isolated
|
||||
_properties: &filesystem_prop__test_delete_share__create_from_snap_but_not_isolated
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
cifs_share: [*cifs_share_base]
|
||||
nfs_share: [*nfs_share_base]
|
||||
_methods:
|
||||
delete:
|
||||
update:
|
||||
has_snap: True
|
||||
|
||||
snap: &snap__test_delete_share__create_from_snap_but_not_isolated
|
||||
_properties: &snap_prop__test_delete_share__create_from_snap_but_not_isolated
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_delete_share__create_from_snap_but_not_isolated
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
cifs_share: &cifs_share__test_delete_share__create_from_snap_but_not_isolated
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
snap: *snap__test_delete_share__create_from_snap_but_not_isolated
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_delete_share__create_from_snap_but_not_isolated
|
||||
|
||||
test_delete_share__but_not_isolated:
|
||||
filesystem: &filesystem__test_delete_share__but_not_isolated
|
||||
_properties: &filesystem_prop__test_delete_share__but_not_isolated
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
update:
|
||||
has_snap: True
|
||||
|
||||
cifs_share: &cifs_share__test_delete_share__but_not_isolated
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_delete_share__but_not_isolated
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_delete_share__but_not_isolated
|
||||
|
||||
test_extend_cifs_share:
|
||||
filesystem: &filesystem__test_extend_cifs_share
|
||||
_properties: &filesystem_prop__test_extend_cifs_share
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
extend:
|
||||
|
||||
cifs_share: &cifs_share__test_extend_cifs_share
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_extend_cifs_share
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_extend_cifs_share
|
||||
|
||||
test_extend_nfs_share:
|
||||
filesystem: &filesystem__test_extend_nfs_share
|
||||
_properties: &filesystem_prop__test_extend_nfs_share
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
extend:
|
||||
|
||||
cifs_share: &cifs_share__test_extend_nfs_share
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_extend_nfs_share
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nfs_share: *cifs_share__test_extend_nfs_share
|
||||
|
||||
test_extend_share__create_from_snap:
|
||||
snap: &snap__test_extend_share__create_from_snap
|
||||
_properties: &snap_prop__test_extend_share__create_from_snap
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
|
||||
cifs_share: &cifs_share__test_extend_share__create_from_snap
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
snap: *snap__test_extend_share__create_from_snap
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_extend_share__create_from_snap
|
||||
|
||||
test_create_snapshot_from_filesystem:
|
||||
filesystem: &filesystem__test_create_snapshot_from_filesystem
|
||||
_properties: &filesystem_prop__test_create_snapshot_from_filesystem
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
create_snap:
|
||||
|
||||
cifs_share: &cifs_share__test_create_snapshot_from_filesystem
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_create_snapshot_from_filesystem
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_create_snapshot_from_filesystem
|
||||
|
||||
test_create_snapshot_from_snapshot:
|
||||
snap: &snap__test_create_snapshot_from_snapshot
|
||||
_properties: &snap_prop__test_create_snapshot_from_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
create_snap:
|
||||
|
||||
cifs_share: &cifs_share__test_create_snapshot_from_snapshot
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
snap: *snap__test_create_snapshot_from_snapshot
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_create_snapshot_from_snapshot
|
||||
get_snap: *snap__test_create_snapshot_from_snapshot
|
||||
|
||||
test_delete_snapshot:
|
||||
snap: &snap__test_delete_snapshot
|
||||
_properties: &snap_prop__test_delete_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_snap: *snap__test_delete_snapshot
|
||||
|
||||
test_ensure_share_exists:
|
||||
cifs_share: &cifs_share_ensure_share_exists
|
||||
_properties:
|
||||
existed: True
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share_ensure_share_exists
|
||||
|
||||
test_ensure_share_not_exists:
|
||||
cifs_share: &cifs_share_ensure_share_not_exists
|
||||
_properties:
|
||||
existed: False
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share_ensure_share_not_exists
|
||||
|
||||
test_update_share_stats:
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2], *pool_1, [*pool_1, *pool_2]]
|
||||
|
||||
test_update_share_stats__nonexistent_pools:
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool:
|
||||
_side_effect: [[*pool_1, *pool_2], *pool_1, []]
|
||||
|
||||
test_get_pool:
|
||||
filesystem: &filesystem__test_get_pool
|
||||
_properties: &filesystem_prop__test_get_pool
|
||||
<<: *filesystem_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
pool: *pool_1
|
||||
|
||||
cifs_share: &cifs_share__test_get_pool
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
filesystem: *filesystem__test_get_pool
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_get_pool
|
||||
|
||||
test_setup_server: &test_setup_server
|
||||
nas_server_1: &nas_server_1__test_setup_server
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
existed: false
|
||||
|
||||
nas_server_2: &nas_server_2__test_setup_server
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods: &nas_server_2__test_setup_server_mehtod
|
||||
create_file_interface:
|
||||
enable_nfs_service:
|
||||
|
||||
unity:
|
||||
_methods: &unity_method__test_setup_server
|
||||
<<: *unity_base_method
|
||||
get_nas_server: *nas_server_1__test_setup_server
|
||||
create_nas_server: *nas_server_2__test_setup_server
|
||||
|
||||
test_setup_server__vlan_network:
|
||||
<<: *test_setup_server
|
||||
nas_server: &nas_server__test_setup_server_flat_network
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
existed: true
|
||||
_methods:
|
||||
create_file_interface:
|
||||
create_dns_server:
|
||||
enable_nfs_service:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_method__test_setup_server
|
||||
get_nas_server: *nas_server__test_setup_server_flat_network
|
||||
|
||||
test_setup_server__active_directory:
|
||||
<<: *test_setup_server
|
||||
nas_server_2: &nas_server_2__test_setup_server__active_directory
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
create_file_interface:
|
||||
create_dns_server:
|
||||
enable_cifs_service:
|
||||
enable_nfs_service:
|
||||
|
||||
unity:
|
||||
_methods: &unity_method__test_setup_server__active_directory
|
||||
<<: *unity_method__test_setup_server
|
||||
create_nas_server: *nas_server_2__test_setup_server__active_directory
|
||||
|
||||
test_setup_server__kerberos: *test_setup_server
|
||||
|
||||
test_setup_server__throw_exception:
|
||||
<<: *test_setup_server
|
||||
nas_server_1: &nas_server_1__test_setup_server__throw_exception
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
existed: false
|
||||
|
||||
nas_server_2: &nas_server_2__test_setup_server__throw_exception
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
create_file_interface:
|
||||
create_dns_server:
|
||||
enable_cifs_service:
|
||||
enable_nfs_service:
|
||||
_raise:
|
||||
UnityException: 'Failed to enable NFS service.'
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_method__test_setup_server
|
||||
get_nas_server: *nas_server_2__test_setup_server__throw_exception
|
||||
create_nas_server: *nas_server_2__test_setup_server__throw_exception
|
||||
|
||||
test_teardown_server:
|
||||
nas_server: &nas_server__test_teardown_server
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
delete:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nas_server: *nas_server__test_teardown_server
|
||||
|
||||
test__get_managed_pools: &test__get_managed_pools
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool: [*pool_1, *pool_2, *nas_server_pool]
|
||||
|
||||
test__get_managed_pools__invalid_pool_configuration: *test__get_managed_pools
|
||||
|
||||
test__get_managed_ports: &test__get_managed_ports
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_pool: [*pool_1, *pool_2, *nas_server_pool]
|
||||
get_ip_port: [*port_1, *port_2, *port_internal_port]
|
||||
|
||||
test__get_managed_pools__invalid_port_configuration: *test__get_managed_ports
|
||||
|
||||
|
||||
test_create_cifs_share_from_snapshot:
|
||||
cifs_share: &cifs_share__test_create_cifs_share_from_snapshot
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
enable_ace:
|
||||
|
||||
snapshot_1: &snapshot_1__test_create_cifs_share_from_snapshot
|
||||
_properties: &snapshot_1_prop__test_create_cifs_share_from_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
create_cifs_share: *cifs_share__test_create_cifs_share_from_snapshot
|
||||
|
||||
snapshot_2: &snapshot_2__test_create_cifs_share_from_snapshot
|
||||
_properties: &snapshot__prop__test_create_cifs_share_from_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
create_snap: *snapshot_1__test_create_cifs_share_from_snapshot
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nas_server: *nas_server
|
||||
get_snap: *snapshot_2__test_create_cifs_share_from_snapshot
|
||||
|
||||
test_create_nfs_share_from_snapshot:
|
||||
nfs_share: &nfs_share__test_create_nfs_share_from_snapshot
|
||||
_properties:
|
||||
<<: *nfs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
enable_ace:
|
||||
|
||||
snapshot_1: &snapshot_1__test_create_nfs_share_from_snapshot
|
||||
_properties: &snapshot_1_prop__test_create_nfs_share_from_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
create_nfs_share: *nfs_share__test_create_nfs_share_from_snapshot
|
||||
|
||||
snapshot_2: &snapshot_2__test_create_nfs_share_from_snapshot
|
||||
_properties: &snapshot__prop__test_create_nfs_share_from_snapshot
|
||||
<<: *snap_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd4587340'
|
||||
_methods:
|
||||
create_snap: *snapshot_1__test_create_nfs_share_from_snapshot
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nas_server: *nas_server
|
||||
get_snap: *snapshot_2__test_create_nfs_share_from_snapshot
|
||||
|
||||
test_create_share_from_snapshot_no_server_name:
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nas_server:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'NAS server is not found'
|
||||
|
||||
test_clear_share_access_cifs:
|
||||
cifs_share: &cifs_share__test_clear_share_access_cifs
|
||||
_methods:
|
||||
clear_access:
|
||||
_raise:
|
||||
UnityException: 'clear cifs access invoked'
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_clear_share_access_cifs
|
||||
|
||||
test_clear_share_access_nfs:
|
||||
nfs_share: &nfs_share__test_clear_share_access_nfs
|
||||
_methods:
|
||||
clear_access:
|
||||
_raise:
|
||||
UnityException: 'clear nfs access invoked'
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nfs_share: *nfs_share__test_clear_share_access_nfs
|
||||
|
||||
test_allow_rw_cifs_share_access: &test_allow_rw_cifs_share_access
|
||||
cifs_share: &cifs_share__test_allow_rw_cifs_share_access
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
add_ace:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_allow_rw_cifs_share_access
|
||||
|
||||
test_update_access_allow_rw: *test_allow_rw_cifs_share_access
|
||||
|
||||
test_update_access_recovery:
|
||||
cifs_share: &cifs_share__test_update_access_recovery
|
||||
_methods:
|
||||
add_ace:
|
||||
clear_access:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_update_access_recovery
|
||||
|
||||
test_allow_ro_cifs_share_access: *test_allow_rw_cifs_share_access
|
||||
|
||||
test_allow_rw_nfs_share_access:
|
||||
nfs_share: &nfs_share__test_allow_rw_nfs_share_access
|
||||
_properties:
|
||||
<<: *nfs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
allow_read_write_access:
|
||||
allow_root_access:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nfs_share: *nfs_share__test_allow_rw_nfs_share_access
|
||||
|
||||
test_allow_ro_nfs_share_access:
|
||||
nfs_share: &nfs_share__test_allow_ro_nfs_share_access
|
||||
_properties:
|
||||
<<: *nfs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
allow_read_only_access:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nfs_share: *nfs_share__test_allow_ro_nfs_share_access
|
||||
|
||||
test_deny_cifs_share_access:
|
||||
cifs_share: &cifs_share__test_deny_cifs_share_access
|
||||
_properties:
|
||||
<<: *cifs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
delete_ace:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_cifs_share: *cifs_share__test_deny_cifs_share_access
|
||||
|
||||
test_deny_nfs_share_access: &test_deny_nfs_share_access
|
||||
nfs_share: &nfs_share__test_deny_nfs_share_access
|
||||
_properties:
|
||||
<<: *nfs_share_base_prop
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
_methods:
|
||||
delete_access:
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
<<: *unity_base_method
|
||||
get_nfs_share: *nfs_share__test_deny_nfs_share_access
|
||||
|
||||
test_update_access_deny_nfs: *test_deny_nfs_share_access
|
||||
|
||||
# The following test cases are for client.py
|
||||
|
||||
test_create_cifs_share__existed_expt:
|
||||
filesystem:
|
||||
_methods:
|
||||
create_cifs_share:
|
||||
_raise:
|
||||
UnitySmbShareNameExistedError: 'CIFS share already exists.'
|
||||
|
||||
cifs_share: &cifs_share__test_create_cifs_share__existed_expt
|
||||
_properties:
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
get_cifs_share: *cifs_share__test_create_cifs_share__existed_expt
|
||||
|
||||
test_create_nfs_share__existed_expt:
|
||||
filesystem:
|
||||
_methods:
|
||||
create_nfs_share:
|
||||
_raise:
|
||||
UnityNfsShareNameExistedError: 'NFS share already exists.'
|
||||
|
||||
nfs_share: &nfs_share__test_create_nfs_share__existed_expt
|
||||
_properties:
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
get_nfs_share: *nfs_share__test_create_nfs_share__existed_expt
|
||||
|
||||
test_get_share_with_invalid_proto:
|
||||
share:
|
||||
_properties:
|
||||
<<: *share_base_prop
|
||||
|
||||
test_create_filesystem__existed_expt:
|
||||
filesystem: &filesystem__test_create_filesystem__existed_expt
|
||||
_properties:
|
||||
name: '716100cc-e0b4-416b-ac27-d38dd019330d'
|
||||
size: 10
|
||||
proto: 'CIFS'
|
||||
|
||||
pool:
|
||||
_methods:
|
||||
create_filesystem:
|
||||
_raise:
|
||||
UnityFileSystemNameAlreadyExisted: 'Pool already exists.'
|
||||
|
||||
nas_server:
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
get_filesystem: *filesystem__test_create_filesystem__existed_expt
|
||||
|
||||
test_delete_filesystem__nonexistent_expt:
|
||||
filesystem:
|
||||
_properties:
|
||||
name: already removed filsystem
|
||||
_methods:
|
||||
delete:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'Filesystem is non-existent.'
|
||||
|
||||
test_create_nas_server__existed_expt:
|
||||
sp:
|
||||
_properites:
|
||||
name: 'SP'
|
||||
|
||||
pool:
|
||||
_properites:
|
||||
name: 'fake_pool'
|
||||
|
||||
nas_server: &nas_server__test_create_nas_server__existed_expt
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
create_nas_server:
|
||||
_raise:
|
||||
UnityNasServerNameUsedError: 'NAS Server already exists.'
|
||||
get_nas_server: *nas_server__test_create_nas_server__existed_expt
|
||||
|
||||
test_delete_nas_server__nonexistent_expt:
|
||||
nas_server: &nas_server__test_delete_nas_server__nonexistent_expt
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
delete:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'NAS server is non-existent.'
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
get_nas_server: *nas_server__test_delete_nas_server__nonexistent_expt
|
||||
|
||||
test_create_dns_server__existed_expt:
|
||||
nas_server:
|
||||
_methods:
|
||||
create_dns_server:
|
||||
_raise:
|
||||
UnityOneDnsPerNasServerError: 'DNS server already exists.'
|
||||
|
||||
test_create_interface__existed_expt:
|
||||
nas_server:
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
create_file_interface:
|
||||
_raise:
|
||||
UnityIpAddressUsedError: 'IP address is already used.'
|
||||
|
||||
test_enable_cifs_service__existed_expt:
|
||||
nas_server:
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
enable_cifs_service:
|
||||
_raise:
|
||||
UnitySmbNameInUseError: 'CIFS server already exists.'
|
||||
|
||||
test_enable_nfs_service__existed_expt:
|
||||
nas_server:
|
||||
_properties:
|
||||
<<: *nas_server_prop
|
||||
_methods:
|
||||
enable_nfs_service:
|
||||
_raise:
|
||||
UnityNfsAlreadyEnabledError: 'NFS server already exists.'
|
||||
|
||||
test_create_snapshot__existed_expt:
|
||||
filesystem:
|
||||
_properties:
|
||||
<<: *filesystem_base_prop
|
||||
_methods:
|
||||
create_snap:
|
||||
_raise:
|
||||
UnitySnapNameInUseError: 'Snapshot already exists.'
|
||||
|
||||
snapshot:
|
||||
_properties:
|
||||
<<: *snap_base_prop
|
||||
|
||||
test_create_snap_of_snap__existed_expt:
|
||||
src_snapshot:
|
||||
_methods:
|
||||
create_snap:
|
||||
_raise:
|
||||
UnitySnapNameInUseError: 'Snapshot already exists.'
|
||||
|
||||
dest_snapshot: &dest_snapshot__test_create_snap_of_snap__existed_expt
|
||||
_properties:
|
||||
<<: *snap_base_prop
|
||||
|
||||
unity:
|
||||
_methods:
|
||||
get_snap: *dest_snapshot__test_create_snap_of_snap__existed_expt
|
||||
|
||||
test_delete_snapshot__nonexistent_expt:
|
||||
snapshot:
|
||||
_properties:
|
||||
<<: *snap_base_prop
|
||||
_methods:
|
||||
delete:
|
||||
_raise:
|
||||
UnityResourceNotFoundError: 'Snapshot is non-existent.'
|
||||
|
||||
test_nfs_deny_access__nonexistent_expt:
|
||||
nfs_share: &nfs_share__test_nfs_deny_access__nonexistent_expt
|
||||
_methods:
|
||||
delete_access:
|
||||
_raise:
|
||||
UnityHostNotFoundException: "Unity Host is non-existent"
|
||||
unity:
|
||||
_methods:
|
||||
get_nfs_share: *nfs_share__test_nfs_deny_access__nonexistent_expt
|
||||
|
||||
test_get_storage_processor:
|
||||
unity:
|
||||
_methods:
|
||||
get_sp: *sp_a
|
337
manila/tests/share/drivers/emc/plugins/unity/res_mock.py
Normal file
337
manila/tests/share/drivers/emc/plugins/unity/res_mock.py
Normal file
@ -0,0 +1,337 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo_log import log
|
||||
|
||||
from manila.share import configuration as conf
|
||||
from manila.share.drivers.emc.plugins.unity import client
|
||||
from manila.share.drivers.emc.plugins.unity import connection
|
||||
from manila.tests.db import fakes as db_fakes
|
||||
from manila.tests import fake_share
|
||||
from manila.tests.share.drivers.emc.plugins.unity import fake_exceptions
|
||||
from manila.tests.share.drivers.emc.plugins.unity import utils
|
||||
|
||||
client.storops_ex = fake_exceptions
|
||||
connection.storops_ex = fake_exceptions
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
SYMBOL_TYPE = '_type'
|
||||
SYMBOL_PROPERTIES = '_properties'
|
||||
SYMBOL_METHODS = '_methods'
|
||||
SYMBOL_SIDE_EFFECT = '_side_effect'
|
||||
SYMBOL_RAISE = '_raise'
|
||||
|
||||
|
||||
def _has_side_effect(node):
|
||||
return isinstance(node, dict) and SYMBOL_SIDE_EFFECT in node
|
||||
|
||||
|
||||
def _has_raise(node):
|
||||
return isinstance(node, dict) and SYMBOL_RAISE in node
|
||||
|
||||
|
||||
def fake_share_server(**kwargs):
|
||||
share_server = {
|
||||
'instance_id': 'fake_instance_id',
|
||||
'backend_details': {},
|
||||
}
|
||||
|
||||
share_server.update(kwargs)
|
||||
|
||||
return db_fakes.FakeModel(share_server)
|
||||
|
||||
|
||||
def fake_network_info(**kwargs):
|
||||
network_info = {
|
||||
'id': 'fake_net_id',
|
||||
'name': 'net_name',
|
||||
'subnet': [],
|
||||
}
|
||||
network_info.update(kwargs)
|
||||
return network_info
|
||||
|
||||
|
||||
def fake_server_detail(**kwargs):
|
||||
server_detail = {
|
||||
'share_server_name': 'fake_server_name',
|
||||
}
|
||||
server_detail.update(kwargs)
|
||||
return server_detail
|
||||
|
||||
|
||||
def fake_security_services(**kwargs):
|
||||
return kwargs['services']
|
||||
|
||||
|
||||
def fake_access(**kwargs):
|
||||
access = {}
|
||||
access.update(kwargs)
|
||||
return access
|
||||
|
||||
|
||||
class FakeEMCShareDriver(object):
|
||||
def __init__(self):
|
||||
self.configuration = conf.Configuration(None)
|
||||
self.configuration.emc_share_backend = 'unity'
|
||||
self.configuration.emc_nas_server_container = 'SPA'
|
||||
self.configuration.emc_nas_server = '192.168.1.1'
|
||||
self.configuration.emc_nas_login = 'fake_user'
|
||||
self.configuration.emc_nas_password = 'fake_password'
|
||||
self.configuration.share_backend_name = 'EMC_NAS_Storage'
|
||||
self.configuration.emc_nas_server_pool = 'nas_server_pool'
|
||||
self.configuration.local_conf.max_over_subscription_ratio = 20
|
||||
|
||||
|
||||
STATS = dict(
|
||||
share_backend_name='Unity',
|
||||
vendor_name='EMC',
|
||||
storage_protocol='NFS_CIFS',
|
||||
driver_version='2.0.0,',
|
||||
pools=[],
|
||||
)
|
||||
|
||||
|
||||
class DriverResourceMock(dict):
|
||||
fake_func_mapping = {}
|
||||
|
||||
def __init__(self, yaml_file):
|
||||
yaml_dict = utils.load_yaml(yaml_file)
|
||||
if isinstance(yaml_dict, dict):
|
||||
for name, body in yaml_dict.items():
|
||||
if isinstance(body, dict):
|
||||
props = body[SYMBOL_PROPERTIES]
|
||||
if isinstance(props, dict):
|
||||
for prop_name, prop_value in props.items():
|
||||
if isinstance(prop_value, dict) and prop_value:
|
||||
# get the first key as the convert function
|
||||
func_name = list(prop_value.keys())[0]
|
||||
if func_name.startswith('_'):
|
||||
func = getattr(self, func_name)
|
||||
props[prop_name] = (
|
||||
func(**prop_value[func_name]))
|
||||
if body[SYMBOL_TYPE] in self.fake_func_mapping:
|
||||
self[name] = (
|
||||
self.fake_func_mapping[body[SYMBOL_TYPE]](**props))
|
||||
|
||||
|
||||
class ManilaResourceMock(DriverResourceMock):
|
||||
fake_func_mapping = {
|
||||
'share': fake_share.fake_share,
|
||||
'snapshot': fake_share.fake_snapshot,
|
||||
'network_info': fake_network_info,
|
||||
'share_server': fake_share_server,
|
||||
'server_detail': fake_server_detail,
|
||||
'security_services': fake_security_services,
|
||||
'access': fake_access,
|
||||
}
|
||||
|
||||
def __init__(self, yaml_file):
|
||||
super(ManilaResourceMock, self).__init__(yaml_file)
|
||||
|
||||
|
||||
class StorageObjectMock(object):
|
||||
PROPS = 'props'
|
||||
|
||||
def __init__(self, yaml_dict):
|
||||
self.__dict__[StorageObjectMock.PROPS] = {}
|
||||
props = yaml_dict.get(SYMBOL_PROPERTIES, None)
|
||||
if props:
|
||||
for k, v in props.items():
|
||||
setattr(self, k, StoragePropertyMock(k, v)())
|
||||
|
||||
methods = yaml_dict.get(SYMBOL_METHODS, None)
|
||||
if methods:
|
||||
for k, v in methods.items():
|
||||
setattr(self, k, StorageMethodMock(k, v))
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self.__dict__[StorageObjectMock.PROPS][key] = value
|
||||
|
||||
def __getattr__(self, item):
|
||||
try:
|
||||
super(StorageObjectMock, self).__getattr__(item)
|
||||
except AttributeError:
|
||||
return self.__dict__[StorageObjectMock.PROPS][item]
|
||||
except KeyError:
|
||||
raise KeyError('No such method or property for mock object.')
|
||||
|
||||
|
||||
class StoragePropertyMock(mock.PropertyMock):
|
||||
def __init__(self, name, property_body):
|
||||
return_value = property_body
|
||||
side_effect = None
|
||||
|
||||
# only support return_value and side_effect for property
|
||||
if _has_side_effect(property_body):
|
||||
side_effect = property_body[SYMBOL_SIDE_EFFECT]
|
||||
return_value = None
|
||||
|
||||
if side_effect:
|
||||
super(StoragePropertyMock, self).__init__(
|
||||
name=name,
|
||||
side_effect=side_effect)
|
||||
elif return_value:
|
||||
super(StoragePropertyMock, self).__init__(
|
||||
name=name,
|
||||
return_value=_build_mock_object(return_value))
|
||||
else:
|
||||
super(StoragePropertyMock, self).__init__(
|
||||
name=name,
|
||||
return_value=return_value)
|
||||
|
||||
|
||||
class StorageMethodMock(mock.Mock):
|
||||
def __init__(self, name, method_body):
|
||||
return_value = method_body
|
||||
exception = None
|
||||
side_effect = None
|
||||
|
||||
# support return_value, side_effect and exception for method
|
||||
if _has_side_effect(method_body) or _has_raise(method_body):
|
||||
exception = method_body.get(SYMBOL_RAISE, None)
|
||||
side_effect = method_body.get(SYMBOL_SIDE_EFFECT, None)
|
||||
return_value = None
|
||||
|
||||
if exception:
|
||||
if isinstance(exception, dict) and exception:
|
||||
ex_name = list(exception.keys())[0]
|
||||
ex = getattr(fake_exceptions, ex_name)
|
||||
super(StorageMethodMock, self).__init__(
|
||||
name=name,
|
||||
side_effect=ex(exception[ex_name]))
|
||||
elif side_effect:
|
||||
super(StorageMethodMock, self).__init__(
|
||||
name=name,
|
||||
side_effect=_build_mock_object(side_effect))
|
||||
elif return_value:
|
||||
super(StorageMethodMock, self).__init__(
|
||||
name=name,
|
||||
return_value=_build_mock_object(return_value))
|
||||
else:
|
||||
super(StorageMethodMock, self).__init__(
|
||||
name=name, return_value=None)
|
||||
|
||||
|
||||
class StorageResourceMock(dict):
|
||||
def __init__(self, yaml_file):
|
||||
yaml_dict = utils.load_yaml(yaml_file)
|
||||
if isinstance(yaml_dict, dict):
|
||||
for section, sec_body in yaml_dict.items():
|
||||
self[section] = {}
|
||||
if isinstance(sec_body, dict):
|
||||
for obj_name, obj_body in sec_body.items():
|
||||
self[section][obj_name] = _build_mock_object(obj_body)
|
||||
|
||||
|
||||
def _is_mock_object(yaml_info):
|
||||
return (isinstance(yaml_info, dict) and
|
||||
(SYMBOL_PROPERTIES in yaml_info or SYMBOL_METHODS in yaml_info))
|
||||
|
||||
|
||||
def _build_mock_object(yaml_dict):
|
||||
if _is_mock_object(yaml_dict):
|
||||
return StorageObjectMock(yaml_dict)
|
||||
elif isinstance(yaml_dict, dict):
|
||||
return {k: _build_mock_object(v) for k, v in yaml_dict.items()}
|
||||
elif isinstance(yaml_dict, list):
|
||||
return [_build_mock_object(each) for each in yaml_dict]
|
||||
else:
|
||||
return yaml_dict
|
||||
|
||||
|
||||
manila_res = ManilaResourceMock('mocked_manila.yaml')
|
||||
unity_res = StorageResourceMock('mocked_unity.yaml')
|
||||
STORAGE_RES_MAPPING = {
|
||||
'TestClient': unity_res,
|
||||
'TestConnection': unity_res,
|
||||
}
|
||||
|
||||
|
||||
def mock_input(resource):
|
||||
def inner_dec(func):
|
||||
def decorated(cls, *args, **kwargs):
|
||||
if cls._testMethodName in resource:
|
||||
storage_res = resource[cls._testMethodName]
|
||||
return func(cls, storage_res, *args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
return inner_dec
|
||||
|
||||
|
||||
mock_client_input = mock_input(unity_res)
|
||||
|
||||
|
||||
def patch_client(func):
|
||||
def client_decorator(cls, *args, **kwargs):
|
||||
storage_res = {}
|
||||
if func.__name__ in STORAGE_RES_MAPPING[cls.__class__.__name__]:
|
||||
storage_res = (
|
||||
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
|
||||
with utils.patch_system as patched_system:
|
||||
if 'unity' in storage_res:
|
||||
patched_system.return_value = storage_res['unity']
|
||||
_client = client.UnityClient(host='fake_host',
|
||||
username='fake_user',
|
||||
password='fake_passwd')
|
||||
return func(cls, _client, *args, **kwargs)
|
||||
|
||||
return client_decorator
|
||||
|
||||
|
||||
def mock_driver_input(resource):
|
||||
def inner_dec(func):
|
||||
def decorated(cls, *args, **kwargs):
|
||||
return func(cls, resource, *args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
return inner_dec
|
||||
|
||||
|
||||
mock_manila_input = mock_driver_input(manila_res)
|
||||
|
||||
|
||||
def patch_connection_init(func):
|
||||
def connection_decorator(cls, *args, **kwargs):
|
||||
storage_res = {}
|
||||
if func.__name__ in STORAGE_RES_MAPPING[cls.__class__.__name__]:
|
||||
storage_res = (
|
||||
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
|
||||
with utils.patch_system as patched_system:
|
||||
if 'unity' in storage_res:
|
||||
patched_system.return_value = storage_res['unity']
|
||||
conn = connection.UnityStorageConnection(LOG)
|
||||
return func(cls, conn, *args, **kwargs)
|
||||
|
||||
return connection_decorator
|
||||
|
||||
|
||||
def patch_connection(func):
|
||||
def connection_decorator(cls, *args, **kwargs):
|
||||
storage_res = {}
|
||||
if func.__name__ in STORAGE_RES_MAPPING[cls.__class__.__name__]:
|
||||
storage_res = (
|
||||
STORAGE_RES_MAPPING[cls.__class__.__name__][func.__name__])
|
||||
with utils.patch_system as patched_system:
|
||||
if 'unity' in storage_res:
|
||||
patched_system.return_value = storage_res['unity']
|
||||
conn = connection.UnityStorageConnection(LOG)
|
||||
conn.connect(FakeEMCShareDriver(), None)
|
||||
return func(cls, conn, *args, **kwargs)
|
||||
|
||||
return connection_decorator
|
158
manila/tests/share/drivers/emc/plugins/unity/test_client.py
Normal file
158
manila/tests/share/drivers/emc/plugins/unity/test_client.py
Normal file
@ -0,0 +1,158 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from manila import exception
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.emc.plugins.unity import res_mock
|
||||
|
||||
|
||||
class TestClient(test.TestCase):
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_cifs_share__existed_expt(self, client, mocked_input):
|
||||
resource = mocked_input['filesystem']
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
new_share = client.create_cifs_share(resource, share.name)
|
||||
|
||||
self.assertEqual(share.name, new_share.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_nfs_share__existed_expt(self, client, mocked_input):
|
||||
resource = mocked_input['filesystem']
|
||||
share = mocked_input['nfs_share']
|
||||
|
||||
new_share = client.create_nfs_share(resource, share.name)
|
||||
|
||||
self.assertEqual(share.name, new_share.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_get_share_with_invalid_proto(self, client, mocked_input):
|
||||
share = mocked_input['share']
|
||||
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
client.get_share,
|
||||
share.name,
|
||||
'fake_proto')
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_filesystem__existed_expt(self, client, mocked_input):
|
||||
pool = mocked_input['pool']
|
||||
nas_server = mocked_input['nas_server']
|
||||
filesystem = mocked_input['filesystem']
|
||||
|
||||
new_filesystem = client.create_filesystem(pool,
|
||||
nas_server,
|
||||
filesystem.name,
|
||||
filesystem.size,
|
||||
filesystem.proto)
|
||||
|
||||
self.assertEqual(filesystem.name, new_filesystem.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_delete_filesystem__nonexistent_expt(self, client, mocked_input):
|
||||
filesystem = mocked_input['filesystem']
|
||||
|
||||
client.delete_filesystem(filesystem)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_nas_server__existed_expt(self, client, mocked_input):
|
||||
sp = mocked_input['sp']
|
||||
pool = mocked_input['pool']
|
||||
nas_server = mocked_input['nas_server']
|
||||
|
||||
new_nas_server = client.create_nas_server(nas_server.name, sp, pool)
|
||||
|
||||
self.assertEqual(nas_server.name, new_nas_server.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_delete_nas_server__nonexistent_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['nas_server']
|
||||
|
||||
client.delete_nas_server(nas_server.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_dns_server__existed_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['nas_server']
|
||||
|
||||
client.create_dns_server(nas_server, 'fake_domain', 'fake_dns_ip')
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_interface__existed_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['nas_server']
|
||||
port_set = ('fake_port',)
|
||||
|
||||
self.assertRaises(exception.IPAddressInUse, client.create_interface,
|
||||
nas_server, 'fake_ip_addr', 'fake_mask',
|
||||
'fake_gateway', ports=port_set)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_enable_cifs_service__existed_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['nas_server']
|
||||
|
||||
client.enable_cifs_service(
|
||||
nas_server, 'domain_name', 'fake_user', 'fake_passwd')
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_enable_nfs_service__existed_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['nas_server']
|
||||
|
||||
client.enable_nfs_service(nas_server)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_snapshot__existed_expt(self, client, mocked_input):
|
||||
nas_server = mocked_input['filesystem']
|
||||
exp_snap = mocked_input['snapshot']
|
||||
|
||||
client.create_snapshot(nas_server, exp_snap.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_create_snap_of_snap__existed_expt(self, client, mocked_input):
|
||||
snapshot = mocked_input['src_snapshot']
|
||||
dest_snap = mocked_input['dest_snapshot']
|
||||
|
||||
new_snap = client.create_snap_of_snap(
|
||||
snapshot, dest_snap.name, 'checkpoint')
|
||||
|
||||
self.assertEqual(dest_snap.name, new_snap.name)
|
||||
|
||||
@res_mock.mock_client_input
|
||||
@res_mock.patch_client
|
||||
def test_delete_snapshot__nonexistent_expt(self, client, mocked_input):
|
||||
snapshot = mocked_input['snapshot']
|
||||
|
||||
client.delete_snapshot(snapshot)
|
||||
|
||||
@res_mock.patch_client
|
||||
def test_nfs_deny_access__nonexistent_expt(self, client):
|
||||
client.nfs_deny_access('fake_share_name', 'fake_ip_addr')
|
||||
|
||||
@res_mock.patch_client
|
||||
def test_get_storage_processor(self, client):
|
||||
sp = client.get_storage_processor(sp_id='SPA')
|
||||
|
||||
self.assertEqual('SPA', sp.name)
|
617
manila/tests/share/drivers/emc/plugins/unity/test_connection.py
Normal file
617
manila/tests/share/drivers/emc/plugins/unity/test_connection.py
Normal file
@ -0,0 +1,617 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
from manila import exception
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.emc.plugins.unity import fake_exceptions
|
||||
from manila.tests.share.drivers.emc.plugins.unity import res_mock
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestConnection(test.TestCase):
|
||||
client = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.emc_share_driver = res_mock.FakeEMCShareDriver()
|
||||
|
||||
@res_mock.patch_connection_init
|
||||
def test_connect(self, connection):
|
||||
connection.connect(res_mock.FakeEMCShareDriver(), None)
|
||||
|
||||
@res_mock.patch_connection_init
|
||||
def test_connect__invalid_sp_configuration(self, connection):
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
connection.connect,
|
||||
res_mock.FakeEMCShareDriver(), None)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test_connect__invalid_pool_configuration(self, connection):
|
||||
f = connection.client.system.get_pool
|
||||
f.side_effect = fake_exceptions.UnityResourceNotFoundError()
|
||||
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
connection._config_pool,
|
||||
'faked_pool_name')
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_nfs_share(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
location = connection.create_share(None, share, share_server)
|
||||
|
||||
exp_location = [
|
||||
{'path': 'fake_ip_addr_1:/cb532599-8dc6-4c3e-bb21-74ea54be566c'},
|
||||
{'path': 'fake_ip_addr_2:/cb532599-8dc6-4c3e-bb21-74ea54be566c'},
|
||||
]
|
||||
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_create_cifs_share(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
location = connection.create_share(None, share, share_server)
|
||||
|
||||
exp_location = [
|
||||
{'path': r'\\fake_ip_addr_1\716100cc-e0b4-416b-ac27-d38dd019330d'},
|
||||
{'path': r'\\fake_ip_addr_2\716100cc-e0b4-416b-ac27-d38dd019330d'},
|
||||
]
|
||||
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_create_share_with_invalid_proto(self, connection, mocked_input):
|
||||
share = mocked_input['invalid_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
self.assertRaises(exception.InvalidShare,
|
||||
connection.create_share,
|
||||
None,
|
||||
share,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_share_without_share_server(self, connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
connection.create_share,
|
||||
None,
|
||||
share,
|
||||
None)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_share__no_server_name_in_backend_details(self, connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = {
|
||||
'backend_details': {'share_server_name': None},
|
||||
'id': 'test',
|
||||
}
|
||||
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
connection.create_share,
|
||||
None,
|
||||
share,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_share_with_invalid_share_server(self, connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
self.assertRaises(exception.EMCUnityError,
|
||||
connection.create_share,
|
||||
None,
|
||||
share,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_share(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.delete_share(None, share, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_share__with_invalid_share(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
connection.delete_share(None, share, None)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_share__create_from_snap(self, connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.delete_share(None, share, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_share__create_from_snap_but_not_isolated(self,
|
||||
connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.delete_share(None, share, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_share__but_not_isolated(self, connection,
|
||||
mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.delete_share(None, share, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_extend_cifs_share(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
new_size = 50 * units.Gi
|
||||
|
||||
connection.extend_share(share, new_size, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_extend_nfs_share(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
new_size = 50 * units.Gi
|
||||
|
||||
connection.extend_share(share, new_size, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_extend_share__create_from_snap(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
share_server = mocked_input['share_server']
|
||||
new_size = 50 * units.Gi
|
||||
|
||||
self.assertRaises(exception.ShareExtendingError,
|
||||
connection.extend_share,
|
||||
share,
|
||||
new_size,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_snapshot_from_filesystem(self, connection, mocked_input):
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.create_snapshot(None, snapshot, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_snapshot_from_snapshot(self, connection, mocked_input):
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.create_snapshot(None, snapshot, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_delete_snapshot(self, connection, mocked_input):
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.delete_snapshot(None, snapshot, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_ensure_share_exists(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
connection.ensure_share(None, share, None)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_ensure_share_not_exists(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
self.assertRaises(exception.ShareNotFound,
|
||||
connection.ensure_share,
|
||||
None,
|
||||
share,
|
||||
None)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test_update_share_stats(self, connection):
|
||||
stat_dict = copy.deepcopy(res_mock.STATS)
|
||||
|
||||
connection.update_share_stats(stat_dict)
|
||||
self.assertEqual(5, len(stat_dict))
|
||||
pool = stat_dict['pools'][0]
|
||||
self.assertEqual('pool_1', pool['pool_name'])
|
||||
self.assertEqual(500000.0, pool['total_capacity_gb'])
|
||||
self.assertEqual(False, pool['qos'])
|
||||
self.assertEqual(30000.0, pool['provisioned_capacity_gb'])
|
||||
self.assertEqual(20, pool['max_over_subscription_ratio'])
|
||||
self.assertEqual(10000.0, pool['allocated_capacity_gb'])
|
||||
self.assertEqual(0, pool['reserved_percentage'])
|
||||
self.assertTrue(pool['thin_provisioning'])
|
||||
self.assertEqual(490000.0, pool['free_capacity_gb'])
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test_update_share_stats__nonexistent_pools(self, connection):
|
||||
stat_dict = copy.deepcopy(res_mock.STATS)
|
||||
|
||||
self.assertRaises(exception.EMCUnityError,
|
||||
connection.update_share_stats,
|
||||
stat_dict)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_get_pool(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
connection.get_pool(share)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__flat']
|
||||
|
||||
connection.setup_server(network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server__vlan_network(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__vlan']
|
||||
|
||||
connection.setup_server(network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server__vxlan_network(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__vxlan']
|
||||
|
||||
self.assertRaises(exception.NetworkBadConfigurationException,
|
||||
connection.setup_server,
|
||||
network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server__active_directory(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__active_directory']
|
||||
|
||||
connection.setup_server(network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server__kerberos(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__kerberos']
|
||||
|
||||
connection.setup_server(network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_setup_server__throw_exception(self, connection, mocked_input):
|
||||
network_info = mocked_input['network_info__flat']
|
||||
|
||||
self.assertRaises(fake_exceptions.UnityException,
|
||||
connection.setup_server,
|
||||
network_info)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_teardown_server(self, connection, mocked_input):
|
||||
server_detail = mocked_input['server_detail']
|
||||
security_services = mocked_input['security_services']
|
||||
|
||||
connection.teardown_server(server_detail, security_services)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_teardown_server__no_server_detail(self, connection, mocked_input):
|
||||
security_services = mocked_input['security_services']
|
||||
|
||||
connection.teardown_server(None, security_services)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_teardown_server__no_share_server_name(self, connection,
|
||||
mocked_input):
|
||||
server_detail = {'share_server_name': None}
|
||||
security_services = mocked_input['security_services']
|
||||
|
||||
connection.teardown_server(server_detail, security_services)
|
||||
|
||||
@ddt.data({'configured_pools': None,
|
||||
'matched_pools': {'pool_1', 'pool_2', 'nas_server_pool'}},
|
||||
{'configured_pools': ['*'],
|
||||
'matched_pools': {'pool_1', 'pool_2', 'nas_server_pool'}},
|
||||
{'configured_pools': ['pool_*'],
|
||||
'matched_pools': {'pool_1', 'pool_2'}},
|
||||
{'configured_pools': ['*pool'],
|
||||
'matched_pools': {'nas_server_pool'}},
|
||||
{'configured_pools': ['nas_server_pool'],
|
||||
'matched_pools': {'nas_server_pool'}},
|
||||
{'configured_pools': ['nas_*', 'pool_*'],
|
||||
'matched_pools': {'pool_1', 'pool_2', 'nas_server_pool'}})
|
||||
@res_mock.patch_connection
|
||||
@ddt.unpack
|
||||
def test__get_managed_pools(self, connection, mocked_input):
|
||||
configured_pools = mocked_input['configured_pools']
|
||||
matched_pool = mocked_input['matched_pools']
|
||||
|
||||
pools = connection._get_managed_pools(configured_pools)
|
||||
|
||||
self.assertEqual(matched_pool, pools)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test__get_managed_pools__invalid_pool_configuration(self, connection):
|
||||
configured_pools = 'fake_pool'
|
||||
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
connection._get_managed_pools,
|
||||
configured_pools)
|
||||
|
||||
@ddt.data({'configured_ports': None,
|
||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
||||
{'configured_ports': ['*'],
|
||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
||||
{'configured_ports': ['spa_*'],
|
||||
'matched_ports': {'spa_eth1', 'spa_eth2'}},
|
||||
{'configured_ports': ['*_eth1'],
|
||||
'matched_ports': {'spa_eth1'}},
|
||||
{'configured_ports': ['spa_eth1'],
|
||||
'matched_ports': {'spa_eth1'}},
|
||||
{'configured_ports': ['spa_eth1', 'spa_eth2'],
|
||||
'matched_ports': {'spa_eth1', 'spa_eth2'}})
|
||||
@res_mock.patch_connection
|
||||
@ddt.unpack
|
||||
def test__get_managed_ports(self, connection, mocked_input):
|
||||
sp = mock.Mock()
|
||||
sp.id = 'SPA'
|
||||
configured_ports = mocked_input['configured_ports']
|
||||
matched_ports = mocked_input['matched_ports']
|
||||
|
||||
ports = connection._get_managed_ports(configured_ports, sp)
|
||||
|
||||
self.assertEqual(matched_ports, ports)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test__get_managed_ports__invalid_port_configuration(self, connection):
|
||||
configured_ports = 'fake_port'
|
||||
sp = mock.Mock()
|
||||
sp.id = 'SPA'
|
||||
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
connection._get_managed_ports,
|
||||
configured_ports,
|
||||
sp)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test__get_pool_name_from_host__no_pool_name(self, connection):
|
||||
host = 'openstack@Unity'
|
||||
|
||||
self.assertRaises(exception.InvalidHost,
|
||||
connection._get_pool_name_from_host,
|
||||
host)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_cifs_share_from_snapshot(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.create_share_from_snapshot(None, share, snapshot,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_nfs_share_from_snapshot(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.create_share_from_snapshot(None, share, snapshot,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_create_share_from_snapshot_no_server_name(self,
|
||||
connection,
|
||||
mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
snapshot = mocked_input['snapshot']
|
||||
share_server = mocked_input['share_server__no_share_server_name']
|
||||
|
||||
self.assertRaises(exception.EMCUnityError,
|
||||
connection.create_share_from_snapshot,
|
||||
None,
|
||||
share,
|
||||
snapshot,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_clear_share_access_cifs(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
|
||||
self.assertRaises(fake_exceptions.UnityException,
|
||||
connection.clear_access,
|
||||
share)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_clear_share_access_nfs(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
|
||||
self.assertRaises(fake_exceptions.UnityException,
|
||||
connection.clear_access,
|
||||
share)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_rw_cifs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['cifs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.allow_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_update_access_allow_rw(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['cifs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.update_access(None, share, None, [rw_access], None,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_update_access_recovery(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['cifs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.update_access(None, share, [rw_access], None, None,
|
||||
share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_ro_cifs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['cifs_ro_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.allow_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_rw_nfs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['nfs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.allow_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_rw_nfs_share_access_cidr(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['nfs_rw_access_cidr']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.allow_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_ro_nfs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
ro_access = mocked_input['nfs_ro_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.allow_access(None, share, ro_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_deny_cifs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['cifs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.deny_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_deny_nfs_share_access(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['nfs_rw_access']
|
||||
share_server = mocked_input['share_server']
|
||||
|
||||
connection.deny_access(None, share, rw_access, share_server)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_update_access_deny_nfs(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['nfs_rw_access']
|
||||
|
||||
connection.update_access(None, share, None, None, [rw_access], None)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test__validate_cifs_share_access_type(self, connection, mocked_input):
|
||||
share = mocked_input['cifs_share']
|
||||
rw_access = mocked_input['invalid_access']
|
||||
|
||||
self.assertRaises(exception.InvalidShareAccess,
|
||||
connection._validate_share_access_type,
|
||||
share,
|
||||
rw_access)
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test__validate_nfs_share_access_type(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['invalid_access']
|
||||
|
||||
self.assertRaises(exception.InvalidShareAccess,
|
||||
connection._validate_share_access_type,
|
||||
share,
|
||||
rw_access)
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test_get_network_allocations_number(self, connection):
|
||||
self.assertEqual(2, connection.get_network_allocations_number())
|
||||
|
||||
@res_mock.patch_connection
|
||||
def test_get_proto_enum(self, connection):
|
||||
self.assertIn('FSSupportedProtocolEnum.CIFS',
|
||||
six.text_type(connection._get_proto_enum('CIFS')))
|
||||
self.assertIn('FSSupportedProtocolEnum.NFS',
|
||||
six.text_type(connection._get_proto_enum('nfs')))
|
||||
|
||||
@res_mock.mock_manila_input
|
||||
@res_mock.patch_connection
|
||||
def test_allow_access_error_access_level(self, connection, mocked_input):
|
||||
share = mocked_input['nfs_share']
|
||||
rw_access = mocked_input['invalid_access']
|
||||
|
||||
self.assertRaises(exception.InvalidShareAccessLevel,
|
||||
connection.allow_access,
|
||||
None, share, rw_access)
|
50
manila/tests/share/drivers/emc/plugins/unity/test_utils.py
Normal file
50
manila/tests/share/drivers/emc/plugins/unity/test_utils.py
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ddt
|
||||
|
||||
from manila.share.drivers.emc.plugins.unity import utils
|
||||
from manila import test
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestUtils(test.TestCase):
|
||||
@ddt.data({'matcher': None,
|
||||
'matched': {'pool_1', 'pool_2', 'nas_server_pool'},
|
||||
'not_matched': set()},
|
||||
{'matcher': ['*'],
|
||||
'matched': {'pool_1', 'pool_2', 'nas_server_pool'},
|
||||
'not_matched': set()},
|
||||
{'matcher': ['pool_*'],
|
||||
'matched': {'pool_1', 'pool_2'},
|
||||
'not_matched': {'nas_server_pool'}},
|
||||
{'matcher': ['*pool'],
|
||||
'matched': {'nas_server_pool'},
|
||||
'not_matched': {'pool_1', 'pool_2'}},
|
||||
{'matcher': ['nas_server_pool'],
|
||||
'matched': {'nas_server_pool'},
|
||||
'not_matched': {'pool_1', 'pool_2'}},
|
||||
{'matcher': ['nas_*', 'pool_*'],
|
||||
'matched': {'pool_1', 'pool_2', 'nas_server_pool'},
|
||||
'not_matched': set()})
|
||||
def test_do_match(self, data):
|
||||
full = ['pool_1 ', ' pool_2', ' nas_server_pool ']
|
||||
matcher = data['matcher']
|
||||
expected_matched = data['matched']
|
||||
expected_not_matched = data['not_matched']
|
||||
|
||||
matched, not_matched = utils.do_match(full, matcher)
|
||||
self.assertEqual(expected_matched, matched)
|
||||
self.assertEqual(expected_not_matched, not_matched)
|
32
manila/tests/share/drivers/emc/plugins/unity/utils.py
Normal file
32
manila/tests/share/drivers/emc/plugins/unity/utils.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2016 EMC Corporation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from os import path
|
||||
import yaml
|
||||
|
||||
import mock
|
||||
from oslo_log import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
patch_system = mock.patch('storops.UnitySystem')
|
||||
|
||||
|
||||
def load_yaml(file_name):
|
||||
yaml_file = '{}/{}'.format(path.dirname(path.abspath(__file__)), file_name)
|
||||
with open(yaml_file) as f:
|
||||
res = yaml.load(f)
|
||||
LOG.debug('Loaded yaml mock objects from %s.', yaml_file)
|
||||
return res
|
@ -84,16 +84,19 @@ class StorageConnectionTestCase(test.TestCase):
|
||||
@ddt.data({'pool_conf': None,
|
||||
'real_pools': ['fake_pool', 'nas_pool'],
|
||||
'matched_pool': set()},
|
||||
{'pool_conf': '*',
|
||||
{'pool_conf': [],
|
||||
'real_pools': ['fake_pool', 'nas_pool'],
|
||||
'matched_pool': set()},
|
||||
{'pool_conf': ['*'],
|
||||
'real_pools': ['fake_pool', 'nas_pool'],
|
||||
'matched_pool': {'fake_pool', 'nas_pool'}},
|
||||
{'pool_conf': 'fake_*',
|
||||
{'pool_conf': ['fake_*'],
|
||||
'real_pools': ['fake_pool', 'nas_pool', 'Perf_Pool'],
|
||||
'matched_pool': {'fake_pool'}},
|
||||
{'pool_conf': '*pool',
|
||||
{'pool_conf': ['*pool'],
|
||||
'real_pools': ['fake_pool', 'NAS_Pool', 'Perf_POOL'],
|
||||
'matched_pool': {'fake_pool'}},
|
||||
{'pool_conf': 'nas_pool',
|
||||
{'pool_conf': ['nas_pool'],
|
||||
'real_pools': ['fake_pool', 'nas_pool', 'perf_pool'],
|
||||
'matched_pool': {'nas_pool'}})
|
||||
@ddt.unpack
|
||||
@ -120,11 +123,11 @@ class StorageConnectionTestCase(test.TestCase):
|
||||
xml_req_mock.assert_has_calls(expected_calls)
|
||||
|
||||
@ddt.data(
|
||||
{'pool_conf': 'fake_*',
|
||||
{'pool_conf': ['fake_*'],
|
||||
'real_pools': ['nas_pool', 'Perf_Pool']},
|
||||
{'pool_conf': '*pool',
|
||||
{'pool_conf': ['*pool'],
|
||||
'real_pools': ['NAS_Pool', 'Perf_POOL']},
|
||||
{'pool_conf': 'nas_pool',
|
||||
{'pool_conf': ['nas_pool'],
|
||||
'real_pools': ['fake_pool', 'perf_pool']},
|
||||
)
|
||||
@ddt.unpack
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
prelude: >
|
||||
Add a new EMC Unity plugin in manila which allows user to create NFS/CIFS
|
||||
share with a EMC Unity backend.
|
||||
features:
|
||||
- |
|
||||
Add a new Unity plugin in manila which allows user to create NFS/CIFS
|
||||
share with a EMC Unity backend. This plugin performs the operations on
|
||||
Unity by REST API.
|
||||
issues:
|
||||
- EMC Unity does not support the same IP in different VLANs.
|
@ -60,6 +60,7 @@ oslo.config.opts.defaults =
|
||||
manila = manila.common.config:set_middleware_defaults
|
||||
manila.share.drivers.emc.plugins =
|
||||
vnx = manila.share.drivers.emc.plugins.vnx.connection:VNXStorageConnection
|
||||
unity = manila.share.drivers.emc.plugins.unity.connection:UnityStorageConnection
|
||||
isilon = manila.share.drivers.emc.plugins.isilon.isilon:IsilonStorageConnection
|
||||
manila.tests.scheduler.fakes =
|
||||
FakeWeigher1 = manila.tests.scheduler.fakes:FakeWeigher1
|
||||
|
Loading…
Reference in New Issue
Block a user