Browse Source

Remove Brick from cinder codebase

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
changes/52/155552/12
Walter A. Boring IV 7 years ago
parent
commit
88da45569c
  1. 2
      cinder/backup/drivers/nfs.py
  2. 12
      cinder/brick/README.txt
  3. 126
      cinder/brick/exception.py
  4. 34
      cinder/brick/executor.py
  5. 0
      cinder/brick/initiator/__init__.py
  6. 1251
      cinder/brick/initiator/connector.py
  7. 30
      cinder/brick/initiator/host_driver.py
  8. 212
      cinder/brick/initiator/linuxfc.py
  9. 232
      cinder/brick/initiator/linuxscsi.py
  10. 4
      cinder/brick/local_dev/lvm.py
  11. 0
      cinder/brick/remotefs/__init__.py
  12. 174
      cinder/brick/remotefs/remotefs.py
  13. 12
      cinder/exception.py
  14. 2
      cinder/tests/unit/backup/drivers/test_backup_nfs.py
  15. 1166
      cinder/tests/unit/brick/test_brick_connector.py
  16. 66
      cinder/tests/unit/brick/test_brick_exception.py
  17. 245
      cinder/tests/unit/brick/test_brick_linuxfc.py
  18. 258
      cinder/tests/unit/brick/test_brick_linuxscsi.py
  19. 2
      cinder/tests/unit/brick/test_brick_lvm.py
  20. 173
      cinder/tests/unit/brick/test_brick_remotefs.py
  21. 12
      cinder/tests/unit/test_glusterfs.py
  22. 4
      cinder/tests/unit/test_utils.py
  23. 11
      cinder/tests/unit/test_volume.py
  24. 2
      cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py
  25. 2
      cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_cmode.py
  26. 2
      cinder/utils.py
  27. 2
      cinder/volume/drivers/glusterfs.py
  28. 3
      cinder/volume/drivers/lvm.py
  29. 2
      cinder/volume/drivers/nfs.py
  30. 2
      cinder/volume/drivers/smbfs.py
  31. 2
      cinder/volume/drivers/windows/remotefs.py

2
cinder/backup/drivers/nfs.py

@ -18,11 +18,11 @@
import os
import os.path
from os_brick.remotefs import remotefs as remotefs_brick
from oslo_config import cfg
from oslo_log import log as logging
from cinder.backup import chunkeddriver
from cinder.brick.remotefs import remotefs as remotefs_brick
from cinder import exception
from cinder.i18n import _
from cinder import utils

12
cinder/brick/README.txt

@ -1,9 +1,5 @@
Brick is a new library that currently is maintained in Cinder for
the Havana release. It will eventually be moved external to Cinder,
possibly oslo, or pypi. Any defects found in Brick, should be submitted
against Cinder and fixed there, then pulled into other projects that
are using brick.
Brick has been migrated to a new standalone
pypi library called os-brick.
* Brick is used outside of Cinder and therefore
cannot have any dependencies on Cinder and/or
it's database.
We are leaving the local_dev directory here for the time
being until we can migrate it to a new home.

126
cinder/brick/exception.py

@ -1,126 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Exceptions for the Brick library."""
from oslo_log import log as logging
import six
from cinder.i18n import _, _LE
LOG = logging.getLogger(__name__)
class BrickException(Exception):
"""Base Brick Exception
To correctly use this class, inherit from it and define
a 'msg_fmt' property. That msg_fmt will get printf'd
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
code = 500
headers = {}
safe = False
def __init__(self, message=None, **kwargs):
self.kwargs = kwargs
if 'code' not in self.kwargs:
try:
self.kwargs['code'] = self.code
except AttributeError:
pass
if not message:
try:
message = self.message % kwargs
except Exception:
# kwargs doesn't match a variable in the message
# log the issue and the kwargs
LOG.exception(_LE("Exception in string format operation. "
"msg='%s'"),
self.message)
for name, value in kwargs.iteritems():
LOG.error(_LE("%(n)s: %(v)s"), {'n': name, 'v': value})
# at least get the core message out if something happened
message = self.message
# Put the message in 'msg' so that we can access it. If we have it in
# message it will be overshadowed by the class' message attribute
self.msg = message
super(BrickException, self).__init__(message)
def __unicode__(self):
return six.text_type(self.msg)
class NotFound(BrickException):
message = _("Resource could not be found.")
code = 404
safe = True
class Invalid(BrickException):
message = _("Unacceptable parameters.")
code = 400
# Cannot be templated as the error syntax varies.
# msg needs to be constructed when raised.
class InvalidParameterValue(Invalid):
message = _("%(err)s")
class NoFibreChannelHostsFound(BrickException):
message = _("We are unable to locate any Fibre Channel devices.")
class NoFibreChannelVolumeDeviceFound(BrickException):
message = _("Unable to find a Fibre Channel volume device.")
class VolumeDeviceNotFound(BrickException):
message = _("Volume device not found at %(device)s.")
class VolumePathNotRemoved(BrickException):
message = _("Volume path %(volume_path)s was not removed in time.")
class VolumeGroupNotFound(BrickException):
message = _('Unable to find Volume Group: %(vg_name)s')
class VolumeGroupCreationFailed(BrickException):
message = _('Failed to create Volume Group: %(vg_name)s')
class ISCSITargetCreateFailed(BrickException):
message = _("Failed to create iscsi target for volume %(volume_id)s.")
class ISCSITargetRemoveFailed(BrickException):
message = _("Failed to remove iscsi target for volume %(volume_id)s.")
class ISCSITargetAttachFailed(BrickException):
message = _("Failed to attach iSCSI target for volume %(volume_id)s.")
class ProtocolNotSupported(BrickException):
message = _("Connect to volume via protocol %(protocol)s not supported.")

