This patch removes the existing brick initiator code from Cinder.
The new os-brick pypi library takes over.
The only thing left behind is the local_dev directory
as it's not part of os-brick. We should possibly
move this elsewhere in a follow up patch.
Change-Id: Iaa22b30b852ea055a8698e0faaefa5caff84d090
Depends-On: I0096e76f958e04829b98d5c4c47f49c82b58d8aa
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
-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.
+++ /dev/null
-# (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.")
+++ /dev/null
-# (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
+++ /dev/null
-# 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
+++ /dev/null
-# 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
+++ /dev/null
-# (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})
+++ /dev/null
-# (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<wwid>.+)\)")
-
-
-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
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
+++ /dev/null
-# 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]
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):
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
+++ /dev/null
-# (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': '<store method only>',
- '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': '<store method only>',
- 'vport_delete': '<store method only>'}]
-
- 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)
+++ /dev/null
-
-# 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)
+++ /dev/null
-# (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 = <store method only>
- 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 = <store method only>
- vport_delete = <store method only>
-
- Device = "host0"
- Device path = "/sys/devices/pci0000:20/0000:20:03.0/0000:21:00.0/host0"
- edc = <store method only>
- optrom_ctl = <store method only>
- reset = <store method only>
- 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 = <store method only>
- 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 = <store method only>
- vport_delete = <store method only>
-
- Device = "host2"
- Device path = "/sys/devices/pci0000:20/0000:20:03.0/0000:21:00.1/host2"
- edc = <store method only>
- optrom_ctl = <store method only>
- reset = <store method only>
- 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"
-
-"""
+++ /dev/null
-# (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'])
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
+++ /dev/null
-# (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)
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
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):
"""_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)
"""
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()
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
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
"""
@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):
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')
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
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')
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).\
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')
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).\
@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,
"""
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
"""
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
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
import retrying
import six
-from cinder.brick.initiator import connector
from cinder import exception
from cinder.i18n import _, _LE
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
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
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)
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
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
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