From 9e8876612514116ce316020a16e54c49359a8ff5 Mon Sep 17 00:00:00 2001 From: zhongjun Date: Thu, 23 Apr 2015 23:29:19 +0800 Subject: [PATCH] Huawei manila driver code refactoring The Huawei manila driver code should be restructured. Instead of using a loose interface definition of share drivers, use the ABC python library to build abstract classes that enforces the driver to implement the needed functionality. The huawei_helper.py is too long, the code structure should be adjusted. Change-Id: I4fa6434b397becccab75ceb1f181394a278b249a Partially-Implements: blueprint huawei-manila-driver-refactor --- manila/share/drivers/huawei/base.py | 59 +++ manila/share/drivers/huawei/huawei_nas.py | 185 ++----- manila/share/drivers/huawei/v3/__init__.py | 0 manila/share/drivers/huawei/v3/connection.py | 388 +++++++++++++++ .../huawei/{huawei_helper.py => v3/helper.py} | 255 ++-------- .../share/drivers/huawei/test_huawei_nas.py | 459 +++++++++++++----- 6 files changed, 871 insertions(+), 475 deletions(-) create mode 100644 manila/share/drivers/huawei/base.py create mode 100644 manila/share/drivers/huawei/v3/__init__.py create mode 100644 manila/share/drivers/huawei/v3/connection.py rename manila/share/drivers/huawei/{huawei_helper.py => v3/helper.py} (68%) diff --git a/manila/share/drivers/huawei/base.py b/manila/share/drivers/huawei/base.py new file mode 100644 index 00000000..7d079653 --- /dev/null +++ b/manila/share/drivers/huawei/base.py @@ -0,0 +1,59 @@ +# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# 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. + +"""Abstract base class to work with share.""" +import abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class HuaweiBase(object): + """Interface to work with share.""" + + def __init__(self, configuration): + """Do initialization.""" + self.configuration = configuration + + @abc.abstractmethod + def create_share(self, share, share_server): + """Is called to create share.""" + + @abc.abstractmethod + def create_snapshot(self, snapshot, share_server): + """Is called to create snapshot.""" + + @abc.abstractmethod + def delete_share(self, share, share_server): + """Is called to remove share.""" + + @abc.abstractmethod + def delete_snapshot(self, snapshot, share_server): + """Is called to remove snapshot.""" + + @abc.abstractmethod + def allow_access(self, share, access, share_server): + """Allow access to the share.""" + + @abc.abstractmethod + def deny_access(self, share, access, share_server): + """Deny access to the share.""" + + @abc.abstractmethod + def get_network_allocations_number(self): + """Get number of network interfaces to be created.""" + + def update_share_stats(self, stats_dict): + """Retrieve stats info from share group.""" diff --git a/manila/share/drivers/huawei/huawei_nas.py b/manila/share/drivers/huawei/huawei_nas.py index 714b6dfd..ca4040c7 100644 --- a/manila/share/drivers/huawei/huawei_nas.py +++ b/manila/share/drivers/huawei/huawei_nas.py @@ -13,20 +13,21 @@ # License for the specific language governing permissions and limitations # under the License. -"""Huawei Nas Driver for Huawei OceanStor V3 storage arrays.""" -import time +"""Huawei Nas Driver for Huawei storage arrays.""" +from xml.etree import ElementTree as ET from oslo_config import cfg from oslo_log import log -from oslo_utils import excutils -from oslo_utils import units +from oslo_utils import importutils from manila import exception -from manila.i18n import _, _LI, _LW -from manila.openstack.common import loopingcall +from manila.i18n import _ from manila.share import driver -from manila.share.drivers.huawei import constants -from manila.share.drivers.huawei import huawei_helper + + +HUAWEI_UNIFIED_DRIVER_REGISTRY = { + 'V3': 'manila.share.drivers.huawei.v3.connection.V3StorageConnection', } + huawei_opts = [ cfg.StrOpt('manila_huawei_conf_file', @@ -54,56 +55,47 @@ class HuaweiNasDriver(driver.ShareDriver): self.configuration = kwargs.get('configuration', None) if self.configuration: self.configuration.append_config_values(huawei_opts) - self.helper = huawei_helper.RestHelper(self.configuration) + backend_driver = self.get_backend_driver() + self.plugin = importutils.import_object(backend_driver, + self.configuration) else: raise exception.InvalidShare(_("Huawei configuration missing.")) def check_for_setup_error(self): """Returns an error if prerequisites aren't met.""" - self.helper._check_conf_file() - self.helper._check_service() + self.plugin.check_conf_file() + self.plugin.check_service() + + def get_backend_driver(self): + filename = self.configuration.manila_huawei_conf_file + + try: + tree = ET.parse(filename) + root = tree.getroot() + except Exception as err: + message = (_('Read Huawei config file(%(filename)s)' + ' for Manila error: %(err)s') + % {'filename': filename, + 'err': err}) + LOG.error(message) + raise exception.InvalidInput(reason=message) + product = root.findtext('Storage/Product') + backend_driver = HUAWEI_UNIFIED_DRIVER_REGISTRY.get(product) + if backend_driver is None: + raise exception.InvalidInput( + reason=_('Storage %s is not supported.') % product) + + return backend_driver def do_setup(self, context): """Any initialization the huawei nas driver does while starting.""" LOG.debug("Do setup the plugin.") - return self.helper.login() + self.plugin.connect() def create_share(self, context, share, share_server=None): """Create a share.""" LOG.debug("Create a share.") - share_name = share['name'] - share_proto = share['share_proto'] - size = share['size'] * units.Mi * 2 - - fs_id = None - # We sleep here to ensure the newly created filesystem can be read. - wait_interval = self._get_wait_interval() - try: - fs_id = self.helper.allocate_container(share_name, size) - - def _create_share_complete(): - fs = self.helper._get_fs_info_by_id(fs_id) - if fs['HEALTHSTATUS'] == constants.STATUS_FS_HEALTH\ - and fs['RUNNINGSTATUS'] == constants.STATUS_FS_RUNNING: - return True - else: - return False - self._wait_for_condition(_create_share_complete, - int(wait_interval)) - except Exception: - with excutils.save_and_reraise_exception(): - if fs_id is not None: - self.helper._delete_fs(fs_id) - raise exception.InvalidShare('The status of filesystem error.') - - try: - self.helper._create_share(share_name, fs_id, share_proto) - except Exception: - with excutils.save_and_reraise_exception(): - if fs_id is not None: - self.helper._delete_fs(fs_id) - - location = self.helper._get_location_path(share_name, share_proto) + location = self.plugin.create_share(share, share_server) return location def create_share_from_snapshot(self, context, share, snapshot, @@ -116,48 +108,16 @@ class HuaweiNasDriver(driver.ShareDriver): """Delete a share.""" LOG.debug("Delete a share.") - self.helper._delete_share(share['name'], share['share_proto']) + self.plugin.delete_share(share, share_server) def create_snapshot(self, context, snapshot, share_server=None): """Create a snapshot.""" - snap_name = snapshot['id'] - share_proto = snapshot['share_proto'] - - share_name = self.helper._get_share_name_by_id(snapshot['share_id']) - share_type = self.helper._get_share_type(share_proto) - share = self.helper._get_share_by_name(share_name, share_type) - - if not share: - err_msg = (_("Create a snapshot,share fs id is empty.")) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) - - sharefsid = share['FSID'] - snapshot_name = "share_snapshot_" + snap_name - snap_id = self.helper._create_snapshot(sharefsid, - snapshot_name) - LOG.info(_LI('Creating snapshot id %s.'), snap_id) + self.plugin.create_snapshot(snapshot, share_server) def delete_snapshot(self, context, snapshot, share_server=None): """Delete a snapshot.""" LOG.debug("Delete a snapshot.") - snap_name = snapshot['id'] - - share_name = self.helper._get_share_name_by_id(snapshot['share_id']) - sharefsid = self.helper._get_fsid_by_name(share_name) - - if sharefsid is None: - LOG.warn(_LW('Delete snapshot share id %s fs has been deleted.'), - snap_name) - return - - snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name) - snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id) - - if snapshot_flag is True: - self.helper._delete_snapshot(snapshot_id) - else: - LOG.warn(_LW("Can not find snapshot %s in array."), snap_name) + self.plugin.delete_snapshot(snapshot, share_server) def ensure_share(self, context, share, share_server=None): """Ensure that storages are mounted and exported.""" @@ -167,80 +127,27 @@ class HuaweiNasDriver(driver.ShareDriver): """Allow access to the share.""" LOG.debug("Allow access.") - self.helper._allow_access(share['name'], access, share['share_proto']) + self.plugin.allow_access(share, access, share_server) def deny_access(self, context, share, access, share_server=None): """Deny access to the share.""" LOG.debug("Deny access.") - self.helper._deny_access(share['name'], access, share['share_proto']) + self.plugin.deny_access(share, access, share_server) def get_network_allocations_number(self): """Get number of network interfaces to be created.""" LOG.debug("Get network allocations number.") - return constants.IP_ALLOCATIONS + return self.plugin.get_network_allocations_number() def _update_share_stats(self): """Retrieve status info from share group.""" backend_name = self.configuration.safe_get('share_backend_name') - capacity = self.helper._get_capacity() data = dict( share_backend_name=backend_name or 'HUAWEI_NAS_Driver', vendor_name='Huawei', - storage_protocol='NFS_CIFS', - total_capacity_gb=capacity['total_capacity'], - free_capacity_gb=capacity['free_capacity']) + storage_protocol='NFS_CIFS') + + self.plugin.update_share_stats(data) super(HuaweiNasDriver, self)._update_share_stats(data) - - def _get_wait_interval(self): - """Get wait interval from huawei conf file.""" - root = self.helper._read_xml() - wait_interval = root.findtext('Filesystem/WaitInterval') - if wait_interval: - return wait_interval - else: - LOG.info(_LI( - "Wait interval is not configured in huawei " - "conf file. Use default: %(default_wait_interval)d."), - {"default_wait_interval": constants.DEFAULT_WAIT_INTERVAL}) - return constants.DEFAULT_WAIT_INTERVAL - - def _get_timeout(self): - """Get timeout from huawei conf file.""" - root = self.helper._read_xml() - timeout = root.findtext('Filesystem/Timeout') - if timeout: - return timeout - else: - LOG.info(_LI( - "Timeout is not configured in huawei conf file. " - "Use default: %(default_timeout)d."), - {"default_timeout": constants.DEFAULT_TIMEOUT}) - return constants.DEFAULT_TIMEOUT - - def _wait_for_condition(self, func, interval, timeout=None): - start_time = time.time() - if timeout is None: - timeout = self._get_timeout() - - def _inner(): - try: - res = func() - except Exception as ex: - res = False - LOG.debug('_wait_for_condition: %(func_name)s ' - 'failed for %(exception)s.', - {'func_name': func.__name__, - 'exception': ex.message}) - if res: - raise loopingcall.LoopingCallDone() - - if int(time.time()) - int(start_time) > int(timeout): - msg = (_('_wait_for_condition: %s timed out.') - % func.__name__) - LOG.error(msg) - raise exception.InvalidShare(data=msg) - - timer = loopingcall.FixedIntervalLoopingCall(_inner) - timer.start(interval=interval).wait() diff --git a/manila/share/drivers/huawei/v3/__init__.py b/manila/share/drivers/huawei/v3/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/manila/share/drivers/huawei/v3/connection.py b/manila/share/drivers/huawei/v3/connection.py new file mode 100644 index 00000000..efe06c78 --- /dev/null +++ b/manila/share/drivers/huawei/v3/connection.py @@ -0,0 +1,388 @@ +# Copyright (c) 2015 Huawei Technologies Co., Ltd. +# 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 time + +from oslo_log import log +from oslo_utils import units + +from manila import exception +from manila.i18n import _, _LI, _LW +from manila.share.drivers.huawei import base as driver +from manila.share.drivers.huawei import constants +from manila.share.drivers.huawei.v3 import helper + +LOG = log.getLogger(__name__) + + +class V3StorageConnection(driver.HuaweiBase): + """Helper class for Huawei OceanStor V3 storage system.""" + + def __init__(self, configuration): + super(V3StorageConnection, self).__init__(configuration) + + def connect(self): + """Try to connect to V3 server.""" + if self.configuration: + self.helper = helper.RestHelper(self.configuration) + else: + raise exception.InvalidInput(_("Huawei configuration missing.")) + self.helper.login() + + def create_share(self, share, share_server=None): + """Create a share.""" + share_name = share['name'] + share_proto = share['share_proto'] + + fs_id = None + # We sleep here to ensure the newly created filesystem can be read. + wait_interval = self._get_wait_interval() + timeout = self._get_timeout() + + try: + fs_id = self.allocate_container(share) + fs = self.helper._get_fs_info_by_id(fs_id) + end_time = time.time() + timeout + + while not (self.check_fs_status(fs['HEALTHSTATUS'], + fs['RUNNINGSTATUS']) + or time.time() > end_time): + time.sleep(wait_interval) + fs = self.helper._get_fs_info_by_id(fs_id) + + if not self.check_fs_status(fs['HEALTHSTATUS'], + fs['RUNNINGSTATUS']): + raise exception.InvalidShare( + reason=(_('Invalid status of filesystem: %(health)s ' + '%(running)s.') + % {'health': fs['HEALTHSTATUS'], + 'running': fs['RUNNINGSTATUS']})) + except Exception as err: + if fs_id is not None: + self.helper._delete_fs(fs_id) + message = (_('Failed to create share %(name)s.' + 'Reason: %(err)s.') + % {'name': share_name, + 'err': err}) + raise exception.InvalidShare(reason=message) + + try: + self.helper._create_share(share_name, fs_id, share_proto) + except Exception as err: + if fs_id is not None: + self.helper._delete_fs(fs_id) + raise exception.InvalidShare( + reason=(_('Failed to create share %(name)s.' + 'Reason: %(err)s.') + % {'name': share_name, + 'err': err})) + + location = self._get_location_path(share_name, share_proto) + return location + + def check_fs_status(self, health_status, running_status): + if (health_status == constants.STATUS_FS_HEALTH + and running_status == constants.STATUS_FS_RUNNING): + return True + else: + return False + + def create_snapshot(self, snapshot, share_server=None): + """Create a snapshot.""" + snap_name = snapshot['id'] + share_proto = snapshot['share_proto'] + + share_name = self.helper._get_share_name_by_id(snapshot['share_id']) + share_type = self.helper._get_share_type(share_proto) + share = self.helper._get_share_by_name(share_name, share_type) + + if not share: + err_msg = _('Can not create snapshot,' + ' because share_id is not provided.') + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) + + sharefsid = share['FSID'] + snapshot_name = "share_snapshot_" + snap_name + snap_id = self.helper._create_snapshot(sharefsid, + snapshot_name) + LOG.info(_LI('Creating snapshot id %s.'), snap_id) + + def delete_snapshot(self, snapshot, share_server=None): + """Delete a snapshot.""" + LOG.debug("Delete a snapshot.") + snap_name = snapshot['id'] + + share_name = self.helper._get_share_name_by_id(snapshot['share_id']) + sharefsid = self.helper._get_fsid_by_name(share_name) + + if sharefsid is None: + LOG.warning(_LW('Delete snapshot share id %s fs has been ' + 'deleted.'), snap_name) + return + + snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name) + snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id) + + if snapshot_flag: + self.helper._delete_snapshot(snapshot_id) + else: + LOG.warning(_LW("Can not find snapshot %s in array."), snap_name) + + def update_share_stats(self, stats_dict): + """Retrieve status info from share group.""" + capacity = self._get_capacity() + + stats_dict["pools"] = [] + pool = {} + pool.update(dict( + pool_name=capacity['name'], + total_capacity_gb=capacity['TOTALCAPACITY'], + free_capacity_gb=capacity['CAPACITY'], + QoS_support=False, + reserved_percentage=0, + )) + stats_dict["pools"].append(pool) + + def delete_share(self, share, share_server=None): + """Delete share.""" + share_name = share['name'] + share_proto = self.helper._get_share_type(share['share_proto']) + share = self.helper._get_share_by_name(share_name, share_proto) + + if not share: + LOG.warning(_LW('The share was not found. Share name:%s'), + share_name) + fsid = self.helper._get_fsid_by_name(share_name) + if fsid: + self.helper._delete_fs(fsid) + return + LOG.warning(_LW('The filesystem was not found.')) + return + + share_id = share['ID'] + share_fs_id = share['FSID'] + + if share_id: + self.helper._delete_share_by_id(share_id, share_proto) + + if share_fs_id: + self.helper._delete_fs(share_fs_id) + + return share + + def get_network_allocations_number(self): + """Get number of network interfaces to be created.""" + return constants.IP_ALLOCATIONS + + def _get_capacity(self): + """Get free capacity and total capacity of the pools.""" + poolinfo = self.helper._find_pool_info() + + if poolinfo: + total = int(poolinfo['TOTALCAPACITY']) / units.Mi / 2 + free = int(poolinfo['CAPACITY']) / units.Mi / 2 + poolinfo['TOTALCAPACITY'] = total + poolinfo['CAPACITY'] = free + + return poolinfo + + def _init_filesys_para(self, share): + """Init basic filesystem parameters.""" + name = share['name'] + size = share['size'] * units.Mi * 2 + poolinfo = self.helper._find_pool_info() + fileparam = { + "NAME": name.replace("-", "_"), + "DESCRIPTION": "", + "ALLOCTYPE": 1, + "CAPACITY": size, + "PARENTID": poolinfo['ID'], + "INITIALALLOCCAPACITY": units.Ki * 20, + "PARENTTYPE": 216, + "SNAPSHOTRESERVEPER": 20, + "INITIALDISTRIBUTEPOLICY": 0, + "ISSHOWSNAPDIR": True, + "RECYCLESWITCH": 0, + "RECYCLEHOLDTIME": 15, + "RECYCLETHRESHOLD": 0, + "RECYCLEAUTOCLEANSWITCH": 0, + "ENABLEDEDUP": False, + "ENABLECOMPRESSION": False, + } + + root = self.helper._read_xml() + fstype = root.findtext('Filesystem/AllocType') + if fstype: + fstype = fstype.strip() + if fstype == 'Thin': + fileparam['ALLOCTYPE'] = 1 + elif fstype == 'Thick': + fileparam['ALLOCTYPE'] = 0 + else: + err_msg = (_( + 'Config file is wrong. Filesystem type must be "Thin"' + ' or "Thick". AllocType:%(fetchtype)s') % + {'fetchtype': fstype}) + LOG.error(err_msg) + raise exception.InvalidShare(reason=err_msg) + + return fileparam + + def deny_access(self, share, access, share_server=None): + """Deny access to share.""" + share_proto = share['share_proto'] + share_name = share['name'] + share_type = self.helper._get_share_type(share_proto) + share_client_type = self.helper._get_share_client_type(share_proto) + access_type = access['access_type'] + if share_proto == 'NFS' and access_type != 'ip': + LOG.warning(_LW('Only IP access type is allowed for NFS shares.')) + return + elif share_proto == 'CIFS' and access_type != 'user': + LOG.warning(_LW('Only USER access type is allowed for' + ' CIFS shares.')) + return + + access_to = access['access_to'] + share = self.helper._get_share_by_name(share_name, share_type) + if not share: + LOG.warning(_LW('Can not get share. share_name: %s'), share_name) + return + + access_id = self.helper._get_access_from_share(share['ID'], access_to, + share_client_type) + if not access_id: + LOG.warning(_LW('Can not get access id from share. ' + 'share_name: %s'), share_name) + return + + self.helper._remove_access_from_share(access_id, share_client_type) + + def allow_access(self, share, access, share_server=None): + """Allow access to the share.""" + share_proto = share['share_proto'] + share_name = share['name'] + share_type = self.helper._get_share_type(share_proto) + access_type = access['access_type'] + if share_proto == 'NFS' and access_type != 'ip': + message = _('Only IP access type is allowed for NFS shares.') + raise exception.InvalidShareAccess(reason=message) + elif share_proto == 'CIFS' and access_type != 'user': + message = _('Only USER access type is allowed for CIFS shares.') + raise exception.InvalidShareAccess(reason=message) + + access_to = access['access_to'] + + share = self.helper._get_share_by_name(share_name, share_type) + if not share: + err_msg = (_("Can not get share ID by share %s.") + % share_name) + LOG.error(err_msg) + raise exception.InvalidShareAccess(reason=err_msg) + + share_id = share['ID'] + self.helper._allow_access_rest(share_id, access_to, share_proto) + + def allocate_container(self, share): + """Creates filesystem associated to share by name.""" + fileParam = self._init_filesys_para(share) + fsid = self.helper._create_filesystem(fileParam) + return fsid + + def _get_location_path(self, share_name, share_proto): + root = self.helper._read_xml() + target_ip = root.findtext('Storage/LogicalPortIP').strip() + + location = None + if share_proto == 'NFS': + location = '%s:/%s' % (target_ip, + share_name.replace("-", "_")) + elif share_proto == 'CIFS': + location = '\\\\%s\\%s' % (target_ip, + share_name.replace("-", "_")) + else: + raise exception.InvalidShareAccess( + reason=(_('Invalid NAS protocol supplied: %s.') + % share_proto)) + + return location + + def _get_wait_interval(self): + """Get wait interval from huawei conf file.""" + root = self.helper._read_xml() + wait_interval = root.findtext('Filesystem/WaitInterval') + if wait_interval: + return int(wait_interval) + else: + LOG.info(_LI( + "Wait interval is not configured in huawei " + "conf file. Use default: %(default_wait_interval)d."), + {"default_wait_interval": constants.DEFAULT_WAIT_INTERVAL}) + return constants.DEFAULT_WAIT_INTERVAL + + def _get_timeout(self): + """Get timeout from huawei conf file.""" + root = self.helper._read_xml() + timeout = root.findtext('Filesystem/Timeout') + if timeout: + return int(timeout) + else: + LOG.info(_LI( + "Timeout is not configured in huawei conf file. " + "Use default: %(default_timeout)d."), + {"default_timeout": constants.DEFAULT_TIMEOUT}) + return constants.DEFAULT_TIMEOUT + + def check_conf_file(self): + """Check the config file, make sure the essential items are set.""" + root = self.helper._read_xml() + resturl = root.findtext('Storage/RestURL') + username = root.findtext('Storage/UserName') + pwd = root.findtext('Storage/UserPassword') + product = root.findtext('Storage/Product') + pool_node = root.findtext('Filesystem/StoragePool') + + if product != "V3": + err_msg = (_( + '_check_conf_file: Config file invalid. ' + 'Product must be set to V3.')) + LOG.error(err_msg) + raise exception.InvalidInput(err_msg) + + if not (resturl and username and pwd): + err_msg = (_( + '_check_conf_file: Config file invalid. RestURL,' + ' UserName and UserPassword must be set.')) + LOG.error(err_msg) + raise exception.InvalidInput(err_msg) + + if not pool_node: + err_msg = (_( + '_check_conf_file: Config file invalid. ' + 'StoragePool must be set.')) + LOG.error(err_msg) + raise exception.InvalidInput(err_msg) + + def check_service(self): + running_status = self.helper._get_cifs_service_status() + if running_status != constants.STATUS_SERVICE_RUNNING: + self.helper._start_cifs_service_status() + + service = self.helper._get_nfs_service_status() + if ((service['RUNNINGSTATUS'] != constants.STATUS_SERVICE_RUNNING) or + (service['SUPPORTV3'] == 'false') or + (service['SUPPORTV4'] == 'false')): + self.helper._start_nfs_service_status() diff --git a/manila/share/drivers/huawei/huawei_helper.py b/manila/share/drivers/huawei/v3/helper.py similarity index 68% rename from manila/share/drivers/huawei/huawei_helper.py rename to manila/share/drivers/huawei/v3/helper.py index 48a95973..10805a94 100644 --- a/manila/share/drivers/huawei/huawei_helper.py +++ b/manila/share/drivers/huawei/v3/helper.py @@ -18,20 +18,20 @@ from xml.etree import ElementTree as ET from oslo_log import log from oslo_serialization import jsonutils -from oslo_utils import units import six from six.moves import http_cookiejar from six.moves.urllib import request as urlreq # pylint: disable=E0611 from manila import exception -from manila.i18n import _, _LE, _LW +from manila.i18n import _ +from manila.i18n import _LE from manila.share.drivers.huawei import constants from manila import utils LOG = log.getLogger(__name__) -class RestHelper(): +class RestHelper(object): """Helper class for Huawei OceanStor V3 storage system.""" def __init__(self, configuration): @@ -203,31 +203,6 @@ class RestHelper(): return result['data']['ID'] - def _delete_share(self, share_name, share_proto): - """Delete share.""" - share_type = self._get_share_type(share_proto) - share = self._get_share_by_name(share_name, share_type) - - if not share: - LOG.warn(_LW('The share was not found. share_name:%s'), share_name) - fsid = self._get_fsid_by_name(share_name) - if fsid: - self._delete_fs(fsid) - return - LOG.warn(_LW('The filesystem was not found.')) - return - - share_id = share['ID'] - share_fs_id = share['FSID'] - - if share_id: - self._delete_share_by_id(share_id, share_type) - - if share_fs_id: - self._delete_fs(share_fs_id) - - return share - def _delete_share_by_id(self, share_id, share_type): """Delete share by share id.""" url = self.url + "/" + share_type + "/" + share_id @@ -317,32 +292,16 @@ class RestHelper(): poolinfo = {} pool_name = pool_name.strip() - if "data" in result: - for item in result['data']: - if pool_name == item['NAME']: - poolinfo['ID'] = item['ID'] - poolinfo['CAPACITY'] = item['USERFREECAPACITY'] - poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] - break + for item in result.get('data', []): + if pool_name == item['NAME']: + poolinfo['name'] = pool_name + poolinfo['ID'] = item['ID'] + poolinfo['CAPACITY'] = item['USERFREECAPACITY'] + poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] + break return poolinfo - def _get_capacity(self): - """Get free capacity and total capacity of the pools.""" - poolinfo = self._find_pool_info() - pool_capacity = { - 'total_capacity': 0.0, - 'free_capacity': 0.0 - } - - if poolinfo: - total = int(poolinfo['TOTALCAPACITY']) / units.Mi / 2 - free = int(poolinfo['CAPACITY']) / units.Mi / 2 - pool_capacity['total_capacity'] = total - pool_capacity['free_capacity'] = free - - return pool_capacity - def _read_xml(self): """Open xml file and parse the content.""" filename = self.configuration.manila_huawei_conf_file @@ -350,78 +309,14 @@ class RestHelper(): tree = ET.parse(filename) root = tree.getroot() except Exception as err: - LOG.error(_LE('Read Huawei config file(%(filename)s)' - ' for Manila error: %(err)s') % - {'filename': filename, - 'err': err}) - raise err + message = (_('Read Huawei config file(%(filename)s)' + ' for Manila error: %(err)s') + % {'filename': filename, + 'err': err}) + LOG.error(message) + raise exception.InvalidInput(reason=message) return root - def _init_filesys_para(self, name, size): - """Init basic filesystem parameters.""" - poolinfo = self._find_pool_info() - fileparam = { - "NAME": name.replace("-", "_"), - "DESCRIPTION": "", - "ALLOCTYPE": 1, - "CAPACITY": size, - "PARENTID": poolinfo['ID'], - "INITIALALLOCCAPACITY": units.Ki * 20, - "PARENTTYPE": 216, - "SNAPSHOTRESERVEPER": 20, - "INITIALDISTRIBUTEPOLICY": 0, - "ISSHOWSNAPDIR": 'true', - "RECYCLESWITCH": 0, - "RECYCLEHOLDTIME": 15, - "RECYCLETHRESHOLD": 0, - "RECYCLEAUTOCLEANSWITCH": 0, - } - - root = self._read_xml() - fstype = root.findtext('Filesystem/AllocType') - if fstype: - fstype = fstype.strip() - if fstype == 'Thin': - fileparam['ALLOCTYPE'] = 1 - elif fstype == 'Thick': - fileparam['ALLOCTYPE'] = 0 - else: - err_msg = (_( - 'Config file is wrong. Filesystem type must be "Thin"' - ' or "Thick". AllocType:%(fetchtype)s') % - {'fetchtype': fstype}) - LOG.error(err_msg) - raise exception.InvalidShare(reason=err_msg) - return fileparam - - def _deny_access(self, share_name, access, share_proto): - """Deny access to share.""" - share_type = self._get_share_type(share_proto) - share_client_type = self._get_share_client_type(share_proto) - access_type = access['access_type'] - if share_proto == 'NFS' and access_type != 'ip': - LOG.warn(_LW('Only ip access type allowed.')) - return - - if share_proto == 'CIFS' and access_type != 'user': - LOG.warn(_LW('Only user access type allowed.')) - return - - access_to = access['access_to'] - share = self._get_share_by_name(share_name, share_type) - if not share: - LOG.warn(_LW('Can not get share. share_name: %s'), share_name) - return - - access_id = self._get_access_from_share(share['ID'], access_to, - share_client_type) - if not access_id: - LOG.warn(_LW('Can not get access id from share. share_name: %s'), - share_name) - return - - self._remove_access_from_share(access_id, share_client_type) - def _remove_access_from_share(self, access_id, access_type): url = self.url + "/" + access_type + "/" + access_id result = self.call(url, None, "DELETE") @@ -467,34 +362,9 @@ class RestHelper(): result = self.call(url, None, "GET") self._assert_rest_result(result, 'Get access id by share error!') - if "data" in result: - for item in result['data']: - if access_to == item['NAME']: - return item['ID'] - - def _allow_access(self, share_name, access, share_proto): - """Allow access to the share.""" - - share_type = self._get_share_type(share_proto) - access_type = access['access_type'] - if share_proto == 'NFS' and access_type != 'ip': - message = _('Only IP access type is allowed for NFS shares.') - raise exception.InvalidShareAccess(reason=message) - - if share_proto == 'CIFS' and access_type != 'user': - message = _('Only USER access type is allowed for CIFS shares.') - raise exception.InvalidShareAccess(reason=message) - - access_to = access['access_to'] - - share = self._get_share_by_name(share_name, share_type) - if not share: - err_msg = (_('Can not get share.')) - LOG.error(err_msg) - raise exception.InvalidShareAccess(reason=err_msg) - - share_id = share['ID'] - self._allow_access_rest(share_id, access_to, share_proto) + for item in result.get('data', []): + if access_to == item['NAME']: + return item['ID'] def _allow_access_rest(self, share_id, access_to, share_proto): """Allow access to the share.""" @@ -532,7 +402,7 @@ class RestHelper(): elif share_proto == 'CIFS': share_client_type = "CIFS_SHARE_AUTH_CLIENT" else: - raise exception.InvalidShare( + raise exception.InvalidInput( reason=(_('Invalid NAS protocol supplied: %s.') % share_proto)) @@ -622,12 +492,11 @@ class RestHelper(): share_path = self._get_share_path(share_name) share = {} - if "data" in result: - for item in result['data']: - if share_path == item['SHAREPATH']: - share['ID'] = item['ID'] - share['FSID'] = item['FSID'] - break + for item in result.get('data', []): + if share_path == item['SHAREPATH']: + share['ID'] = item['ID'] + share['FSID'] = item['FSID'] + break return share @@ -638,7 +507,7 @@ class RestHelper(): elif share_proto == 'CIFS': share_type = "CIFSHARE" else: - raise exception.InvalidShare( + raise exception.InvalidInput( reason=(_('Invalid NAS protocol supplied: %s.') % share_proto)) @@ -650,10 +519,9 @@ class RestHelper(): self._assert_rest_result(result, 'Get filesystem by name error!') sharename = share_name.replace("-", "_") - if "data" in result: - for item in result['data']: - if sharename == item['NAME']: - return item['ID'] + for item in result.get('data', []): + if sharename == item['NAME']: + return item['ID'] def _get_fs_info_by_id(self, fsid): url = self.url + "/filesystem/%s" % fsid @@ -668,64 +536,10 @@ class RestHelper(): fs['RUNNINGSTATUS'] = result['data']['RUNNINGSTATUS'] return fs - def allocate_container(self, share_name, size): - """Creates filesystem associated to share by name.""" - fileParam = self._init_filesys_para(share_name, size) - fsid = self._create_filesystem(fileParam) - return fsid - - def _check_conf_file(self): - """Check the config file, make sure the essential items are set.""" - root = self._read_xml() - resturl = root.findtext('Storage/RestURL') - username = root.findtext('Storage/UserName') - pwd = root.findtext('Storage/UserPassword') - product = root.findtext('Storage/Product') - pool_node = root.findall('Filesystem/StoragePool') - - if product != "V3": - err_msg = (_( - '_check_conf_file: Config file invalid. ' - 'Product must be set to V3.')) - LOG.error(err_msg) - raise exception.InvalidInput(err_msg) - - if (not resturl) or (not username) or (not pwd): - err_msg = (_( - '_check_conf_file: Config file invalid. RestURL,' - ' UserName and UserPassword must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(err_msg) - - if not pool_node: - err_msg = (_( - '_check_conf_file: Config file invalid. ' - 'StoragePool must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(err_msg) - def _get_share_path(self, share_name): share_path = "/" + share_name.replace("-", "_") + "/" return share_path - def _get_location_path(self, share_name, share_proto): - root = self._read_xml() - target_ip = root.findtext('Storage/LogicalPortIP').strip() - - location = None - if share_proto == 'NFS': - location = '%s:/%s' % (target_ip, - share_name.replace("-", "_")) - elif share_proto == 'CIFS': - location = '\\\\%s\\%s' % (target_ip, - share_name.replace("-", "_")) - else: - raise exception.InvalidShare( - reason=(_('Invalid NAS protocol supplied: %s.') - % share_proto)) - - return location - def _get_share_name_by_id(self, share_id): share_name = "share_" + share_id return share_name @@ -734,14 +548,3 @@ class RestHelper(): snapshot_id = (fs_id + "@" + "share_snapshot_" + snap_name.replace("-", "_")) return snapshot_id - - def _check_service(self): - running_status = self._get_cifs_service_status() - if running_status != constants.STATUS_SERVICE_RUNNING: - self._start_cifs_service_status() - - service = self._get_nfs_service_status() - if ((service['RUNNINGSTATUS'] != constants.STATUS_SERVICE_RUNNING) or - (service['SUPPORTV3'] == 'false') or - (service['SUPPORTV4'] == 'false')): - self._start_nfs_service_status() diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index cf59b3cb..aeb68069 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -28,8 +28,9 @@ from oslo_serialization import jsonutils from manila import context from manila import exception from manila.share import configuration as conf -from manila.share.drivers.huawei import huawei_helper from manila.share.drivers.huawei import huawei_nas +from manila.share.drivers.huawei.v3 import connection +from manila.share.drivers.huawei.v3 import helper from manila import test @@ -64,18 +65,10 @@ def filesystem(method, fs_status_flag): return data -class FakeHuaweiNasDriver(huawei_nas.HuaweiNasDriver): - """Fake Huawei Storage, Rewrite some methods of HuaweiNasDriver.""" +class FakeHuaweiNasHelper(helper.RestHelper): def __init__(self, *args, **kwargs): - huawei_nas.HuaweiNasDriver.__init__(self, *args, **kwargs) - self.helper = FakeHuaweiNasHelper(self.configuration) - - -class FakeHuaweiNasHelper(huawei_helper.RestHelper): - - def __init__(self, *args, **kwargs): - huawei_helper.RestHelper.__init__(self, *args, **kwargs) + helper.RestHelper.__init__(self, *args, **kwargs) self.test_normal = True self.deviceid = None self.delete_flag = False @@ -86,12 +79,15 @@ class FakeHuaweiNasHelper(huawei_helper.RestHelper): self.fs_status_flag = True self.create_share_flag = False self.snapshot_flag = True + self.service_status_flag = True + self.share_exist = True + self.service_nfs_status_flag = True + self.create_share_data_flag = False def _change_file_mode(self, filepath): pass def call(self, url, data=None, method=None): - url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '') url = url.replace('/210235G7J20000000000/', '') data = None @@ -114,16 +110,25 @@ class FakeHuaweiNasHelper(huawei_helper.RestHelper): if url == "NFSHARE" or url == "CIFSHARE": if self.create_share_flag: data = '{"error":{"code":31755596}}' + elif self.create_share_data_flag: + data = '{"error":{"code":0}}' else: data = """{"error":{"code":0},"data":{ "ID":"10"}}""" if url == "NFSHARE?range=[100-200]": - data = """{"error":{"code":0}, - "data":[{"ID":"1", - "FSID":"4", - "NAME":"test", - "SHAREPATH":"/share_fake_uuid/"}]}""" + if self.share_exist: + data = """{"error":{"code":0}, + "data":[{"ID":"1", + "FSID":"4", + "NAME":"test", + "SHAREPATH":"/share_fake_uuid/"}]}""" + else: + data = """{"error":{"code":0}, + "data":[{"ID":"1", + "FSID":"4", + "NAME":"test", + "SHAREPATH":"/share_fake_uuid_fail/"}]}""" if url == "CIFSHARE?range=[100-200]": data = """{"error":{"code":0}, @@ -218,14 +223,24 @@ class FakeHuaweiNasHelper(huawei_helper.RestHelper): "COUNT":"196"}}""" if url == "CIFSSERVICE": - data = """{"error":{"code":0},"data":{ - "RUNNINGSTATUS":"2"}}""" + if self.service_status_flag: + data = """{"error":{"code":0},"data":{ + "RUNNINGSTATUS":"2"}}""" + else: + data = """{"error":{"code":0},"data":{ + "RUNNINGSTATUS":"1"}}""" if url == "NFSSERVICE": - data = """{"error":{"code":0}, - "data":{"RUNNINGSTATUS":"2", - "SUPPORTV3":"true", - "SUPPORTV4":"true"}}""" + if self.service_nfs_status_flag: + data = """{"error":{"code":0}, + "data":{"RUNNINGSTATUS":"2", + "SUPPORTV3":"true", + "SUPPORTV4":"true"}}""" + else: + data = """{"error":{"code":0}, + "data":{"RUNNINGSTATUS":"1", + "SUPPORTV3":"true", + "SUPPORTV4":"true"}}""" self.setupserver_flag = True if url == "FILESYSTEM?range=[0-8191]": @@ -244,12 +259,28 @@ class FakeHuaweiNasHelper(huawei_helper.RestHelper): return res_json +class FakeHuaweiNasDriver(huawei_nas.HuaweiNasDriver): + """Fake HuaweiNasDriver.""" + + def __init__(self, *args, **kwargs): + huawei_nas.HuaweiNasDriver.__init__(self, *args, **kwargs) + self.plugin = FakeV3StorageConnection(self.configuration) + + +class FakeV3StorageConnection(connection.V3StorageConnection): + """Fake V3StorageConnection.""" + + def __init__(self, configuration): + connection.V3StorageConnection.__init__(self, configuration) + self.configuration = configuration + self.helper = FakeHuaweiNasHelper(self.configuration) + + class HuaweiShareDriverTestCase(test.TestCase): """Tests GenericShareDriver.""" def setUp(self): super(HuaweiShareDriverTestCase, self).setUp() - self._context = context.get_admin_context() self.tmp_dir = tempfile.mkdtemp() self.fake_conf_file = self.tmp_dir + '/manila_huawei_conf.xml' @@ -264,12 +295,17 @@ class HuaweiShareDriverTestCase(test.TestCase): self.configuration.safe_get = mock.Mock(side_effect=_safe_get) self.configuration.network_config_group = 'fake_network_config_group' self.configuration.share_backend_name = 'fake_share_backend_name' - self.configuration.driver_handles_share_servers = False + self.configuration.huawei_share_backend = 'V3' + self.configuration.manila_huawei_conf_file = self.fake_conf_file + self.configuration.driver_handles_share_servers = False + self._helper_fake = mock.Mock() + self.mock_object(huawei_nas.importutils, 'import_object', + mock.Mock(return_value=self._helper_fake)) + self.mock_object(time, 'sleep', fake_sleep) - driver = FakeHuaweiNasDriver(configuration=self.configuration) - self.driver = driver - self.driver.helper.test_normal = True + self.driver = FakeHuaweiNasDriver(configuration=self.configuration) + self.driver.plugin.helper.test_normal = True self.share_nfs = { 'id': 'fake_uuid', @@ -334,8 +370,6 @@ class HuaweiShareDriverTestCase(test.TestCase): } self.share_server = None - self.helper = mock.Mock() - self.driver._helpers = {'FAKE': self.helper} self.driver._licenses = ['fake'] self.network_info = { @@ -349,38 +383,151 @@ class HuaweiShareDriverTestCase(test.TestCase): ], } + def test_conf_product_fail(self): + self.recreate_fake_conf_file(product_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_conf_file) + + def test_conf_pool_node_fail(self): + self.recreate_fake_conf_file(pool_node_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_conf_file) + + def test_conf_username_fail(self): + self.recreate_fake_conf_file(username_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_conf_file) + + def test_conf_timeout_fail(self): + self.recreate_fake_conf_file(timeout_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + timeout = self.driver.plugin._get_timeout() + self.assertEqual(60, timeout) + + def test_conf_wait_interval_fail(self): + self.recreate_fake_conf_file(wait_interval_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + wait_interval = self.driver.plugin._get_wait_interval() + self.assertEqual(3, wait_interval) + + def test_get_backend_driver_fail(self): + test_fake_conf_file = None + self.driver.plugin.configuration.manila_huawei_conf_file = ( + test_fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.get_backend_driver) + + def test_get_backend_driver_fail_driver_none(self): + self.recreate_fake_conf_file(product_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.get_backend_driver) + + def test_create_share_nfs_alloctype_fail(self): + self.recreate_fake_conf_file(alloctype_value='alloctype_fail') + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidShare, + self.driver.create_share, + self._context, + self.share_nfs, + self.share_server) + + def test_create_share_nfs_storagepool_fail(self): + self.recreate_fake_conf_file(pool_node_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidShare, + self.driver.create_share, + self._context, + self.share_nfs, + self.share_server) + + def test_create_share_nfs_no_data_fail(self): + self.driver.plugin.helper.create_share_data_flag = True + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidShare, + self.driver.create_share, + self._context, + self.share_nfs, + self.share_server) + + def test_read_xml_fail(self): + test_fake_conf_file = None + self.driver.plugin.configuration.manila_huawei_conf_file = ( + test_fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.helper._read_xml) + + def test_connect_fail(self): + self.driver.plugin.configuration = None + self.assertRaises(exception.InvalidInput, + self.driver.plugin.connect) + def test_login_success(self): - deviceid = self.driver.helper.login() + deviceid = self.driver.plugin.helper.login() self.assertEqual("210235G7J20000000000", deviceid) - def test_create_share_nfs_alloctype_thick_success(self): - self.recreate_fake_conf_file(alloctype_value='Thick') - self.driver.helper.configuration.manila_huawei_conf_file = ( + def test_check_for_setup_success(self): + self.driver.plugin.helper.login() + self.driver.check_for_setup_error() + + def test_check_for_setup_service_down(self): + self.driver.plugin.helper.service_status_flag = False + self.driver.plugin.helper.login() + self.driver.check_for_setup_error() + + def test_check_for_setup_nfs_down(self): + self.driver.plugin.helper.service_nfs_status_flag = False + self.driver.plugin.helper.login() + self.driver.check_for_setup_error() + + def test_check_for_setup_service_false(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False + self.assertRaises(exception.InvalidShare, + self.driver.check_for_setup_error) + + def test_create_share_nfs_alloctype_thin_success(self): + self.recreate_fake_conf_file(alloctype_value='Thin') + self.driver.plugin.configuration.manila_huawei_conf_file = ( self.fake_conf_file) - self.driver.helper.login() + self.driver.plugin.helper.login() location = self.driver.create_share(self._context, self.share_nfs, self.share_server) self.assertEqual("100.115.10.68:/share_fake_uuid", location) def test_create_share_nfs_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() location = self.driver.create_share(self._context, self.share_nfs, self.share_server) self.assertEqual("100.115.10.68:/share_fake_uuid", location) def test_create_share_cifs_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() location = self.driver.create_share(self._context, self.share_cifs, self.share_server) self.assertEqual("\\\\100.115.10.68\\share_fake_uuid", location) def test_login_fail(self): - self.driver.helper.test_normal = False - self.assertRaises(exception.InvalidShare, self.driver.helper.login) + self.driver.plugin.helper.test_normal = False + self.assertRaises(exception.InvalidShare, + self.driver.plugin.helper.login) def test_create_share_nfs_fs_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.create_share, self._context, @@ -388,8 +535,8 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_server) def test_create_share_nfs_status_fail(self): - self.driver.helper.login() - self.driver.helper.fs_status_flag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.fs_status_flag = False self.assertRaises(exception.InvalidShare, self.driver.create_share, self._context, @@ -397,8 +544,8 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_server) def test_create_share_cifs_fs_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.create_share, self._context, @@ -406,8 +553,8 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_server) def test_create_share_cifs_fail(self): - self.driver.helper.login() - self.driver.helper.create_share_flag = True + self.driver.plugin.helper.login() + self.driver.plugin.helper.create_share_flag = True self.assertRaises(exception.InvalidShare, self.driver.create_share, self._context, @@ -415,8 +562,8 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_server) def test_create_share_nfs_fail(self): - self.driver.helper.login() - self.driver.helper.create_share_flag = True + self.driver.plugin.helper.login() + self.driver.plugin.helper.create_share_flag = True self.assertRaises(exception.InvalidShare, self.driver.create_share, self._context, @@ -424,29 +571,48 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_server) def test_delete_share_nfs_success(self): - self.driver.helper.login() - self.driver.helper.delete_flag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.delete_flag = False self.driver.delete_share(self._context, self.share_nfs, self.share_server) - self.assertTrue(self.driver.helper.delete_flag) + self.assertTrue(self.driver.plugin.helper.delete_flag) + + def test_check_snapshot_id_exist_fail(self): + snapshot_id = "4" + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False + self.assertRaises(exception.InvalidShare, + self.driver.plugin.helper._check_snapshot_id_exist, + snapshot_id) + + def test_delete_share_nfs_fail_not_exist(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.delete_flag = False + self.driver.plugin.helper.share_exist = False + self.driver.delete_share(self._context, + self.share_nfs, self.share_server) + self.assertTrue(self.driver.plugin.helper.delete_flag) def test_delete_share_cifs_success(self): - self.driver.helper.login() - self.driver.helper.delete_flag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.delete_flag = False self.driver.delete_share(self._context, self.share_cifs, self.share_server) - self.assertTrue(self.driver.helper.delete_flag) + self.assertTrue(self.driver.plugin.helper.delete_flag) - def test_get_share_stats_refresh_false(self): - self.driver._stats = {'fake_key': 'fake_value'} + def test_get_network_allocations_number(self): + number = self.driver.get_network_allocations_number() + self.assertEqual(0, number) - result = self.driver.get_share_stats(False) + def test_create_share_from_snapshot(self): + self.assertRaises(NotImplementedError, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, self.nfs_snapshot, + self.share_server) - self.assertEqual(self.driver._stats, result) - - def test_get_share_stats_refresh_true(self): - self.driver.helper.login() - data = self.driver.get_share_stats(True) + def test_get_share_stats_refresh(self): + self.driver.plugin.helper.login() + self.driver._update_share_stats() expected = {} expected["share_backend_name"] = "fake_share_backend_name" @@ -454,131 +620,204 @@ class HuaweiShareDriverTestCase(test.TestCase): expected["vendor_name"] = 'Huawei' expected["driver_version"] = '1.0' expected["storage_protocol"] = 'NFS_CIFS' - expected['total_capacity_gb'] = 2 - expected['free_capacity_gb'] = 1 expected['reserved_percentage'] = 0 + expected['total_capacity_gb'] = 'infinite' + expected['free_capacity_gb'] = 'infinite' expected['QoS_support'] = False - self.assertDictMatch(expected, data) + expected["pools"] = [] + pool = {} + pool.update(dict( + pool_name='OpenStack_Pool', + total_capacity_gb=2, + free_capacity_gb=1, + QoS_support=False, + reserved_percentage=0, + )) + expected["pools"].append(pool) + self.assertEqual(expected, self.driver._stats) def test_get_capacity_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() capacity = {} - capacity = self.driver.helper._get_capacity() - self.assertEqual(2, capacity['total_capacity']) - self.assertEqual(1, capacity['free_capacity']) + capacity = self.driver.plugin._get_capacity() + self.assertEqual(2, capacity['TOTALCAPACITY']) + self.assertEqual(1, capacity['CAPACITY']) def test_allow_access_ip_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() self.allow_flag = False self.driver.allow_access(self._context, self.share_nfs, self.access_ip, self.share_server) - self.assertTrue(self.driver.helper.allow_flag) + self.assertTrue(self.driver.plugin.helper.allow_flag) def test_allow_access_user_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() self.allow_flag = False self.driver.allow_access(self._context, self.share_cifs, self.access_user, self.share_server) - self.assertTrue(self.driver.helper.allow_flag) + self.assertTrue(self.driver.plugin.helper.allow_flag) + + def test_get_share_client_type_fail(self): + share_proto = 'fake_proto' + self.assertRaises(exception.InvalidInput, + self.driver.plugin.helper._get_share_client_type, + share_proto) + + def test_get_share_type_fail(self): + share_proto = 'fake_proto' + self.assertRaises(exception.InvalidInput, + self.driver.plugin.helper._get_share_type, + share_proto) + + def test_get_location_path_fail(self): + share_name = 'share-fake-uuid' + share_proto = 'fake_proto' + self.assertRaises(exception.InvalidShareAccess, + self.driver.plugin._get_location_path, share_name, + share_proto) + + def test_allow_access_ip_proto_fail(self): + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidShareAccess, + self.driver.allow_access, self._context, + self.share_nfs, self.access_user, self.share_server) + + def test_allow_access_user_proto_fail(self): + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidShareAccess, + self.driver.allow_access, self._context, + self.share_cifs, self.access_ip, self.share_server) + + def test_deny_access_ip_proto_fail(self): + self.driver.plugin.helper.login() + result = self.driver.deny_access(self._context, self.share_nfs, + self.access_user, self.share_server) + self.assertEqual(None, result) + + def test_deny_access_user_proto_fail(self): + self.driver.plugin.helper.login() + result = self.driver.deny_access(self._context, self.share_cifs, + self.access_ip, self.share_server) + self.assertEqual(None, result) + + def test_allow_access_ip_share_not_exist(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.share_exist = False + self.assertRaises(exception.InvalidShareAccess, + self.driver.allow_access, self._context, + self.share_nfs, self.access_ip, self.share_server) + + def test_deny_access_ip_share_not_exist(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.share_exist = False + self.driver.deny_access(self._context, self.share_nfs, + self.access_ip, self.share_server) def test_allow_access_ip_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.allow_access, self._context, self.share_nfs, self.access_ip, self.share_server) def test_allow_access_user_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.allow_access, self._context, self.share_cifs, self.access_user, self.share_server) def test_deny_access_ip_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() self.deny_flag = False self.driver.deny_access(self._context, self.share_nfs, self.access_ip, self.share_server) - self.assertTrue(self.driver.helper.deny_flag) + self.assertTrue(self.driver.plugin.helper.deny_flag) def test_deny_access_user_success(self): - self.driver.helper.login() + self.driver.plugin.helper.login() self.deny_flag = False self.driver.deny_access(self._context, self.share_cifs, self.access_user, self.share_server) - self.assertTrue(self.driver.helper.deny_flag) + self.assertTrue(self.driver.plugin.helper.deny_flag) def test_deny_access_ip_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.deny_access, self._context, self.share_nfs, self.access_ip, self.share_server) def test_deny_access_user_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.deny_access, self._context, self.share_cifs, self.access_user, self.share_server) def test_create_nfs_snapshot_success(self): - self.driver.helper.login() - self.driver.helper.create_snapflag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.create_snapflag = False self.driver.create_snapshot(self._context, self.nfs_snapshot, self.share_server) - self.assertTrue(self.driver.helper.create_snapflag) + self.assertTrue(self.driver.plugin.helper.create_snapflag) + + def test_create_nfs_snapshot_share_not_exist(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.share_exist = False + self.assertRaises(exception.InvalidInput, + self.driver.create_snapshot, self._context, + self.nfs_snapshot, self.share_server) def test_create_cifs_snapshot_success(self): - self.driver.helper.login() - self.driver.helper.create_snapflag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.create_snapflag = False self.driver.create_snapshot(self._context, self.cifs_snapshot, self.share_server) - self.assertTrue(self.driver.helper.create_snapflag) + self.assertTrue(self.driver.plugin.helper.create_snapflag) def test_delete_snapshot_success(self): - self.driver.helper.login() - self.driver.helper.delete_flag = False - self.driver.helper.snapshot_flag = True + self.driver.plugin.helper.login() + self.driver.plugin.helper.delete_flag = False + self.driver.plugin.helper.snapshot_flag = True self.driver.delete_snapshot(self._context, self.nfs_snapshot, self.share_server) - self.assertTrue(self.driver.helper.delete_flag) + self.assertTrue(self.driver.plugin.helper.delete_flag) def test_delete_snapshot_not_exist_success(self): - self.driver.helper.login() - self.driver.helper.delete_flag = False - self.driver.helper.snapshot_flag = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.delete_flag = False + self.driver.plugin.helper.snapshot_flag = False self.driver.delete_snapshot(self._context, self.nfs_snapshot, self.share_server) - self.assertTrue(self.driver.helper.delete_flag) + self.assertTrue(self.driver.plugin.helper.delete_flag) def test_create_nfs_snapshot_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.create_snapshot, self._context, self.nfs_snapshot, self.share_server) def test_create_cifs_snapshot_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.create_snapshot, self._context, self.cifs_snapshot, self.share_server) def test_delete_nfs_snapshot_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.delete_snapshot, self._context, self.nfs_snapshot, self.share_server) def test_delete_cifs_snapshot_fail(self): - self.driver.helper.login() - self.driver.helper.test_normal = False + self.driver.plugin.helper.login() + self.driver.plugin.helper.test_normal = False self.assertRaises(exception.InvalidShare, self.driver.delete_snapshot, self._context, self.cifs_snapshot, self.share_server) @@ -641,14 +880,14 @@ class HuaweiShareDriverTestCase(test.TestCase): timeout = doc.createElement('Timeout') if timeout_flag: - timeout_text = doc.createTextNode('1') + timeout_text = doc.createTextNode('0') else: timeout_text = doc.createTextNode('') timeout.appendChild(timeout_text) waitinterval = doc.createElement('WaitInterval') if wait_interval_flag: - waitinterval_text = doc.createTextNode('1') + waitinterval_text = doc.createTextNode('0') else: waitinterval_text = doc.createTextNode('') waitinterval.appendChild(waitinterval_text)