34
cinder/brick/executor.py

@ -1,34 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Generic exec utility that allows us to set the
execute and root_helper attributes for putils.
Some projects need their own execute wrapper
and root_helper settings, so this provides that hook.
"""
from oslo_concurrency import processutils as putils
class Executor(object):
def __init__(self, root_helper, execute=putils.execute,
*args, **kwargs):
self.set_execute(execute)
self.set_root_helper(root_helper)
def set_execute(self, execute):
self._execute = execute
def set_root_helper(self, helper):
self._root_helper = helper

0
cinder/brick/initiator/__init__.py

1251
cinder/brick/initiator/connector.py
File diff suppressed because it is too large
View File

30
cinder/brick/initiator/host_driver.py

@ -1,30 +0,0 @@
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
class HostDriver(object):
def get_all_block_devices(self):
"""Get the list of all block devices seen in /dev/disk/by-path/."""
files = []
dir = "/dev/disk/by-path/"
if os.path.isdir(dir):
files = os.listdir(dir)
devices = []
for file in files:
devices.append(dir + file)
return devices

212
cinder/brick/initiator/linuxfc.py

@ -1,212 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Generic linux Fibre Channel utilities."""
import errno
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
from cinder.brick.initiator import linuxscsi
from cinder.i18n import _LW
LOG = logging.getLogger(__name__)
class LinuxFibreChannel(linuxscsi.LinuxSCSI):
def __init__(self, root_helper, execute=putils.execute,
*args, **kwargs):
super(LinuxFibreChannel, self).__init__(root_helper, execute,
*args, **kwargs)
def rescan_hosts(self, hbas):
for hba in hbas:
self.echo_scsi_command("/sys/class/scsi_host/%s/scan"
% hba['host_device'], "- - -")
def get_fc_hbas(self):
"""Get the Fibre Channel HBA information."""
out = None
try:
out, _err = self._execute('systool', '-c', 'fc_host', '-v',
run_as_root=True,
root_helper=self._root_helper)
except putils.ProcessExecutionError as exc:
# This handles the case where rootwrap is used
# and systool is not installed
# 96 = nova.cmd.rootwrap.RC_NOEXECFOUND:
if exc.exit_code == 96:
LOG.warning(_LW("systool is not installed"))
return []
except OSError as exc:
# This handles the case where rootwrap is NOT used
# and systool is not installed
if exc.errno == errno.ENOENT:
LOG.warning(_LW("systool is not installed"))
return []
# No FC HBAs were found
if out is None:
return []
lines = out.split('\n')
# ignore the first 2 lines
lines = lines[2:]
hbas = []
hba = {}
lastline = None
for line in lines:
line = line.strip()
# 2 newlines denotes a new hba port
if line == '' and lastline == '':
if len(hba) > 0:
hbas.append(hba)
hba = {}
else:
val = line.split('=')
if len(val) == 2:
key = val[0].strip().replace(" ", "")
value = val[1].strip()
hba[key] = value.replace('"', '')
lastline = line
return hbas
def get_fc_hbas_info(self):
"""Get Fibre Channel WWNs and device paths from the system, if any."""
# Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys
# and are obtainable via the systool app
hbas = self.get_fc_hbas()
if not hbas:
return []
hbas_info = []
for hba in hbas:
wwpn = hba['port_name'].replace('0x', '')
wwnn = hba['node_name'].replace('0x', '')
device_path = hba['ClassDevicepath']
device = hba['ClassDevice']
hbas_info.append({'port_name': wwpn,
'node_name': wwnn,
'host_device': device,
'device_path': device_path})
return hbas_info
def get_fc_wwpns(self):
"""Get Fibre Channel WWPNs from the system, if any."""
# Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys
# and are obtainable via the systool app
hbas = self.get_fc_hbas()
wwpns = []
if hbas:
for hba in hbas:
if hba['port_state'] == 'Online':
wwpn = hba['port_name'].replace('0x', '')
wwpns.append(wwpn)
return wwpns
def get_fc_wwnns(self):
"""Get Fibre Channel WWNNs from the system, if any."""
# Note(walter-boring) modern Linux kernels contain the FC HBA's in /sys
# and are obtainable via the systool app
hbas = self.get_fc_hbas()
if not hbas:
return []
wwnns = []
if hbas:
for hba in hbas:
if hba['port_state'] == 'Online':
wwnn = hba['node_name'].replace('0x', '')
wwnns.append(wwnn)
return wwnns
class LinuxFibreChannelS390X(LinuxFibreChannel):
def __init__(self, root_helper, execute=putils.execute,
*args, **kwargs):
super(LinuxFibreChannelS390X, self).__init__(root_helper, execute,
*args, **kwargs)
def get_fc_hbas_info(self):
"""Get Fibre Channel WWNs and device paths from the system, if any."""
hbas = self.get_fc_hbas()
if not hbas:
return []
hbas_info = []
for hba in hbas:
if hba['port_state'] == 'Online':
wwpn = hba['port_name'].replace('0x', '')
wwnn = hba['node_name'].replace('0x', '')
device_path = hba['ClassDevicepath']
device = hba['ClassDevice']
hbas_info.append({'port_name': wwpn,
'node_name': wwnn,
'host_device': device,
'device_path': device_path})
return hbas_info
def configure_scsi_device(self, device_number, target_wwn, lun):
"""Write the LUN to the port's unit_add attribute.
If auto-discovery of LUNs is disabled on s390 platforms
luns need to be added to the configuration through the
unit_add interface
"""
LOG.debug("Configure lun for s390: device_number=(%(device_num)s) "
"target_wwn=(%(target_wwn)s) target_lun=(%(target_lun)s)",
{'device_num': device_number,
'target_wwn': target_wwn,
'target_lun': lun})
zfcp_device_command = ("/sys/bus/ccw/drivers/zfcp/%s/%s/unit_add" %
(device_number, target_wwn))
LOG.debug("unit_add call for s390 execute: %s", zfcp_device_command)
try:
self.echo_scsi_command(zfcp_device_command, lun)
except putils.ProcessExecutionError as exc:
LOG.warning(_LW("unit_add call for s390 failed exit (%(code)s), "
"stderr (%(stderr)s)"),
{'code': exc.exit_code, 'stderr': exc.stderr})
def deconfigure_scsi_device(self, device_number, target_wwn, lun):
"""Write the LUN to the port's unit_remove attribute.
If auto-discovery of LUNs is disabled on s390 platforms
luns need to be removed from the configuration through the
unit_remove interface
"""
LOG.debug("Deconfigure lun for s390: "
"device_number=(%(device_num)s) "
"target_wwn=(%(target_wwn)s) target_lun=(%(target_lun)s)",
{'device_num': device_number,
'target_wwn': target_wwn,
'target_lun': lun})
zfcp_device_command = ("/sys/bus/ccw/drivers/zfcp/%s/%s/unit_remove" %
(device_number, target_wwn))
LOG.debug("unit_remove call for s390 execute: %s", zfcp_device_command)
try:
self.echo_scsi_command(zfcp_device_command, lun)
except putils.ProcessExecutionError as exc:
LOG.warning(_LW("unit_remove call for s390 failed exit (%(code)s)"
", stderr (%(stderr)s)"),
{'code': exc.exit_code, 'stderr': exc.stderr})

