diff --git a/cinder/backup/drivers/nfs.py b/cinder/backup/drivers/nfs.py index 455d50dfbb4..18dd9405031 100644 --- a/cinder/backup/drivers/nfs.py +++ b/cinder/backup/drivers/nfs.py @@ -18,11 +18,11 @@ import os import os.path +from os_brick.remotefs import remotefs as remotefs_brick from oslo_config import cfg from oslo_log import log as logging from cinder.backup import chunkeddriver -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import exception from cinder.i18n import _ from cinder import utils diff --git a/cinder/brick/README.txt b/cinder/brick/README.txt index bc26cd11bb5..35f90ca3629 100644 --- a/cinder/brick/README.txt +++ b/cinder/brick/README.txt @@ -1,9 +1,5 @@ -Brick is a new library that currently is maintained in Cinder for -the Havana release. It will eventually be moved external to Cinder, -possibly oslo, or pypi. Any defects found in Brick, should be submitted -against Cinder and fixed there, then pulled into other projects that -are using brick. +Brick has been migrated to a new standalone +pypi library called os-brick. -* Brick is used outside of Cinder and therefore - cannot have any dependencies on Cinder and/or - it's database. +We are leaving the local_dev directory here for the time +being until we can migrate it to a new home. diff --git a/cinder/brick/exception.py b/cinder/brick/exception.py deleted file mode 100644 index 75708c64a9e..00000000000 --- a/cinder/brick/exception.py +++ /dev/null @@ -1,126 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -"""Exceptions for the Brick library.""" - -from oslo_log import log as logging -import six - -from cinder.i18n import _, _LE - - -LOG = logging.getLogger(__name__) - - -class BrickException(Exception): - """Base Brick Exception - - To correctly use this class, inherit from it and define - a 'msg_fmt' property. That msg_fmt will get printf'd - with the keyword arguments provided to the constructor. - """ - message = _("An unknown exception occurred.") - code = 500 - headers = {} - safe = False - - def __init__(self, message=None, **kwargs): - self.kwargs = kwargs - - if 'code' not in self.kwargs: - try: - self.kwargs['code'] = self.code - except AttributeError: - pass - - if not message: - try: - message = self.message % kwargs - - except Exception: - # kwargs doesn't match a variable in the message - # log the issue and the kwargs - LOG.exception(_LE("Exception in string format operation. " - "msg='%s'"), - self.message) - for name, value in kwargs.iteritems(): - LOG.error(_LE("%(n)s: %(v)s"), {'n': name, 'v': value}) - - # at least get the core message out if something happened - message = self.message - - # Put the message in 'msg' so that we can access it. If we have it in - # message it will be overshadowed by the class' message attribute - self.msg = message - super(BrickException, self).__init__(message) - - def __unicode__(self): - return six.text_type(self.msg) - - -class NotFound(BrickException): - message = _("Resource could not be found.") - code = 404 - safe = True - - -class Invalid(BrickException): - message = _("Unacceptable parameters.") - code = 400 - - -# Cannot be templated as the error syntax varies. -# msg needs to be constructed when raised. -class InvalidParameterValue(Invalid): - message = _("%(err)s") - - -class NoFibreChannelHostsFound(BrickException): - message = _("We are unable to locate any Fibre Channel devices.") - - -class NoFibreChannelVolumeDeviceFound(BrickException): - message = _("Unable to find a Fibre Channel volume device.") - - -class VolumeDeviceNotFound(BrickException): - message = _("Volume device not found at %(device)s.") - - -class VolumePathNotRemoved(BrickException): - message = _("Volume path %(volume_path)s was not removed in time.") - - -class VolumeGroupNotFound(BrickException): - message = _('Unable to find Volume Group: %(vg_name)s') - - -class VolumeGroupCreationFailed(BrickException): - message = _('Failed to create Volume Group: %(vg_name)s') - - -class ISCSITargetCreateFailed(BrickException): - message = _("Failed to create iscsi target for volume %(volume_id)s.") - - -class ISCSITargetRemoveFailed(BrickException): - message = _("Failed to remove iscsi target for volume %(volume_id)s.") - - -class ISCSITargetAttachFailed(BrickException): - message = _("Failed to attach iSCSI target for volume %(volume_id)s.") - - -class ProtocolNotSupported(BrickException): - message = _("Connect to volume via protocol %(protocol)s not supported.") diff --git a/cinder/brick/executor.py b/cinder/brick/executor.py deleted file mode 100644 index fe82b88265f..00000000000 --- a/cinder/brick/executor.py +++ /dev/null @@ -1,34 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -"""Generic exec utility that allows us to set the - execute and root_helper attributes for putils. - Some projects need their own execute wrapper - and root_helper settings, so this provides that hook. -""" - -from oslo_concurrency import processutils as putils - - -class Executor(object): - def __init__(self, root_helper, execute=putils.execute, - *args, **kwargs): - self.set_execute(execute) - self.set_root_helper(root_helper) - - def set_execute(self, execute): - self._execute = execute - - def set_root_helper(self, helper): - self._root_helper = helper diff --git a/cinder/brick/initiator/__init__.py b/cinder/brick/initiator/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/brick/initiator/connector.py b/cinder/brick/initiator/connector.py deleted file mode 100644 index 449f83e9e1e..00000000000 --- a/cinder/brick/initiator/connector.py +++ /dev/null @@ -1,1251 +0,0 @@ -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import copy -import os -import platform -import re -import socket -import time - -from oslo_concurrency import lockutils -from oslo_concurrency import processutils as putils -from oslo_log import log as logging -from oslo_utils import strutils -import six - -from cinder.brick import exception -from cinder.brick import executor -from cinder.brick.initiator import host_driver -from cinder.brick.initiator import linuxfc -from cinder.brick.initiator import linuxscsi -from cinder.brick.remotefs import remotefs -from cinder.i18n import _, _LE, _LW -from cinder.openstack.common import loopingcall - -S390X = "s390x" -S390 = "s390" - -LOG = logging.getLogger(__name__) - -synchronized = lockutils.synchronized_with_prefix('brick-') -DEVICE_SCAN_ATTEMPTS_DEFAULT = 3 -MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$") - - -def _check_multipathd_running(root_helper, enforce_multipath): - try: - putils.execute('multipathd', 'show', 'status', - run_as_root=True, root_helper=root_helper) - except putils.ProcessExecutionError as err: - LOG.error(_LE('multipathd is not running: exit code %(err)s'), - {'err': err.exit_code}) - if enforce_multipath: - raise - return False - - return True - - -def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath): - """Get the connection properties for all protocols. - - When the connector wants to use multipath, multipath=True should be - specified. If enforce_multipath=True is specified too, an exception is - thrown when multipathd is not running. Otherwise, it falls back to - multipath=False and only the first path shown up is used. - For the compatibility reason, even if multipath=False is specified, - some cinder storage drivers may export the target for multipath, which - can be found via sendtargets discovery. - """ - - iscsi = ISCSIConnector(root_helper=root_helper) - fc = linuxfc.LinuxFibreChannel(root_helper=root_helper) - - props = {} - props['ip'] = my_ip - props['host'] = socket.gethostname() - initiator = iscsi.get_initiator() - if initiator: - props['initiator'] = initiator - wwpns = fc.get_fc_wwpns() - if wwpns: - props['wwpns'] = wwpns - wwnns = fc.get_fc_wwnns() - if wwnns: - props['wwnns'] = wwnns - props['multipath'] = (multipath and - _check_multipathd_running(root_helper, - enforce_multipath)) - return props - - -class InitiatorConnector(executor.Executor): - def __init__(self, root_helper, driver=None, - execute=putils.execute, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - super(InitiatorConnector, self).__init__(root_helper, execute=execute, - *args, **kwargs) - if not driver: - driver = host_driver.HostDriver() - self.set_driver(driver) - self.device_scan_attempts = device_scan_attempts - - def set_driver(self, driver): - """The driver is used to find used LUNs.""" - - self.driver = driver - - @staticmethod - def factory(protocol, root_helper, driver=None, - execute=putils.execute, use_multipath=False, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - arch=platform.machine(), - *args, **kwargs): - """Build a Connector object based upon protocol and architecture.""" - LOG.debug("Factory for %(proto)s on %(arch)s", - {'proto': protocol, 'arch': arch}) - protocol = protocol.upper() - if protocol == "ISCSI": - return ISCSIConnector(root_helper=root_helper, - driver=driver, - execute=execute, - use_multipath=use_multipath, - device_scan_attempts=device_scan_attempts, - *args, **kwargs) - elif protocol == "ISER": - return ISERConnector(root_helper=root_helper, - driver=driver, - execute=execute, - use_multipath=use_multipath, - device_scan_attempts=device_scan_attempts, - *args, **kwargs) - elif protocol == "FIBRE_CHANNEL": - if arch in (S390, S390X): - return FibreChannelConnectorS390X(root_helper=root_helper, - driver=driver, - execute=execute, - use_multipath=use_multipath, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - else: - return FibreChannelConnector(root_helper=root_helper, - driver=driver, - execute=execute, - use_multipath=use_multipath, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - elif protocol == "AOE": - return AoEConnector(root_helper=root_helper, - driver=driver, - execute=execute, - device_scan_attempts=device_scan_attempts, - *args, **kwargs) - elif protocol == "NFS" or protocol == "GLUSTERFS": - return RemoteFsConnector(mount_type=protocol.lower(), - root_helper=root_helper, - driver=driver, - execute=execute, - device_scan_attempts=device_scan_attempts, - *args, **kwargs) - elif protocol == "LOCAL": - return LocalConnector(root_helper=root_helper, - driver=driver, - execute=execute, - device_scan_attempts=device_scan_attempts, - *args, **kwargs) - elif protocol == "HUAWEISDSHYPERVISOR": - return HuaweiStorHyperConnector(root_helper=root_helper, - driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - else: - msg = (_("Invalid InitiatorConnector protocol " - "specified %(protocol)s") % - dict(protocol=protocol)) - raise ValueError(msg) - - def check_valid_device(self, path, run_as_root=True): - cmd = ('dd', 'if=%(path)s' % {"path": path}, - 'of=/dev/null', 'count=1') - out, info = None, None - try: - out, info = self._execute(*cmd, run_as_root=run_as_root, - root_helper=self._root_helper) - except putils.ProcessExecutionError as e: - LOG.error(_LE("Failed to access the device on the path " - "%(path)s: %(error)s %(info)s."), - {"path": path, "error": e.stderr, - "info": info}) - return False - # If the info is none, the path does not exist. - if info is None: - return False - return True - - def connect_volume(self, connection_properties): - """Connect to a volume. - - The connection_properties describes the information needed by - the specific protocol to use to make the connection. - """ - raise NotImplementedError() - - def disconnect_volume(self, connection_properties, device_info): - """Disconnect a volume from the local host. - - The connection_properties are the same as from connect_volume. - The device_info is returned from connect_volume. - """ - raise NotImplementedError() - - -class ISCSIConnector(InitiatorConnector): - """Connector class to attach/detach iSCSI volumes.""" - - def __init__(self, root_helper, driver=None, - execute=putils.execute, use_multipath=False, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute) - super(ISCSIConnector, self).__init__(root_helper, driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - self.use_multipath = use_multipath - - def set_execute(self, execute): - super(ISCSIConnector, self).set_execute(execute) - self._linuxscsi.set_execute(execute) - - def _iterate_all_targets(self, connection_properties): - for ip, iqn, lun in self._get_all_targets(connection_properties): - props = copy.deepcopy(connection_properties) - props['target_portal'] = ip - props['target_iqn'] = iqn - props['target_lun'] = lun - for key in ('target_portals', 'target_iqns', 'target_luns'): - props.pop(key, None) - yield props - - def _get_all_targets(self, connection_properties): - if all([key in connection_properties for key in ('target_portals', - 'target_iqns', - 'target_luns')]): - return zip(connection_properties['target_portals'], - connection_properties['target_iqns'], - connection_properties['target_luns']) - - return [(connection_properties['target_portal'], - connection_properties['target_iqn'], - connection_properties.get('target_lun', 0))] - - def _discover_iscsi_portals(self, connection_properties): - if all([key in connection_properties for key in ('target_portals', - 'target_iqns')]): - # Use targets specified by connection_properties - return zip(connection_properties['target_portals'], - connection_properties['target_iqns']) - - # Discover and return every available target - out = self._run_iscsiadm_bare(['-m', - 'discovery', - '-t', - 'sendtargets', - '-p', - connection_properties['target_portal']], - check_exit_code=[0, 255])[0] \ - or "" - - return self._get_target_portals_from_iscsiadm_output(out) - - @synchronized('connect_volume') - def connect_volume(self, connection_properties): - """Attach the volume to instance_name. - - connection_properties for iSCSI must include: - target_portal(s) - ip and optional port - target_iqn(s) - iSCSI Qualified Name - target_lun(s) - LUN id of the volume - Note that plural keys may be used when use_multipath=True - """ - - device_info = {'type': 'block'} - - if self.use_multipath: - # multipath installed, discovering other targets if available - for ip, iqn in self._discover_iscsi_portals(connection_properties): - props = copy.deepcopy(connection_properties) - props['target_portal'] = ip - props['target_iqn'] = iqn - self._connect_to_iscsi_portal(props) - - self._rescan_iscsi() - host_devices = self._get_device_path(connection_properties) - else: - target_props = connection_properties - for props in self._iterate_all_targets(connection_properties): - if self._connect_to_iscsi_portal(props): - target_props = props - break - else: - LOG.warning(_LW( - 'Failed to login to any of the iSCSI targets.')) - - host_devices = self._get_device_path(target_props) - - # The /dev/disk/by-path/... node is not always present immediately - # TODO(justinsb): This retry-with-delay is a pattern, move to utils? - tries = 0 - # Loop until at least 1 path becomes available - while all(map(lambda x: not os.path.exists(x), host_devices)): - if tries >= self.device_scan_attempts: - raise exception.VolumeDeviceNotFound(device=host_devices) - - LOG.warning(_LW("ISCSI volume not yet found at: %(host_devices)s. " - "Will rescan & retry. Try number: %(tries)s"), - {'host_devices': host_devices, - 'tries': tries}) - - # The rescan isn't documented as being necessary(?), but it helps - if self.use_multipath: - self._rescan_iscsi() - else: - self._run_iscsiadm(target_props, ("--rescan",)) - - tries = tries + 1 - if all(map(lambda x: not os.path.exists(x), host_devices)): - time.sleep(tries ** 2) - else: - break - - if tries != 0: - LOG.debug("Found iSCSI node %(host_devices)s " - "(after %(tries)s rescans)", - {'host_devices': host_devices, 'tries': tries}) - - # Choose an accessible host device - host_device = next(dev for dev in host_devices if os.path.exists(dev)) - - if self.use_multipath: - # we use the multipath device instead of the single path device - self._rescan_multipath() - multipath_device = self._get_multipath_device_name(host_device) - if multipath_device is not None: - host_device = multipath_device - - device_info['path'] = host_device - return device_info - - @synchronized('connect_volume') - def disconnect_volume(self, connection_properties, device_info): - """Detach the volume from instance_name. - - connection_properties for iSCSI must include: - target_portal(s) - IP and optional port - target_iqn(s) - iSCSI Qualified Name - target_lun(s) - LUN id of the volume - """ - # Moved _rescan_iscsi and _rescan_multipath - # from _disconnect_volume_multipath_iscsi to here. - # Otherwise, if we do rescan after _linuxscsi.remove_multipath_device - # but before logging out, the removed devices under /dev/disk/by-path - # will reappear after rescan. - self._rescan_iscsi() - if self.use_multipath: - self._rescan_multipath() - host_device = multipath_device = None - host_devices = self._get_device_path(connection_properties) - # Choose an accessible host device - for dev in host_devices: - if os.path.exists(dev): - host_device = dev - multipath_device = self._get_multipath_device_name(dev) - if multipath_device: - break - if not host_device: - LOG.error(_LE("No accessible volume device: %(host_devices)s"), - {'host_devices': host_devices}) - raise exception.VolumeDeviceNotFound(device=host_devices) - - if multipath_device: - device_realpath = os.path.realpath(host_device) - self._linuxscsi.remove_multipath_device(device_realpath) - return self._disconnect_volume_multipath_iscsi( - connection_properties, multipath_device) - - # When multiple portals/iqns/luns are specified, we need to remove - # unused devices created by logging into other LUNs' session. - for props in self._iterate_all_targets(connection_properties): - self._disconnect_volume_iscsi(props) - - def _disconnect_volume_iscsi(self, connection_properties): - # remove the device from the scsi subsystem - # this eliminates any stale entries until logout - host_device = self._get_device_path(connection_properties)[0] - dev_name = self._linuxscsi.get_name_from_path(host_device) - if dev_name: - self._linuxscsi.remove_scsi_device(dev_name) - - # NOTE(jdg): On busy systems we can have a race here - # where remove_iscsi_device is called before the device file - # has actually been removed. The result is an orphaned - # iscsi session that never gets logged out. The following - # call to wait addresses that issue. - self._linuxscsi.wait_for_volume_removal(host_device) - - # NOTE(vish): Only disconnect from the target if no luns from the - # target are in use. - device_prefix = ("/dev/disk/by-path/ip-%(portal)s-iscsi-%(iqn)s-lun-" % - {'portal': connection_properties['target_portal'], - 'iqn': connection_properties['target_iqn']}) - devices = self.driver.get_all_block_devices() - devices = [dev for dev in devices if dev.startswith(device_prefix) - and os.path.exists(dev)] - - if not devices: - self._disconnect_from_iscsi_portal(connection_properties) - - def _get_device_path(self, connection_properties): - return ["/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" % x for x in - self._get_all_targets(connection_properties)] - - def get_initiator(self): - """Secure helper to read file as root.""" - file_path = '/etc/iscsi/initiatorname.iscsi' - try: - lines, _err = self._execute('cat', file_path, run_as_root=True, - root_helper=self._root_helper) - - for l in lines.split('\n'): - if l.startswith('InitiatorName='): - return l[l.index('=') + 1:].strip() - except putils.ProcessExecutionError: - LOG.warning(_LW("Could not find the iSCSI Initiator File %s"), - file_path) - return None - - def _run_iscsiadm(self, connection_properties, iscsi_command, **kwargs): - check_exit_code = kwargs.pop('check_exit_code', 0) - (out, err) = self._execute('iscsiadm', '-m', 'node', '-T', - connection_properties['target_iqn'], - '-p', - connection_properties['target_portal'], - *iscsi_command, run_as_root=True, - root_helper=self._root_helper, - check_exit_code=check_exit_code) - msg = ("iscsiadm %(cmd)s: stdout=%(out)s stderr=%(err)s", - {'cmd': iscsi_command, 'out': out, 'err': err}) - LOG.debug(strutils.mask_password(msg)) - - return (out, err) - - def _iscsiadm_update(self, connection_properties, property_key, - property_value, **kwargs): - iscsi_command = ('--op', 'update', '-n', property_key, - '-v', property_value) - return self._run_iscsiadm(connection_properties, iscsi_command, - **kwargs) - - def _get_target_portals_from_iscsiadm_output(self, output): - # return both portals and iqns - return [line.split() for line in output.splitlines()] - - def _disconnect_volume_multipath_iscsi(self, connection_properties, - multipath_name): - """This removes a multipath device and it's LUNs.""" - LOG.debug("Disconnect multipath device %s", multipath_name) - block_devices = self.driver.get_all_block_devices() - devices = [] - for dev in block_devices: - if os.path.exists(dev): - if "/mapper/" in dev: - devices.append(dev) - else: - mpdev = self._get_multipath_device_name(dev) - if mpdev: - devices.append(mpdev) - - # Do a discovery to find all targets. - # Targets for multiple paths for the same multipath device - # may not be the same. - ips_iqns = self._discover_iscsi_portals(connection_properties) - - if not devices: - # disconnect if no other multipath devices - self._disconnect_mpath(connection_properties, ips_iqns) - return - - # Get a target for all other multipath devices - other_iqns = [self._get_multipath_iqn(device) - for device in devices] - # Get all the targets for the current multipath device - current_iqns = [iqn for ip, iqn in ips_iqns] - - in_use = False - for current in current_iqns: - if current in other_iqns: - in_use = True - break - - # If no other multipath device attached has the same iqn - # as the current device - if not in_use: - # disconnect if no other multipath devices with same iqn - self._disconnect_mpath(connection_properties, ips_iqns) - return - - # else do not disconnect iscsi portals, - # as they are used for other luns - return - - def _connect_to_iscsi_portal(self, connection_properties): - # NOTE(vish): If we are on the same host as nova volume, the - # discovery makes the target so we don't need to - # run --op new. Therefore, we check to see if the - # target exists, and if we get 255 (Not Found), then - # we run --op new. This will also happen if another - # volume is using the same target. - try: - self._run_iscsiadm(connection_properties, ()) - except putils.ProcessExecutionError as exc: - # iscsiadm returns 21 for "No records found" after version 2.0-871 - if exc.exit_code in [21, 255]: - self._run_iscsiadm(connection_properties, ('--op', 'new')) - else: - raise - - if connection_properties.get('auth_method'): - self._iscsiadm_update(connection_properties, - "node.session.auth.authmethod", - connection_properties['auth_method']) - self._iscsiadm_update(connection_properties, - "node.session.auth.username", - connection_properties['auth_username']) - self._iscsiadm_update(connection_properties, - "node.session.auth.password", - connection_properties['auth_password']) - - # duplicate logins crash iscsiadm after load, - # so we scan active sessions to see if the node is logged in. - out = self._run_iscsiadm_bare(["-m", "session"], - run_as_root=True, - check_exit_code=[0, 1, 21])[0] or "" - - portals = [{'portal': p.split(" ")[2], 'iqn': p.split(" ")[3]} - for p in out.splitlines() if p.startswith("tcp:")] - - stripped_portal = connection_properties['target_portal'].split(",")[0] - if len(portals) == 0 or len([s for s in portals - if stripped_portal == - s['portal'].split(",")[0] - and - s['iqn'] == - connection_properties['target_iqn']] - ) == 0: - try: - self._run_iscsiadm(connection_properties, - ("--login",), - check_exit_code=[0, 255]) - except putils.ProcessExecutionError as err: - # exit_code=15 means the session already exists, so it should - # be regarded as successful login. - if err.exit_code not in [15]: - LOG.warning( - _LW('Failed to login iSCSI target %(iqn)s on portal ' - '%(portal)s (exit code %(err)s).'), - {'iqn': connection_properties['target_iqn'], - 'portal': connection_properties['target_portal'], - 'err': err.exit_code}) - return False - - self._iscsiadm_update(connection_properties, - "node.startup", - "automatic") - return True - - def _disconnect_from_iscsi_portal(self, connection_properties): - self._iscsiadm_update(connection_properties, "node.startup", "manual", - check_exit_code=[0, 21, 255]) - self._run_iscsiadm(connection_properties, ("--logout",), - check_exit_code=[0, 21, 255]) - self._run_iscsiadm(connection_properties, ('--op', 'delete'), - check_exit_code=[0, 21, 255]) - - def _get_multipath_device_name(self, single_path_device): - device = os.path.realpath(single_path_device) - out = self._run_multipath(['-ll', - device], - check_exit_code=[0, 1])[0] - mpath_line = [line for line in out.splitlines() - if not re.match(MULTIPATH_ERROR_REGEX, line)] - if len(mpath_line) > 0 and len(mpath_line[0]) > 0: - return "/dev/mapper/%s" % mpath_line[0].split(" ")[0] - - return None - - def _get_iscsi_devices(self): - try: - devices = list(os.walk('/dev/disk/by-path'))[0][-1] - except IndexError: - return [] - return [entry for entry in devices if entry.startswith("ip-")] - - def _disconnect_mpath(self, connection_properties, ips_iqns): - for ip, iqn in ips_iqns: - props = copy.deepcopy(connection_properties) - props['target_portal'] = ip - props['target_iqn'] = iqn - self._disconnect_from_iscsi_portal(props) - - self._rescan_multipath() - - def _get_multipath_iqn(self, multipath_device): - entries = self._get_iscsi_devices() - for entry in entries: - entry_real_path = os.path.realpath("/dev/disk/by-path/%s" % entry) - entry_multipath = self._get_multipath_device_name(entry_real_path) - if entry_multipath == multipath_device: - return entry.split("iscsi-")[1].split("-lun")[0] - return None - - def _run_iscsiadm_bare(self, iscsi_command, **kwargs): - check_exit_code = kwargs.pop('check_exit_code', 0) - (out, err) = self._execute('iscsiadm', - *iscsi_command, - run_as_root=True, - root_helper=self._root_helper, - check_exit_code=check_exit_code) - LOG.debug("iscsiadm %(cmd)s: stdout=%(out)s stderr=%(err)s", - {'cmd': iscsi_command, 'out': out, 'err': err}) - return (out, err) - - def _run_multipath(self, multipath_command, **kwargs): - check_exit_code = kwargs.pop('check_exit_code', 0) - (out, err) = self._execute('multipath', - *multipath_command, - run_as_root=True, - root_helper=self._root_helper, - check_exit_code=check_exit_code) - LOG.debug("multipath %(cmd)s: stdout=%(out)s stderr=%(err)s", - {'cmd': multipath_command, 'out': out, 'err': err}) - return (out, err) - - def _rescan_iscsi(self): - self._run_iscsiadm_bare(('-m', 'node', '--rescan'), - check_exit_code=[0, 1, 21, 255]) - self._run_iscsiadm_bare(('-m', 'session', '--rescan'), - check_exit_code=[0, 1, 21, 255]) - - def _rescan_multipath(self): - self._run_multipath(['-r'], check_exit_code=[0, 1, 21]) - - -class ISERConnector(ISCSIConnector): - - def _get_device_path(self, iser_properties): - return ("/dev/disk/by-path/ip-%s-iser-%s-lun-%s" % - (iser_properties['target_portal'], - iser_properties['target_iqn'], - iser_properties.get('target_lun', 0))) - - -class FibreChannelConnector(InitiatorConnector): - """Connector class to attach/detach Fibre Channel volumes.""" - - def __init__(self, root_helper, driver=None, - execute=putils.execute, use_multipath=False, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute) - self._linuxfc = linuxfc.LinuxFibreChannel(root_helper, execute) - super(FibreChannelConnector, self).__init__(root_helper, driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - self.use_multipath = use_multipath - - def set_execute(self, execute): - super(FibreChannelConnector, self).set_execute(execute) - self._linuxscsi.set_execute(execute) - self._linuxfc.set_execute(execute) - - @synchronized('connect_volume') - def connect_volume(self, connection_properties): - """Attach the volume to instance_name. - - connection_properties for Fibre Channel must include: - target_portal - ip and optional port - target_iqn - iSCSI Qualified Name - target_lun - LUN id of the volume - """ - LOG.debug("execute = %s", self._execute) - device_info = {'type': 'block'} - - hbas = self._linuxfc.get_fc_hbas_info() - ports = connection_properties['target_wwn'] - possible_devs = self._get_possible_devices(hbas, ports) - - lun = connection_properties.get('target_lun', 0) - host_devices = self._get_host_devices(possible_devs, lun) - - if len(host_devices) == 0: - # this is empty because we don't have any FC HBAs - LOG.warning(_LW("We are unable to locate any Fibre Channel " - "devices")) - raise exception.NoFibreChannelHostsFound() - - # The /dev/disk/by-path/... node is not always present immediately - # We only need to find the first device. Once we see the first device - # multipath will have any others. - def _wait_for_device_discovery(host_devices): - tries = self.tries - for device in host_devices: - LOG.debug("Looking for Fibre Channel dev %(device)s", - {'device': device}) - if os.path.exists(device): - self.host_device = device - # get the /dev/sdX device. This is used - # to find the multipath device. - self.device_name = os.path.realpath(device) - raise loopingcall.LoopingCallDone() - - if self.tries >= self.device_scan_attempts: - LOG.error(_LE("Fibre Channel volume device not found.")) - raise exception.NoFibreChannelVolumeDeviceFound() - - LOG.warning(_LW("Fibre volume not yet found. " - "Will rescan & retry. Try number: %(tries)s"), - {'tries': tries}) - - self._linuxfc.rescan_hosts(hbas) - self.tries = self.tries + 1 - - self.host_device = None - self.device_name = None - self.tries = 0 - timer = loopingcall.FixedIntervalLoopingCall( - _wait_for_device_discovery, host_devices) - timer.start(interval=2).wait() - - tries = self.tries - if self.host_device is not None and self.device_name is not None: - LOG.debug("Found Fibre Channel volume %(name)s " - "(after %(tries)s rescans)", - {'name': self.device_name, 'tries': tries}) - - # see if the new drive is part of a multipath - # device. If so, we'll use the multipath device. - if self.use_multipath: - mdev_info = self._linuxscsi.find_multipath_device(self.device_name) - if mdev_info is not None: - LOG.debug("Multipath device discovered %(device)s", - {'device': mdev_info['device']}) - device_path = mdev_info['device'] - devices = mdev_info['devices'] - device_info['multipath_id'] = mdev_info['id'] - else: - # we didn't find a multipath device. - # so we assume the kernel only sees 1 device - device_path = self.host_device - dev_info = self._linuxscsi.get_device_info(self.device_name) - devices = [dev_info] - else: - device_path = self.host_device - dev_info = self._linuxscsi.get_device_info(self.device_name) - devices = [dev_info] - - device_info['path'] = device_path - device_info['devices'] = devices - return device_info - - def _get_host_devices(self, possible_devs, lun): - host_devices = [] - for pci_num, target_wwn in possible_devs: - host_device = "/dev/disk/by-path/pci-%s-fc-%s-lun-%s" % ( - pci_num, - target_wwn, - lun) - host_devices.append(host_device) - return host_devices - - def _get_possible_devices(self, hbas, wwnports): - """Compute the possible valid fibre channel device options. - - :param hbas: available hba devices. - :param wwnports: possible wwn addresses. Can either be string - or list of strings. - - :returns: list of (pci_id, wwn) tuples - - Given one or more wwn (mac addresses for fibre channel) ports - do the matrix math to figure out a set of pci device, wwn - tuples that are potentially valid (they won't all be). This - provides a search space for the device connection. - - """ - # the wwn (think mac addresses for fiber channel devices) can - # either be a single value or a list. Normalize it to a list - # for further operations. - wwns = [] - if isinstance(wwnports, list): - for wwn in wwnports: - wwns.append(str(wwn)) - elif isinstance(wwnports, six.string_types): - wwns.append(str(wwnports)) - - raw_devices = [] - for hba in hbas: - pci_num = self._get_pci_num(hba) - if pci_num is not None: - for wwn in wwns: - target_wwn = "0x%s" % wwn.lower() - raw_devices.append((pci_num, target_wwn)) - return raw_devices - - @synchronized('connect_volume') - def disconnect_volume(self, connection_properties, device_info): - """Detach the volume from instance_name. - - connection_properties for Fibre Channel must include: - target_wwn - iSCSI Qualified Name - target_lun - LUN id of the volume - """ - devices = device_info['devices'] - - # If this is a multipath device, we need to search again - # and make sure we remove all the devices. Some of them - # might not have shown up at attach time. - if self.use_multipath and 'multipath_id' in device_info: - multipath_id = device_info['multipath_id'] - mdev_info = self._linuxscsi.find_multipath_device(multipath_id) - devices = mdev_info['devices'] - LOG.debug("devices to remove = %s", devices) - self._linuxscsi.flush_multipath_device(multipath_id) - - self._remove_devices(connection_properties, devices) - - def _remove_devices(self, connection_properties, devices): - # There may have been more than 1 device mounted - # by the kernel for this volume. We have to remove - # all of them - for device in devices: - self._linuxscsi.remove_scsi_device(device["device"]) - - def _get_pci_num(self, hba): - # NOTE(walter-boring) - # device path is in format of - # /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.3/host2/fc_host/host2 - # sometimes an extra entry exists before the host2 value - # we always want the value prior to the host2 value - pci_num = None - if hba is not None: - if "device_path" in hba: - index = 0 - device_path = hba['device_path'].split('/') - for value in device_path: - if value.startswith('host'): - break - index = index + 1 - - if index > 0: - pci_num = device_path[index - 1] - - return pci_num - - -class FibreChannelConnectorS390X(FibreChannelConnector): - """Connector class to attach/detach Fibre Channel volumes on S390X arch.""" - - def __init__(self, root_helper, driver=None, - execute=putils.execute, use_multipath=False, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - super(FibreChannelConnectorS390X, self).__init__(root_helper, - driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - LOG.debug("Initializing Fibre Channel connector for S390") - self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute) - self._linuxfc = linuxfc.LinuxFibreChannelS390X(root_helper, execute) - self.use_multipath = use_multipath - - def set_execute(self, execute): - super(FibreChannelConnectorS390X, self).set_execute(execute) - self._linuxscsi.set_execute(execute) - self._linuxfc.set_execute(execute) - - def _get_host_devices(self, possible_devs, lun): - host_devices = [] - for pci_num, target_wwn in possible_devs: - target_lun = self._get_lun_string(lun) - host_device = self._get_device_file_path( - pci_num, - target_wwn, - target_lun) - self._linuxfc.configure_scsi_device(pci_num, target_wwn, - target_lun) - host_devices.append(host_device) - return host_devices - - def _get_lun_string(self, lun): - target_lun = 0 - if lun < 256: - target_lun = "0x00%02x000000000000" % lun - elif lun <= 0xffffffff: - target_lun = "0x%08x00000000" % lun - return target_lun - - def _get_device_file_path(self, pci_num, target_wwn, target_lun): - host_device = "/dev/disk/by-path/ccw-%s-zfcp-%s:%s" % ( - pci_num, - target_wwn, - target_lun) - return host_device - - def _remove_devices(self, connection_properties, devices): - hbas = self._linuxfc.get_fc_hbas_info() - ports = connection_properties['target_wwn'] - possible_devs = self._get_possible_devices(hbas, ports) - lun = connection_properties.get('target_lun', 0) - target_lun = self._get_lun_string(lun) - for pci_num, target_wwn in possible_devs: - self._linuxfc.deconfigure_scsi_device(pci_num, - target_wwn, - target_lun) - - -class AoEConnector(InitiatorConnector): - """Connector class to attach/detach AoE volumes.""" - def __init__(self, root_helper, driver=None, - execute=putils.execute, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - super(AoEConnector, self).__init__(root_helper, driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - - def _get_aoe_info(self, connection_properties): - shelf = connection_properties['target_shelf'] - lun = connection_properties['target_lun'] - aoe_device = 'e%(shelf)s.%(lun)s' % {'shelf': shelf, - 'lun': lun} - aoe_path = '/dev/etherd/%s' % (aoe_device) - return aoe_device, aoe_path - - @lockutils.synchronized('aoe_control', 'aoe-') - def connect_volume(self, connection_properties): - """Discover and attach the volume. - - connection_properties for AoE must include: - target_shelf - shelf id of volume - target_lun - lun id of volume - """ - aoe_device, aoe_path = self._get_aoe_info(connection_properties) - - device_info = { - 'type': 'block', - 'device': aoe_device, - 'path': aoe_path, - } - - if os.path.exists(aoe_path): - self._aoe_revalidate(aoe_device) - else: - self._aoe_discover() - - waiting_status = {'tries': 0} - - # NOTE(jbr_): Device path is not always present immediately - def _wait_for_discovery(aoe_path): - if os.path.exists(aoe_path): - raise loopingcall.LoopingCallDone - - if waiting_status['tries'] >= self.device_scan_attempts: - raise exception.VolumeDeviceNotFound(device=aoe_path) - - LOG.warning(_LW("AoE volume not yet found at: %(path)s. " - "Try number: %(tries)s"), - {'path': aoe_device, - 'tries': waiting_status['tries']}) - - self._aoe_discover() - waiting_status['tries'] += 1 - - timer = loopingcall.FixedIntervalLoopingCall(_wait_for_discovery, - aoe_path) - timer.start(interval=2).wait() - - if waiting_status['tries']: - LOG.debug("Found AoE device %(path)s " - "(after %(tries)s rediscover)", - {'path': aoe_path, - 'tries': waiting_status['tries']}) - - return device_info - - @lockutils.synchronized('aoe_control', 'aoe-') - def disconnect_volume(self, connection_properties, device_info): - """Detach and flush the volume. - - connection_properties for AoE must include: - target_shelf - shelf id of volume - target_lun - lun id of volume - """ - aoe_device, aoe_path = self._get_aoe_info(connection_properties) - - if os.path.exists(aoe_path): - self._aoe_flush(aoe_device) - - def _aoe_discover(self): - (out, err) = self._execute('aoe-discover', - run_as_root=True, - root_helper=self._root_helper, - check_exit_code=0) - - LOG.debug('aoe-discover: stdout=%(out)s stderr%(err)s', - {'out': out, 'err': err}) - - def _aoe_revalidate(self, aoe_device): - (out, err) = self._execute('aoe-revalidate', - aoe_device, - run_as_root=True, - root_helper=self._root_helper, - check_exit_code=0) - - LOG.debug('aoe-revalidate %(dev)s: stdout=%(out)s stderr%(err)s', - {'dev': aoe_device, 'out': out, 'err': err}) - - def _aoe_flush(self, aoe_device): - (out, err) = self._execute('aoe-flush', - aoe_device, - run_as_root=True, - root_helper=self._root_helper, - check_exit_code=0) - LOG.debug('aoe-flush %(dev)s: stdout=%(out)s stderr%(err)s', - {'dev': aoe_device, 'out': out, 'err': err}) - - -class RemoteFsConnector(InitiatorConnector): - """Connector class to attach/detach NFS and GlusterFS volumes.""" - - def __init__(self, mount_type, root_helper, driver=None, - execute=putils.execute, - device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT, - *args, **kwargs): - kwargs = kwargs or {} - conn = kwargs.get('conn') - if conn: - mount_point_base = conn.get('mount_point_base') - if mount_type.lower() == 'nfs': - kwargs['nfs_mount_point_base'] =\ - kwargs.get('nfs_mount_point_base') or\ - mount_point_base - elif mount_type.lower() == 'glusterfs': - kwargs['glusterfs_mount_point_base'] =\ - kwargs.get('glusterfs_mount_point_base') or\ - mount_point_base - else: - LOG.warning(_LW("Connection details not present." - " RemoteFsClient may not initialize properly.")) - self._remotefsclient = remotefs.RemoteFsClient(mount_type, root_helper, - execute=execute, - *args, **kwargs) - super(RemoteFsConnector, self).__init__(root_helper, driver=driver, - execute=execute, - device_scan_attempts= - device_scan_attempts, - *args, **kwargs) - - def set_execute(self, execute): - super(RemoteFsConnector, self).set_execute(execute) - self._remotefsclient.set_execute(execute) - - def connect_volume(self, connection_properties): - """Ensure that the filesystem containing the volume is mounted. - - connection_properties must include: - export - remote filesystem device (e.g. '172.18.194.100:/var/nfs') - name - file name within the filesystem - - connection_properties may optionally include: - options - options to pass to mount - """ - - mnt_flags = [] - if connection_properties.get('options'): - mnt_flags = connection_properties['options'].split() - - nfs_share = connection_properties['export'] - self._remotefsclient.mount(nfs_share, mnt_flags) - mount_point = self._remotefsclient.get_mount_point(nfs_share) - - path = mount_point + '/' + connection_properties['name'] - - return {'path': path} - - def disconnect_volume(self, connection_properties, device_info): - """No need to do anything to disconnect a volume in a filesystem.""" - - -class LocalConnector(InitiatorConnector): - """"Connector class to attach/detach File System backed volumes.""" - - def __init__(self, root_helper, driver=None, execute=putils.execute, - *args, **kwargs): - super(LocalConnector, self).__init__(root_helper, driver=driver, - execute=execute, *args, **kwargs) - - def connect_volume(self, connection_properties): - """Connect to a volume. - - connection_properties must include: - device_path - path to the volume to be connected - """ - if 'device_path' not in connection_properties: - msg = (_("Invalid connection_properties specified " - "no device_path attribute")) - raise ValueError(msg) - - device_info = {'type': 'local', - 'path': connection_properties['device_path']} - return device_info - - def disconnect_volume(self, connection_properties, device_info): - """Disconnect a volume from the local host.""" - pass - - -class HuaweiStorHyperConnector(InitiatorConnector): - """"Connector class to attach/detach SDSHypervisor volumes.""" - attached_success_code = 0 - has_been_attached_code = 50151401 - attach_mnid_done_code = 50151405 - vbs_unnormal_code = 50151209 - not_mount_node_code = 50155007 - iscliexist = True - - def __init__(self, root_helper, driver=None, execute=putils.execute, - *args, **kwargs): - self.cli_path = os.getenv('HUAWEISDSHYPERVISORCLI_PATH') - if not self.cli_path: - self.cli_path = '/usr/local/bin/sds/sds_cli' - LOG.debug("CLI path is not configured, using default %s.", - self.cli_path) - if not os.path.isfile(self.cli_path): - self.iscliexist = False - LOG.error(_LE('SDS CLI file not found, ' - 'HuaweiStorHyperConnector init failed.')) - super(HuaweiStorHyperConnector, self).__init__(root_helper, - driver=driver, - execute=execute, - *args, **kwargs) - - @synchronized('connect_volume') - def connect_volume(self, connection_properties): - """Connect to a volume.""" - LOG.debug("Connect_volume connection properties: %s.", - connection_properties) - out = self._attach_volume(connection_properties['volume_id']) - if not out or int(out['ret_code']) not in (self.attached_success_code, - self.has_been_attached_code, - self.attach_mnid_done_code): - msg = (_("Attach volume failed, " - "error code is %s") % out['ret_code']) - raise exception.BrickException(msg=msg) - out = self._query_attached_volume( - connection_properties['volume_id']) - if not out or int(out['ret_code']) != 0: - msg = _("query attached volume failed or volume not attached.") - raise exception.BrickException(msg=msg) - - device_info = {'type': 'block', - 'path': out['dev_addr']} - return device_info - - @synchronized('connect_volume') - def disconnect_volume(self, connection_properties, device_info): - """Disconnect a volume from the local host.""" - LOG.debug("Disconnect_volume: %s.", connection_properties) - out = self._detach_volume(connection_properties['volume_id']) - if not out or int(out['ret_code']) not in (self.attached_success_code, - self.vbs_unnormal_code, - self.not_mount_node_code): - msg = (_("Disconnect_volume failed, " - "error code is %s") % out['ret_code']) - raise exception.BrickException(msg=msg) - - def is_volume_connected(self, volume_name): - """Check if volume already connected to host.""" - LOG.debug('Check if volume %s already connected to a host.', - volume_name) - out = self._query_attached_volume(volume_name) - if out: - return int(out['ret_code']) == 0 - return False - - def _attach_volume(self, volume_name): - return self._cli_cmd('attach', volume_name) - - def _detach_volume(self, volume_name): - return self._cli_cmd('detach', volume_name) - - def _query_attached_volume(self, volume_name): - return self._cli_cmd('querydev', volume_name) - - def _cli_cmd(self, method, volume_name): - LOG.debug("Enter into _cli_cmd.") - if not self.iscliexist: - msg = _("SDS command line doesn't exist, " - "can't execute SDS command.") - raise exception.BrickException(msg=msg) - if not method or volume_name is None: - return - cmd = [self.cli_path, '-c', method, '-v', volume_name] - out, clilog = self._execute(*cmd, run_as_root=False, - root_helper=self._root_helper) - analyse_result = self._analyze_output(out) - LOG.debug('%(method)s volume returns %(analyse_result)s.', - {'method': method, 'analyse_result': analyse_result}) - if clilog: - LOG.error(_LE("SDS CLI output some log: %s."), clilog) - return analyse_result - - def _analyze_output(self, out): - LOG.debug("Enter into _analyze_output.") - if out: - analyse_result = {} - out_temp = out.split('\n') - for line in out_temp: - LOG.debug("Line is %s.", line) - if line.find('=') != -1: - key, val = line.split('=', 1) - LOG.debug("key %(k)s = %(v)s", {'k': key, 'v': val}) - if key in ['ret_code', 'ret_desc', 'dev_addr']: - analyse_result[key] = val - return analyse_result - else: - return None diff --git a/cinder/brick/initiator/host_driver.py b/cinder/brick/initiator/host_driver.py deleted file mode 100644 index 5de57ccdac0..00000000000 --- a/cinder/brick/initiator/host_driver.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2013 OpenStack Foundation. -# 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 os - - -class HostDriver(object): - - def get_all_block_devices(self): - """Get the list of all block devices seen in /dev/disk/by-path/.""" - files = [] - dir = "/dev/disk/by-path/" - if os.path.isdir(dir): - files = os.listdir(dir) - devices = [] - for file in files: - devices.append(dir + file) - return devices diff --git a/cinder/brick/initiator/linuxfc.py b/cinder/brick/initiator/linuxfc.py deleted file mode 100644 index b66c02489d0..00000000000 --- a/cinder/brick/initiator/linuxfc.py +++ /dev/null @@ -1,212 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -"""Generic linux Fibre Channel utilities.""" - -import errno - -from oslo_concurrency import processutils as putils -from oslo_log import log as logging - -from cinder.brick.initiator import linuxscsi -from cinder.i18n import _LW - -LOG = logging.getLogger(__name__) - - -class LinuxFibreChannel(linuxscsi.LinuxSCSI): - def __init__(self, root_helper, execute=putils.execute, - *args, **kwargs): - super(LinuxFibreChannel, self).__init__(root_helper, execute, - *args, **kwargs) - - def rescan_hosts(self, hbas): - for hba in hbas: - self.echo_scsi_command("/sys/class/scsi_host/%s/scan" - % hba['host_device'], "- - -") - - def get_fc_hbas(self): - """Get the Fibre Channel HBA information.""" - out = None - try: - out, _err = self._execute('systool', '-c', 'fc_host', '-v', - run_as_root=True, - root_helper=self._root_helper) - except putils.ProcessExecutionError as exc: - # This handles the case where rootwrap is used - # and systool is not installed - # 96 = nova.cmd.rootwrap.RC_NOEXECFOUND: - if exc.exit_code == 96: - LOG.warning(_LW("systool is not installed")) - return [] - except OSError as exc: - # This handles the case where rootwrap is NOT used - # and systool is not installed - if exc.errno == errno.ENOENT: - LOG.warning(_LW("systool is not installed")) - return [] - - # No FC HBAs were found - if out is None: - return [] - - lines = out.split('\n') - # ignore the first 2 lines - lines = lines[2:] - hbas = [] - hba = {} - lastline = None - for line in lines: - line = line.strip() - # 2 newlines denotes a new hba port - if line == '' and lastline == '': - if len(hba) > 0: - hbas.append(hba) - hba = {} - else: - val = line.split('=') - if len(val) == 2: - key = val[0].strip().replace(" ", "") - value = val[1].strip() - hba[key] = value.replace('"', '') - lastline = line - - return hbas - - def get_fc_hbas_info(self): - """Get Fibre Channel WWNs and device paths from the system, if any.""" - - # Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys - # and are obtainable via the systool app - hbas = self.get_fc_hbas() - if not hbas: - return [] - - hbas_info = [] - for hba in hbas: - wwpn = hba['port_name'].replace('0x', '') - wwnn = hba['node_name'].replace('0x', '') - device_path = hba['ClassDevicepath'] - device = hba['ClassDevice'] - hbas_info.append({'port_name': wwpn, - 'node_name': wwnn, - 'host_device': device, - 'device_path': device_path}) - return hbas_info - - def get_fc_wwpns(self): - """Get Fibre Channel WWPNs from the system, if any.""" - - # Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys - # and are obtainable via the systool app - hbas = self.get_fc_hbas() - - wwpns = [] - if hbas: - for hba in hbas: - if hba['port_state'] == 'Online': - wwpn = hba['port_name'].replace('0x', '') - wwpns.append(wwpn) - - return wwpns - - def get_fc_wwnns(self): - """Get Fibre Channel WWNNs from the system, if any.""" - - # Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys - # and are obtainable via the systool app - hbas = self.get_fc_hbas() - if not hbas: - return [] - - wwnns = [] - if hbas: - for hba in hbas: - if hba['port_state'] == 'Online': - wwnn = hba['node_name'].replace('0x', '') - wwnns.append(wwnn) - - return wwnns - - -class LinuxFibreChannelS390X(LinuxFibreChannel): - def __init__(self, root_helper, execute=putils.execute, - *args, **kwargs): - super(LinuxFibreChannelS390X, self).__init__(root_helper, execute, - *args, **kwargs) - - def get_fc_hbas_info(self): - """Get Fibre Channel WWNs and device paths from the system, if any.""" - - hbas = self.get_fc_hbas() - if not hbas: - return [] - - hbas_info = [] - for hba in hbas: - if hba['port_state'] == 'Online': - wwpn = hba['port_name'].replace('0x', '') - wwnn = hba['node_name'].replace('0x', '') - device_path = hba['ClassDevicepath'] - device = hba['ClassDevice'] - hbas_info.append({'port_name': wwpn, - 'node_name': wwnn, - 'host_device': device, - 'device_path': device_path}) - return hbas_info - - def configure_scsi_device(self, device_number, target_wwn, lun): - """Write the LUN to the port's unit_add attribute. - - If auto-discovery of LUNs is disabled on s390 platforms - luns need to be added to the configuration through the - unit_add interface - """ - LOG.debug("Configure lun for s390: device_number=(%(device_num)s) " - "target_wwn=(%(target_wwn)s) target_lun=(%(target_lun)s)", - {'device_num': device_number, - 'target_wwn': target_wwn, - 'target_lun': lun}) - zfcp_device_command = ("/sys/bus/ccw/drivers/zfcp/%s/%s/unit_add" % - (device_number, target_wwn)) - LOG.debug("unit_add call for s390 execute: %s", zfcp_device_command) - try: - self.echo_scsi_command(zfcp_device_command, lun) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("unit_add call for s390 failed exit (%(code)s), " - "stderr (%(stderr)s)"), - {'code': exc.exit_code, 'stderr': exc.stderr}) - - def deconfigure_scsi_device(self, device_number, target_wwn, lun): - """Write the LUN to the port's unit_remove attribute. - - If auto-discovery of LUNs is disabled on s390 platforms - luns need to be removed from the configuration through the - unit_remove interface - """ - LOG.debug("Deconfigure lun for s390: " - "device_number=(%(device_num)s) " - "target_wwn=(%(target_wwn)s) target_lun=(%(target_lun)s)", - {'device_num': device_number, - 'target_wwn': target_wwn, - 'target_lun': lun}) - zfcp_device_command = ("/sys/bus/ccw/drivers/zfcp/%s/%s/unit_remove" % - (device_number, target_wwn)) - LOG.debug("unit_remove call for s390 execute: %s", zfcp_device_command) - try: - self.echo_scsi_command(zfcp_device_command, lun) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("unit_remove call for s390 failed exit (%(code)s)" - ", stderr (%(stderr)s)"), - {'code': exc.exit_code, 'stderr': exc.stderr}) diff --git a/cinder/brick/initiator/linuxscsi.py b/cinder/brick/initiator/linuxscsi.py deleted file mode 100644 index e642bf073d2..00000000000 --- a/cinder/brick/initiator/linuxscsi.py +++ /dev/null @@ -1,232 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -"""Generic linux scsi subsystem and Multipath utilities. - - Note, this is not iSCSI. -""" -import os -import re - -from oslo_concurrency import processutils as putils -from oslo_log import log as logging - -from cinder.brick import exception -from cinder.brick import executor -from cinder.i18n import _LW, _LE -from cinder.openstack.common import loopingcall - -LOG = logging.getLogger(__name__) - -MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$") -MULTIPATH_WWID_REGEX = re.compile("\((?P.+)\)") - - -class LinuxSCSI(executor.Executor): - def __init__(self, root_helper, execute=putils.execute, - *args, **kwargs): - super(LinuxSCSI, self).__init__(root_helper, execute, - *args, **kwargs) - - def echo_scsi_command(self, path, content): - """Used to echo strings to scsi subsystem.""" - - args = ["-a", path] - kwargs = dict(process_input=content, - run_as_root=True, - root_helper=self._root_helper) - self._execute('tee', *args, **kwargs) - - def get_name_from_path(self, path): - """Translates /dev/disk/by-path/ entry to /dev/sdX.""" - - name = os.path.realpath(path) - if name.startswith("/dev/"): - return name - else: - return None - - def remove_scsi_device(self, device): - """Removes a scsi device based upon /dev/sdX name.""" - - path = "/sys/block/%s/device/delete" % device.replace("/dev/", "") - if os.path.exists(path): - # flush any outstanding IO first - self.flush_device_io(device) - - LOG.debug("Remove SCSI device(%(dev)s) with %(path)s", - {'dev': device, 'path': path}) - self.echo_scsi_command(path, "1") - - def wait_for_volume_removal(self, volume_path): - """This is used to ensure that volumes are gone.""" - - def _wait_for_volume_removal(volume_path): - LOG.debug("Waiting for SCSI mount point %s to be removed.", - volume_path) - if os.path.exists(volume_path): - if self.tries >= self.scan_attempts: - LOG.error(_LE("Exceeded the number of attempts to detect " - "volume removal.")) - raise exception.VolumePathNotRemoved( - volume_path=volume_path) - - LOG.debug("%(path)s still exists, rescanning. Try number: " - "%(tries)s", - {'path': volume_path, 'tries': self.tries}) - self.tries = self.tries + 1 - else: - LOG.debug("SCSI mount point %s has been removed.", volume_path) - raise loopingcall.LoopingCallDone() - - # Setup a loop here to give the kernel time - # to remove the volume from /dev/disk/by-path/ - self.tries = 0 - self.scan_attempts = 3 - timer = loopingcall.FixedIntervalLoopingCall( - _wait_for_volume_removal, volume_path) - timer.start(interval=2).wait() - - def get_device_info(self, device): - (out, _err) = self._execute('sg_scan', device, run_as_root=True, - root_helper=self._root_helper) - dev_info = {'device': device, 'host': None, - 'channel': None, 'id': None, 'lun': None} - if out: - line = out.strip() - line = line.replace(device + ": ", "") - info = line.split(" ") - - for item in info: - if '=' in item: - pair = item.split('=') - dev_info[pair[0]] = pair[1] - elif 'scsi' in item: - dev_info['host'] = item.replace('scsi', '') - - return dev_info - - def remove_multipath_device(self, multipath_name): - """This removes LUNs associated with a multipath device - and the multipath device itself. - """ - - LOG.debug("remove multipath device %s", multipath_name) - mpath_dev = self.find_multipath_device(multipath_name) - if mpath_dev: - devices = mpath_dev['devices'] - LOG.debug("multipath LUNs to remove %s", devices) - for device in devices: - self.remove_scsi_device(device['device']) - self.flush_multipath_device(mpath_dev['id']) - - def flush_device_io(self, device): - """This is used to flush any remaining IO in the buffers.""" - try: - LOG.debug("Flushing IO for device %s", device) - self._execute('blockdev', '--flushbufs', device, run_as_root=True, - root_helper=self._root_helper) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("Failed to flush IO buffers prior to removing" - " device: (%(code)s)"), - {'code': exc.exit_code}) - - def flush_multipath_device(self, device): - try: - LOG.debug("Flush multipath device %s", device) - self._execute('multipath', '-f', device, run_as_root=True, - root_helper=self._root_helper) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("multipath call failed exit (%(code)s)"), - {'code': exc.exit_code}) - - def flush_multipath_devices(self): - try: - self._execute('multipath', '-F', run_as_root=True, - root_helper=self._root_helper) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("multipath call failed exit (%(code)s)"), - {'code': exc.exit_code}) - - def find_multipath_device(self, device): - """Find a multipath device associated with a LUN device name. - - device can be either a /dev/sdX entry or a multipath id. - """ - - mdev = None - devices = [] - out = None - try: - (out, _err) = self._execute('multipath', '-l', device, - run_as_root=True, - root_helper=self._root_helper) - except putils.ProcessExecutionError as exc: - LOG.warning(_LW("multipath call failed exit (%(code)s)"), - {'code': exc.exit_code}) - return None - - if out: - lines = out.strip() - lines = lines.split("\n") - lines = [line for line in lines - if not re.match(MULTIPATH_ERROR_REGEX, line)] - if lines: - - # Use the device name, be it the WWID, mpathN or custom alias - # of a device to build the device path. This should be the - # first item on the first line of output from `multipath -l - # ${path}` or `multipath -l ${wwid}`.. - mdev_name = lines[0].split(" ")[0] - mdev = '/dev/mapper/%s' % mdev_name - - # Find the WWID for the LUN if we are using mpathN or aliases. - wwid_search = MULTIPATH_WWID_REGEX.search(lines[0]) - if wwid_search is not None: - mdev_id = wwid_search.group('wwid') - else: - mdev_id = mdev_name - - # Confirm that the device is present. - try: - os.stat(mdev) - except OSError: - LOG.warning(_LW("Couldn't find multipath device %s"), mdev) - return None - - LOG.debug("Found multipath device = %(mdev)s", - {'mdev': mdev}) - device_lines = lines[3:] - for dev_line in device_lines: - if dev_line.find("policy") != -1: - continue - - dev_line = dev_line.lstrip(' |-`') - dev_info = dev_line.split() - address = dev_info[0].split(":") - - dev = {'device': '/dev/%s' % dev_info[1], - 'host': address[0], 'channel': address[1], - 'id': address[2], 'lun': address[3] - } - - devices.append(dev) - - if mdev is not None: - info = {"device": mdev, - "id": mdev_id, - "name": mdev_name, - "devices": devices} - return info - return None diff --git a/cinder/brick/local_dev/lvm.py b/cinder/brick/local_dev/lvm.py index f4c7753c807..1dfa14fe847 100644 --- a/cinder/brick/local_dev/lvm.py +++ b/cinder/brick/local_dev/lvm.py @@ -22,12 +22,12 @@ import math import os import re +from os_brick import executor from oslo_concurrency import processutils as putils from oslo_log import log as logging from oslo_utils import excutils -from cinder.brick import exception -from cinder.brick import executor +from cinder import exception from cinder.i18n import _LE, _LI from cinder import utils diff --git a/cinder/brick/remotefs/__init__.py b/cinder/brick/remotefs/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/brick/remotefs/remotefs.py b/cinder/brick/remotefs/remotefs.py deleted file mode 100644 index 4ff3e129385..00000000000 --- a/cinder/brick/remotefs/remotefs.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# 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. - -"""Remote filesystem client utilities.""" - -import hashlib -import os -import re - -from oslo_concurrency import processutils as putils -from oslo_log import log as logging -import six - -from cinder.brick import exception -from cinder.i18n import _, _LI - -LOG = logging.getLogger(__name__) - - -class RemoteFsClient(object): - - def __init__(self, mount_type, root_helper, - execute=putils.execute, *args, **kwargs): - - self._mount_type = mount_type - if mount_type == "nfs": - self._mount_base = kwargs.get('nfs_mount_point_base', None) - if not self._mount_base: - raise exception.InvalidParameterValue( - err=_('nfs_mount_point_base required')) - self._mount_options = kwargs.get('nfs_mount_options', None) - self._check_nfs_options() - elif mount_type == "cifs": - self._mount_base = kwargs.get('smbfs_mount_point_base', None) - if not self._mount_base: - raise exception.InvalidParameterValue( - err=_('smbfs_mount_point_base required')) - self._mount_options = kwargs.get('smbfs_mount_options', None) - elif mount_type == "glusterfs": - self._mount_base = kwargs.get('glusterfs_mount_point_base', None) - if not self._mount_base: - raise exception.InvalidParameterValue( - err=_('glusterfs_mount_point_base required')) - self._mount_options = None - else: - raise exception.ProtocolNotSupported(protocol=mount_type) - self.root_helper = root_helper - self.set_execute(execute) - - def set_execute(self, execute): - self._execute = execute - - def _get_hash_str(self, base_str): - """Return a string that represents hash of base_str - (in a hex format). - """ - return hashlib.md5(base_str).hexdigest() - - def get_mount_point(self, device_name): - """Get Mount Point. - - :param device_name: example 172.18.194.100:/var/nfs - """ - return os.path.join(self._mount_base, - self._get_hash_str(device_name)) - - def _read_mounts(self): - (out, _err) = self._execute('mount', check_exit_code=0) - lines = out.split('\n') - mounts = {} - for line in lines: - tokens = line.split() - if 2 < len(tokens): - device = tokens[0] - mnt_point = tokens[2] - mounts[mnt_point] = device - return mounts - - def mount(self, share, flags=None): - """Mount given share.""" - mount_path = self.get_mount_point(share) - - if mount_path in self._read_mounts(): - LOG.info(_LI('Already mounted: %s'), mount_path) - return - - self._execute('mkdir', '-p', mount_path, check_exit_code=0) - if self._mount_type == 'nfs': - self._mount_nfs(share, mount_path, flags) - else: - self._do_mount(self._mount_type, share, mount_path, - self._mount_options, flags) - - def _do_mount(self, mount_type, share, mount_path, mount_options=None, - flags=None): - """Mounts share based on the specified params.""" - mnt_cmd = ['mount', '-t', mount_type] - if mount_options is not None: - mnt_cmd.extend(['-o', mount_options]) - if flags is not None: - mnt_cmd.extend(flags) - mnt_cmd.extend([share, mount_path]) - - self._execute(*mnt_cmd, root_helper=self.root_helper, - run_as_root=True, check_exit_code=0) - - def _mount_nfs(self, nfs_share, mount_path, flags=None): - """Mount nfs share using present mount types.""" - mnt_errors = {} - - # This loop allows us to first try to mount with NFS 4.1 for pNFS - # support but falls back to mount NFS 4 or NFS 3 if either the client - # or server do not support it. - for mnt_type in sorted(self._nfs_mount_type_opts.keys(), reverse=True): - options = self._nfs_mount_type_opts[mnt_type] - try: - self._do_mount('nfs', nfs_share, mount_path, options, flags) - LOG.debug('Mounted %(sh)s using %(mnt_type)s.', - {'sh': nfs_share, 'mnt_type': mnt_type}) - return - except Exception as e: - mnt_errors[mnt_type] = six.text_type(e) - LOG.debug('Failed to do %s mount.', mnt_type) - raise exception.BrickException(_("NFS mount failed for share %(sh)s. " - "Error - %(error)s") - % {'sh': nfs_share, - 'error': mnt_errors}) - - def _check_nfs_options(self): - """Checks and prepares nfs mount type options.""" - self._nfs_mount_type_opts = {'nfs': self._mount_options} - nfs_vers_opt_patterns = ['^nfsvers', '^vers', '^v[\d]'] - for opt in nfs_vers_opt_patterns: - if self._option_exists(self._mount_options, opt): - return - - # pNFS requires NFS 4.1. The mount.nfs4 utility does not automatically - # negotiate 4.1 support, we have to ask for it by specifying two - # options: vers=4 and minorversion=1. - pnfs_opts = self._update_option(self._mount_options, 'vers', '4') - pnfs_opts = self._update_option(pnfs_opts, 'minorversion', '1') - self._nfs_mount_type_opts['pnfs'] = pnfs_opts - - def _option_exists(self, options, opt_pattern): - """Checks if the option exists in nfs options and returns position.""" - options = [x.strip() for x in options.split(',')] if options else [] - pos = 0 - for opt in options: - pos = pos + 1 - if re.match(opt_pattern, opt, flags=0): - return pos - return 0 - - def _update_option(self, options, option, value=None): - """Update option if exists else adds it and returns new options.""" - opts = [x.strip() for x in options.split(',')] if options else [] - pos = self._option_exists(options, option) - if pos: - opts.pop(pos - 1) - opt = '%s=%s' % (option, value) if value else option - opts.append(opt) - return ",".join(opts) if len(opts) > 1 else opts[0] diff --git a/cinder/exception.py b/cinder/exception.py index 9e53667354c..5ceab099073 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -687,6 +687,18 @@ class ReadOnlyFieldError(CinderException): msg_fmt = _('Cannot modify readonly field %(field)s') +class VolumeGroupNotFound(CinderException): + msg_fmt = _('Unable to find Volume Group: %(vg_name)s') + + +class VolumeGroupCreationFailed(CinderException): + msg_fmt = _('Failed to create Volume Group: %(vg_name)s') + + +class VolumeDeviceNotFound(CinderException): + msg_fmt = _('Volume device not found at %(device)s.') + + # Driver specific exceptions # Coraid class CoraidException(VolumeDriverException): diff --git a/cinder/tests/unit/backup/drivers/test_backup_nfs.py b/cinder/tests/unit/backup/drivers/test_backup_nfs.py index 7ac948506ee..5427cfb85f3 100644 --- a/cinder/tests/unit/backup/drivers/test_backup_nfs.py +++ b/cinder/tests/unit/backup/drivers/test_backup_nfs.py @@ -27,11 +27,11 @@ import tempfile import zlib import mock +from os_brick.remotefs import remotefs as remotefs_brick from oslo_config import cfg from oslo_log import log as logging from cinder.backup.drivers import nfs -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import context from cinder import db from cinder import exception diff --git a/cinder/tests/unit/brick/test_brick_connector.py b/cinder/tests/unit/brick/test_brick_connector.py deleted file mode 100644 index 26f9f30634c..00000000000 --- a/cinder/tests/unit/brick/test_brick_connector.py +++ /dev/null @@ -1,1166 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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 os.path -import socket -import string -import tempfile -import time - -import mock -from oslo_concurrency import processutils as putils -from oslo_config import cfg -from oslo_log import log as logging -import six - -from cinder.brick import exception -from cinder.brick.initiator import connector -from cinder.brick.initiator import host_driver -from cinder.brick.initiator import linuxfc -from cinder.brick.initiator import linuxscsi -from cinder.i18n import _LE -from cinder.openstack.common import loopingcall -from cinder import test - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class ConnectorUtilsTestCase(test.TestCase): - - @mock.patch.object(socket, 'gethostname', return_value='fakehost') - @mock.patch.object(connector.ISCSIConnector, 'get_initiator', - return_value='fakeinitiator') - @mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_wwpns', - return_value=None) - @mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_wwnns', - return_value=None) - def _test_brick_get_connector_properties(self, multipath, - enforce_multipath, - multipath_result, - mock_wwnns, mock_wwpns, - mock_initiator, mock_gethostname): - props_actual = connector.get_connector_properties('sudo', - CONF.my_ip, - multipath, - enforce_multipath) - props = {'initiator': 'fakeinitiator', - 'host': 'fakehost', - 'ip': CONF.my_ip, - 'multipath': multipath_result} - self.assertEqual(props, props_actual) - - def test_brick_get_connector_properties(self): - self._test_brick_get_connector_properties(False, False, False) - - @mock.patch.object(putils, 'execute') - def test_brick_get_connector_properties_multipath(self, mock_execute): - self._test_brick_get_connector_properties(True, True, True) - mock_execute.assert_called_once_with('multipathd', 'show', 'status', - run_as_root=True, - root_helper='sudo') - - @mock.patch.object(putils, 'execute', - side_effect=putils.ProcessExecutionError) - def test_brick_get_connector_properties_fallback(self, mock_execute): - self._test_brick_get_connector_properties(True, False, False) - mock_execute.assert_called_once_with('multipathd', 'show', 'status', - run_as_root=True, - root_helper='sudo') - - @mock.patch.object(putils, 'execute', - side_effect=putils.ProcessExecutionError) - def test_brick_get_connector_properties_raise(self, mock_execute): - self.assertRaises(putils.ProcessExecutionError, - self._test_brick_get_connector_properties, - True, True, None) - - -class ConnectorTestCase(test.TestCase): - - def setUp(self): - super(ConnectorTestCase, self).setUp() - self.cmds = [] - - def fake_execute(self, *cmd, **kwargs): - self.cmds.append(string.join(cmd)) - return "", None - - def test_connect_volume(self): - self.connector = connector.InitiatorConnector(None) - self.assertRaises(NotImplementedError, - self.connector.connect_volume, None) - - def test_disconnect_volume(self): - self.connector = connector.InitiatorConnector(None) - self.assertRaises(NotImplementedError, - self.connector.disconnect_volume, None, None) - - def test_factory(self): - obj = connector.InitiatorConnector.factory('iscsi', None) - self.assertEqual(obj.__class__.__name__, "ISCSIConnector") - - obj = connector.InitiatorConnector.factory('fibre_channel', None) - self.assertEqual(obj.__class__.__name__, "FibreChannelConnector") - - obj = connector.InitiatorConnector.factory('fibre_channel', None, - arch='s390x') - self.assertEqual(obj.__class__.__name__, "FibreChannelConnectorS390X") - - obj = connector.InitiatorConnector.factory('aoe', None) - self.assertEqual(obj.__class__.__name__, "AoEConnector") - - obj = connector.InitiatorConnector.factory( - 'nfs', None, nfs_mount_point_base='/mnt/test') - self.assertEqual(obj.__class__.__name__, "RemoteFsConnector") - - obj = connector.InitiatorConnector.factory( - 'glusterfs', None, glusterfs_mount_point_base='/mnt/test') - self.assertEqual(obj.__class__.__name__, "RemoteFsConnector") - - obj = connector.InitiatorConnector.factory('local', None) - self.assertEqual(obj.__class__.__name__, "LocalConnector") - - self.assertRaises(ValueError, - connector.InitiatorConnector.factory, - "bogus", None) - - def test_check_valid_device_with_wrong_path(self): - self.connector = connector.InitiatorConnector(None) - self.stubs.Set(self.connector, - '_execute', lambda *args, **kwargs: ("", None)) - self.assertFalse(self.connector.check_valid_device('/d0v')) - - def test_check_valid_device(self): - self.connector = connector.InitiatorConnector(None) - self.stubs.Set(self.connector, - '_execute', lambda *args, **kwargs: ("", "")) - self.assertTrue(self.connector.check_valid_device('/dev')) - - def test_check_valid_device_with_cmd_error(self): - def raise_except(*args, **kwargs): - raise putils.ProcessExecutionError - self.connector = connector.InitiatorConnector(None) - self.stubs.Set(self.connector, - '_execute', raise_except) - self.assertFalse(self.connector.check_valid_device('/dev')) - - -class HostDriverTestCase(test.TestCase): - - def setUp(self): - super(HostDriverTestCase, self).setUp() - self.stubs.Set(os.path, 'isdir', lambda x: True) - self.devlist = ['device1', 'device2'] - self.stubs.Set(os, 'listdir', lambda x: self.devlist) - - def test_host_driver(self): - expected = ['/dev/disk/by-path/' + dev for dev in self.devlist] - driver = host_driver.HostDriver() - actual = driver.get_all_block_devices() - self.assertEqual(expected, actual) - - -class ISCSIConnectorTestCase(ConnectorTestCase): - - def setUp(self): - super(ISCSIConnectorTestCase, self).setUp() - self.connector = connector.ISCSIConnector( - None, execute=self.fake_execute, use_multipath=False) - self.connector_with_multipath = connector.ISCSIConnector( - None, execute=self.fake_execute, use_multipath=True) - self.stubs.Set(self.connector._linuxscsi, - 'get_name_from_path', lambda x: "/dev/sdb") - - def iscsi_connection(self, volume, location, iqn): - return { - 'driver_volume_type': 'iscsi', - 'data': { - 'volume_id': volume['id'], - 'target_portal': location, - 'target_iqn': iqn, - 'target_lun': 1, - } - } - - def iscsi_connection_multipath(self, volume, locations, iqns, luns): - return { - 'driver_volume_type': 'iscsi', - 'data': { - 'volume_id': volume['id'], - 'target_portals': locations, - 'target_iqns': iqns, - 'target_luns': luns, - } - } - - def test_get_initiator(self): - def initiator_no_file(*args, **kwargs): - raise putils.ProcessExecutionError('No file') - - def initiator_get_text(*arg, **kwargs): - text = ('## DO NOT EDIT OR REMOVE THIS FILE!\n' - '## If you remove this file, the iSCSI daemon ' - 'will not start.\n' - '## If you change the InitiatorName, existing ' - 'access control lists\n' - '## may reject this initiator. The InitiatorName must ' - 'be unique\n' - '## for each iSCSI initiator. Do NOT duplicate iSCSI ' - 'InitiatorNames.\n' - 'InitiatorName=iqn.1234-56.foo.bar:01:23456789abc') - return text, None - - self.stubs.Set(self.connector, '_execute', initiator_no_file) - initiator = self.connector.get_initiator() - self.assertIsNone(initiator) - self.stubs.Set(self.connector, '_execute', initiator_get_text) - initiator = self.connector.get_initiator() - self.assertEqual(initiator, 'iqn.1234-56.foo.bar:01:23456789abc') - - def _test_connect_volume(self, extra_props, additional_commands): - self.stubs.Set(os.path, 'exists', lambda x: True) - location = '10.0.2.15:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - vol = {'id': 1, 'name': name} - connection_info = self.iscsi_connection(vol, location, iqn) - for key, value in extra_props.iteritems(): - connection_info['data'][key] = value - device = self.connector.connect_volume(connection_info['data']) - dev_str = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location, iqn) - self.assertEqual(device['type'], 'block') - self.assertEqual(device['path'], dev_str) - - self.connector.disconnect_volume(connection_info['data'], device) - expected_commands = [('iscsiadm -m node -T %s -p %s' % - (iqn, location)), - ('iscsiadm -m session'), - ('iscsiadm -m node -T %s -p %s --login' % - (iqn, location)), - ('iscsiadm -m node -T %s -p %s --op update' - ' -n node.startup -v automatic' - % (iqn, location)), - ('iscsiadm -m node --rescan'), - ('iscsiadm -m session --rescan'), - ('blockdev --flushbufs /dev/sdb'), - ('tee -a /sys/block/sdb/device/delete'), - ('iscsiadm -m node -T %s -p %s --op update' - ' -n node.startup -v manual' % (iqn, location)), - ('iscsiadm -m node -T %s -p %s --logout' % - (iqn, location)), - ('iscsiadm -m node -T %s -p %s --op delete' % - (iqn, location)), ] + additional_commands - LOG.debug("self.cmds = %s" % self.cmds) - LOG.debug("expected = %s" % expected_commands) - - self.assertEqual(expected_commands, self.cmds) - - @test.testtools.skipUnless(os.path.exists('/dev/disk/by-path'), - 'Test requires /dev/disk/by-path') - @mock.patch.object(linuxscsi.LinuxSCSI, 'wait_for_volume_removal', - return_value=None) - def test_connect_volume(self, mock_wait_for_vol_rem): - self._test_connect_volume({}, []) - - @test.testtools.skipUnless(os.path.exists('/dev/disk/by-path'), - 'Test requires /dev/disk/by-path') - @mock.patch.object(linuxscsi.LinuxSCSI, 'wait_for_volume_removal', - return_value=None) - def test_connect_volume_with_alternative_targets(self, - mock_wait_for_vol_rem): - location = '10.0.2.15:3260' - location2 = '10.0.3.15:3260' - iqn = 'iqn.2010-10.org.openstack:volume-00000001' - iqn2 = 'iqn.2010-10.org.openstack:volume-00000001-2' - extra_props = {'target_portals': [location, location2], - 'target_iqns': [iqn, iqn2], - 'target_luns': [1, 2]} - additional_commands = [('blockdev --flushbufs /dev/sdb'), - ('tee -a /sys/block/sdb/device/delete'), - ('iscsiadm -m node -T %s -p %s --op update' - ' -n node.startup -v manual' % - (iqn2, location2)), - ('iscsiadm -m node -T %s -p %s --logout' % - (iqn2, location2)), - ('iscsiadm -m node -T %s -p %s --op delete' % - (iqn2, location2))] - self._test_connect_volume(extra_props, additional_commands) - - @test.testtools.skipUnless(os.path.exists('/dev/disk/by-path'), - 'Test requires /dev/disk/by-path') - @mock.patch.object(os.path, 'exists') - @mock.patch.object(connector.ISCSIConnector, '_run_iscsiadm') - @mock.patch.object(linuxscsi.LinuxSCSI, 'wait_for_volume_removal', - return_value=None) - def test_connect_volume_with_alternative_targets_primary_error( - self, mock_wait_for_vol_rem, mock_iscsiadm, mock_exists): - location = '10.0.2.15:3260' - location2 = '10.0.3.15:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - iqn2 = 'iqn.2010-10.org.openstack:%s-2' % name - vol = {'id': 1, 'name': name} - connection_info = self.iscsi_connection(vol, location, iqn) - connection_info['data']['target_portals'] = [location, location2] - connection_info['data']['target_iqns'] = [iqn, iqn2] - connection_info['data']['target_luns'] = [1, 2] - dev_str2 = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2) - - def fake_run_iscsiadm(iscsi_properties, iscsi_command, **kwargs): - if iscsi_properties['target_portal'] == location: - if iscsi_command == ('--login',): - raise putils.ProcessExecutionError(None, None, 21) - return mock.DEFAULT - - mock_iscsiadm.side_effect = fake_run_iscsiadm - mock_exists.side_effect = lambda x: x == dev_str2 - device = self.connector.connect_volume(connection_info['data']) - self.assertEqual('block', device['type']) - self.assertEqual(dev_str2, device['path']) - props = connection_info['data'].copy() - for key in ('target_portals', 'target_iqns', 'target_luns'): - props.pop(key, None) - props['target_portal'] = location2 - props['target_iqn'] = iqn2 - props['target_lun'] = 2 - mock_iscsiadm.assert_any_call(props, ('--login',), - check_exit_code=[0, 255]) - - mock_iscsiadm.reset_mock() - self.connector.disconnect_volume(connection_info['data'], device) - props = connection_info['data'].copy() - for key in ('target_portals', 'target_iqns', 'target_luns'): - props.pop(key, None) - mock_iscsiadm.assert_any_call(props, ('--logout',), - check_exit_code=[0, 21, 255]) - props['target_portal'] = location2 - props['target_iqn'] = iqn2 - props['target_lun'] = 2 - mock_iscsiadm.assert_any_call(props, ('--logout',), - check_exit_code=[0, 21, 255]) - - def test_connect_volume_with_multipath(self): - location = '10.0.2.15:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - vol = {'id': 1, 'name': name} - connection_properties = self.iscsi_connection(vol, location, iqn) - - self.stubs.Set(self.connector_with_multipath, - '_run_iscsiadm_bare', - lambda *args, **kwargs: "%s %s" % (location, iqn)) - self.stubs.Set(self.connector_with_multipath, - '_get_target_portals_from_iscsiadm_output', - lambda x: [[location, iqn]]) - self.stubs.Set(self.connector_with_multipath, - '_connect_to_iscsi_portal', - lambda x: None) - self.stubs.Set(self.connector_with_multipath, - '_rescan_iscsi', - lambda: None) - self.stubs.Set(self.connector_with_multipath, - '_rescan_multipath', - lambda: None) - self.stubs.Set(self.connector_with_multipath, - '_get_multipath_device_name', - lambda x: 'iqn.2010-10.org.openstack:%s' % name) - self.stubs.Set(os.path, 'exists', lambda x: True) - result = self.connector_with_multipath.connect_volume( - connection_properties['data']) - expected_result = {'path': 'iqn.2010-10.org.openstack:volume-00000001', - 'type': 'block'} - self.assertEqual(result, expected_result) - - @mock.patch.object(os.path, 'exists', return_value=True) - @mock.patch.object(host_driver.HostDriver, 'get_all_block_devices') - @mock.patch.object(connector.ISCSIConnector, '_rescan_multipath') - @mock.patch.object(connector.ISCSIConnector, '_run_multipath') - @mock.patch.object(connector.ISCSIConnector, '_get_multipath_device_name') - @mock.patch.object(connector.ISCSIConnector, '_get_multipath_iqn') - def test_connect_volume_with_multiple_portals( - self, mock_get_iqn, mock_device_name, mock_run_multipath, - mock_rescan_multipath, mock_devices, mock_exists): - location1 = '10.0.2.15:3260' - location2 = '10.0.3.15:3260' - name1 = 'volume-00000001-1' - name2 = 'volume-00000001-2' - iqn1 = 'iqn.2010-10.org.openstack:%s' % name1 - iqn2 = 'iqn.2010-10.org.openstack:%s' % name2 - fake_multipath_dev = '/dev/mapper/fake-multipath-dev' - vol = {'id': 1, 'name': name1} - connection_properties = self.iscsi_connection_multipath( - vol, [location1, location2], [iqn1, iqn2], [1, 2]) - devs = ['/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location1, iqn1), - '/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2)] - mock_devices.return_value = devs - mock_device_name.return_value = fake_multipath_dev - mock_get_iqn.return_value = [iqn1, iqn2] - - result = self.connector_with_multipath.connect_volume( - connection_properties['data']) - expected_result = {'path': fake_multipath_dev, 'type': 'block'} - cmd_format = 'iscsiadm -m node -T %s -p %s --%s' - expected_commands = [cmd_format % (iqn1, location1, 'login'), - cmd_format % (iqn2, location2, 'login')] - self.assertEqual(expected_result, result) - for command in expected_commands: - self.assertIn(command, self.cmds) - mock_device_name.assert_called_once_with(devs[0]) - - self.cmds = [] - self.connector_with_multipath.disconnect_volume( - connection_properties['data'], result) - expected_commands = [cmd_format % (iqn1, location1, 'logout'), - cmd_format % (iqn2, location2, 'logout')] - for command in expected_commands: - self.assertIn(command, self.cmds) - - @mock.patch.object(os.path, 'exists') - @mock.patch.object(host_driver.HostDriver, 'get_all_block_devices') - @mock.patch.object(connector.ISCSIConnector, '_rescan_multipath') - @mock.patch.object(connector.ISCSIConnector, '_run_multipath') - @mock.patch.object(connector.ISCSIConnector, '_get_multipath_device_name') - @mock.patch.object(connector.ISCSIConnector, '_get_multipath_iqn') - @mock.patch.object(connector.ISCSIConnector, '_run_iscsiadm') - def test_connect_volume_with_multiple_portals_primary_error( - self, mock_iscsiadm, mock_get_iqn, mock_device_name, - mock_run_multipath, mock_rescan_multipath, mock_devices, - mock_exists): - location1 = '10.0.2.15:3260' - location2 = '10.0.3.15:3260' - name1 = 'volume-00000001-1' - name2 = 'volume-00000001-2' - iqn1 = 'iqn.2010-10.org.openstack:%s' % name1 - iqn2 = 'iqn.2010-10.org.openstack:%s' % name2 - fake_multipath_dev = '/dev/mapper/fake-multipath-dev' - vol = {'id': 1, 'name': name1} - connection_properties = self.iscsi_connection_multipath( - vol, [location1, location2], [iqn1, iqn2], [1, 2]) - dev1 = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location1, iqn1) - dev2 = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2) - - def fake_run_iscsiadm(iscsi_properties, iscsi_command, **kwargs): - if iscsi_properties['target_portal'] == location1: - if iscsi_command == ('--login',): - raise putils.ProcessExecutionError(None, None, 21) - return mock.DEFAULT - - mock_exists.side_effect = lambda x: x != dev1 - mock_devices.return_value = [dev2] - mock_device_name.return_value = fake_multipath_dev - mock_get_iqn.return_value = [iqn2] - mock_iscsiadm.side_effect = fake_run_iscsiadm - - props = connection_properties['data'].copy() - result = self.connector_with_multipath.connect_volume( - connection_properties['data']) - - expected_result = {'path': fake_multipath_dev, 'type': 'block'} - self.assertEqual(expected_result, result) - mock_device_name.assert_called_once_with(dev2) - props['target_portal'] = location1 - props['target_iqn'] = iqn1 - mock_iscsiadm.assert_any_call(props, ('--login',), - check_exit_code=[0, 255]) - props['target_portal'] = location2 - props['target_iqn'] = iqn2 - mock_iscsiadm.assert_any_call(props, ('--login',), - check_exit_code=[0, 255]) - - mock_iscsiadm.reset_mock() - self.connector_with_multipath.disconnect_volume( - connection_properties['data'], result) - - props = connection_properties['data'].copy() - props['target_portal'] = location1 - props['target_iqn'] = iqn1 - mock_iscsiadm.assert_any_call(props, ('--logout',), - check_exit_code=[0, 21, 255]) - props['target_portal'] = location2 - props['target_iqn'] = iqn2 - mock_iscsiadm.assert_any_call(props, ('--logout',), - check_exit_code=[0, 21, 255]) - - def test_connect_volume_with_not_found_device(self): - self.stubs.Set(os.path, 'exists', lambda x: False) - self.stubs.Set(time, 'sleep', lambda x: None) - location = '10.0.2.15:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - vol = {'id': 1, 'name': name} - connection_info = self.iscsi_connection(vol, location, iqn) - self.assertRaises(exception.VolumeDeviceNotFound, - self.connector.connect_volume, - connection_info['data']) - - def test_get_target_portals_from_iscsiadm_output(self): - connector = self.connector - test_output = '''10.15.84.19:3260 iqn.1992-08.com.netapp:sn.33615311 - 10.15.85.19:3260 iqn.1992-08.com.netapp:sn.33615311''' - res = connector._get_target_portals_from_iscsiadm_output(test_output) - ip_iqn1 = ['10.15.84.19:3260', 'iqn.1992-08.com.netapp:sn.33615311'] - ip_iqn2 = ['10.15.85.19:3260', 'iqn.1992-08.com.netapp:sn.33615311'] - expected = [ip_iqn1, ip_iqn2] - self.assertEqual(expected, res) - - def test_get_multipath_device_name(self): - self.stubs.Set(os.path, 'realpath', lambda x: None) - multipath_return_string = [('mpath2 (20017380006c00036)' - 'dm-7 IBM,2810XIV')] - self.stubs.Set(self.connector, '_run_multipath', - lambda *args, **kwargs: multipath_return_string) - expected = '/dev/mapper/mpath2' - self.assertEqual(expected, - self.connector. - _get_multipath_device_name('/dev/md-1')) - - @mock.patch.object(os.path, 'realpath', return_value='/dev/sda') - @mock.patch.object(connector.ISCSIConnector, '_run_multipath') - def test_get_multipath_device_name_with_error(self, mock_multipath, - mock_realpath): - mock_multipath.return_value = [ - "Mar 17 14:32:37 | sda: No fc_host device for 'host-1'\n" - "mpathb (36e00000000010001) dm-4 IET ,VIRTUAL-DISK\n" - "size=1.0G features='0' hwhandler='0' wp=rw\n" - "|-+- policy='service-time 0' prio=0 status=active\n" - "| `- 2:0:0:1 sda 8:0 active undef running\n" - "`-+- policy='service-time 0' prio=0 status=enabled\n" - " `- 3:0:0:1 sdb 8:16 active undef running\n"] - expected = '/dev/mapper/mpathb' - self.assertEqual(expected, - self.connector. - _get_multipath_device_name('/dev/sda')) - - def test_get_iscsi_devices(self): - paths = [('ip-10.0.0.1:3260-iscsi-iqn.2013-01.ro.' - 'com.netapp:node.netapp02-lun-0')] - self.stubs.Set(os, 'walk', lambda x: [(['.'], ['by-path'], paths)]) - self.assertEqual(self.connector._get_iscsi_devices(), paths) - - def test_get_iscsi_devices_with_empty_dir(self): - self.stubs.Set(os, 'walk', lambda x: []) - self.assertEqual(self.connector._get_iscsi_devices(), []) - - def test_get_multipath_iqn(self): - paths = [('ip-10.0.0.1:3260-iscsi-iqn.2013-01.ro.' - 'com.netapp:node.netapp02-lun-0')] - self.stubs.Set(os.path, 'realpath', - lambda x: '/dev/disk/by-path/%s' % paths[0]) - self.stubs.Set(self.connector, '_get_iscsi_devices', lambda: paths) - self.stubs.Set(self.connector, '_get_multipath_device_name', - lambda x: paths[0]) - self.assertEqual(self.connector._get_multipath_iqn(paths[0]), - 'iqn.2013-01.ro.com.netapp:node.netapp02') - - def test_disconnect_volume_multipath_iscsi(self): - result = [] - - def fake_disconnect_from_iscsi_portal(properties): - result.append(properties) - iqn1 = 'iqn.2013-01.ro.com.netapp:node.netapp01' - iqn2 = 'iqn.2013-01.ro.com.netapp:node.netapp02' - iqns = [iqn1, iqn2] - portal = '10.0.0.1:3260' - dev = ('ip-%s-iscsi-%s-lun-0' % (portal, iqn1)) - self.stubs.Set(self.connector, - '_get_target_portals_from_iscsiadm_output', - lambda x: [[portal, iqn1]]) - self.stubs.Set(self.connector, '_rescan_iscsi', lambda: None) - self.stubs.Set(self.connector, '_rescan_multipath', lambda: None) - self.stubs.Set(self.connector.driver, 'get_all_block_devices', - lambda: [dev, '/dev/mapper/md-1']) - self.stubs.Set(self.connector, '_get_multipath_device_name', - lambda x: '/dev/mapper/md-3') - self.stubs.Set(self.connector, '_get_multipath_iqn', - lambda x: iqns.pop()) - self.stubs.Set(self.connector, '_disconnect_from_iscsi_portal', - fake_disconnect_from_iscsi_portal) - self.stubs.Set(os.path, 'exists', lambda x: True) - fake_property = {'target_portal': portal, - 'target_iqn': iqn1} - self.connector._disconnect_volume_multipath_iscsi(fake_property, - 'fake/multipath') - # Target in use by other mp devices, don't disconnect - self.assertEqual([], result) - - def test_disconnect_volume_multipath_iscsi_without_other_mp_devices(self): - result = [] - - def fake_disconnect_from_iscsi_portal(properties): - result.append(properties) - portal = '10.0.2.15:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - self.stubs.Set(self.connector, - '_get_target_portals_from_iscsiadm_output', - lambda x: [[portal, iqn]]) - self.stubs.Set(self.connector, '_rescan_iscsi', lambda: None) - self.stubs.Set(self.connector, '_rescan_multipath', lambda: None) - self.stubs.Set(self.connector.driver, 'get_all_block_devices', - lambda: []) - self.stubs.Set(self.connector, '_disconnect_from_iscsi_portal', - fake_disconnect_from_iscsi_portal) - self.stubs.Set(os.path, 'exists', lambda x: True) - fake_property = {'target_portal': portal, - 'target_iqn': iqn} - self.connector._disconnect_volume_multipath_iscsi(fake_property, - 'fake/multipath') - # Target not in use by other mp devices, disconnect - self.assertEqual([fake_property], result) - - def test_disconnect_volume_multipath_iscsi_with_invalid_symlink(self): - result = [] - - def fake_disconnect_from_iscsi_portal(properties): - result.append(properties) - - portal = '10.0.0.1:3260' - name = 'volume-00000001' - iqn = 'iqn.2010-10.org.openstack:%s' % name - dev = ('ip-%s-iscsi-%s-lun-0' % (portal, iqn)) - self.stubs.Set(self.connector, - '_get_target_portals_from_iscsiadm_output', - lambda x: [[portal, iqn]]) - self.stubs.Set(self.connector, '_rescan_iscsi', lambda: None) - self.stubs.Set(self.connector, '_rescan_multipath', lambda: None) - self.stubs.Set(self.connector.driver, 'get_all_block_devices', - lambda: [dev, '/dev/mapper/md-1']) - self.stubs.Set(self.connector, '_disconnect_from_iscsi_portal', - fake_disconnect_from_iscsi_portal) - # Simulate a broken symlink by returning False for os.path.exists(dev) - self.stubs.Set(os.path, 'exists', lambda x: False) - fake_property = {'target_portal': portal, - 'target_iqn': iqn} - self.connector._disconnect_volume_multipath_iscsi(fake_property, - 'fake/multipath') - # Target not in use by other mp devices, disconnect - self.assertEqual([fake_property], result) - - -class FibreChannelConnectorTestCase(ConnectorTestCase): - def setUp(self): - super(FibreChannelConnectorTestCase, self).setUp() - self.connector = connector.FibreChannelConnector( - None, execute=self.fake_execute, use_multipath=False) - self.assertIsNotNone(self.connector) - self.assertIsNotNone(self.connector._linuxfc) - self.assertIsNotNone(self.connector._linuxscsi) - - def fake_get_fc_hbas(self): - return [{'ClassDevice': 'host1', - 'ClassDevicePath': '/sys/devices/pci0000:00/0000:00:03.0' - '/0000:05:00.2/host1/fc_host/host1', - 'dev_loss_tmo': '30', - 'fabric_name': '0x1000000533f55566', - 'issue_lip': '', - 'max_npiv_vports': '255', - 'maxframe_size': '2048 bytes', - 'node_name': '0x200010604b019419', - 'npiv_vports_inuse': '0', - 'port_id': '0x680409', - 'port_name': '0x100010604b019419', - 'port_state': 'Online', - 'port_type': 'NPort (fabric via point-to-point)', - 'speed': '10 Gbit', - 'supported_classes': 'Class 3', - 'supported_speeds': '10 Gbit', - 'symbolic_name': 'Emulex 554M FV4.0.493.0 DV8.3.27', - 'tgtid_bind_type': 'wwpn (World Wide Port Name)', - 'uevent': None, - 'vport_create': '', - 'vport_delete': ''}] - - def fake_get_fc_hbas_info(self): - hbas = self.fake_get_fc_hbas() - info = [{'port_name': hbas[0]['port_name'].replace('0x', ''), - 'node_name': hbas[0]['node_name'].replace('0x', ''), - 'host_device': hbas[0]['ClassDevice'], - 'device_path': hbas[0]['ClassDevicePath']}] - return info - - def fibrechan_connection(self, volume, location, wwn): - return {'driver_volume_type': 'fibrechan', - 'data': { - 'volume_id': volume['id'], - 'target_portal': location, - 'target_wwn': wwn, - 'target_lun': 1, - }} - - def test_connect_volume(self): - self.stubs.Set(self.connector._linuxfc, "get_fc_hbas", - self.fake_get_fc_hbas) - self.stubs.Set(self.connector._linuxfc, "get_fc_hbas_info", - self.fake_get_fc_hbas_info) - self.stubs.Set(os.path, 'exists', lambda x: True) - self.stubs.Set(os.path, 'realpath', lambda x: '/dev/sdb') - - multipath_devname = '/dev/md-1' - devices = {"device": multipath_devname, - "id": "1234567890", - "devices": [{'device': '/dev/sdb', - 'address': '1:0:0:1', - 'host': 1, 'channel': 0, - 'id': 0, 'lun': 1}]} - self.stubs.Set(self.connector._linuxscsi, 'find_multipath_device', - lambda x: devices) - self.stubs.Set(self.connector._linuxscsi, 'remove_scsi_device', - lambda x: None) - self.stubs.Set(self.connector._linuxscsi, 'get_device_info', - lambda x: devices['devices'][0]) - location = '10.0.2.15:3260' - name = 'volume-00000001' - vol = {'id': 1, 'name': name} - # Should work for string, unicode, and list - wwns = ['1234567890123456', six.text_type('1234567890123456'), - ['1234567890123456', '1234567890123457']] - for wwn in wwns: - connection_info = self.fibrechan_connection(vol, location, wwn) - dev_info = self.connector.connect_volume(connection_info['data']) - exp_wwn = wwn[0] if isinstance(wwn, list) else wwn - dev_str = ('/dev/disk/by-path/pci-0000:05:00.2-fc-0x%s-lun-1' % - exp_wwn) - self.assertEqual(dev_info['type'], 'block') - self.assertEqual(dev_info['path'], dev_str) - - self.connector.disconnect_volume(connection_info['data'], dev_info) - expected_commands = [] - self.assertEqual(expected_commands, self.cmds) - - # Should not work for anything other than string, unicode, and list - connection_info = self.fibrechan_connection(vol, location, 123) - self.assertRaises(exception.NoFibreChannelHostsFound, - self.connector.connect_volume, - connection_info['data']) - - self.stubs.Set(self.connector._linuxfc, 'get_fc_hbas', - lambda: []) - self.stubs.Set(self.connector._linuxfc, 'get_fc_hbas_info', - lambda: []) - self.assertRaises(exception.NoFibreChannelHostsFound, - self.connector.connect_volume, - connection_info['data']) - - -class FibreChannelConnectorS390XTestCase(ConnectorTestCase): - - def setUp(self): - super(FibreChannelConnectorS390XTestCase, self).setUp() - self.connector = connector.FibreChannelConnectorS390X( - None, execute=self.fake_execute, use_multipath=False) - self.assertIsNotNone(self.connector) - self.assertIsNotNone(self.connector._linuxfc) - self.assertEqual(self.connector._linuxfc.__class__.__name__, - "LinuxFibreChannelS390X") - self.assertIsNotNone(self.connector._linuxscsi) - - @mock.patch.object(linuxfc.LinuxFibreChannelS390X, 'configure_scsi_device') - def test_get_host_devices(self, mock_configure_scsi_device): - lun = 2 - possible_devs = [(3, 5), ] - devices = self.connector._get_host_devices(possible_devs, lun) - mock_configure_scsi_device.assert_called_with(3, 5, - "0x0002000000000000") - self.assertEqual(1, len(devices)) - device_path = "/dev/disk/by-path/ccw-3-zfcp-5:0x0002000000000000" - self.assertEqual(devices[0], device_path) - - @mock.patch.object(connector.FibreChannelConnectorS390X, - '_get_possible_devices', return_value=[(3, 5), ]) - @mock.patch.object(linuxfc.LinuxFibreChannelS390X, 'get_fc_hbas_info', - return_value=[]) - @mock.patch.object(linuxfc.LinuxFibreChannelS390X, - 'deconfigure_scsi_device') - def test_remove_devices(self, mock_deconfigure_scsi_device, - mock_get_fc_hbas_info, mock_get_possible_devices): - connection_properties = {'target_wwn': 5, 'target_lun': 2} - self.connector._remove_devices(connection_properties, devices=None) - mock_deconfigure_scsi_device.assert_called_with(3, 5, - "0x0002000000000000") - mock_get_fc_hbas_info.assert_called_once_with() - mock_get_possible_devices.assert_called_once_with([], 5) - - -class FakeFixedIntervalLoopingCall(object): - def __init__(self, f=None, *args, **kw): - self.args = args - self.kw = kw - self.f = f - self._stop = False - - def stop(self): - self._stop = True - - def wait(self): - return self - - def start(self, interval, initial_delay=None): - while not self._stop: - try: - self.f(*self.args, **self.kw) - except loopingcall.LoopingCallDone: - return self - except Exception: - LOG.exception(_LE('in fixed duration looping call')) - raise - - -class AoEConnectorTestCase(ConnectorTestCase): - """Test cases for AoE initiator class.""" - def setUp(self): - super(AoEConnectorTestCase, self).setUp() - self.connector = connector.AoEConnector('sudo') - self.connection_properties = {'target_shelf': 'fake_shelf', - 'target_lun': 'fake_lun'} - self.stubs.Set(loopingcall, - 'FixedIntervalLoopingCall', - FakeFixedIntervalLoopingCall) - - def _mock_path_exists(self, aoe_path, mock_values=None): - mock_values = mock_values or [] - self.mox.StubOutWithMock(os.path, 'exists') - for value in mock_values: - os.path.exists(aoe_path).AndReturn(value) - - def test_connect_volume(self): - """Ensure that if path exist aoe-revaliadte was called.""" - aoe_device, aoe_path = self.connector._get_aoe_info( - self.connection_properties) - - self._mock_path_exists(aoe_path, [True, True]) - - self.mox.StubOutWithMock(self.connector, '_execute') - self.connector._execute('aoe-revalidate', - aoe_device, - run_as_root=True, - root_helper='sudo', - check_exit_code=0).AndReturn(("", "")) - self.mox.ReplayAll() - - self.connector.connect_volume(self.connection_properties) - - def test_connect_volume_without_path(self): - """Ensure that if path doesn't exist aoe-discovery was called.""" - - aoe_device, aoe_path = self.connector._get_aoe_info( - self.connection_properties) - expected_info = { - 'type': 'block', - 'device': aoe_device, - 'path': aoe_path, - } - - self._mock_path_exists(aoe_path, [False, True]) - - self.mox.StubOutWithMock(self.connector, '_execute') - self.connector._execute('aoe-discover', - run_as_root=True, - root_helper='sudo', - check_exit_code=0).AndReturn(("", "")) - self.mox.ReplayAll() - - volume_info = self.connector.connect_volume( - self.connection_properties) - - self.assertDictMatch(volume_info, expected_info) - - def test_connect_volume_could_not_discover_path(self): - _aoe_device, aoe_path = self.connector._get_aoe_info( - self.connection_properties) - - number_of_calls = 4 - self._mock_path_exists(aoe_path, [False] * (number_of_calls + 1)) - self.mox.StubOutWithMock(self.connector, '_execute') - - for _i in xrange(number_of_calls): - self.connector._execute('aoe-discover', - run_as_root=True, - root_helper='sudo', - check_exit_code=0).AndReturn(("", "")) - self.mox.ReplayAll() - self.assertRaises(exception.VolumeDeviceNotFound, - self.connector.connect_volume, - self.connection_properties) - - def test_disconnect_volume(self): - """Ensure that if path exist aoe-revaliadte was called.""" - aoe_device, aoe_path = self.connector._get_aoe_info( - self.connection_properties) - - self._mock_path_exists(aoe_path, [True]) - - self.mox.StubOutWithMock(self.connector, '_execute') - self.connector._execute('aoe-flush', - aoe_device, - run_as_root=True, - root_helper='sudo', - check_exit_code=0).AndReturn(("", "")) - self.mox.ReplayAll() - - self.connector.disconnect_volume(self.connection_properties, {}) - - -class RemoteFsConnectorTestCase(ConnectorTestCase): - """Test cases for Remote FS initiator class.""" - TEST_DEV = '172.18.194.100:/var/nfs' - TEST_PATH = '/mnt/test/df0808229363aad55c27da50c38d6328' - - def setUp(self): - super(RemoteFsConnectorTestCase, self).setUp() - self.connection_properties = { - 'export': self.TEST_DEV, - 'name': '9c592d52-ce47-4263-8c21-4ecf3c029cdb'} - self.connector = connector.RemoteFsConnector( - 'nfs', root_helper='sudo', nfs_mount_point_base='/mnt/test', - nfs_mount_options='vers=3') - - def test_connect_volume(self): - """Test the basic connect volume case.""" - client = self.connector._remotefsclient - self.mox.StubOutWithMock(client, '_execute') - client._execute('mount', - check_exit_code=0).AndReturn(("", "")) - client._execute('mkdir', '-p', self.TEST_PATH, - check_exit_code=0).AndReturn(("", "")) - client._execute('mount', '-t', 'nfs', '-o', 'vers=3', - self.TEST_DEV, self.TEST_PATH, - root_helper='sudo', run_as_root=True, - check_exit_code=0).AndReturn(("", "")) - self.mox.ReplayAll() - - self.connector.connect_volume(self.connection_properties) - - def test_disconnect_volume(self): - """Nothing should happen here -- make sure it doesn't blow up.""" - self.connector.disconnect_volume(self.connection_properties, {}) - - -class LocalConnectorTestCase(test.TestCase): - - def setUp(self): - super(LocalConnectorTestCase, self).setUp() - self.connection_properties = {'name': 'foo', - 'device_path': '/tmp/bar'} - - def test_connect_volume(self): - self.connector = connector.LocalConnector(None) - cprops = self.connection_properties - dev_info = self.connector.connect_volume(cprops) - self.assertEqual(dev_info['type'], 'local') - self.assertEqual(dev_info['path'], cprops['device_path']) - - def test_connect_volume_with_invalid_connection_data(self): - self.connector = connector.LocalConnector(None) - cprops = {} - self.assertRaises(ValueError, - self.connector.connect_volume, cprops) - - -class HuaweiStorHyperConnectorTestCase(ConnectorTestCase): - """Test cases for StorHyper initiator class.""" - - attached = False - - def setUp(self): - super(HuaweiStorHyperConnectorTestCase, self).setUp() - self.fake_sdscli_file = tempfile.mktemp() - self.addCleanup(os.remove, self.fake_sdscli_file) - newefile = open(self.fake_sdscli_file, 'w') - newefile.write('test') - newefile.close() - - self.connector = connector.HuaweiStorHyperConnector( - None, execute=self.fake_execute) - self.connector.cli_path = self.fake_sdscli_file - self.connector.iscliexist = True - - self.connector_fail = connector.HuaweiStorHyperConnector( - None, execute=self.fake_execute_fail) - self.connector_fail.cli_path = self.fake_sdscli_file - self.connector_fail.iscliexist = True - - self.connector_nocli = connector.HuaweiStorHyperConnector( - None, execute=self.fake_execute_fail) - self.connector_nocli.cli_path = self.fake_sdscli_file - self.connector_nocli.iscliexist = False - - self.connection_properties = { - 'access_mode': 'rw', - 'qos_specs': None, - 'volume_id': 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f' - } - - self.device_info = {'type': 'block', - 'path': '/dev/vdxxx'} - HuaweiStorHyperConnectorTestCase.attached = False - - def fake_execute(self, *cmd, **kwargs): - method = cmd[2] - self.cmds.append(string.join(cmd)) - if 'attach' == method: - HuaweiStorHyperConnectorTestCase.attached = True - return 'ret_code=0', None - if 'querydev' == method: - if HuaweiStorHyperConnectorTestCase.attached: - return 'ret_code=0\ndev_addr=/dev/vdxxx', None - else: - return 'ret_code=1\ndev_addr=/dev/vdxxx', None - if 'detach' == method: - HuaweiStorHyperConnectorTestCase.attached = False - return 'ret_code=0', None - - def fake_execute_fail(self, *cmd, **kwargs): - method = cmd[2] - self.cmds.append(string.join(cmd)) - if 'attach' == method: - HuaweiStorHyperConnectorTestCase.attached = False - return 'ret_code=330151401', None - if 'querydev' == method: - if HuaweiStorHyperConnectorTestCase.attached: - return 'ret_code=0\ndev_addr=/dev/vdxxx', None - else: - return 'ret_code=1\ndev_addr=/dev/vdxxx', None - if 'detach' == method: - HuaweiStorHyperConnectorTestCase.attached = True - return 'ret_code=330155007', None - - def test_connect_volume(self): - """Test the basic connect volume case.""" - - retval = self.connector.connect_volume(self.connection_properties) - self.assertEqual(self.device_info, retval) - - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) - - self.assertEqual(expected_commands, self.cmds) - - def test_disconnect_volume(self): - """Test the basic disconnect volume case.""" - self.connector.connect_volume(self.connection_properties) - self.assertTrue(HuaweiStorHyperConnectorTestCase.attached) - self.connector.disconnect_volume(self.connection_properties, - self.device_info) - self.assertFalse(HuaweiStorHyperConnectorTestCase.attached) - - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c detach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) - - self.assertEqual(expected_commands, self.cmds) - - def test_is_volume_connected(self): - """Test if volume connected to host case.""" - self.connector.connect_volume(self.connection_properties) - self.assertTrue(HuaweiStorHyperConnectorTestCase.attached) - is_connected = self.connector.is_volume_connected( - 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f') - self.assertEqual(HuaweiStorHyperConnectorTestCase.attached, - is_connected) - self.connector.disconnect_volume(self.connection_properties, - self.device_info) - self.assertFalse(HuaweiStorHyperConnectorTestCase.attached) - is_connected = self.connector.is_volume_connected( - 'volume-b2911673-863c-4380-a5f2-e1729eecfe3f') - self.assertEqual(HuaweiStorHyperConnectorTestCase.attached, - is_connected) - - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c detach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) - - self.assertEqual(expected_commands, self.cmds) - - def test__analyze_output(self): - cliout = 'ret_code=0\ndev_addr=/dev/vdxxx\nret_desc="success"' - analyze_result = {'dev_addr': '/dev/vdxxx', - 'ret_desc': '"success"', - 'ret_code': '0'} - result = self.connector._analyze_output(cliout) - self.assertEqual(analyze_result, result) - - def test_connect_volume_fail(self): - """Test the fail connect volume case.""" - self.assertRaises(exception.BrickException, - self.connector_fail.connect_volume, - self.connection_properties) - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) - self.assertEqual(expected_commands, self.cmds) - - def test_disconnect_volume_fail(self): - """Test the fail disconnect volume case.""" - self.connector.connect_volume(self.connection_properties) - self.assertTrue(HuaweiStorHyperConnectorTestCase.attached) - self.assertRaises(exception.BrickException, - self.connector_fail.disconnect_volume, - self.connection_properties, - self.device_info) - - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c detach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) - - self.assertEqual(expected_commands, self.cmds) - - def test_connect_volume_nocli(self): - """Test the fail connect volume case.""" - self.assertRaises(exception.BrickException, - self.connector_nocli.connect_volume, - self.connection_properties) - - def test_disconnect_volume_nocli(self): - """Test the fail disconnect volume case.""" - self.connector.connect_volume(self.connection_properties) - self.assertTrue(HuaweiStorHyperConnectorTestCase.attached) - self.assertRaises(exception.BrickException, - self.connector_nocli.disconnect_volume, - self.connection_properties, - self.device_info) - expected_commands = [self.fake_sdscli_file + ' -c attach' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f', - self.fake_sdscli_file + ' -c querydev' - ' -v volume-b2911673-863c-4380-a5f2-e1729eecfe3f'] - - LOG.debug("self.cmds = %s." % self.cmds) - LOG.debug("expected = %s." % expected_commands) diff --git a/cinder/tests/unit/brick/test_brick_exception.py b/cinder/tests/unit/brick/test_brick_exception.py deleted file mode 100644 index 9799b80467b..00000000000 --- a/cinder/tests/unit/brick/test_brick_exception.py +++ /dev/null @@ -1,66 +0,0 @@ - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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 cinder.brick import exception -from cinder import test - -import six - - -class BrickExceptionTestCase(test.TestCase): - def test_default_error_msg(self): - class FakeBrickException(exception.BrickException): - message = "default message" - - exc = FakeBrickException() - self.assertEqual(six.text_type(exc), 'default message') - - def test_error_msg(self): - self.assertEqual(six.text_type(exception.BrickException('test')), - 'test') - - def test_default_error_msg_with_kwargs(self): - class FakeBrickException(exception.BrickException): - message = "default message: %(code)s" - - exc = FakeBrickException(code=500) - self.assertEqual(six.text_type(exc), 'default message: 500') - - def test_error_msg_exception_with_kwargs(self): - # NOTE(dprince): disable format errors for this test - self.flags(fatal_exception_format_errors=False) - - class FakeBrickException(exception.BrickException): - message = "default message: %(mispelled_code)s" - - exc = FakeBrickException(code=500) - self.assertEqual(six.text_type(exc), - 'default message: %(mispelled_code)s') - - def test_default_error_code(self): - class FakeBrickException(exception.BrickException): - code = 404 - - exc = FakeBrickException() - self.assertEqual(exc.kwargs['code'], 404) - - def test_error_code_from_kwarg(self): - class FakeBrickException(exception.BrickException): - code = 500 - - exc = FakeBrickException(code=404) - self.assertEqual(exc.kwargs['code'], 404) diff --git a/cinder/tests/unit/brick/test_brick_linuxfc.py b/cinder/tests/unit/brick/test_brick_linuxfc.py deleted file mode 100644 index 939f24b2f78..00000000000 --- a/cinder/tests/unit/brick/test_brick_linuxfc.py +++ /dev/null @@ -1,245 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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 os.path -import string - -from oslo_log import log as logging - -from cinder.brick.initiator import linuxfc -from cinder import test - -LOG = logging.getLogger(__name__) - - -class LinuxFCTestCase(test.TestCase): - - def setUp(self): - super(LinuxFCTestCase, self).setUp() - self.cmds = [] - self.stubs.Set(os.path, 'exists', lambda x: True) - self.lfc = linuxfc.LinuxFibreChannel(None, execute=self.fake_execute) - - def fake_execute(self, *cmd, **kwargs): - self.cmds.append(string.join(cmd)) - return "", None - - def test_rescan_hosts(self): - hbas = [{'host_device': 'foo'}, - {'host_device': 'bar'}, ] - self.lfc.rescan_hosts(hbas) - expected_commands = ['tee -a /sys/class/scsi_host/foo/scan', - 'tee -a /sys/class/scsi_host/bar/scan'] - self.assertEqual(expected_commands, self.cmds) - - def test_get_fc_hbas_fail(self): - def fake_exec1(a, b, c, d, run_as_root=True, root_helper='sudo'): - raise OSError - - def fake_exec2(a, b, c, d, run_as_root=True, root_helper='sudo'): - return None, 'None found' - - self.stubs.Set(self.lfc, "_execute", fake_exec1) - hbas = self.lfc.get_fc_hbas() - self.assertEqual(0, len(hbas)) - self.stubs.Set(self.lfc, "_execute", fake_exec2) - hbas = self.lfc.get_fc_hbas() - self.assertEqual(0, len(hbas)) - - def test_get_fc_hbas(self): - def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'): - return SYSTOOL_FC, None - self.stubs.Set(self.lfc, "_execute", fake_exec) - hbas = self.lfc.get_fc_hbas() - self.assertEqual(2, len(hbas)) - hba1 = hbas[0] - self.assertEqual(hba1["ClassDevice"], "host0") - hba2 = hbas[1] - self.assertEqual(hba2["ClassDevice"], "host2") - - def test_get_fc_hbas_info(self): - def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'): - return SYSTOOL_FC, None - self.stubs.Set(self.lfc, "_execute", fake_exec) - hbas_info = self.lfc.get_fc_hbas_info() - expected_info = [{'device_path': '/sys/devices/pci0000:20/' - '0000:20:03.0/0000:21:00.0/' - 'host0/fc_host/host0', - 'host_device': 'host0', - 'node_name': '50014380242b9751', - 'port_name': '50014380242b9750'}, - {'device_path': '/sys/devices/pci0000:20/' - '0000:20:03.0/0000:21:00.1/' - 'host2/fc_host/host2', - 'host_device': 'host2', - 'node_name': '50014380242b9753', - 'port_name': '50014380242b9752'}, ] - self.assertEqual(expected_info, hbas_info) - - def test_get_fc_wwpns(self): - def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'): - return SYSTOOL_FC, None - self.stubs.Set(self.lfc, "_execute", fake_exec) - wwpns = self.lfc.get_fc_wwpns() - expected_wwpns = ['50014380242b9750', '50014380242b9752'] - self.assertEqual(expected_wwpns, wwpns) - - def test_get_fc_wwnns(self): - def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'): - return SYSTOOL_FC, None - self.stubs.Set(self.lfc, "_execute", fake_exec) - wwnns = self.lfc.get_fc_wwpns() - expected_wwnns = ['50014380242b9750', '50014380242b9752'] - self.assertEqual(expected_wwnns, wwnns) - -SYSTOOL_FC = """ -Class = "fc_host" - - Class Device = "host0" - Class Device path = "/sys/devices/pci0000:20/0000:20:03.0/\ -0000:21:00.0/host0/fc_host/host0" - dev_loss_tmo = "16" - fabric_name = "0x100000051ea338b9" - issue_lip = - max_npiv_vports = "0" - node_name = "0x50014380242b9751" - npiv_vports_inuse = "0" - port_id = "0x960d0d" - port_name = "0x50014380242b9750" - port_state = "Online" - port_type = "NPort (fabric via point-to-point)" - speed = "8 Gbit" - supported_classes = "Class 3" - supported_speeds = "1 Gbit, 2 Gbit, 4 Gbit, 8 Gbit" - symbolic_name = "QMH2572 FW:v4.04.04 DVR:v8.03.07.12-k" - system_hostname = "" - tgtid_bind_type = "wwpn (World Wide Port Name)" - uevent = - vport_create = - vport_delete = - - Device = "host0" - Device path = "/sys/devices/pci0000:20/0000:20:03.0/0000:21:00.0/host0" - edc = - optrom_ctl = - reset = - uevent = "DEVTYPE=scsi_host" - - - Class Device = "host2" - Class Device path = "/sys/devices/pci0000:20/0000:20:03.0/\ -0000:21:00.1/host2/fc_host/host2" - dev_loss_tmo = "16" - fabric_name = "0x100000051ea33b79" - issue_lip = - max_npiv_vports = "0" - node_name = "0x50014380242b9753" - npiv_vports_inuse = "0" - port_id = "0x970e09" - port_name = "0x50014380242b9752" - port_state = "Online" - port_type = "NPort (fabric via point-to-point)" - speed = "8 Gbit" - supported_classes = "Class 3" - supported_speeds = "1 Gbit, 2 Gbit, 4 Gbit, 8 Gbit" - symbolic_name = "QMH2572 FW:v4.04.04 DVR:v8.03.07.12-k" - system_hostname = "" - tgtid_bind_type = "wwpn (World Wide Port Name)" - uevent = - vport_create = - vport_delete = - - Device = "host2" - Device path = "/sys/devices/pci0000:20/0000:20:03.0/0000:21:00.1/host2" - edc = - optrom_ctl = - reset = - uevent = "DEVTYPE=scsi_host" - - -""" - - -class LinuxFCS390XTestCase(LinuxFCTestCase): - - def setUp(self): - super(LinuxFCS390XTestCase, self).setUp() - self.cmds = [] - self.stubs.Set(os.path, 'exists', lambda x: True) - self.lfc = linuxfc.LinuxFibreChannelS390X(None, - execute=self.fake_execute) - - def test_get_fc_hbas_info(self): - def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'): - return SYSTOOL_FC_S390X, None - self.stubs.Set(self.lfc, "_execute", fake_exec) - hbas_info = self.lfc.get_fc_hbas_info() - expected = [{'device_path': '/sys/devices/css0/0.0.02ea/' - '0.0.3080/host0/fc_host/host0', - 'host_device': 'host0', - 'node_name': '1234567898765432', - 'port_name': 'c05076ffe680a960'}] - self.assertEqual(expected, hbas_info) - - def test_configure_scsi_device(self): - device_number = "0.0.2319" - target_wwn = "0x50014380242b9751" - lun = 1 - self.lfc.configure_scsi_device(device_number, target_wwn, lun) - expected_commands = [('tee -a /sys/bus/ccw/drivers/zfcp/' - '0.0.2319/0x50014380242b9751/unit_add')] - self.assertEqual(expected_commands, self.cmds) - - def test_deconfigure_scsi_device(self): - device_number = "0.0.2319" - target_wwn = "0x50014380242b9751" - lun = 1 - self.lfc.deconfigure_scsi_device(device_number, target_wwn, lun) - expected_commands = [('tee -a /sys/bus/ccw/drivers/zfcp/' - '0.0.2319/0x50014380242b9751/unit_remove')] - self.assertEqual(expected_commands, self.cmds) - -SYSTOOL_FC_S390X = """ -Class = "fc_host" - - Class Device = "host0" - Class Device path = "/sys/devices/css0/0.0.02ea/0.0.3080/host0/fc_host/host0" - active_fc4s = "0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 \ - 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 \ - 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 " - dev_loss_tmo = "60" - maxframe_size = "2112 bytes" - node_name = "0x1234567898765432" - permanent_port_name = "0xc05076ffe6803081" - port_id = "0x010014" - port_name = "0xc05076ffe680a960" - port_state = "Online" - port_type = "NPIV VPORT" - serial_number = "IBM00000000000P30" - speed = "8 Gbit" - supported_classes = "Class 2, Class 3" - supported_fc4s = "0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 \ - 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 \ - 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 " - supported_speeds = "2 Gbit, 4 Gbit" - symbolic_name = "IBM 2827 00000000000P30 \ - PCHID: 0308 NPIV UlpId: 01EA0A00 DEVNO: 0.0.1234 NAME: dummy" - tgtid_bind_type = "wwpn (World Wide Port Name)" - uevent = - - Device = "host0" - Device path = "/sys/devices/css0/0.0.02ea/0.0.3080/host0" - uevent = "DEVTYPE=scsi_host" - -""" diff --git a/cinder/tests/unit/brick/test_brick_linuxscsi.py b/cinder/tests/unit/brick/test_brick_linuxscsi.py deleted file mode 100644 index 4225799aef9..00000000000 --- a/cinder/tests/unit/brick/test_brick_linuxscsi.py +++ /dev/null @@ -1,258 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# -# 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 os -import os.path -import string - -import mock -from oslo_log import log as logging - -from cinder.brick import exception -from cinder.brick.initiator import linuxscsi -from cinder import test -from cinder.tests.unit import utils - -LOG = logging.getLogger(__name__) - - -class LinuxSCSITestCase(test.TestCase): - def setUp(self): - super(LinuxSCSITestCase, self).setUp() - self.cmds = [] - self.stubs.Set(os.path, 'realpath', lambda x: '/dev/sdc') - self.linuxscsi = linuxscsi.LinuxSCSI(None, execute=self.fake_execute) - self.fake_stat_result = os.stat(__file__) - - def fake_execute(self, *cmd, **kwargs): - self.cmds.append(string.join(cmd)) - return "", None - - def fake_stat(self, path): - return self.fake_stat_result - - def test_echo_scsi_command(self): - self.linuxscsi.echo_scsi_command("/some/path", "1") - expected_commands = ['tee -a /some/path'] - self.assertEqual(expected_commands, self.cmds) - - def test_get_name_from_path(self): - device_name = "/dev/sdc" - self.stubs.Set(os.path, 'realpath', lambda x: device_name) - disk_path = ("/dev/disk/by-path/ip-10.10.220.253:3260-" - "iscsi-iqn.2000-05.com.3pardata:21810002ac00383d-lun-0") - name = self.linuxscsi.get_name_from_path(disk_path) - self.assertEqual(name, device_name) - self.stubs.Set(os.path, 'realpath', lambda x: "bogus") - name = self.linuxscsi.get_name_from_path(disk_path) - self.assertIsNone(name) - - def test_remove_scsi_device(self): - self.stubs.Set(os.path, "exists", lambda x: False) - self.linuxscsi.remove_scsi_device("/dev/sdc") - expected_commands = [] - self.assertEqual(expected_commands, self.cmds) - self.stubs.Set(os.path, "exists", lambda x: True) - self.linuxscsi.remove_scsi_device("/dev/sdc") - expected_commands = [ - ('blockdev --flushbufs /dev/sdc'), - ('tee -a /sys/block/sdc/device/delete')] - self.assertEqual(expected_commands, self.cmds) - - @mock.patch('cinder.openstack.common.loopingcall.FixedIntervalLoopingCall', - new=utils.ZeroIntervalLoopingCall) - def test_wait_for_volume_removal(self): - fake_path = '/dev/disk/by-path/fake-iscsi-iqn-lun-0' - self.stubs.Set(os.path, "exists", lambda x: True) - self.assertRaises(exception.VolumePathNotRemoved, - self.linuxscsi.wait_for_volume_removal, - fake_path) - - self.stubs.Set(os.path, "exists", lambda x: False) - self.linuxscsi.wait_for_volume_removal(fake_path) - expected_commands = [] - self.assertEqual(expected_commands, self.cmds) - - def test_flush_multipath_device(self): - self.linuxscsi.flush_multipath_device('/dev/dm-9') - expected_commands = [('multipath -f /dev/dm-9')] - self.assertEqual(expected_commands, self.cmds) - - def test_flush_multipath_devices(self): - self.linuxscsi.flush_multipath_devices() - expected_commands = [('multipath -F')] - self.assertEqual(expected_commands, self.cmds) - - def test_remove_multipath_device(self): - def fake_find_multipath_device(device): - devices = [{'device': '/dev/sde', 'host': 0, - 'channel': 0, 'id': 0, 'lun': 1}, - {'device': '/dev/sdf', 'host': 2, - 'channel': 0, 'id': 0, 'lun': 1}, ] - - info = {"device": "dm-3", - "id": "350002ac20398383d", - "devices": devices} - return info - - self.stubs.Set(os.path, "exists", lambda x: True) - self.stubs.Set(self.linuxscsi, 'find_multipath_device', - fake_find_multipath_device) - - self.linuxscsi.remove_multipath_device('/dev/dm-3') - expected_commands = [ - ('blockdev --flushbufs /dev/sde'), - ('tee -a /sys/block/sde/device/delete'), - ('blockdev --flushbufs /dev/sdf'), - ('tee -a /sys/block/sdf/device/delete'), - ('multipath -f 350002ac20398383d'), ] - self.assertEqual(expected_commands, self.cmds) - - def test_find_multipath_device_3par_ufn(self): - def fake_execute(*cmd, **kwargs): - out = ("mpath6 (350002ac20398383d) dm-3 3PARdata,VV\n" - "size=2.0G features='0' hwhandler='0' wp=rw\n" - "`-+- policy='round-robin 0' prio=-1 status=active\n" - " |- 0:0:0:1 sde 8:64 active undef running\n" - " `- 2:0:0:1 sdf 8:80 active undef running\n" - ) - return out, None - - self.stubs.Set(self.linuxscsi, '_execute', fake_execute) - self.stubs.SmartSet(os, 'stat', self.fake_stat) - - info = self.linuxscsi.find_multipath_device('/dev/sde') - LOG.error("info = %s" % info) - - self.assertEqual("350002ac20398383d", info['id']) - self.assertEqual("mpath6", info['name']) - self.assertEqual("/dev/mapper/mpath6", info['device']) - - self.assertEqual("/dev/sde", info['devices'][0]['device']) - self.assertEqual("0", info['devices'][0]['host']) - self.assertEqual("0", info['devices'][0]['id']) - self.assertEqual("0", info['devices'][0]['channel']) - self.assertEqual("1", info['devices'][0]['lun']) - - self.assertEqual("/dev/sdf", info['devices'][1]['device']) - self.assertEqual("2", info['devices'][1]['host']) - self.assertEqual("0", info['devices'][1]['id']) - self.assertEqual("0", info['devices'][1]['channel']) - self.assertEqual("1", info['devices'][1]['lun']) - - def test_find_multipath_device_svc(self): - def fake_execute(*cmd, **kwargs): - out = ("36005076da00638089c000000000004d5 dm-2 IBM,2145\n" - "size=954M features='1 queue_if_no_path' hwhandler='0'" - " wp=rw\n" - "|-+- policy='round-robin 0' prio=-1 status=active\n" - "| |- 6:0:2:0 sde 8:64 active undef running\n" - "| `- 6:0:4:0 sdg 8:96 active undef running\n" - "`-+- policy='round-robin 0' prio=-1 status=enabled\n" - " |- 6:0:3:0 sdf 8:80 active undef running\n" - " `- 6:0:5:0 sdh 8:112 active undef running\n" - ) - return out, None - - self.stubs.Set(self.linuxscsi, '_execute', fake_execute) - self.stubs.SmartSet(os, 'stat', self.fake_stat) - - info = self.linuxscsi.find_multipath_device('/dev/sde') - LOG.error("info = %s" % info) - - self.assertEqual("36005076da00638089c000000000004d5", info["id"]) - self.assertEqual("36005076da00638089c000000000004d5", info["name"]) - self.assertEqual("/dev/mapper/36005076da00638089c000000000004d5", - info["device"]) - - self.assertEqual("/dev/sde", info['devices'][0]['device']) - self.assertEqual("6", info['devices'][0]['host']) - self.assertEqual("0", info['devices'][0]['channel']) - self.assertEqual("2", info['devices'][0]['id']) - self.assertEqual("0", info['devices'][0]['lun']) - - self.assertEqual("/dev/sdf", info['devices'][2]['device']) - self.assertEqual("6", info['devices'][2]['host']) - self.assertEqual("0", info['devices'][2]['channel']) - self.assertEqual("3", info['devices'][2]['id']) - self.assertEqual("0", info['devices'][2]['lun']) - - def test_find_multipath_device_ds8000(self): - def fake_execute(*cmd, **kwargs): - out = ("36005076303ffc48e0000000000000101 dm-2 IBM,2107900\n" - "size=1.0G features='1 queue_if_no_path' hwhandler='0'" - " wp=rw\n" - "`-+- policy='round-robin 0' prio=-1 status=active\n" - " |- 6:0:2:0 sdd 8:64 active undef running\n" - " `- 6:1:0:3 sdc 8:32 active undef running\n" - ) - return out, None - - self.stubs.Set(self.linuxscsi, '_execute', fake_execute) - self.stubs.SmartSet(os, 'stat', self.fake_stat) - - info = self.linuxscsi.find_multipath_device('/dev/sdd') - LOG.error("info = %s" % info) - - self.assertEqual("36005076303ffc48e0000000000000101", info["id"]) - self.assertEqual("36005076303ffc48e0000000000000101", info["name"]) - self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101", - info["device"]) - - self.assertEqual("/dev/sdd", info['devices'][0]['device']) - self.assertEqual("6", info['devices'][0]['host']) - self.assertEqual("0", info['devices'][0]['channel']) - self.assertEqual("2", info['devices'][0]['id']) - self.assertEqual("0", info['devices'][0]['lun']) - - self.assertEqual("/dev/sdc", info['devices'][1]['device']) - self.assertEqual("6", info['devices'][1]['host']) - self.assertEqual("1", info['devices'][1]['channel']) - self.assertEqual("0", info['devices'][1]['id']) - self.assertEqual("3", info['devices'][1]['lun']) - - def test_find_multipath_device_with_error(self): - def fake_execute(*cmd, **kwargs): - out = ("Oct 13 10:24:01 | /lib/udev/scsi_id exitted with 1\n" - "36005076303ffc48e0000000000000101 dm-2 IBM,2107900\n" - "size=1.0G features='1 queue_if_no_path' hwhandler='0'" - " wp=rw\n" - "`-+- policy='round-robin 0' prio=-1 status=active\n" - " |- 6:0:2:0 sdd 8:64 active undef running\n" - " `- 6:1:0:3 sdc 8:32 active undef running\n" - ) - return out, None - - self.stubs.Set(self.linuxscsi, '_execute', fake_execute) - self.stubs.SmartSet(os, 'stat', self.fake_stat) - - info = self.linuxscsi.find_multipath_device('/dev/sdd') - LOG.error("info = %s" % info) - - self.assertEqual("36005076303ffc48e0000000000000101", info["id"]) - self.assertEqual("36005076303ffc48e0000000000000101", info["name"]) - self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101", - info["device"]) - - self.assertEqual("/dev/sdd", info['devices'][0]['device']) - self.assertEqual("6", info['devices'][0]['host']) - self.assertEqual("0", info['devices'][0]['channel']) - self.assertEqual("2", info['devices'][0]['id']) - self.assertEqual("0", info['devices'][0]['lun']) - - self.assertEqual("/dev/sdc", info['devices'][1]['device']) - self.assertEqual("6", info['devices'][1]['host']) - self.assertEqual("1", info['devices'][1]['channel']) - self.assertEqual("0", info['devices'][1]['id']) - self.assertEqual("3", info['devices'][1]['lun']) diff --git a/cinder/tests/unit/brick/test_brick_lvm.py b/cinder/tests/unit/brick/test_brick_lvm.py index fae03d2ff49..c7659ea550c 100644 --- a/cinder/tests/unit/brick/test_brick_lvm.py +++ b/cinder/tests/unit/brick/test_brick_lvm.py @@ -16,8 +16,8 @@ from mox3 import mox from oslo_concurrency import processutils from oslo_log import log as logging -from cinder.brick import exception from cinder.brick.local_dev import lvm as brick +from cinder import exception from cinder import test from cinder.volume import configuration as conf diff --git a/cinder/tests/unit/brick/test_brick_remotefs.py b/cinder/tests/unit/brick/test_brick_remotefs.py deleted file mode 100644 index d97c252deea..00000000000 --- a/cinder/tests/unit/brick/test_brick_remotefs.py +++ /dev/null @@ -1,173 +0,0 @@ -# (c) Copyright 2013 OpenStack Foundation -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from oslo_log import log as logging - -from cinder.brick import exception -from cinder.brick.remotefs import remotefs -from cinder.i18n import _ -from cinder import test - -LOG = logging.getLogger(__name__) - - -class BrickRemoteFsTestCase(test.TestCase): - TEST_EXPORT = '1.2.3.4/export1' - TEST_MNT_BASE = '/mnt/test' - TEST_HASH = '4d664fd43b6ff86d80a4ea969c07b3b9' - TEST_MNT_POINT = TEST_MNT_BASE + '/' + TEST_HASH - - def setUp(self): - super(BrickRemoteFsTestCase, self).setUp() - self._nfsclient = remotefs.RemoteFsClient( - 'nfs', 'sudo', nfs_mount_point_base=self.TEST_MNT_BASE) - - def test_get_hash_str(self): - """_get_hash_str should calculation correct value.""" - - self.assertEqual(self.TEST_HASH, - self._nfsclient._get_hash_str(self.TEST_EXPORT)) - - def test_get_mount_point(self): - mnt_point = self._nfsclient.get_mount_point(self.TEST_EXPORT) - self.assertEqual(mnt_point, self.TEST_MNT_POINT) - - def test_mount_nfs_should_mount_correctly(self): - mox = self.mox - client = self._nfsclient - - mox.StubOutWithMock(client, '_execute') - client._execute('mount', check_exit_code=0).AndReturn(("", "")) - client._execute('mkdir', '-p', self.TEST_MNT_POINT, - check_exit_code=0).AndReturn(("", "")) - client._execute('mount', '-t', 'nfs', '-o', 'vers=4,minorversion=1', - self.TEST_EXPORT, - self.TEST_MNT_POINT, - root_helper='sudo', run_as_root=True, - check_exit_code=0).AndReturn(("", "")) - mox.ReplayAll() - - client.mount(self.TEST_EXPORT) - - mox.VerifyAll() - - def test_mount_nfs_with_specific_vers(self): - opts = ['vers=2,nointr', 'nfsvers=3,lock', 'nolock,v2', 'v4.0'] - for opt in opts: - client = remotefs.RemoteFsClient( - 'nfs', 'sudo', nfs_mount_point_base=self.TEST_MNT_BASE, - nfs_mount_options=opt) - - client._read_mounts = mock.Mock(return_value=[]) - client._execute = mock.Mock(return_value=True) - - client.mount(self.TEST_EXPORT) - client._execute.assert_any_call('mkdir', '-p', self.TEST_MNT_POINT, - check_exit_code=0) - client._execute.assert_any_call('mount', '-t', 'nfs', '-o', - opt, self.TEST_EXPORT, - self.TEST_MNT_POINT, - root_helper='sudo', - run_as_root=True, - check_exit_code=0) - - def test_mount_nfs_with_fallback_no_vers(self): - def execute(*args, **kwargs): - if 'mkdir' in args: - return True - elif 'mount' in args: - if 'lock,nointr,vers=4,minorversion=1' in args: - raise Exception() - else: - return True - else: - self.fail(_("Unexpected call to _execute.")) - - opts = 'lock,nointr' - client = remotefs.RemoteFsClient( - 'nfs', 'sudo', nfs_mount_point_base=self.TEST_MNT_BASE, - nfs_mount_options=opts) - - client._read_mounts = mock.Mock(return_value=[]) - client._execute = mock.Mock(wraps=execute) - - client.mount(self.TEST_EXPORT) - client._execute.assert_any_call('mkdir', '-p', self.TEST_MNT_POINT, - check_exit_code=0) - client._execute.assert_any_call('mount', '-t', 'nfs', '-o', - 'lock,nointr,vers=4,minorversion=1', - self.TEST_EXPORT, - self.TEST_MNT_POINT, - root_helper='sudo', - run_as_root=True, - check_exit_code=0) - client._execute.assert_any_call('mount', '-t', 'nfs', '-o', - 'lock,nointr', - self.TEST_EXPORT, - self.TEST_MNT_POINT, - root_helper='sudo', - run_as_root=True, - check_exit_code=0) - - def test_mount_nfs_with_fallback_all_fail(self): - def execute(*args, **kwargs): - if 'mkdir' in args: - return True - else: - raise Exception(_("mount failed.")) - - opts = 'lock,nointr' - client = remotefs.RemoteFsClient( - 'nfs', 'sudo', nfs_mount_point_base=self.TEST_MNT_BASE, - nfs_mount_options=opts) - - client._read_mounts = mock.Mock(return_value=[]) - client._execute = mock.Mock(wraps=execute) - self.assertRaises(exception.BrickException, client.mount, - self.TEST_EXPORT) - - def test_mount_nfs_should_not_remount(self): - mox = self.mox - client = self._nfsclient - - line = "%s on %s type nfs (rw)\n" % (self.TEST_EXPORT, - self.TEST_MNT_POINT) - mox.StubOutWithMock(client, '_execute') - client._execute('mount', check_exit_code=0).AndReturn((line, "")) - mox.ReplayAll() - - client.mount(self.TEST_EXPORT) - - mox.VerifyAll() - - def test_nfs_mount_options(self): - opts = 'test_nfs_mount_options' - client = remotefs.RemoteFsClient( - 'nfs', 'sudo', nfs_mount_point_base=self.TEST_MNT_BASE, - nfs_mount_options=opts) - self.assertEqual(opts, client._mount_options) - - def test_nfs_mount_point_base(self): - base = '/mnt/test/nfs/mount/point/base' - client = remotefs.RemoteFsClient('nfs', 'sudo', - nfs_mount_point_base=base) - self.assertEqual(base, client._mount_base) - - def test_glusterfs_mount_point_base(self): - base = '/mnt/test/glusterfs/mount/point/base' - client = remotefs.RemoteFsClient('glusterfs', 'sudo', - glusterfs_mount_point_base=base) - self.assertEqual(base, client._mount_base) diff --git a/cinder/tests/unit/test_glusterfs.py b/cinder/tests/unit/test_glusterfs.py index 8e453e41484..82a42684595 100644 --- a/cinder/tests/unit/test_glusterfs.py +++ b/cinder/tests/unit/test_glusterfs.py @@ -22,11 +22,11 @@ import time import traceback import mock +import os_brick from oslo_concurrency import processutils as putils from oslo_config import cfg from oslo_utils import units -from cinder import brick from cinder import compute from cinder import context from cinder import db @@ -123,7 +123,7 @@ class GlusterFsDriverTestCase(test.TestCase): def test_set_execute(self): drv = self._driver - rfsclient = brick.remotefs.remotefs.RemoteFsClient + rfsclient = os_brick.remotefs.remotefs.RemoteFsClient with mock.patch.object(rfsclient, 'set_execute') as mock_set_execute: def my_execute(*a, **k): @@ -151,7 +151,7 @@ class GlusterFsDriverTestCase(test.TestCase): """_mount_glusterfs common case usage.""" drv = self._driver - with mock.patch.object(brick.remotefs.remotefs.RemoteFsClient, + with mock.patch.object(os_brick.remotefs.remotefs.RemoteFsClient, 'mount') as mock_mount: drv._mount_glusterfs(self.TEST_EXPORT1) @@ -162,7 +162,7 @@ class GlusterFsDriverTestCase(test.TestCase): """ drv = self._driver - with mock.patch.object(brick.remotefs.remotefs.RemoteFsClient, + with mock.patch.object(os_brick.remotefs.remotefs.RemoteFsClient, 'mount') as mock_mount: mock_mount.side_effect = exception.GlusterfsException() @@ -182,7 +182,7 @@ class GlusterFsDriverTestCase(test.TestCase): drv = self._driver hashed_path = '/mnt/test/abcdefabcdef' - with mock.patch.object(brick.remotefs.remotefs.RemoteFsClient, + with mock.patch.object(os_brick.remotefs.remotefs.RemoteFsClient, 'get_mount_point') as mock_get_mount_point: mock_get_mount_point.return_value = hashed_path @@ -351,7 +351,7 @@ class GlusterFsDriverTestCase(test.TestCase): mock_get_file_mode,\ mock.patch.object(tempfile, 'NamedTemporaryFile') as \ mock_named_temp,\ - mock.patch.object(brick.remotefs.remotefs.RemoteFsClient, + mock.patch.object(os_brick.remotefs.remotefs.RemoteFsClient, 'mount') as mock_mount: drv._load_shares_config = self._fake_load_shares_config mock_named_temp.return_value = self._fake_NamedTemporaryFile diff --git a/cinder/tests/unit/test_utils.py b/cinder/tests/unit/test_utils.py index 91dd50953ae..4631a723b9f 100644 --- a/cinder/tests/unit/test_utils.py +++ b/cinder/tests/unit/test_utils.py @@ -1252,7 +1252,7 @@ class BrickUtils(test.TestCase): """ @mock.patch('cinder.utils.CONF') - @mock.patch('cinder.brick.initiator.connector.get_connector_properties') + @mock.patch('os_brick.initiator.connector.get_connector_properties') @mock.patch('cinder.utils.get_root_helper') def test_brick_get_connector_properties(self, mock_helper, mock_get, mock_conf): @@ -1263,7 +1263,7 @@ class BrickUtils(test.TestCase): False, False) self.assertEqual(mock_get.return_value, output) - @mock.patch('cinder.brick.initiator.connector.InitiatorConnector.factory') + @mock.patch('os_brick.initiator.connector.InitiatorConnector.factory') @mock.patch('cinder.utils.get_root_helper') def test_brick_get_connector(self, mock_helper, mock_factory): output = utils.brick_get_connector('protocol') diff --git a/cinder/tests/unit/test_volume.py b/cinder/tests/unit/test_volume.py index f9301a41d9b..e8766a04f61 100644 --- a/cinder/tests/unit/test_volume.py +++ b/cinder/tests/unit/test_volume.py @@ -29,6 +29,7 @@ import time import eventlet import mock from mox3 import mox +import os_brick from oslo_concurrency import processutils from oslo_config import cfg from oslo_serialization import jsonutils @@ -5215,7 +5216,7 @@ class GenericVolumeDriverTestCase(DriverTestCase): backup_service = self.mox.CreateMock(backup_driver.BackupDriver) root_helper = 'sudo cinder-rootwrap /etc/cinder/rootwrap.conf' self.mox.StubOutWithMock(self.volume.driver.db, 'volume_get') - self.mox.StubOutWithMock(cinder.brick.initiator.connector, + self.mox.StubOutWithMock(os_brick.initiator.connector, 'get_connector_properties') self.mox.StubOutWithMock(self.volume.driver, '_attach_volume') self.mox.StubOutWithMock(os, 'getuid') @@ -5226,7 +5227,7 @@ class GenericVolumeDriverTestCase(DriverTestCase): self.volume.driver.db.volume_get(self.context, vol['id']).\ AndReturn(vol) - cinder.brick.initiator.connector.\ + os_brick.initiator.connector.\ get_connector_properties(root_helper, CONF.my_ip, False, False).\ AndReturn(properties) self.volume.driver._attach_volume(self.context, vol, properties).\ @@ -5250,7 +5251,7 @@ class GenericVolumeDriverTestCase(DriverTestCase): attach_info = {'device': {'path': '/dev/null'}} root_helper = 'sudo cinder-rootwrap /etc/cinder/rootwrap.conf' backup_service = self.mox.CreateMock(backup_driver.BackupDriver) - self.mox.StubOutWithMock(cinder.brick.initiator.connector, + self.mox.StubOutWithMock(os_brick.initiator.connector, 'get_connector_properties') self.mox.StubOutWithMock(self.volume.driver, '_attach_volume') self.mox.StubOutWithMock(os, 'getuid') @@ -5259,7 +5260,7 @@ class GenericVolumeDriverTestCase(DriverTestCase): self.mox.StubOutWithMock(self.volume.driver, '_detach_volume') self.mox.StubOutWithMock(self.volume.driver, 'terminate_connection') - cinder.brick.initiator.connector.\ + os_brick.initiator.connector.\ get_connector_properties(root_helper, CONF.my_ip, False, False).\ AndReturn(properties) self.volume.driver._attach_volume(self.context, vol, properties).\ @@ -5622,7 +5623,7 @@ class LVMVolumeDriverTestCase(DriverTestCase): @mock.patch.object(utils, 'temporary_chown') @mock.patch.object(fileutils, 'file_open') - @mock.patch.object(cinder.brick.initiator.connector, + @mock.patch.object(os_brick.initiator.connector, 'get_connector_properties') @mock.patch.object(db, 'volume_get') def test_backup_volume(self, mock_volume_get, diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py index a8cac6d35dc..086985b6211 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py @@ -17,8 +17,8 @@ Mock unit tests for the NetApp nfs storage driver """ import mock +from os_brick.remotefs import remotefs as remotefs_brick -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import test from cinder import utils from cinder.volume.drivers.netapp.dataontap import nfs_base diff --git a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py index cb6e5b47019..41ed696506a 100644 --- a/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py +++ b/cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py @@ -17,8 +17,8 @@ Mock unit tests for the NetApp cmode nfs storage driver """ import mock +from os_brick.remotefs import remotefs as remotefs_brick -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import test from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes from cinder import utils diff --git a/cinder/utils.py b/cinder/utils.py index c84f10ae99c..ecc8572f51a 100644 --- a/cinder/utils.py +++ b/cinder/utils.py @@ -35,6 +35,7 @@ from xml import sax from xml.sax import expatreader from xml.sax import saxutils +from os_brick.initiator import connector from oslo_concurrency import lockutils from oslo_concurrency import processutils from oslo_config import cfg @@ -44,7 +45,6 @@ from oslo_utils import timeutils import retrying import six -from cinder.brick.initiator import connector from cinder import exception from cinder.i18n import _, _LE diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 5bdc77dbd4a..0b2953061a4 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -17,12 +17,12 @@ import errno import os import stat +from os_brick.remotefs import remotefs as remotefs_brick from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import units -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py index e5935a4946d..855a2df6dfe 100644 --- a/cinder/volume/drivers/lvm.py +++ b/cinder/volume/drivers/lvm.py @@ -25,7 +25,6 @@ from oslo_log import log as logging from oslo_utils import importutils from oslo_utils import units -from cinder.brick import exception as brick_exception from cinder.brick.local_dev import lvm as lvm from cinder import exception from cinder.i18n import _, _LE, _LI, _LW @@ -262,7 +261,7 @@ class LVMVolumeDriver(driver.VolumeDriver): executor=self._execute, lvm_conf=lvm_conf_file) - except brick_exception.VolumeGroupNotFound: + except exception.VolumeGroupNotFound: message = (_("Volume Group %s does not exist") % self.configuration.volume_group) raise exception.VolumeBackendAPIException(data=message) diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index cf1617ddd9d..0ffe8a3f290 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -17,13 +17,13 @@ import errno import os import time +from os_brick.remotefs import remotefs as remotefs_brick from oslo_concurrency import processutils as putils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import units import six -from cinder.brick.remotefs import remotefs as remotefs_brick from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils diff --git a/cinder/volume/drivers/smbfs.py b/cinder/volume/drivers/smbfs.py index 9d59cbfd6c9..6f98dd17fe3 100644 --- a/cinder/volume/drivers/smbfs.py +++ b/cinder/volume/drivers/smbfs.py @@ -15,12 +15,12 @@ import os +from os_brick.remotefs import remotefs from oslo_concurrency import processutils as putils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import units -from cinder.brick.remotefs import remotefs from cinder import exception from cinder.i18n import _, _LI, _LW from cinder.image import image_utils diff --git a/cinder/volume/drivers/windows/remotefs.py b/cinder/volume/drivers/windows/remotefs.py index 6f7f9ae6e0d..fd6dc37299a 100644 --- a/cinder/volume/drivers/windows/remotefs.py +++ b/cinder/volume/drivers/windows/remotefs.py @@ -19,10 +19,10 @@ import sys if sys.platform == 'win32': import wmi +from os_brick.remotefs import remotefs from oslo_log import log as logging import six -from cinder.brick.remotefs import remotefs from cinder import exception from cinder.i18n import _, _LE, _LI