From 052859fbdb10f7f796512ef4f9a3d6e87fd3f87f Mon Sep 17 00:00:00 2001 From: "Walter A. Boring IV" Date: Thu, 17 Jan 2013 16:36:22 -0800 Subject: Fibre channel block storage support (nova changes) implements blueprint libvirt-fibre-channel These changes constitute the required libvirt changes to support attaching Fibre Channel volumes to a VM. This requires a few new packages to be available on the system sysfsutils -- This is needed to discover the FC HBAs sg3-utils -- this package is needed for scsi device discovery multipath -- This package is needed for multipath support. Typical Fibre Channel arrays support exporting volumes via multiple ports, so multipath support is highly desirable for fault tolerance. If multipath is not installed, the new FibreChannel libvirt volume driver will still work. If multipath is enabled, the new Fibre Channel volume driver detects each of the attached devices for the volume, and properly removes every one of them on detach. In order to use this, the cinder volume driver's initialize_connection will simply return a dictionary with a new driver_volume_type called 'fibrechan'. The target_wwn can be a single entry or a list of wwns that correspond to the list of remote wwn(s) that will export the volume. return {'driver_volume_type': 'fibre_channel', 'data': {'target_lun', 1, 'target_wwn': '1234567890123'}} or return {'driver_volume_type': 'fibre_channel', 'data': {'target_lun', 1, 'target_wwn': ['1234567890123', '0987654321321']}} Change-Id: Ifccc56f960ef434f7cb56a9367e4cad288358440 --- nova/tests/fake_libvirt_utils.py | 54 ++++++++++++++++++++++++++ nova/tests/test_libvirt.py | 6 ++- nova/tests/test_libvirt_volume.py | 79 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) (limited to 'nova/tests') diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index 285a4b7e3..caa5e72d1 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -34,6 +34,60 @@ def get_iscsi_initiator(): return "fake.initiator.iqn" +def get_fc_hbas(): + return [{'ClassDevice': 'host1', + 'ClassDevicePath': '/sys/devices/pci0000:00/0000:00:03.0' + '/0000:05:00.2/host1/fc_host/host1', + 'dev_loss_tmo': '30', + 'fabric_name': '0x1000000533f55566', + 'issue_lip': '', + 'max_npiv_vports': '255', + 'maxframe_size': '2048 bytes', + 'node_name': '0x200010604b019419', + 'npiv_vports_inuse': '0', + 'port_id': '0x680409', + 'port_name': '0x100010604b019419', + 'port_state': 'Online', + 'port_type': 'NPort (fabric via point-to-point)', + 'speed': '10 Gbit', + 'supported_classes': 'Class 3', + 'supported_speeds': '10 Gbit', + 'symbolic_name': 'Emulex 554M FV4.0.493.0 DV8.3.27', + 'tgtid_bind_type': 'wwpn (World Wide Port Name)', + 'uevent': None, + 'vport_create': '', + 'vport_delete': ''}] + + +def get_fc_hbas_info(): + hbas = 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 get_fc_wwpns(): + hbas = get_fc_hbas() + wwpns = [] + for hba in hbas: + wwpn = hba['port_name'].replace('0x', '') + wwpns.append(wwpn) + + return wwpns + + +def get_fc_wwnns(): + hbas = get_fc_hbas() + wwnns = [] + for hba in hbas: + wwnn = hba['node_name'].replace('0x', '') + wwnns.append(wwnn) + + return wwnns + + def create_image(disk_format, path, size): pass diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6cff3f567..2c5b07428 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -338,6 +338,8 @@ class LibvirtConnTestCase(test.TestCase): initiator = 'fake.initiator.iqn' ip = 'fakeip' host = 'fakehost' + wwpns = ['100010604b019419'] + wwnns = ['200010604b019419'] self.flags(my_ip=ip) self.flags(host=host) @@ -345,7 +347,9 @@ class LibvirtConnTestCase(test.TestCase): expected = { 'ip': ip, 'initiator': initiator, - 'host': host + 'host': host, + 'wwpns': wwpns, + 'wwnns': wwnns } volume = { 'id': 'fake' diff --git a/nova/tests/test_libvirt_volume.py b/nova/tests/test_libvirt_volume.py index 1d157ba34..fa71782ee 100644 --- a/nova/tests/test_libvirt_volume.py +++ b/nova/tests/test_libvirt_volume.py @@ -17,10 +17,14 @@ import os +from nova import exception from nova.openstack.common import cfg +from nova.storage import linuxscsi from nova import test +from nova.tests import fake_libvirt_utils from nova import utils from nova.virt import fake +from nova.virt.libvirt import utils as libvirt_utils from nova.virt.libvirt import volume CONF = cfg.CONF @@ -467,3 +471,78 @@ class LibvirtVolumeTestCase(test.TestCase): ('stat', export_mnt_base), ('mount', '-t', 'glusterfs', export_string, export_mnt_base)] self.assertEqual(self.executes, expected_commands) + + 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_libvirt_fibrechan_driver(self): + self.stubs.Set(libvirt_utils, 'get_fc_hbas', + fake_libvirt_utils.get_fc_hbas) + self.stubs.Set(libvirt_utils, 'get_fc_hbas_info', + fake_libvirt_utils.get_fc_hbas_info) + # NOTE(vish) exists is to make driver assume connecting worked + self.stubs.Set(os.path, 'exists', lambda x: True) + self.stubs.Set(os.path, 'realpath', lambda x: '/dev/sdb') + libvirt_driver = volume.LibvirtFibreChannelVolumeDriver(self.fake_conn) + multipath_devname = '/dev/md-1' + devices = {"device": multipath_devname, + "devices": [{'device': '/dev/sdb', + 'address': '1:0:0:1', + 'host': 1, 'channel': 0, + 'id': 0, 'lun': 1}]} + self.stubs.Set(linuxscsi, 'find_multipath_device', lambda x: devices) + self.stubs.Set(linuxscsi, 'remove_device', lambda x: None) + location = '10.0.2.15:3260' + name = 'volume-00000001' + wwn = '1234567890123456' + vol = {'id': 1, 'name': name} + connection_info = self.fibrechan_connection(vol, location, wwn) + mount_device = "vde" + disk_info = { + "bus": "virtio", + "dev": mount_device, + "type": "disk" + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) + tree = conf.format_dom() + dev_str = '/dev/disk/by-path/pci-0000:05:00.2-fc-0x%s-lun-1' % wwn + self.assertEqual(tree.get('type'), 'block') + self.assertEqual(tree.find('./source').get('dev'), multipath_devname) + connection_info["data"]["devices"] = devices["devices"] + libvirt_driver.disconnect_volume(connection_info, mount_device) + expected_commands = [] + self.assertEqual(self.executes, expected_commands) + + self.stubs.Set(libvirt_utils, 'get_fc_hbas', + lambda: []) + self.stubs.Set(libvirt_utils, 'get_fc_hbas_info', + lambda: []) + self.assertRaises(exception.NovaException, + libvirt_driver.connect_volume, + connection_info, disk_info) + + self.stubs.Set(libvirt_utils, 'get_fc_hbas', lambda: []) + self.stubs.Set(libvirt_utils, 'get_fc_hbas_info', lambda: []) + self.assertRaises(exception.NovaException, + libvirt_driver.connect_volume, + connection_info, disk_info) + + def test_libvirt_fibrechan_getpci_num(self): + libvirt_driver = volume.LibvirtFibreChannelVolumeDriver(self.fake_conn) + hba = {'device_path': "/sys/devices/pci0000:00/0000:00:03.0" + "/0000:05:00.3/host2/fc_host/host2"} + pci_num = libvirt_driver._get_pci_num(hba) + self.assertEqual("0000:05:00.3", pci_num) + + hba = {'device_path': "/sys/devices/pci0000:00/0000:00:03.0" + "/0000:05:00.3/0000:06:00.6/host2/fc_host/host2"} + pci_num = libvirt_driver._get_pci_num(hba) + self.assertEqual("0000:06:00.6", pci_num) -- cgit