232
cinder/brick/initiator/linuxscsi.py

@ -1,232 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Generic linux scsi subsystem and Multipath utilities.
Note, this is not iSCSI.
"""
import os
import re
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
from cinder.brick import exception
from cinder.brick import executor
from cinder.i18n import _LW, _LE
from cinder.openstack.common import loopingcall
LOG = logging.getLogger(__name__)
MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$")
MULTIPATH_WWID_REGEX = re.compile("\((?P<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

4
cinder/brick/local_dev/lvm.py

@ -22,12 +22,12 @@ import math
import os
import re
from os_brick import executor
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
from oslo_utils import excutils
from cinder.brick import exception
from cinder.brick import executor
from cinder import exception
from cinder.i18n import _LE, _LI
from cinder import utils

0
cinder/brick/remotefs/__init__.py

174
cinder/brick/remotefs/remotefs.py

@ -1,174 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Remote filesystem client utilities."""
import hashlib
import os
import re
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
import six
from cinder.brick import exception
from cinder.i18n import _, _LI
LOG = logging.getLogger(__name__)
class RemoteFsClient(object):
def __init__(self, mount_type, root_helper,
execute=putils.execute, *args, **kwargs):
self._mount_type = mount_type
if mount_type == "nfs":
self._mount_base = kwargs.get('nfs_mount_point_base', None)
if not self._mount_base:
raise exception.InvalidParameterValue(
err=_('nfs_mount_point_base required'))
self._mount_options = kwargs.get('nfs_mount_options', None)
self._check_nfs_options()
elif mount_type == "cifs":
self._mount_base = kwargs.get('smbfs_mount_point_base', None)
if not self._mount_base:
raise exception.InvalidParameterValue(
err=_('smbfs_mount_point_base required'))
self._mount_options = kwargs.get('smbfs_mount_options', None)
elif mount_type == "glusterfs":
self._mount_base = kwargs.get('glusterfs_mount_point_base', None)
if not self._mount_base:
raise exception.InvalidParameterValue(
err=_('glusterfs_mount_point_base required'))
self._mount_options = None
else:
raise exception.ProtocolNotSupported(protocol=mount_type)
self.root_helper = root_helper
self.set_execute(execute)
def set_execute(self, execute):
self._execute = execute
def _get_hash_str(self, base_str):
"""Return a string that represents hash of base_str
(in a hex format).
"""
return hashlib.md5(base_str).hexdigest()
def get_mount_point(self, device_name):
"""Get Mount Point.
:param device_name: example 172.18.194.100:/var/nfs
"""
return os.path.join(self._mount_base,
self._get_hash_str(device_name))
def _read_mounts(self):
(out, _err) = self._execute('mount', check_exit_code=0)
lines = out.split('\n')
mounts = {}
for line in lines:
tokens = line.split()
if 2 < len(tokens):
device = tokens[0]
mnt_point = tokens[2]
mounts[mnt_point] = device
return mounts
def mount(self, share, flags=None):
"""Mount given share."""
mount_path = self.get_mount_point(share)
if mount_path in self._read_mounts():
LOG.info(_LI('Already mounted: %s'), mount_path)
return
self._execute('mkdir', '-p', mount_path, check_exit_code=0)
if self._mount_type == 'nfs':
self._mount_nfs(share, mount_path, flags)
else:
self._do_mount(self._mount_type, share, mount_path,
self._mount_options, flags)
def _do_mount(self, mount_type, share, mount_path, mount_options=None,
flags=None):
"""Mounts share based on the specified params."""
mnt_cmd = ['mount', '-t', mount_type]
if mount_options is not None:
mnt_cmd.extend(['-o', mount_options])
if flags is not None:
mnt_cmd.extend(flags)
mnt_cmd.extend([share, mount_path])
self._execute(*mnt_cmd, root_helper=self.root_helper,
run_as_root=True, check_exit_code=0)
def _mount_nfs(self, nfs_share, mount_path, flags=None):
"""Mount nfs share using present mount types."""
mnt_errors = {}
# This loop allows us to first try to mount with NFS 4.1 for pNFS
# support but falls back to mount NFS 4 or NFS 3 if either the client
# or server do not support it.
for mnt_type in sorted(self._nfs_mount_type_opts.keys(), reverse=True):
options = self._nfs_mount_type_opts[mnt_type]
try:
self._do_mount('nfs', nfs_share, mount_path, options, flags)
LOG.debug('Mounted %(sh)s using %(mnt_type)s.',
{'sh': nfs_share, 'mnt_type': mnt_type})
return
except Exception as e:
mnt_errors[mnt_type] = six.text_type(e)
LOG.debug('Failed to do %s mount.', mnt_type)
raise exception.BrickException(_("NFS mount failed for share %(sh)s. "
"Error - %(error)s")
% {'sh': nfs_share,
'error': mnt_errors})
def _check_nfs_options(self):
"""Checks and prepares nfs mount type options."""
self._nfs_mount_type_opts = {'nfs': self._mount_options}
nfs_vers_opt_patterns = ['^nfsvers', '^vers', '^v[\d]']
for opt in nfs_vers_opt_patterns:
if self._option_exists(self._mount_options, opt):
return
# pNFS requires NFS 4.1. The mount.nfs4 utility does not automatically
# negotiate 4.1 support, we have to ask for it by specifying two
# options: vers=4 and minorversion=1.
pnfs_opts = self._update_option(self._mount_options, 'vers', '4')
pnfs_opts = self._update_option(pnfs_opts, 'minorversion', '1')
self._nfs_mount_type_opts['pnfs'] = pnfs_opts
def _option_exists(self, options, opt_pattern):
"""Checks if the option exists in nfs options and returns position."""
options = [x.strip() for x in options.split(',')] if options else []
pos = 0
for opt in options:
pos = pos + 1
if re.match(opt_pattern, opt, flags=0):
return pos
return 0
def _update_option(self, options, option, value=None):
"""Update option if exists else adds it and returns new options."""
opts = [x.strip() for x in options.split(',')] if options else []
pos = self._option_exists(options, option)
if pos:
opts.pop(pos - 1)
opt = '%s=%s' % (option, value) if value else option
opts.append(opt)
return ",".join(opts) if len(opts) > 1 else opts[0]

