Merge "VMAX VNX Manila - Refactor VMAX and VNX to use common code"
This commit is contained in:
commit
de2ad84bf0
|
@ -25,7 +25,8 @@ from six.moves.urllib import request as url_request # pylint: disable=E0611
|
|||
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -39,9 +40,13 @@ class XMLAPIConnector(object):
|
|||
self.password = configuration.emc_nas_password
|
||||
self.debug = debug
|
||||
self.auth_url = 'https://' + self.storage_ip + '/Login'
|
||||
self._url = ('https://' + self.storage_ip
|
||||
+ '/servlets/CelerraManagementServices')
|
||||
https_handler = url_request.HTTPSHandler()
|
||||
self._url = 'https://{}/servlets/CelerraManagementServices'.format(
|
||||
self.storage_ip)
|
||||
context = enas_utils.create_ssl_context(configuration)
|
||||
if context:
|
||||
https_handler = url_request.HTTPSHandler(context=context)
|
||||
else:
|
||||
https_handler = url_request.HTTPSHandler()
|
||||
cookie_handler = url_request.HTTPCookieProcessor(
|
||||
http_cookiejar.CookieJar())
|
||||
self.url_opener = url_request.build_opener(https_handler,
|
|
@ -39,7 +39,7 @@ MSG_INTERFACE_NON_EXISTENT = '13691781134'
|
|||
MSG_JOIN_DOMAIN = '13157007726'
|
||||
MSG_UNJOIN_DOMAIN = '13157007723'
|
||||
|
||||
# Necessary to retry when VMAX database is locked for provisioning operation
|
||||
# Necessary to retry when ENAS database is locked for provisioning operation
|
||||
MSG_CODE_RETRY = '13421840537'
|
||||
|
||||
IP_ALLOCATIONS = 2
|
|
@ -28,10 +28,10 @@ if storops:
|
|||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||
from manila.share.drivers.dell_emc.plugins import base as driver
|
||||
from manila.share.drivers.dell_emc.plugins.unity import client
|
||||
from manila.share.drivers.dell_emc.plugins.unity import utils as unity_utils
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import utils as emc_utils
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils
|
||||
|
||||
|
@ -64,14 +64,14 @@ CONF = cfg.CONF
|
|||
CONF.register_opts(UNITY_OPTS)
|
||||
|
||||
|
||||
@emc_utils.decorate_all_methods(emc_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
@enas_utils.decorate_all_methods(enas_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
class UnityStorageConnection(driver.StorageConnection):
|
||||
"""Implements Unity specific functionality for EMC Manila driver."""
|
||||
|
||||
IP_ALLOCATIONS = 1
|
||||
|
||||
@emc_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnityStorageConnection, self).__init__(*args, **kwargs)
|
||||
if 'configuration' in kwargs:
|
||||
|
|
|
@ -25,11 +25,11 @@ from oslo_utils import units
|
|||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||
from manila.share.drivers.dell_emc.plugins import base as driver
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import (
|
||||
object_manager as manager)
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import utils as vmax_utils
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils
|
||||
|
||||
|
@ -53,12 +53,12 @@ CONF = cfg.CONF
|
|||
CONF.register_opts(VMAX_OPTS)
|
||||
|
||||
|
||||
@vmax_utils.decorate_all_methods(vmax_utils.log_enter_exit,
|
||||
@enas_utils.decorate_all_methods(enas_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
class VMAXStorageConnection(driver.StorageConnection):
|
||||
"""Implements vmax specific functionality for EMC Manila driver."""
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VMAXStorageConnection, self).__init__(*args, **kwargs)
|
||||
if 'configuration' in kwargs:
|
||||
|
@ -154,7 +154,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
self._get_context('FileSystem').extend(share['id'], pool_name,
|
||||
nwe_size)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _create_cifs_share(self, share_name, share_server):
|
||||
"""Create CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -186,7 +186,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
|
||||
return locations
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _create_nfs_share(self, share_name, share_server):
|
||||
"""Create NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -264,7 +264,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
raise exception.InvalidShare(
|
||||
reason=_('Unsupported share protocol'))
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _delete_cifs_share(self, share, share_server):
|
||||
"""Delete CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -275,7 +275,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
|
||||
self._deallocate_container(name, vdm_name)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _delete_nfs_share(self, share, share_server):
|
||||
"""Delete NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -286,7 +286,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
|
||||
self._deallocate_container(name, vdm_name)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _deallocate_container(self, share_name, vdm_name):
|
||||
"""Delete underneath objects of the share."""
|
||||
path = '/' + share_name
|
||||
|
@ -347,7 +347,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
reason=(_('Invalid NAS protocol supplied: %s.')
|
||||
% share_proto))
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_allow_access(self, context, share, access, share_server):
|
||||
"""Allow access to CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -381,7 +381,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
server['domain'],
|
||||
access=cifs_access)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_allow_access(self, context, share, access, share_server):
|
||||
"""Allow access to NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -423,7 +423,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
elif share_proto == 'NFS':
|
||||
self._nfs_clear_access(share_name, share_server, white_list)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_clear_access(self, share_name, share_server, white_list):
|
||||
"""Clear access for CIFS share except hosts in the white list."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -444,7 +444,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
domain=server['domain'],
|
||||
white_list_users=white_list)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_clear_access(self, share_name, share_server, white_list):
|
||||
"""Clear access for NFS share except hosts in the white list."""
|
||||
self._get_context('NFSShare').clear_share_access(
|
||||
|
@ -464,7 +464,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
raise exception.InvalidShare(
|
||||
reason=_('Unsupported share protocol'))
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_deny_access(self, share, access, share_server):
|
||||
"""Deny access to CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -498,7 +498,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
server['domain'],
|
||||
access=cifs_access)
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_deny_access(self, share, access, share_server):
|
||||
"""Deny access to NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -539,7 +539,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
|
||||
real_pools = set([item for item in backend_pools])
|
||||
conf_pools = set([item.strip() for item in pools])
|
||||
matched_pools, unmatched_pools = vmax_utils.do_match_any(
|
||||
matched_pools, unmatched_pools = enas_utils.do_match_any(
|
||||
real_pools, conf_pools)
|
||||
|
||||
if not matched_pools:
|
||||
|
@ -581,7 +581,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
"Data Mover can be used.")
|
||||
return real_ports
|
||||
|
||||
matched_ports, unmanaged_ports = vmax_utils.do_match_any(
|
||||
matched_ports, unmanaged_ports = enas_utils.do_match_any(
|
||||
real_ports, self.port_conf)
|
||||
|
||||
if not matched_ports:
|
||||
|
@ -739,7 +739,7 @@ class VMAXStorageConnection(driver.StorageConnection):
|
|||
'nfs_if': nfs_if,
|
||||
}
|
||||
|
||||
@vmax_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _vdm_exist(self, name):
|
||||
status, out = self._get_context('VDM').get(name)
|
||||
if constants.STATUS_OK != status:
|
||||
|
|
|
@ -25,10 +25,10 @@ import six
|
|||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import utils as vmax_utils
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import xml_api_parser as parser
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as vmax_utils
|
||||
from manila.share.drivers.dell_emc.common.enas import xml_api_parser as parser
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -1016,7 +1016,8 @@ class VDM(StorageObject):
|
|||
if_name = m_if.group('if').strip()
|
||||
if 'cifs' == m_if.group('type') and if_name != '':
|
||||
interfaces['cifs'].append(if_name)
|
||||
elif 'vdm' == m_if.group('type') and if_name != '':
|
||||
elif (m_if.group('type') in ('vdm', 'nfs')
|
||||
and if_name != ''):
|
||||
interfaces['nfs'].append(if_name)
|
||||
|
||||
return interfaces
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
# Copyright (c) 2016 Dell Inc. or its subsidiaries.
|
||||
# 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 types
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import fnmatch
|
||||
from oslo_utils import timeutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def decorate_all_methods(decorator, debug_only=False):
|
||||
if debug_only and not CONF.debug:
|
||||
return lambda cls: cls
|
||||
|
||||
def _decorate_all_methods(cls):
|
||||
for attr_name, attr_val in cls.__dict__.items():
|
||||
if (isinstance(attr_val, types.FunctionType) and
|
||||
not attr_name.startswith("_")):
|
||||
setattr(cls, attr_name, decorator(attr_val))
|
||||
return cls
|
||||
|
||||
return _decorate_all_methods
|
||||
|
||||
|
||||
def log_enter_exit(func):
|
||||
if not CONF.debug:
|
||||
return func
|
||||
|
||||
def inner(self, *args, **kwargs):
|
||||
LOG.debug("Entering %(cls)s.%(method)s.",
|
||||
{'cls': self.__class__.__name__,
|
||||
'method': func.__name__})
|
||||
start = timeutils.utcnow()
|
||||
ret = func(self, *args, **kwargs)
|
||||
end = timeutils.utcnow()
|
||||
LOG.debug("Exiting %(cls)s.%(method)s. "
|
||||
"Spent %(duration)s sec. "
|
||||
"Return %(return)s.",
|
||||
{'cls': self.__class__.__name__,
|
||||
'duration': timeutils.delta_seconds(start, end),
|
||||
'method': func.__name__,
|
||||
'return': ret})
|
||||
return ret
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def do_match_any(full, matcher_list):
|
||||
"""Finds items that match any of the matchers.
|
||||
|
||||
:param full: Full item list
|
||||
:param matcher_list: The list of matchers. Each matcher supports
|
||||
Unix shell-style wildcards
|
||||
:return: The matched items set and the unmatched items set
|
||||
"""
|
||||
matched = set()
|
||||
not_matched = set()
|
||||
|
||||
full = set([item.strip() for item in full])
|
||||
matcher_list = set([item.strip() for item in matcher_list])
|
||||
|
||||
for matcher in matcher_list:
|
||||
for item in full:
|
||||
if fnmatch.fnmatchcase(item, matcher):
|
||||
matched.add(item)
|
||||
not_matched = full - matched
|
||||
return matched, not_matched
|
|
@ -25,10 +25,10 @@ from oslo_utils import units
|
|||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||
from manila.share.drivers.dell_emc.plugins import base as driver
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import object_manager as manager
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import utils as vnx_utils
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils
|
||||
|
||||
|
@ -55,12 +55,12 @@ CONF = cfg.CONF
|
|||
CONF.register_opts(VNX_OPTS)
|
||||
|
||||
|
||||
@vnx_utils.decorate_all_methods(vnx_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
@enas_utils.decorate_all_methods(enas_utils.log_enter_exit,
|
||||
debug_only=True)
|
||||
class VNXStorageConnection(driver.StorageConnection):
|
||||
"""Implements VNX specific functionality for EMC Manila driver."""
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VNXStorageConnection, self).__init__(*args, **kwargs)
|
||||
if 'configuration' in kwargs:
|
||||
|
@ -156,7 +156,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
self._get_context('FileSystem').extend(share['id'], pool_name,
|
||||
nwe_size)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _create_cifs_share(self, share_name, share_server):
|
||||
"""Create CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -186,7 +186,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
|
||||
return location
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _create_nfs_share(self, share_name, share_server):
|
||||
"""Create NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -265,7 +265,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
raise exception.InvalidShare(
|
||||
reason='Unsupported share type')
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _delete_cifs_share(self, share, share_server):
|
||||
"""Delete CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -276,7 +276,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
|
||||
self._deallocate_container(name, vdm_name)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _delete_nfs_share(self, share, share_server):
|
||||
"""Delete NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -287,7 +287,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
|
||||
self._deallocate_container(name, vdm_name)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _deallocate_container(self, share_name, vdm_name):
|
||||
"""Delete underneath objects of the share."""
|
||||
path = '/' + share_name
|
||||
|
@ -342,7 +342,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
reason=(_('Invalid NAS protocol supplied: %s.')
|
||||
% share_proto))
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_allow_access(self, context, share, access, share_server):
|
||||
"""Allow access to CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -376,7 +376,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
server['domain'],
|
||||
access=cifs_access)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_allow_access(self, context, share, access, share_server):
|
||||
"""Allow access to NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -418,7 +418,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
elif share_proto == 'NFS':
|
||||
self._nfs_clear_access(share_name, share_server, white_list)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_clear_access(self, share_name, share_server, white_list):
|
||||
"""Clear access for CIFS share except hosts in the white list."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -439,7 +439,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
domain=server['domain'],
|
||||
white_list_users=white_list)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_clear_access(self, share_name, share_server, white_list):
|
||||
"""Clear access for NFS share except hosts in the white list."""
|
||||
self._get_context('NFSShare').clear_share_access(
|
||||
|
@ -459,7 +459,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
raise exception.InvalidShare(
|
||||
reason=_('Unsupported share type'))
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _cifs_deny_access(self, share, access, share_server):
|
||||
"""Deny access to CIFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -493,7 +493,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
server['domain'],
|
||||
access=cifs_access)
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _nfs_deny_access(self, share, access, share_server):
|
||||
"""Deny access to NFS share."""
|
||||
vdm_name = self._get_share_server_name(share_server)
|
||||
|
@ -534,7 +534,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
|
||||
real_pools = set([item for item in backend_pools])
|
||||
conf_pools = set([item.strip() for item in pools])
|
||||
matched_pools, unmatched_pools = vnx_utils.do_match_any(
|
||||
matched_pools, unmatched_pools = enas_utils.do_match_any(
|
||||
real_pools, conf_pools)
|
||||
|
||||
if not matched_pools:
|
||||
|
@ -576,7 +576,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
"Data Mover can be used.")
|
||||
return real_ports
|
||||
|
||||
matched_ports, unmanaged_ports = vnx_utils.do_match_any(
|
||||
matched_ports, unmanaged_ports = enas_utils.do_match_any(
|
||||
real_ports, self.port_conf)
|
||||
|
||||
if not matched_ports:
|
||||
|
@ -728,7 +728,7 @@ class VNXStorageConnection(driver.StorageConnection):
|
|||
'nfs_if': nfs_if,
|
||||
}
|
||||
|
||||
@vnx_utils.log_enter_exit
|
||||
@enas_utils.log_enter_exit
|
||||
def _vdm_exist(self, name):
|
||||
status, out = self._get_context('VDM').get(name)
|
||||
if constants.STATUS_OK != status:
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
# Copyright (c) 2015 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 pipes
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
import six
|
||||
from six.moves import http_cookiejar
|
||||
from six.moves.urllib import error as url_error # pylint: disable=E0611
|
||||
from six.moves.urllib import request as url_request # pylint: disable=E0611
|
||||
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import utils as vnx_utils
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class XMLAPIConnector(object):
|
||||
def __init__(self, configuration, debug=True):
|
||||
super(XMLAPIConnector, self).__init__()
|
||||
self.storage_ip = configuration.emc_nas_server
|
||||
self.username = configuration.emc_nas_login
|
||||
self.password = configuration.emc_nas_password
|
||||
self.debug = debug
|
||||
self.auth_url = 'https://' + self.storage_ip + '/Login'
|
||||
self._url = 'https://{}/servlets/CelerraManagementServices'.format(
|
||||
self.storage_ip)
|
||||
context = vnx_utils.create_ssl_context(configuration)
|
||||
if context:
|
||||
https_handler = url_request.HTTPSHandler(context=context)
|
||||
else:
|
||||
https_handler = url_request.HTTPSHandler()
|
||||
cookie_handler = url_request.HTTPCookieProcessor(
|
||||
http_cookiejar.CookieJar())
|
||||
self.url_opener = url_request.build_opener(https_handler,
|
||||
cookie_handler)
|
||||
self._do_setup()
|
||||
|
||||
def _do_setup(self):
|
||||
credential = ('user=' + self.username
|
||||
+ '&password=' + self.password
|
||||
+ '&Login=Login')
|
||||
req = url_request.Request(self.auth_url, credential,
|
||||
constants.CONTENT_TYPE_URLENCODE)
|
||||
resp = self.url_opener.open(req)
|
||||
resp_body = resp.read()
|
||||
self._http_log_resp(resp, resp_body)
|
||||
|
||||
def _http_log_req(self, req):
|
||||
if not self.debug:
|
||||
return
|
||||
|
||||
string_parts = ['curl -i']
|
||||
string_parts.append(' -X %s' % req.get_method())
|
||||
|
||||
for k in req.headers:
|
||||
header = ' -H "%s: %s"' % (k, req.headers[k])
|
||||
string_parts.append(header)
|
||||
|
||||
if req.data:
|
||||
string_parts.append(" -d '%s'" % req.data)
|
||||
string_parts.append(' ' + req.get_full_url())
|
||||
LOG.debug("\nREQ: %s.\n", "".join(string_parts))
|
||||
|
||||
def _http_log_resp(self, resp, body):
|
||||
if not self.debug:
|
||||
return
|
||||
|
||||
headers = six.text_type(resp.headers).replace('\n', '\\n')
|
||||
|
||||
LOG.debug(
|
||||
'RESP: [%(code)s] %(resp_hdrs)s\n'
|
||||
'RESP BODY: %(resp_b)s.\n',
|
||||
{
|
||||
'code': resp.getcode(),
|
||||
'resp_hdrs': headers,
|
||||
'resp_b': body,
|
||||
}
|
||||
)
|
||||
|
||||
def _request(self, req_body=None, method=None,
|
||||
header=constants.CONTENT_TYPE_URLENCODE):
|
||||
req = url_request.Request(self._url, req_body, header)
|
||||
if method not in (None, 'GET', 'POST'):
|
||||
req.get_method = lambda: method
|
||||
self._http_log_req(req)
|
||||
try:
|
||||
resp = self.url_opener.open(req)
|
||||
resp_body = resp.read()
|
||||
self._http_log_resp(resp, resp_body)
|
||||
except url_error.HTTPError as http_err:
|
||||
err = {'errorCode': -1,
|
||||
'httpStatusCode': http_err.code,
|
||||
'messages': six.text_type(http_err),
|
||||
'request': req_body}
|
||||
msg = (_("The request is invalid. Reason: %(reason)s") %
|
||||
{'reason': err})
|
||||
if '403' == six.text_type(http_err.code):
|
||||
raise exception.NotAuthorized()
|
||||
else:
|
||||
raise exception.ManilaException(message=msg)
|
||||
|
||||
return resp_body
|
||||
|
||||
def request(self, req_body=None, method=None,
|
||||
header=constants.CONTENT_TYPE_URLENCODE):
|
||||
try:
|
||||
resp_body = self._request(req_body, method, header)
|
||||
except exception.NotAuthorized:
|
||||
LOG.debug("Login again because client certification "
|
||||
"may be expired.")
|
||||
self._do_setup()
|
||||
resp_body = self._request(req_body, method, header)
|
||||
|
||||
return resp_body
|
||||
|
||||
|
||||
class SSHConnector(object):
|
||||
def __init__(self, configuration, debug=True):
|
||||
super(SSHConnector, self).__init__()
|
||||
self.storage_ip = configuration.emc_nas_server
|
||||
self.username = configuration.emc_nas_login
|
||||
self.password = configuration.emc_nas_password
|
||||
self.debug = debug
|
||||
|
||||
self.sshpool = utils.SSHPool(ip=self.storage_ip,
|
||||
port=22,
|
||||
conn_timeout=None,
|
||||
login=self.username,
|
||||
password=self.password)
|
||||
|
||||
def run_ssh(self, cmd_list, check_exit_code=False):
|
||||
command = ' '.join(pipes.quote(cmd_arg) for cmd_arg in cmd_list)
|
||||
|
||||
with self.sshpool.item() as ssh:
|
||||
try:
|
||||
out, err = processutils.ssh_execute(
|
||||
ssh, command, check_exit_code=check_exit_code)
|
||||
self.log_request(command, out, err)
|
||||
|
||||
return out, err
|
||||
except processutils.ProcessExecutionError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception('Error running SSH command: %(cmd)s.',
|
||||
{'cmd': command})
|
||||
|
||||
def log_request(self, cmd, out, err):
|
||||
if not self.debug:
|
||||
return
|
||||
|
||||
LOG.debug("\nSSH command: %s.\n", cmd)
|
||||
LOG.debug("SSH command output: out=%(out)s, err=%(err)s.\n",
|
||||
{'out': out, 'err': err})
|
|
@ -1,55 +0,0 @@
|
|||
# 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.
|
||||
|
||||
STATUS_OK = 'ok'
|
||||
STATUS_INFO = 'info'
|
||||
STATUS_DEBUG = 'debug'
|
||||
STATUS_WARNING = 'warning'
|
||||
STATUS_ERROR = 'error'
|
||||
STATUS_NOT_FOUND = 'not_found'
|
||||
|
||||
MSG_GENERAL_ERROR = '13690601492'
|
||||
MSG_INVALID_VDM_ID = '14227341325'
|
||||
MSG_INVALID_MOVER_ID = '14227341323'
|
||||
|
||||
MSG_FILESYSTEM_NOT_FOUND = "18522112101"
|
||||
MSG_FILESYSTEM_EXIST = '13691191325'
|
||||
|
||||
MSG_VDM_EXIST = '13421840550'
|
||||
|
||||
MSG_SNAP_EXIST = '13690535947'
|
||||
|
||||
MSG_INTERFACE_NAME_EXIST = '13421840550'
|
||||
MSG_INTERFACE_EXIST = '13691781136'
|
||||
MSG_INTERFACE_INVALID_VLAN_ID = '13421850371'
|
||||
MSG_INTERFACE_NON_EXISTENT = '13691781134'
|
||||
|
||||
MSG_JOIN_DOMAIN = '13157007726'
|
||||
MSG_UNJOIN_DOMAIN = '13157007723'
|
||||
|
||||
# Necessary to retry when VNX database is locked for provisioning operation
|
||||
MSG_CODE_RETRY = '13421840537'
|
||||
|
||||
IP_ALLOCATIONS = 2
|
||||
|
||||
CONTENT_TYPE_URLENCODE = {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
|
||||
XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||||
XML_NAMESPACE = 'http://www.emc.com/schemas/celerra/xml_api'
|
||||
|
||||
CIFS_ACL_FULLCONTROL = 'fullcontrol'
|
||||
CIFS_ACL_READ = 'read'
|
||||
|
||||
SSH_DEFAULT_RETRY_PATTERN = r'Error 2201:.*: unable to acquire lock\(s\)'
|
|
@ -25,10 +25,10 @@ import six
|
|||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import utils as vnx_utils
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import xml_api_parser as parser
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import utils as vnx_utils
|
||||
from manila.share.drivers.dell_emc.common.enas import xml_api_parser as parser
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
|
|
@ -1,317 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import re
|
||||
|
||||
from lxml import etree
|
||||
import six
|
||||
|
||||
|
||||
class XMLAPIParser(object):
|
||||
def __init__(self):
|
||||
# The following Boolean acts as the flag for the common sub-element.
|
||||
# For instance:
|
||||
# <CifsServers>
|
||||
# <li> server_1 </li>
|
||||
# </CifsServers>
|
||||
# <Alias>
|
||||
# <li> interface_1 </li>
|
||||
# </Alias>
|
||||
self.is_QueryStatus = False
|
||||
self.is_CifsServers = False
|
||||
self.is_Aliases = False
|
||||
self.is_MoverStatus = False
|
||||
self.is_TaskResponse = False
|
||||
self.is_Vdm = False
|
||||
self.is_Interfaces = False
|
||||
|
||||
self.elt = {}
|
||||
|
||||
def _remove_ns(self, tag):
|
||||
i = tag.find('}')
|
||||
if i >= 0:
|
||||
tag = tag[i + 1:]
|
||||
return tag
|
||||
|
||||
def parse(self, xml):
|
||||
result = {
|
||||
'type': None,
|
||||
'taskId': None,
|
||||
'maxSeverity': None,
|
||||
'objects': [],
|
||||
'problems': [],
|
||||
}
|
||||
|
||||
events = ("start", "end")
|
||||
|
||||
context = etree.iterparse(six.BytesIO(xml.encode('utf-8')),
|
||||
events=events)
|
||||
for action, elem in context:
|
||||
self.tag = self._remove_ns(elem.tag)
|
||||
|
||||
func = self._get_func(action, self.tag)
|
||||
if func in vars(XMLAPIParser):
|
||||
if action == 'start':
|
||||
eval('self.' + func)(elem, result)
|
||||
elif action == 'end':
|
||||
eval('self.' + func)()
|
||||
|
||||
return result
|
||||
|
||||
def _get_func(self, action, tag):
|
||||
if tag == 'W2KServerData':
|
||||
return action + '_' + 'w2k_server_data'
|
||||
|
||||
temp_list = re.sub(r"([A-Z])", r" \1", tag).split()
|
||||
if temp_list:
|
||||
func_name = action + '_' + '_'.join(temp_list)
|
||||
else:
|
||||
func_name = action + '_' + tag
|
||||
return func_name.lower()
|
||||
|
||||
def _copy_property(self, source, target, property, list_property=None):
|
||||
for key in property:
|
||||
if key in source:
|
||||
target[key] = source[key]
|
||||
|
||||
if list_property:
|
||||
for key in list_property:
|
||||
if key in source:
|
||||
target[key] = source[key].split()
|
||||
|
||||
def _append_elm_property(self, elm, result, property, identifier):
|
||||
for obj in result['objects']:
|
||||
if (identifier in obj and identifier in elm.attrib and
|
||||
elm.attrib[identifier] == obj[identifier]):
|
||||
for key, value in elm.attrib.items():
|
||||
if key in property:
|
||||
obj[key] = value
|
||||
|
||||
def _append_element(self, elm, result, property, list_property,
|
||||
identifier):
|
||||
sub_elm = {}
|
||||
self._copy_property(elm.attrib, sub_elm, property, list_property)
|
||||
|
||||
for obj in result['objects']:
|
||||
if (identifier in obj and identifier in elm.attrib and
|
||||
elm.attrib[identifier] == obj[identifier]):
|
||||
if self.tag in obj:
|
||||
obj[self.tag].append(sub_elm)
|
||||
else:
|
||||
obj[self.tag] = [sub_elm]
|
||||
|
||||
def start_task_response(self, elm, result):
|
||||
self.is_TaskResponse = True
|
||||
result['type'] = 'TaskResponse'
|
||||
self._copy_property(elm.attrib, result, ['taskId'])
|
||||
|
||||
def end_task_response(self):
|
||||
self.is_TaskResponse = False
|
||||
|
||||
def start_fault(self, elm, result):
|
||||
result['type'] = 'Fault'
|
||||
|
||||
def start_status(self, elm, result):
|
||||
if self.is_TaskResponse:
|
||||
result['maxSeverity'] = elm.attrib['maxSeverity']
|
||||
elif self.is_MoverStatus or self.is_Vdm:
|
||||
self.elt['maxSeverity'] = elm.attrib['maxSeverity']
|
||||
|
||||
def start_query_status(self, elm, result):
|
||||
self.is_QueryStatus = True
|
||||
result['type'] = 'QueryStatus'
|
||||
self._copy_property(elm.attrib, result, ['maxSeverity'])
|
||||
|
||||
def end_query_status(self):
|
||||
self.is_QueryStatus = False
|
||||
|
||||
def start_problem(self, elm, result):
|
||||
self.elt = {}
|
||||
properties = ('message', 'messageCode')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, properties)
|
||||
result['problems'].append(self.elt)
|
||||
|
||||
def start_description(self, elm, result):
|
||||
self.elt['Description'] = elm.text
|
||||
|
||||
def start_action(self, elm, result):
|
||||
self.elt['Action'] = elm.text
|
||||
|
||||
def start_diagnostics(self, elm, result):
|
||||
self.elt['Diagnostics'] = elm.text
|
||||
|
||||
def start_file_system(self, elm, result):
|
||||
self.elt = {}
|
||||
property = (
|
||||
'fileSystem',
|
||||
'name',
|
||||
'type',
|
||||
'storages',
|
||||
'volume',
|
||||
'dataServicePolicies',
|
||||
'internalUse',
|
||||
)
|
||||
list_property = ('storagePools',)
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property, list_property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_file_system_capacity_info(self, elm, result):
|
||||
property = ('volumeSize',)
|
||||
|
||||
identifier = 'fileSystem'
|
||||
|
||||
self._append_elm_property(elm, result, property, identifier)
|
||||
|
||||
def start_storage_pool(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('name', 'autoSize', 'usedSize', 'diskType', 'pool',
|
||||
'dataServicePolicies', 'virtualProvisioning')
|
||||
list_property = ('movers',)
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property, list_property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_system_storage_pool_data(self, elm, result):
|
||||
property = ('greedy', 'isBackendPool')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
|
||||
def start_mover(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('name', 'host', 'mover', 'role')
|
||||
list_property = ('ntpServers', 'standbyFors', 'standbys')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property, list_property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_mover_status(self, elm, result):
|
||||
self.is_MoverStatus = True
|
||||
|
||||
property = ('version', 'csTime', 'clock', 'timezone', 'uptime')
|
||||
|
||||
identifier = 'mover'
|
||||
|
||||
self._append_elm_property(elm, result, property, identifier)
|
||||
|
||||
def end_mover_status(self):
|
||||
self.is_MoverStatus = False
|
||||
|
||||
def start_mover_dns_domain(self, elm, result):
|
||||
property = ('name', 'protocol')
|
||||
list_property = ('servers',)
|
||||
|
||||
identifier = 'mover'
|
||||
|
||||
self._append_element(elm, result, property, list_property, identifier)
|
||||
|
||||
def start_mover_interface(self, elm, result):
|
||||
property = (
|
||||
'name',
|
||||
'device',
|
||||
'up',
|
||||
'ipVersion',
|
||||
'netMask',
|
||||
'ipAddress',
|
||||
'vlanid',
|
||||
)
|
||||
|
||||
identifier = 'mover'
|
||||
|
||||
self._append_element(elm, result, property, None, identifier)
|
||||
|
||||
def start_logical_network_device(self, elm, result):
|
||||
property = ('name', 'type', 'speed')
|
||||
list_property = ('interfaces',)
|
||||
identifier = 'mover'
|
||||
|
||||
self._append_element(elm, result, property, list_property, identifier)
|
||||
|
||||
def start_vdm(self, elm, result):
|
||||
self.is_Vdm = True
|
||||
|
||||
self.elt = {}
|
||||
property = ('name', 'state', 'mover', 'vdm')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def end_vdm(self):
|
||||
self.is_Vdm = False
|
||||
|
||||
def start_interfaces(self, elm, result):
|
||||
self.is_Interfaces = True
|
||||
self.elt['Interfaces'] = []
|
||||
|
||||
def end_interfaces(self):
|
||||
self.is_Interfaces = False
|
||||
|
||||
def start_li(self, elm, result):
|
||||
if self.is_CifsServers:
|
||||
self.elt['CifsServers'].append(elm.text)
|
||||
elif self.is_Aliases:
|
||||
self.elt['Aliases'].append(elm.text)
|
||||
elif self.is_Interfaces:
|
||||
self.elt['Interfaces'].append(elm.text)
|
||||
|
||||
def start_cifs_server(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('type', 'localUsers', 'name', 'mover', 'moverIdIsVdm')
|
||||
|
||||
list_property = ('interfaces',)
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property, list_property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_aliases(self, elm, result):
|
||||
self.is_Aliases = True
|
||||
self.elt['Aliases'] = []
|
||||
|
||||
def end_aliases(self):
|
||||
self.is_Aliases = False
|
||||
|
||||
def start_w2k_server_data(self, elm, result):
|
||||
property = ('domain', 'compName', 'domainJoined')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
|
||||
def start_cifs_share(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('path', 'fileSystem', 'name', 'mover', 'moverIdIsVdm')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_cifs_servers(self, elm, result):
|
||||
self.is_CifsServers = True
|
||||
self.elt['CifsServers'] = []
|
||||
|
||||
def end_cifs_servers(self):
|
||||
self.is_CifsServers = False
|
||||
|
||||
def start_checkpoint(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('checkpointOf', 'name', 'checkpoint', 'state')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
result['objects'].append(self.elt)
|
||||
|
||||
def start_mount(self, elm, result):
|
||||
self.elt = {}
|
||||
property = ('fileSystem', 'path', 'mover', 'moverIdIsVdm')
|
||||
|
||||
self._copy_property(elm.attrib, self.elt, property)
|
||||
result['objects'].append(self.elt)
|
|
@ -129,6 +129,7 @@ class FakeData(object):
|
|||
fake_error_msg = 'fake error message'
|
||||
|
||||
emc_share_backend = 'vnx'
|
||||
vmax_share_backend = 'vmax'
|
||||
emc_nas_server = '192.168.1.20'
|
||||
emc_nas_login = 'fakename'
|
||||
emc_nas_password = 'fakepassword'
|
||||
|
@ -1484,11 +1485,14 @@ class NFSShareTestData(StorageObjectTestData):
|
|||
|
||||
|
||||
class FakeEMCShareDriver(object):
|
||||
def __init__(self):
|
||||
def __init__(self, enas_type='vnx'):
|
||||
self.configuration = conf.Configuration(None)
|
||||
self.configuration.append_config_values = mock.Mock(return_value=0)
|
||||
self.configuration.emc_share_backend = FakeData.emc_share_backend
|
||||
self.configuration.vnx_server_container = FakeData.mover_name
|
||||
if enas_type == 'vmax':
|
||||
self.configuration.emc_share_backend = FakeData.vmax_share_backend
|
||||
self.configuration.vmax_server_container = FakeData.mover_name
|
||||
self.configuration.emc_nas_server = FakeData.emc_nas_server
|
||||
self.configuration.emc_nas_login = FakeData.emc_nas_login
|
||||
self.configuration.emc_nas_password = FakeData.emc_nas_password
|
||||
|
@ -1587,3 +1591,9 @@ STATS = dict(
|
|||
vendor_name='EMC',
|
||||
storage_protocol='NFS_CIFS',
|
||||
driver_version='2.0.0,')
|
||||
|
||||
STATS_VMAX = dict(
|
||||
share_backend_name='VMAX',
|
||||
vendor_name='EMC',
|
||||
storage_protocol='NFS_CIFS',
|
||||
driver_version='2.0.0,')
|
|
@ -21,10 +21,10 @@ from six.moves.urllib import request as url_request # pylint: disable=E0611
|
|||
|
||||
from manila import exception
|
||||
from manila.share import configuration as conf
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connector
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import utils as emc_utils
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||
from manila import utils
|
||||
|
||||
|
||||
|
@ -110,7 +110,7 @@ class XMLAPIConnectorTest(test.TestCase):
|
|||
xml_socket = mock.Mock()
|
||||
xml_socket.read = mock.Mock(return_value=XML_CONN_TD.FAKE_RESP)
|
||||
|
||||
hook = emc_utils.RequestSideEffect()
|
||||
hook = enas_utils.RequestSideEffect()
|
||||
hook.append(ex=url_error.HTTPError(XML_CONN_TD.req_url(),
|
||||
'403', 'fake_message', None, None))
|
||||
hook.append(xml_socket)
|
||||
|
@ -121,7 +121,7 @@ class XMLAPIConnectorTest(test.TestCase):
|
|||
self.XmlConnector.request(XML_CONN_TD.FAKE_BODY)
|
||||
|
||||
def test_request_with_general_exception(self):
|
||||
hook = emc_utils.RequestSideEffect()
|
||||
hook = enas_utils.RequestSideEffect()
|
||||
hook.append(ex=url_error.HTTPError(XML_CONN_TD.req_url(),
|
||||
'error_code', 'fake_message',
|
||||
None, None))
|
|
@ -17,12 +17,12 @@ import ddt
|
|||
import mock
|
||||
import ssl
|
||||
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import utils
|
||||
from manila.share.drivers.dell_emc.common.enas import utils
|
||||
from manila import test
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VNXUtilsTestCase(test.TestCase):
|
||||
class ENASUtilsTestCase(test.TestCase):
|
||||
|
||||
@ddt.data({'full': ['cge-1-0', 'cge-1-1', 'cge-3-0',
|
||||
'cge-3-1', 'cge-12-3'],
|
|
@ -161,7 +161,13 @@ class EMCNFSShareMock(mock.Mock):
|
|||
return True
|
||||
|
||||
|
||||
def patch_get_managed_ports(*arg, **kwargs):
|
||||
def patch_get_managed_ports_vnx(*arg, **kwargs):
|
||||
return mock.patch('manila.share.drivers.dell_emc.plugins.vnx.connection.'
|
||||
'VNXStorageConnection.get_managed_ports',
|
||||
mock.Mock(*arg, **kwargs))
|
||||
|
||||
|
||||
def patch_get_managed_ports_vmax(*arg, **kwargs):
|
||||
return mock.patch('manila.share.drivers.dell_emc.plugins.vmax.connection.'
|
||||
'VMAXStorageConnection.get_managed_ports',
|
||||
mock.Mock(*arg, **kwargs))
|
File diff suppressed because it is too large
Load Diff
|
@ -20,13 +20,13 @@ import mock
|
|||
from oslo_log import log
|
||||
|
||||
from manila import exception
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import connection
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import object_manager
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connection
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import object_manager
|
||||
from manila import test
|
||||
from manila.tests import fake_share
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import utils
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
@ -38,7 +38,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
super(StorageConnectionTestCase, self).setUp()
|
||||
self.emc_share_driver = fakes.FakeEMCShareDriver()
|
||||
|
||||
self.connection = connection.VMAXStorageConnection(LOG)
|
||||
self.connection = connection.VNXStorageConnection(LOG)
|
||||
|
||||
self.pool = fakes.PoolTestData()
|
||||
self.vdm = fakes.VDMTestData()
|
||||
|
@ -61,7 +61,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
with mock.patch.object(connection.VMAXStorageConnection,
|
||||
with mock.patch.object(connection.VNXStorageConnection,
|
||||
'_get_managed_storage_pools',
|
||||
mock.Mock()):
|
||||
self.connection.check_for_setup_error()
|
||||
|
@ -115,7 +115,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
pool_conf = fakes.FakeData.pool_name
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection._get_managed_storage_pools,
|
||||
pool_conf)
|
||||
|
||||
|
@ -173,7 +173,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
ssh_calls = [mock.call(self.cifs_share.cmd_disable_access(), True)]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
self.assertEqual([r'\\192.168.1.1\%s' % share['name']], location,
|
||||
self.assertEqual(location, r'\\192.168.1.1\%s' % share['name'],
|
||||
'CIFS export path is incorrect')
|
||||
|
||||
def test_create_nfs_share(self):
|
||||
|
@ -204,7 +204,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
ssh_calls = [mock.call(self.nfs_share.cmd_create(), True)]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
self.assertEqual('192.168.1.2:/%s' % share['name'], location,
|
||||
self.assertEqual(location, '192.168.1.2:/%s' % share['name'],
|
||||
'NFS export path is incorrect')
|
||||
|
||||
def test_create_cifs_share_without_share_server(self):
|
||||
|
@ -219,7 +219,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
share_server = copy.deepcopy(fakes.SHARE_SERVER)
|
||||
share_server['backend_details']['share_server_name'] = None
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.create_share,
|
||||
None, share, share_server)
|
||||
|
||||
|
@ -233,7 +233,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.create_share,
|
||||
None, share, share_server)
|
||||
|
||||
|
@ -256,7 +256,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.create_share,
|
||||
None, share, share_server)
|
||||
|
||||
|
@ -333,7 +333,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
self.assertEqual([r'\\192.168.1.1\%s' % share['name']], location,
|
||||
self.assertEqual(location, r'\\192.168.1.1\%s' % share['name'],
|
||||
'CIFS export path is incorrect')
|
||||
|
||||
def test_create_nfs_share_from_snapshot(self):
|
||||
|
@ -382,7 +382,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
self.assertEqual('192.168.1.2:/%s' % share['name'], location,
|
||||
self.assertEqual(location, '192.168.1.2:/%s' % share['name'],
|
||||
'NFS export path is incorrect')
|
||||
|
||||
def test_create_share_with_incorrect_proto(self):
|
||||
|
@ -581,7 +581,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.create_snapshot,
|
||||
None, snapshot, share_server)
|
||||
|
||||
|
@ -609,7 +609,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
xml_req_mock.assert_has_calls(expected_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_but_not_found())
|
||||
|
@ -654,7 +654,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server_with_existing_vdm(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_succeed())
|
||||
|
@ -698,12 +698,12 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
network_info = copy.deepcopy(fakes.NETWORK_INFO)
|
||||
network_info['security_services'][0]['type'] = 'fake_type'
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.setup_server,
|
||||
network_info, None)
|
||||
|
||||
@utils.patch_get_managed_ports(
|
||||
side_effect=exception.EMCVmaxXMLAPIError(
|
||||
@utils.patch_get_managed_ports_vnx(
|
||||
side_effect=exception.EMCVnxXMLAPIError(
|
||||
err="Get managed ports fail."))
|
||||
def test_setup_server_without_valid_physical_device(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
|
@ -716,11 +716,11 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces(nfs_interface=''))
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm(nfs_interface=''))
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.setup_server,
|
||||
fakes.NETWORK_INFO, None)
|
||||
|
||||
|
@ -739,7 +739,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server_with_exception(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_but_not_found())
|
||||
|
@ -755,11 +755,11 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces(nfs_interface=''))
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm(nfs_interface=''))
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.setup_server,
|
||||
fakes.NETWORK_INFO, None)
|
||||
|
||||
|
@ -805,7 +805,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces())
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm())
|
||||
ssh_hook.append()
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
@ -848,7 +848,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces())
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm())
|
||||
ssh_hook.append()
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
@ -906,7 +906,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces())
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm())
|
||||
ssh_hook.append()
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
@ -947,7 +947,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.vdm.output_get_interfaces())
|
||||
ssh_hook.append(self.vdm.output_get_interfaces_vdm())
|
||||
ssh_hook.append()
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
@ -1117,7 +1117,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection._cifs_clear_access,
|
||||
'share_name', server, None)
|
||||
|
||||
|
@ -1238,7 +1238,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.allow_access,
|
||||
None, share, access, share_server)
|
||||
|
||||
|
@ -1376,7 +1376,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.deny_access,
|
||||
None, share, access, share_server)
|
||||
|
||||
|
@ -1430,49 +1430,18 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
share = fakes.CIFS_SHARE
|
||||
access = fake_share.fake_access(access_type='fake_type')
|
||||
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_succeed())
|
||||
hook.append(self.cifs_server.resp_get_succeed(
|
||||
mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=True))
|
||||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.cifs_share.output_allow_access())
|
||||
ssh_cmd_mock = mock.Mock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
||||
self.connection.deny_access(None, share, access, share_server)
|
||||
|
||||
ssh_calls = []
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
expected_calls = []
|
||||
xml_req_mock.assert_has_calls(expected_calls)
|
||||
self.assertRaises(exception.InvalidShareAccess,
|
||||
self.connection.deny_access,
|
||||
None, share, access, share_server)
|
||||
|
||||
def test_deny_nfs_access_with_incorrect_access_type(self):
|
||||
share_server = fakes.SHARE_SERVER
|
||||
share = fakes.NFS_SHARE
|
||||
access = fake_share.fake_access(access_type='fake_type')
|
||||
|
||||
rw_hosts = copy.deepcopy(fakes.FakeData.rw_hosts)
|
||||
rw_hosts.append(access['access_to'])
|
||||
|
||||
ssh_hook = utils.SSHSideEffect()
|
||||
ssh_hook.append(self.nfs_share.output_get_succeed(
|
||||
rw_hosts=rw_hosts,
|
||||
ro_hosts=fakes.FakeData.ro_hosts))
|
||||
ssh_hook.append(self.nfs_share.output_set_access_success())
|
||||
ssh_hook.append(self.nfs_share.output_get_succeed(
|
||||
rw_hosts=fakes.FakeData.rw_hosts,
|
||||
ro_hosts=fakes.FakeData.ro_hosts))
|
||||
ssh_cmd_mock = utils.EMCNFSShareMock(side_effect=ssh_hook)
|
||||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
|
||||
self.connection.deny_access(None, share, access, share_server)
|
||||
|
||||
ssh_calls = []
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
self.assertRaises(exception.InvalidShareAccess,
|
||||
self.connection.deny_access,
|
||||
None, share, access, share_server)
|
||||
|
||||
def test_update_share_stats(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
|
@ -1507,7 +1476,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.update_share_stats,
|
||||
fakes.STATS)
|
||||
|
||||
|
@ -1544,7 +1513,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.get_pool,
|
||||
share)
|
||||
|
||||
|
@ -1560,7 +1529,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.get_pool,
|
||||
share)
|
||||
|
||||
|
@ -1580,7 +1549,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
xml_req_mock = utils.EMCMock(side_effect=hook)
|
||||
self.connection.manager.connectors['XML'].request = xml_req_mock
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.get_pool,
|
||||
share)
|
||||
|
||||
|
@ -1632,5 +1601,5 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.manager.connectors['SSH'].run_ssh = ssh_cmd_mock
|
||||
self.connection.port_conf = ['cge-2-0']
|
||||
|
||||
self.assertRaises(exception.EMCVmaxXMLAPIError,
|
||||
self.assertRaises(exception.EMCVnxXMLAPIError,
|
||||
self.connection.get_managed_ports)
|
||||
|
|
|
@ -1,224 +0,0 @@
|
|||
# Copyright (c) 2016 Dell Inc. or its subsidiaries.
|
||||
# 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 eventlet import greenthread
|
||||
import mock
|
||||
from oslo_concurrency import processutils
|
||||
from six.moves.urllib import error as url_error # pylint: disable=E0611
|
||||
from six.moves.urllib import request as url_request # pylint: disable=E0611
|
||||
|
||||
from manila import exception
|
||||
from manila.share import configuration as conf
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import connector
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import utils as emc_utils
|
||||
from manila import utils
|
||||
|
||||
|
||||
class XMLAPIConnectorTestData(object):
|
||||
FAKE_BODY = '<fakebody></fakebody>'
|
||||
FAKE_RESP = '<Response></Response>'
|
||||
FAKE_METHOD = 'fake_method'
|
||||
|
||||
FAKE_KEY = 'key'
|
||||
FAKE_VALUE = 'value'
|
||||
|
||||
@staticmethod
|
||||
def req_auth_url():
|
||||
return 'https://' + fakes.FakeData.emc_nas_server + '/Login'
|
||||
|
||||
@staticmethod
|
||||
def req_credential():
|
||||
return (
|
||||
'user=' + fakes.FakeData.emc_nas_login
|
||||
+ '&password=' + fakes.FakeData.emc_nas_password
|
||||
+ '&Login=Login'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def req_url_encode():
|
||||
return {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
|
||||
@staticmethod
|
||||
def req_url():
|
||||
return (
|
||||
'https://'
|
||||
+ fakes.FakeData.emc_nas_server
|
||||
+ '/servlets/CelerraManagementServices'
|
||||
)
|
||||
|
||||
|
||||
XML_CONN_TD = XMLAPIConnectorTestData
|
||||
|
||||
|
||||
class XMLAPIConnectorTest(test.TestCase):
|
||||
@mock.patch.object(url_request, 'Request', mock.Mock())
|
||||
def setUp(self):
|
||||
super(XMLAPIConnectorTest, self).setUp()
|
||||
|
||||
emc_share_driver = fakes.FakeEMCShareDriver()
|
||||
|
||||
self.configuration = emc_share_driver.configuration
|
||||
|
||||
xml_socket = mock.Mock()
|
||||
xml_socket.read = mock.Mock(return_value=XML_CONN_TD.FAKE_RESP)
|
||||
opener = mock.Mock()
|
||||
opener.open = mock.Mock(return_value=xml_socket)
|
||||
|
||||
with mock.patch.object(url_request, 'build_opener',
|
||||
mock.Mock(return_value=opener)):
|
||||
self.XmlConnector = connector.XMLAPIConnector(
|
||||
configuration=self.configuration, debug=False)
|
||||
|
||||
expected_calls = [
|
||||
mock.call(XML_CONN_TD.req_auth_url(),
|
||||
XML_CONN_TD.req_credential(),
|
||||
XML_CONN_TD.req_url_encode()),
|
||||
]
|
||||
|
||||
url_request.Request.assert_has_calls(expected_calls)
|
||||
|
||||
def test_request_with_debug(self):
|
||||
self.XmlConnector.debug = True
|
||||
|
||||
request = mock.Mock()
|
||||
request.headers = {XML_CONN_TD.FAKE_KEY: XML_CONN_TD.FAKE_VALUE}
|
||||
request.get_full_url = mock.Mock(
|
||||
return_value=XML_CONN_TD.FAKE_VALUE)
|
||||
|
||||
with mock.patch.object(url_request, 'Request',
|
||||
mock.Mock(return_value=request)):
|
||||
rsp = self.XmlConnector.request(XML_CONN_TD.FAKE_BODY,
|
||||
XML_CONN_TD.FAKE_METHOD)
|
||||
|
||||
self.assertEqual(XML_CONN_TD.FAKE_RESP, rsp)
|
||||
|
||||
def test_request_with_no_authorized_exception(self):
|
||||
xml_socket = mock.Mock()
|
||||
xml_socket.read = mock.Mock(return_value=XML_CONN_TD.FAKE_RESP)
|
||||
|
||||
hook = emc_utils.RequestSideEffect()
|
||||
hook.append(ex=url_error.HTTPError(XML_CONN_TD.req_url(),
|
||||
'403', 'fake_message', None, None))
|
||||
hook.append(xml_socket)
|
||||
hook.append(xml_socket)
|
||||
|
||||
self.XmlConnector.url_opener.open = mock.Mock(side_effect=hook)
|
||||
|
||||
self.XmlConnector.request(XML_CONN_TD.FAKE_BODY)
|
||||
|
||||
def test_request_with_general_exception(self):
|
||||
hook = emc_utils.RequestSideEffect()
|
||||
hook.append(ex=url_error.HTTPError(XML_CONN_TD.req_url(),
|
||||
'error_code', 'fake_message',
|
||||
None, None))
|
||||
self.XmlConnector.url_opener.open = mock.Mock(side_effect=hook)
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.XmlConnector.request,
|
||||
XML_CONN_TD.FAKE_BODY)
|
||||
|
||||
|
||||
class MockSSH(object):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
|
||||
class MockSSHPool(object):
|
||||
def __init__(self):
|
||||
self.ssh = MockSSH()
|
||||
|
||||
def item(self):
|
||||
try:
|
||||
return self.ssh
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
class CmdConnectorTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CmdConnectorTest, self).setUp()
|
||||
|
||||
self.configuration = conf.Configuration(None)
|
||||
self.configuration.append_config_values = mock.Mock(return_value=0)
|
||||
self.configuration.emc_nas_login = fakes.FakeData.emc_nas_login
|
||||
self.configuration.emc_nas_password = fakes.FakeData.emc_nas_password
|
||||
self.configuration.emc_nas_server = fakes.FakeData.emc_nas_server
|
||||
|
||||
self.sshpool = MockSSHPool()
|
||||
with mock.patch.object(utils, "SSHPool",
|
||||
mock.Mock(return_value=self.sshpool)):
|
||||
self.CmdHelper = connector.SSHConnector(
|
||||
configuration=self.configuration, debug=False)
|
||||
|
||||
utils.SSHPool.assert_called_once_with(
|
||||
ip=fakes.FakeData.emc_nas_server,
|
||||
port=22,
|
||||
conn_timeout=None,
|
||||
login=fakes.FakeData.emc_nas_login,
|
||||
password=fakes.FakeData.emc_nas_password)
|
||||
|
||||
def test_run_ssh(self):
|
||||
with mock.patch.object(processutils, "ssh_execute",
|
||||
mock.Mock(return_value=('fake_output', ''))):
|
||||
cmd_list = ['fake', 'cmd']
|
||||
self.CmdHelper.run_ssh(cmd_list)
|
||||
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
self.sshpool.item(), 'fake cmd', check_exit_code=False)
|
||||
|
||||
def test_run_ssh_with_debug(self):
|
||||
self.CmdHelper.debug = True
|
||||
|
||||
with mock.patch.object(processutils, "ssh_execute",
|
||||
mock.Mock(return_value=('fake_output', ''))):
|
||||
cmd_list = ['fake', 'cmd']
|
||||
self.CmdHelper.run_ssh(cmd_list)
|
||||
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
self.sshpool.item(), 'fake cmd', check_exit_code=False)
|
||||
|
||||
@mock.patch.object(
|
||||
processutils, "ssh_execute",
|
||||
mock.Mock(side_effect=processutils.ProcessExecutionError))
|
||||
def test_run_ssh_exception(self):
|
||||
cmd_list = ['fake', 'cmd']
|
||||
|
||||
self.mock_object(greenthread, 'sleep', mock.Mock())
|
||||
|
||||
sshpool = MockSSHPool()
|
||||
|
||||
with mock.patch.object(utils, "SSHPool",
|
||||
mock.Mock(return_value=sshpool)):
|
||||
self.CmdHelper = connector.SSHConnector(self.configuration)
|
||||
|
||||
self.assertRaises(processutils.ProcessExecutionError,
|
||||
self.CmdHelper.run_ssh,
|
||||
cmd_list,
|
||||
True)
|
||||
|
||||
utils.SSHPool.assert_called_once_with(
|
||||
ip=fakes.FakeData.emc_nas_server,
|
||||
port=22,
|
||||
conn_timeout=None,
|
||||
login=fakes.FakeData.emc_nas_login,
|
||||
password=fakes.FakeData.emc_nas_password)
|
||||
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
sshpool.item(), 'fake cmd', check_exit_code=True)
|
|
@ -20,16 +20,17 @@ from lxml import builder
|
|||
import mock
|
||||
from oslo_concurrency import processutils
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import xml_api_parser as parser
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import (
|
||||
object_manager as manager)
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import constants
|
||||
from manila.share.drivers.dell_emc.plugins.vmax import xml_api_parser as parser
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vmax import utils
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import utils
|
||||
|
||||
|
||||
class StorageObjectManagerTestCase(test.TestCase):
|
||||
|
@ -38,7 +39,7 @@ class StorageObjectManagerTestCase(test.TestCase):
|
|||
def setUp(self):
|
||||
super(StorageObjectManagerTestCase, self).setUp()
|
||||
|
||||
emd_share_driver = fakes.FakeEMCShareDriver()
|
||||
emd_share_driver = fakes.FakeEMCShareDriver('vmax')
|
||||
|
||||
self.manager = manager.StorageObjectManager(
|
||||
emd_share_driver.configuration)
|
||||
|
@ -80,7 +81,7 @@ class StorageObjectTestCaseBase(test.TestCase):
|
|||
def setUp(self):
|
||||
super(StorageObjectTestCaseBase, self).setUp()
|
||||
|
||||
emd_share_driver = fakes.FakeEMCShareDriver()
|
||||
emd_share_driver = fakes.FakeEMCShareDriver('vmax')
|
||||
|
||||
self.manager = manager.StorageObjectManager(
|
||||
emd_share_driver.configuration)
|
||||
|
@ -98,7 +99,6 @@ class StorageObjectTestCaseBase(test.TestCase):
|
|||
|
||||
|
||||
class StorageObjectTestCase(StorageObjectTestCaseBase):
|
||||
|
||||
def test_xml_api_retry(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.base.resp_need_retry())
|
||||
|
@ -489,7 +489,7 @@ class FileSystemTestCase(StorageObjectTestCaseBase):
|
|||
self.fs.src_fileystems_name,
|
||||
self.pool.pool_name,
|
||||
self.vdm.vdm_name,
|
||||
self.mover.interconnect_id,)
|
||||
self.mover.interconnect_id)
|
||||
|
||||
ssh_calls = [
|
||||
mock.call(self.fs.cmd_create_from_ckpt(), False),
|
||||
|
@ -1076,11 +1076,11 @@ class VDMTestCase(StorageObjectTestCaseBase):
|
|||
def test_detach_nfs_interface_with_error(self):
|
||||
self.ssh_hook.append(ex=processutils.ProcessExecutionError(
|
||||
stdout=self.vdm.fake_output))
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces(
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces_vdm(
|
||||
self.mover.interface_name2))
|
||||
self.ssh_hook.append(ex=processutils.ProcessExecutionError(
|
||||
stdout=self.vdm.fake_output))
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces(
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces_vdm(
|
||||
nfs_interface=fakes.FakeData.interface_name1))
|
||||
|
||||
context = self.manager.getStorageContext('VDM')
|
||||
|
@ -1103,7 +1103,7 @@ class VDMTestCase(StorageObjectTestCaseBase):
|
|||
context.conn['SSH'].run_ssh.assert_has_calls(ssh_calls)
|
||||
|
||||
def test_get_cifs_nfs_interface(self):
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces())
|
||||
self.ssh_hook.append(self.vdm.output_get_interfaces_vdm())
|
||||
|
||||
context = self.manager.getStorageContext('VDM')
|
||||
context.conn['SSH'].run_ssh = mock.Mock(side_effect=self.ssh_hook)
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
# Copyright (c) 2016 Dell Inc. or its subsidiaries.
|
||||
# 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.dell_emc.plugins.vmax import utils
|
||||
from manila import test
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VMAXUtilsTestCase(test.TestCase):
|
||||
|
||||
@ddt.data({'full': ['cge-1-0', 'cge-1-1', 'cge-3-0',
|
||||
'cge-3-1', 'cge-12-3'],
|
||||
'matchers': ['cge-?-0', 'cge-3*', 'foo'],
|
||||
'matched': set(['cge-1-0', 'cge-3-0',
|
||||
'cge-3-1']),
|
||||
'unmatched': set(['cge-1-1', 'cge-12-3'])},
|
||||
{'full': ['cge-1-0', 'cge-1-1'],
|
||||
'matchers': ['cge-1-0'],
|
||||
'matched': set(['cge-1-0']),
|
||||
'unmatched': set(['cge-1-1'])},
|
||||
{'full': ['cge-1-0', 'cge-1-1'],
|
||||
'matchers': ['foo'],
|
||||
'matched': set([]),
|
||||
'unmatched': set(['cge-1-0', 'cge-1-1'])})
|
||||
@ddt.unpack
|
||||
def test_do_match_any(self, full, matchers, matched, unmatched):
|
||||
real_matched, real_unmatched = utils.do_match_any(
|
||||
full, matchers)
|
||||
self.assertEqual(matched, real_matched)
|
||||
self.assertEqual(unmatched, real_unmatched)
|
|
@ -20,13 +20,13 @@ import mock
|
|||
from oslo_log import log
|
||||
|
||||
from manila import exception
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connection
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import object_manager
|
||||
from manila import test
|
||||
from manila.tests import fake_share
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import utils
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
@ -609,7 +609,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
xml_req_mock.assert_has_calls(expected_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_but_not_found())
|
||||
|
@ -654,7 +654,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server_with_existing_vdm(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_succeed())
|
||||
|
@ -702,7 +702,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
self.connection.setup_server,
|
||||
network_info, None)
|
||||
|
||||
@utils.patch_get_managed_ports(
|
||||
@utils.patch_get_managed_ports_vnx(
|
||||
side_effect=exception.EMCVnxXMLAPIError(
|
||||
err="Get managed ports fail."))
|
||||
def test_setup_server_without_valid_physical_device(self):
|
||||
|
@ -739,7 +739,7 @@ class StorageConnectionTestCase(test.TestCase):
|
|||
]
|
||||
ssh_cmd_mock.assert_has_calls(ssh_calls)
|
||||
|
||||
@utils.patch_get_managed_ports(return_value=['cge-1-0'])
|
||||
@utils.patch_get_managed_ports_vnx(return_value=['cge-1-0'])
|
||||
def test_setup_server_with_exception(self):
|
||||
hook = utils.RequestSideEffect()
|
||||
hook.append(self.vdm.resp_get_but_not_found())
|
||||
|
|
|
@ -19,17 +19,18 @@ import time
|
|||
import ddt
|
||||
from lxml import builder
|
||||
import mock
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import connector
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import connector
|
||||
from manila.share.drivers.dell_emc.common.enas import constants
|
||||
from manila.share.drivers.dell_emc.common.enas import xml_api_parser as parser
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import object_manager as manager
|
||||
from manila.share.drivers.dell_emc.plugins.vnx import xml_api_parser as parser
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import fakes
|
||||
from manila.tests.share.drivers.dell_emc.plugins.vnx import utils
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||
from manila.tests.share.drivers.dell_emc.common.enas import utils
|
||||
|
||||
|
||||
class StorageObjectManagerTestCase(test.TestCase):
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
# Copyright (c) 2015 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 doctest
|
||||
|
||||
from lxml import doctestcompare
|
||||
import mock
|
||||
import six
|
||||
|
||||
|
||||
CHECKER = doctestcompare.LXMLOutputChecker()
|
||||
PARSE_XML = doctest.register_optionflag('PARSE_XML')
|
||||
|
||||
|
||||
class RequestSideEffect(object):
|
||||
def __init__(self):
|
||||
self.actions = []
|
||||
self.started = False
|
||||
|
||||
def append(self, resp=None, ex=None):
|
||||
if not self.started:
|
||||
self.actions.append((resp, ex))
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if not self.started:
|
||||
self.started = True
|
||||
self.actions.reverse()
|
||||
item = self.actions.pop()
|
||||
if item[1]:
|
||||
raise item[1]
|
||||
else:
|
||||
return item[0]
|
||||
|
||||
|
||||
class SSHSideEffect(object):
|
||||
def __init__(self):
|
||||
self.actions = []
|
||||
self.started = False
|
||||
|
||||
def append(self, resp=None, err=None, ex=None):
|
||||
if not self.started:
|
||||
self.actions.append((resp, err, ex))
|
||||
|
||||
def __call__(self, rel_url, req_data=None, method=None,
|
||||
return_rest_err=True, *args, **kwargs):
|
||||
if not self.started:
|
||||
self.started = True
|
||||
self.actions.reverse()
|
||||
item = self.actions.pop()
|
||||
if item[2]:
|
||||
raise item[2]
|
||||
else:
|
||||
if return_rest_err:
|
||||
return item[0:2]
|
||||
else:
|
||||
return item[1]
|
||||
|
||||
|
||||
class EMCMock(mock.Mock):
|
||||
def _get_req_from_call(self, call):
|
||||
if len(call) == 3:
|
||||
return call[1][0]
|
||||
elif len(call) == 2:
|
||||
return call[0][0]
|
||||
|
||||
def assert_has_calls(self, calls):
|
||||
if len(calls) != len(self.mock_calls):
|
||||
raise AssertionError(
|
||||
'Mismatch error.\nExpected: %r\n'
|
||||
'Actual: %r' % (calls, self.mock_calls)
|
||||
)
|
||||
|
||||
iter_expect = iter(calls)
|
||||
iter_actual = iter(self.mock_calls)
|
||||
|
||||
while True:
|
||||
try:
|
||||
expect = self._get_req_from_call(next(iter_expect))
|
||||
actual = self._get_req_from_call(next(iter_actual))
|
||||
except StopIteration:
|
||||
return True
|
||||
|
||||
if not isinstance(expect, six.binary_type):
|
||||
expect = six.b(expect)
|
||||
if not isinstance(actual, six.binary_type):
|
||||
actual = six.b(actual)
|
||||
if not CHECKER.check_output(expect, actual, PARSE_XML):
|
||||
raise AssertionError(
|
||||
'Mismatch error.\nExpected: %r\n'
|
||||
'Actual: %r' % (calls, self.mock_calls)
|
||||
)
|
||||
|
||||
|
||||
class EMCNFSShareMock(mock.Mock):
|
||||
def assert_has_calls(self, calls):
|
||||
if len(calls) != len(self.mock_calls):
|
||||
raise AssertionError(
|
||||
'Mismatch error.\nExpected: %r\n'
|
||||
'Actual: %r' % (calls, self.mock_calls)
|
||||
)
|
||||
|
||||
iter_expect = iter(calls)
|
||||
iter_actual = iter(self.mock_calls)
|
||||
|
||||
while True:
|
||||
try:
|
||||
expect = next(iter_expect)[1][0]
|
||||
actual = next(iter_actual)[1][0]
|
||||
except StopIteration:
|
||||
return True
|
||||
|
||||
if not self._option_check(expect, actual):
|
||||
raise AssertionError(
|
||||
'Mismatch error.\nExpected: %r\n'
|
||||
'Actual: %r' % (calls, self.mock_calls)
|
||||
)
|
||||
|
||||
def _option_parser(self, option):
|
||||
option_map = {}
|
||||
for item in option.split(','):
|
||||
key, value = item.split('=')
|
||||
option_map[key] = value
|
||||
|
||||
return option_map
|
||||
|
||||
@staticmethod
|
||||
def _opt_value_from_map(opt_map, key):
|
||||
value = opt_map.get(key)
|
||||
if value:
|
||||
ret = set(value.split(':'))
|
||||
else:
|
||||
ret = set()
|
||||
return ret
|
||||
|
||||
def _option_check(self, expect, actual):
|
||||
if '-option' in actual and '-option' in expect:
|
||||
exp_option = expect[expect.index('-option') + 1]
|
||||
act_option = actual[actual.index('-option') + 1]
|
||||
|
||||
exp_opt_map = self._option_parser(exp_option)
|
||||
act_opt_map = self._option_parser(act_option)
|
||||
|
||||
for key in exp_opt_map:
|
||||
exp_set = self._opt_value_from_map(exp_opt_map, key)
|
||||
act_set = self._opt_value_from_map(act_opt_map, key)
|
||||
if exp_set != act_set:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def patch_get_managed_ports(*arg, **kwargs):
|
||||
return mock.patch('manila.share.drivers.dell_emc.plugins.vnx.connection.'
|
||||
'VNXStorageConnection.get_managed_ports',
|
||||
mock.Mock(*arg, **kwargs))
|
Loading…
Reference in New Issue