From 07c24d6bbe6b55638f46d9f7dc9123c5d96eb453 Mon Sep 17 00:00:00 2001 From: Sabari Kumar Murugesan Date: Mon, 30 Sep 2013 11:56:33 -0700 Subject: [PATCH] VMware: iscsi target discovery fails while attaching volumes VMware Drivers are unable to discover iscsi targets because the api calls fail to specify the iscsi server location (referred in the code as target portal) to retrieve targets. This is critical for the attach_volume feature to work with iscsi supported cinder drivers. Closes-Bug: #1235112 Change-Id: I60c06ae651bb2eb6d466a5ca61a09f289c21d1a3 --- nova/tests/virt/vmwareapi/test_driver_api.py | 40 +++++++++++++++++--- nova/virt/vmwareapi/fake.py | 17 +++++++++ nova/virt/vmwareapi/volume_util.py | 23 ++++++++++- nova/virt/vmwareapi/volumeops.py | 5 ++- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/nova/tests/virt/vmwareapi/test_driver_api.py b/nova/tests/virt/vmwareapi/test_driver_api.py index 92b67e8c8187..6ed8a55f691a 100644 --- a/nova/tests/virt/vmwareapi/test_driver_api.py +++ b/nova/tests/virt/vmwareapi/test_driver_api.py @@ -267,7 +267,6 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): self.flags(host_ip='test_url', host_username='test_username', host_password='test_pass', - cluster_name='test_cluster', datastore_regex='.*', use_linked_clone=False, group='vmware') self.flags(vnc_enabled=False, @@ -1378,14 +1377,22 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): def test_attach_iscsi_disk_to_vm(self): self._create_vm() connection_info = self._test_vmdk_connection_info('iscsi') - connection_info['data']['target_portal'] = 'fake_target_portal' + connection_info['data']['target_portal'] = 'fake_target_host:port' connection_info['data']['target_iqn'] = 'fake_target_iqn' mount_point = '/dev/vdc' discover = ('fake_name', 'fake_uuid') - self.mox.StubOutWithMock(volumeops.VMwareVolumeOps, - 'discover_st') - volumeops.VMwareVolumeOps.discover_st( - connection_info['data']).AndReturn(discover) + self.mox.StubOutWithMock(volume_util, 'find_st') + # simulate target not found + volume_util.find_st(mox.IgnoreArg(), connection_info['data'], + mox.IgnoreArg()).AndReturn((None, None)) + self.mox.StubOutWithMock(volume_util, '_add_iscsi_send_target_host') + # rescan gets called with target portal + volume_util.rescan_iscsi_hba( + self.conn._session, + target_portal=connection_info['data']['target_portal']) + # simulate target found + volume_util.find_st(mox.IgnoreArg(), connection_info['data'], + mox.IgnoreArg()).AndReturn(discover) self.mox.StubOutWithMock(volumeops.VMwareVolumeOps, 'attach_disk_to_vm') volumeops.VMwareVolumeOps.attach_disk_to_vm(mox.IgnoreArg(), @@ -1395,6 +1402,27 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): self.conn.attach_volume(None, connection_info, self.instance, mount_point) + def test_rescan_iscsi_hba(self): + fake_target_portal = 'fake_target_host:port' + host_storage_sys = vmwareapi_fake._get_objects( + "HostStorageSystem").objects[0] + iscsi_hba_array = host_storage_sys.get('storageDeviceInfo' + '.hostBusAdapter') + iscsi_hba = iscsi_hba_array.HostHostBusAdapter[0] + # Check the host system does not have the send target + self.assertRaises(AttributeError, getattr, iscsi_hba, + 'configuredSendTarget') + # Rescan HBA with the target portal + volume_util.rescan_iscsi_hba(self.conn._session, None, + fake_target_portal) + # Check if HBA has the target portal configured + self.assertEqual('fake_target_host', + iscsi_hba.configuredSendTarget[0].address) + # Rescan HBA with same portal + volume_util.rescan_iscsi_hba(self.conn._session, None, + fake_target_portal) + self.assertEqual(1, len(iscsi_hba.configuredSendTarget)) + def test_find_st(self): data = {'target_portal': 'fake_target_host:port', 'target_iqn': 'fake_target_iqn'} diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index c8e1f96cb6b0..869845183227 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -1218,6 +1218,18 @@ class FakeVim(object): host_mdo = _db_content["HostSystem"][_host_sk] host_mdo._add_port_group(kwargs.get("portgrp")) + def _add_iscsi_send_tgt(self, method, *args, **kwargs): + """Adds a iscsi send target to the hba.""" + send_targets = kwargs.get('targets') + host_storage_sys = _get_objects('HostStorageSystem').objects[0] + iscsi_hba_array = host_storage_sys.get('storageDeviceInfo' + '.hostBusAdapter') + iscsi_hba = iscsi_hba_array.HostHostBusAdapter[0] + if hasattr(iscsi_hba, 'configuredSendTarget'): + iscsi_hba.configuredSendTarget.extend(send_targets) + else: + iscsi_hba.configuredSendTarget = send_targets + def __getattr__(self, attr_name): if attr_name != "Login": self._check_session() @@ -1314,3 +1326,8 @@ class FakeVim(object): return lambda *args, **kwargs: self._just_return_task(attr_name) elif attr_name == "ExitMaintenanceMode_Task": return lambda *args, **kwargs: self._just_return_task(attr_name) + elif attr_name == "AddInternetScsiSendTargets": + return lambda *args, **kwargs: self._add_iscsi_send_tgt(attr_name, + *args, **kwargs) + elif attr_name == "RescanHba": + return lambda *args, **kwargs: self._just_return_task(attr_name) diff --git a/nova/virt/vmwareapi/volume_util.py b/nova/virt/vmwareapi/volume_util.py index f0b689e29ae8..45421bf2bde5 100644 --- a/nova/virt/vmwareapi/volume_util.py +++ b/nova/virt/vmwareapi/volume_util.py @@ -121,7 +121,7 @@ def find_st(session, data, cluster=None): return result -def rescan_iscsi_hba(session, cluster=None): +def rescan_iscsi_hba(session, cluster=None, target_portal=None): """Rescan the iSCSI HBA to discover iSCSI targets.""" host_mor = vm_util.get_host_ref(session, cluster) storage_system_mor = session._call_method(vim_util, "get_dynamic_property", @@ -141,11 +141,30 @@ def rescan_iscsi_hba(session, cluster=None): for hba in host_hbas: if hba.__class__.__name__ == 'HostInternetScsiHba': hba_device = hba.device + if target_portal: + # Check if iscsi host is already in the send target host list + send_targets = getattr(hba, 'configuredSendTarget', []) + send_tgt_portals = ['%s:%s' % (s.address, s.port) for s in + send_targets] + if target_portal not in send_tgt_portals: + _add_iscsi_send_target_host(session, storage_system_mor, + hba_device, target_portal) break else: return - LOG.debug(_("Rescanning HBA %s") % hba_device) session._call_method(session._get_vim(), "RescanHba", storage_system_mor, hbaDevice=hba_device) LOG.debug(_("Rescanned HBA %s ") % hba_device) + + +def _add_iscsi_send_target_host(session, storage_system_mor, hba_device, + target_portal): + """Adds the iscsi host to send target host list.""" + client_factory = session._get_vim().client.factory + send_tgt = client_factory.create('ns0:HostInternetScsiHbaSendTarget') + (send_tgt.address, send_tgt.port) = target_portal.split(':') + LOG.debug(_("Adding iSCSI host %s to send targets"), send_tgt.address) + session._call_method( + session._get_vim(), "AddInternetScsiSendTargets", storage_system_mor, + iScsiHbaDevice=hba_device, targets=[send_tgt]) diff --git a/nova/virt/vmwareapi/volumeops.py b/nova/virt/vmwareapi/volumeops.py index 315376cb96c1..fd84c1b1cbc8 100644 --- a/nova/virt/vmwareapi/volumeops.py +++ b/nova/virt/vmwareapi/volumeops.py @@ -143,8 +143,9 @@ class VMwareVolumeOps(object): if device_name: LOG.debug(_("Storage target found. No need to discover")) return (device_name, uuid) - # Rescan iSCSI HBA - volume_util.rescan_iscsi_hba(self._session, self._cluster) + # Rescan iSCSI HBA with iscsi target host + volume_util.rescan_iscsi_hba(self._session, self._cluster, + target_portal) # Find iSCSI Target again device_name, uuid = volume_util.find_st(self._session, data, self._cluster)