12
cinder/exception.py

@ -687,6 +687,18 @@ class ReadOnlyFieldError(CinderException):
msg_fmt = _('Cannot modify readonly field %(field)s')
class VolumeGroupNotFound(CinderException):
msg_fmt = _('Unable to find Volume Group: %(vg_name)s')
class VolumeGroupCreationFailed(CinderException):
msg_fmt = _('Failed to create Volume Group: %(vg_name)s')
class VolumeDeviceNotFound(CinderException):
msg_fmt = _('Volume device not found at %(device)s.')
# Driver specific exceptions
# Coraid
class CoraidException(VolumeDriverException):

2
cinder/tests/unit/backup/drivers/test_backup_nfs.py

@ -27,11 +27,11 @@ import tempfile
import zlib
import mock
from os_brick.remotefs import remotefs as remotefs_brick
from oslo_config import cfg
from oslo_log import log as logging
from cinder.backup.drivers import nfs
from cinder.brick.remotefs import remotefs as remotefs_brick
from cinder import context
from cinder import db
from cinder import exception

1166
cinder/tests/unit/brick/test_brick_connector.py
File diff suppressed because it is too large
View File

66
cinder/tests/unit/brick/test_brick_exception.py

@ -1,66 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cinder.brick import exception
from cinder import test
import six
class BrickExceptionTestCase(test.TestCase):
def test_default_error_msg(self):
class FakeBrickException(exception.BrickException):
message = "default message"
exc = FakeBrickException()
self.assertEqual(six.text_type(exc), 'default message')
def test_error_msg(self):
self.assertEqual(six.text_type(exception.BrickException('test')),
'test')
def test_default_error_msg_with_kwargs(self):
class FakeBrickException(exception.BrickException):
message = "default message: %(code)s"
exc = FakeBrickException(code=500)
self.assertEqual(six.text_type(exc), 'default message: 500')
def test_error_msg_exception_with_kwargs(self):
# NOTE(dprince): disable format errors for this test
self.flags(fatal_exception_format_errors=False)
class FakeBrickException(exception.BrickException):
message = "default message: %(mispelled_code)s"
exc = FakeBrickException(code=500)
self.assertEqual(six.text_type(exc),
'default message: %(mispelled_code)s')
def test_default_error_code(self):
class FakeBrickException(exception.BrickException):
code = 404
exc = FakeBrickException()
self.assertEqual(exc.kwargs['code'], 404)
def test_error_code_from_kwarg(self):
class FakeBrickException(exception.BrickException):
code = 500
exc = FakeBrickException(code=404)
self.assertEqual(exc.kwargs['code'], 404)

245
cinder/tests/unit/brick/test_brick_linuxfc.py

@ -1,245 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os.path
import string
from oslo_log import log as logging
from cinder.brick.initiator import linuxfc
from cinder import test
LOG = logging.getLogger(__name__)
class LinuxFCTestCase(test.TestCase):
def setUp(self):
super(LinuxFCTestCase, self).setUp()
self.cmds = []
self.stubs.Set(os.path, 'exists', lambda x: True)
self.lfc = linuxfc.LinuxFibreChannel(None, execute=self.fake_execute)
def fake_execute(self, *cmd, **kwargs):
self.cmds.append(string.join(cmd))
return "", None
def test_rescan_hosts(self):
hbas = [{'host_device': 'foo'},
{'host_device': 'bar'}, ]
self.lfc.rescan_hosts(hbas)
expected_commands = ['tee -a /sys/class/scsi_host/foo/scan',
'tee -a /sys/class/scsi_host/bar/scan']
self.assertEqual(expected_commands, self.cmds)
def test_get_fc_hbas_fail(self):
def fake_exec1(a, b, c, d, run_as_root=True, root_helper='sudo'):
raise OSError
def fake_exec2(a, b, c, d, run_as_root=True, root_helper='sudo'):
return None, 'None found'
self.stubs.Set(self.lfc, "_execute", fake_exec1)
hbas = self.lfc.get_fc_hbas()
self.assertEqual(0, len(hbas))
self.stubs.Set(self.lfc, "_execute", fake_exec2)
hbas = self.lfc.get_fc_hbas()
self.assertEqual(0, len(hbas))
def test_get_fc_hbas(self):
def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'):
return SYSTOOL_FC, None
self.stubs.Set(self.lfc, "_execute", fake_exec)
hbas = self.lfc.get_fc_hbas()
self.assertEqual(2, len(hbas))
hba1 = hbas[0]
self.assertEqual(hba1["ClassDevice"], "host0")
hba2 = hbas[1]
self.assertEqual(hba2["ClassDevice"], "host2")
def test_get_fc_hbas_info(self):
def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'):
return SYSTOOL_FC, None
self.stubs.Set(self.lfc, "_execute", fake_exec)
hbas_info = self.lfc.get_fc_hbas_info()
expected_info = [{'device_path': '/sys/devices/pci0000:20/'
'0000:20:03.0/0000:21:00.0/'
'host0/fc_host/host0',
'host_device': 'host0',
'node_name': '50014380242b9751',
'port_name': '50014380242b9750'},
{'device_path': '/sys/devices/pci0000:20/'
'0000:20:03.0/0000:21:00.1/'
'host2/fc_host/host2',
'host_device': 'host2',
'node_name': '50014380242b9753',
'port_name': '50014380242b9752'}, ]
self.assertEqual(expected_info, hbas_info)
def test_get_fc_wwpns(self):
def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'):
return SYSTOOL_FC, None
self.stubs.Set(self.lfc, "_execute", fake_exec)
wwpns = self.lfc.get_fc_wwpns()
expected_wwpns = ['50014380242b9750', '50014380242b9752']
self.assertEqual(expected_wwpns, wwpns)
def test_get_fc_wwnns(self):
def fake_exec(a, b, c, d, run_as_root=True, root_helper='sudo'):
return SYSTOOL_FC, None
self.stubs.Set(self.lfc, "_execute", fake_exec)
wwnns = self.lfc.get_fc_wwpns()
expected_wwnns = ['50014380242b9750', '50014380242b9752']
self.assertEqual(expected_wwnns, wwnns)
SYSTOOL_FC = """
Class = "fc_host"
Class Device = "host0"
Class Device path = "/sys/devices/pci0000:20/0000:20:03.0/\
0000:21:00.0/host0/fc_host/host0"
dev_loss_tmo = "16"
fabric_name = "0x100000051ea338b9"
issue_lip = <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"
"""

258
cinder/tests/unit/brick/test_brick_linuxscsi.py

@ -1,258 +0,0 @@
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import os.path
import string
import mock
from oslo_log import log as logging
from cinder.brick import exception
from cinder.brick.initiator import linuxscsi
from cinder import test
from cinder.tests.unit import utils
LOG = logging.getLogger(__name__)
class LinuxSCSITestCase(test.TestCase):
def setUp(self):
super(LinuxSCSITestCase, self).setUp()
self.cmds = []
self.stubs.Set(os.path, 'realpath', lambda x: '/dev/sdc')
self.linuxscsi = linuxscsi.LinuxSCSI(None, execute=self.fake_execute)
self.fake_stat_result = os.stat(__file__)
def fake_execute(self, *cmd, **kwargs):
self.cmds.append(string.join(cmd))
return "", None
def fake_stat(self, path):
return self.fake_stat_result
def test_echo_scsi_command(self):
self.linuxscsi.echo_scsi_command("/some/path", "1")
expected_commands = ['tee -a /some/path']
self.assertEqual(expected_commands, self.cmds)
def test_get_name_from_path(self):
device_name = "/dev/sdc"
self.stubs.Set(os.path, 'realpath', lambda x: device_name)
disk_path = ("/dev/disk/by-path/ip-10.10.220.253:3260-"
"iscsi-iqn.2000-05.com.3pardata:21810002ac00383d-lun-0")
name = self.linuxscsi.get_name_from_path(disk_path)
self.assertEqual(name, device_name)
self.stubs.Set(os.path, 'realpath', lambda x: "bogus")
name = self.linuxscsi.get_name_from_path(disk_path)
self.assertIsNone(name)
def test_remove_scsi_device(self):
self.stubs.Set(os.path, "exists", lambda x: False)
self.linuxscsi.remove_scsi_device("/dev/sdc")
expected_commands = []
self.assertEqual(expected_commands, self.cmds)
self.stubs.Set(os.path, "exists", lambda x: True)
self.linuxscsi.remove_scsi_device("/dev/sdc")
expected_commands = [
('blockdev --flushbufs /dev/sdc'),
('tee -a /sys/block/sdc/device/delete')]
self.assertEqual(expected_commands, self.cmds)
@mock.patch('cinder.openstack.common.loopingcall.FixedIntervalLoopingCall',
new=utils.ZeroIntervalLoopingCall)
def test_wait_for_volume_removal(self):
fake_path = '/dev/disk/by-path/fake-iscsi-iqn-lun-0'
self.stubs.Set(os.path, "exists", lambda x: True)
self.assertRaises(exception.VolumePathNotRemoved,
self.linuxscsi.wait_for_volume_removal,
fake_path)
self.stubs.Set(os.path, "exists", lambda x: False)
self.linuxscsi.wait_for_volume_removal(fake_path)
expected_commands = []
self.assertEqual(expected_commands, self.cmds)
def test_flush_multipath_device(self):
self.linuxscsi.flush_multipath_device('/dev/dm-9')
expected_commands = [('multipath -f /dev/dm-9')]
self.assertEqual(expected_commands, self.cmds)
def test_flush_multipath_devices(self):
self.linuxscsi.flush_multipath_devices()
expected_commands = [('multipath -F')]
self.assertEqual(expected_commands, self.cmds)
def test_remove_multipath_device(self):
def fake_find_multipath_device(device):
devices = [{'device': '/dev/sde', 'host': 0,
'channel': 0, 'id': 0, 'lun': 1},
{'device': '/dev/sdf', 'host': 2,
'channel': 0, 'id': 0, 'lun': 1}, ]
info = {"device": "dm-3",
"id": "350002ac20398383d",
"devices": devices}
return info
self.stubs.Set(os.path, "exists", lambda x: True)
self.stubs.Set(self.linuxscsi, 'find_multipath_device',
fake_find_multipath_device)
self.linuxscsi.remove_multipath_device('/dev/dm-3')
expected_commands = [
('blockdev --flushbufs /dev/sde'),
('tee -a /sys/block/sde/device/delete'),
('blockdev --flushbufs /dev/sdf'),
('tee -a /sys/block/sdf/device/delete'),
('multipath -f 350002ac20398383d'), ]
self.assertEqual(expected_commands, self.cmds)
def test_find_multipath_device_3par_ufn(self):
def fake_execute(*cmd, **kwargs):
out = ("mpath6 (350002ac20398383d) dm-3 3PARdata,VV\n"
"size=2.0G features='0' hwhandler='0' wp=rw\n"
"`-+- policy='round-robin 0' prio=-1 status=active\n"
" |- 0:0:0:1 sde 8:64 active undef running\n"
" `- 2:0:0:1 sdf 8:80 active undef running\n"
)
return out, None
self.stubs.Set(self.linuxscsi, '_execute', fake_execute)
self.stubs.SmartSet(os, 'stat', self.fake_stat)
info = self.linuxscsi.find_multipath_device('/dev/sde')
LOG.error("info = %s" % info)
self.assertEqual("350002ac20398383d", info['id'])
self.assertEqual("mpath6", info['name'])
self.assertEqual("/dev/mapper/mpath6", info['device'])
self.assertEqual("/dev/sde", info['devices'][0]['device'])
self.assertEqual("0", info['devices'][0]['host'])
self.assertEqual("0", info['devices'][0]['id'])
self.assertEqual("0", info['devices'][0]['channel'])
self.assertEqual("1", info['devices'][0]['lun'])
self.assertEqual("/dev/sdf", info['devices'][1]['device'])
self.assertEqual("2", info['devices'][1]['host'])
self.assertEqual("0", info['devices'][1]['id'])
self.assertEqual("0", info['devices'][1]['channel'])
self.assertEqual("1", info['devices'][1]['lun'])
def test_find_multipath_device_svc(self):
def fake_execute(*cmd, **kwargs):
out = ("36005076da00638089c000000000004d5 dm-2 IBM,2145\n"
"size=954M features='1 queue_if_no_path' hwhandler='0'"
" wp=rw\n"
"|-+- policy='round-robin 0' prio=-1 status=active\n"
"| |- 6:0:2:0 sde 8:64 active undef running\n"
"| `- 6:0:4:0 sdg 8:96 active undef running\n"
"`-+- policy='round-robin 0' prio=-1 status=enabled\n"
" |- 6:0:3:0 sdf 8:80 active undef running\n"
" `- 6:0:5:0 sdh 8:112 active undef running\n"
)
return out, None
self.stubs.Set(self.linuxscsi, '_execute', fake_execute)
self.stubs.SmartSet(os, 'stat', self.fake_stat)
info = self.linuxscsi.find_multipath_device('/dev/sde')
LOG.error("info = %s" % info)
self.assertEqual("36005076da00638089c000000000004d5", info["id"])
self.assertEqual("36005076da00638089c000000000004d5", info["name"])
self.assertEqual("/dev/mapper/36005076da00638089c000000000004d5",
info["device"])
self.assertEqual("/dev/sde", info['devices'][0]['device'])
self.assertEqual("6", info['devices'][0]['host'])
self.assertEqual("0", info['devices'][0]['channel'])
self.assertEqual("2", info['devices'][0]['id'])
self.assertEqual("0", info['devices'][0]['lun'])
self.assertEqual("/dev/sdf", info['devices'][2]['device'])
self.assertEqual("6", info['devices'][2]['host'])
self.assertEqual("0", info['devices'][2]['channel'])
self.assertEqual("3", info['devices'][2]['id'])
self.assertEqual("0", info['devices'][2]['lun'])
def test_find_multipath_device_ds8000(self):
def fake_execute(*cmd, **kwargs):
out = ("36005076303ffc48e0000000000000101 dm-2 IBM,2107900\n"
"size=1.0G features='1 queue_if_no_path' hwhandler='0'"
" wp=rw\n"
"`-+- policy='round-robin 0' prio=-1 status=active\n"
" |- 6:0:2:0 sdd 8:64 active undef running\n"
" `- 6:1:0:3 sdc 8:32 active undef running\n"
)
return out, None
self.stubs.Set(self.linuxscsi, '_execute', fake_execute)
self.stubs.SmartSet(os, 'stat', self.fake_stat)
info = self.linuxscsi.find_multipath_device('/dev/sdd')
LOG.error("info = %s" % info)
self.assertEqual("36005076303ffc48e0000000000000101", info["id"])
self.assertEqual("36005076303ffc48e0000000000000101", info["name"])
self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101",
info["device"])
self.assertEqual("/dev/sdd", info['devices'][0]['device'])
self.assertEqual("6", info['devices'][0]['host'])
self.assertEqual("0", info['devices'][0]['channel'])
self.assertEqual("2", info['devices'][0]['id'])
self.assertEqual("0", info['devices'][0]['lun'])
self.assertEqual("/dev/sdc", info['devices'][1]['device'])
self.assertEqual("6", info['devices'][1]['host'])
self.assertEqual("1", info['devices'][1]['channel'])
self.assertEqual("0", info['devices'][1]['id'])
self.assertEqual("3", info['devices'][1]['lun'])
def test_find_multipath_device_with_error(self):
def fake_execute(*cmd, **kwargs):
out = ("Oct 13 10:24:01 | /lib/udev/scsi_id exitted with 1\n"
"36005076303ffc48e0000000000000101 dm-2 IBM,2107900\n"
"size=1.0G features='1 queue_if_no_path' hwhandler='0'"
" wp=rw\n"
"`-+- policy='round-robin 0' prio=-1 status=active\n"
" |- 6:0:2:0 sdd 8:64 active undef running\n"
" `- 6:1:0:3 sdc 8:32 active undef running\n"
)
return out, None
self.stubs.Set(self.linuxscsi, '_execute', fake_execute)
self.stubs.SmartSet(os, 'stat', self.fake_stat)
info = self.linuxscsi.find_multipath_device('/dev/sdd')
LOG.error("info = %s" % info)
self.assertEqual("36005076303ffc48e0000000000000101", info["id"])
self.assertEqual("36005076303ffc48e0000000000000101", info["name"])
self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101",
info["device"])