diff --git a/nova_powervm/tests/virt/powervm/volume/test_vscsi.py b/nova_powervm/tests/virt/powervm/volume/test_vscsi.py index 26a16233..ffb8d5c2 100644 --- a/nova_powervm/tests/virt/powervm/volume/test_vscsi.py +++ b/nova_powervm/tests/virt/powervm/volume/test_vscsi.py @@ -68,10 +68,16 @@ class BaseVSCSITest(test.TestCase): @mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.getter') @mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid') def init_vol_adpt(mock_pvm_uuid, mock_getter): - con_info = {'data': {'initiator_target_map': {p_wwpn1: ['t1'], - p_wwpn2: ['t2', - 't3']}, - 'target_lun': '1', 'volume_id': 'id'}} + con_info = { + 'data': { + 'initiator_target_map': { + p_wwpn1: ['t1'], + p_wwpn2: ['t2', 't3'] + }, + 'target_lun': '1', + 'volume_id': 'id' + }, + } mock_inst = mock.MagicMock() mock_pvm_uuid.return_value = '1234' @@ -189,6 +195,7 @@ class TestVSCSIAdapter(BaseVSCSITest): # The mock return values mock_hdisk_from_uuid.return_value = 'device_name' mock_get_vm_id.return_value = 'partition_id' + self.vol_drv._set_udid('UDIDIT!') def validate_remove_maps(vios_w, vm_uuid, match_func): self.assertIsInstance(vios_w, pvm_vios.VIOS) @@ -216,6 +223,7 @@ class TestVSCSIAdapter(BaseVSCSITest): mock_remove_maps.return_value = None mock_hdisk_from_uuid.return_value = 'device_name' mock_get_vm_id.return_value = 'partition_id' + self.vol_drv._set_udid('UDIDIT!') # Run the method self.vol_drv.disconnect_volume() @@ -225,6 +233,38 @@ class TestVSCSIAdapter(BaseVSCSITest): self.assertEqual(0, self.ft_fx.patchers['update'].mock.call_count) self.assertEqual(1, len(self.vol_drv._vioses_modified)) + @mock.patch('pypowervm.tasks.hdisk.remove_hdisk') + @mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.hdisk_from_uuid') + @mock.patch('pypowervm.tasks.scsi_mapper.remove_maps') + @mock.patch('nova_powervm.virt.powervm.vm.get_vm_id') + def test_disconnect_volume_no_udid( + self, mock_get_vm_id, mock_remove_maps, mock_hdisk_from_uuid, + mock_remove_hdisk): + + # The mock return values + mock_hdisk_from_uuid.return_value = 'device_name' + mock_get_vm_id.return_value = 'partition_id' + + def validate_remove_maps(vios_w, vm_uuid, match_func): + self.assertIsInstance(vios_w, pvm_vios.VIOS) + self.assertEqual('partition_id', vm_uuid) + return 'removed' + mock_remove_maps.side_effect = validate_remove_maps + + with mock.patch.object( + self.vol_drv, '_discover_volume_on_vios', + return_value=('status', 'dev_name', 'udidit')): + + # Run the method + self.vol_drv.disconnect_volume() + + # As initialized above, remove_maps returns True to trigger update. + self.assertEqual(1, mock_remove_maps.call_count) + self.assertEqual(1, self.ft_fx.patchers['update'].mock.call_count) + mock_remove_hdisk.assert_called_once_with( + self.adpt, mock.ANY, 'dev_name', self.vios_uuid) + self.assertListEqual([self.vios_uuid], self.vol_drv._vioses_modified) + @mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.hdisk_from_uuid') @mock.patch('pypowervm.tasks.scsi_mapper.remove_maps') def test_disconnect_volume_no_valid_vio(self, mock_remove_maps, diff --git a/nova_powervm/virt/powervm/volume/vscsi.py b/nova_powervm/virt/powervm/volume/vscsi.py index 5b65a11d..7497df4a 100644 --- a/nova_powervm/virt/powervm/volume/vscsi.py +++ b/nova_powervm/virt/powervm/volume/vscsi.py @@ -250,16 +250,22 @@ class VscsiVolumeAdapter(v_driver.FibreChannelVolumeAdapter): """ LOG.debug("Disconnect volume %(vol)s from vios uuid %(uuid)s", dict(vol=volume_id, uuid=vios_w.uuid)) - volume_udid = None + udid, device_name = None, None try: - volume_udid = self._get_udid() - device_name = vios_w.hdisk_from_uuid(volume_udid) + udid = self._get_udid() + if not udid: + # We lost our bdm data. We'll need to discover it. + status, device_name, udid = self._discover_volume_on_vios( + vios_w, volume_id) + + if udid and not device_name: + device_name = vios_w.hdisk_from_uuid(udid) if not device_name: LOG.warn(_LW("Disconnect Volume: No mapped device found on " "Virtual I/O Server %(vios)s for volume " "%(volume_id)s. Volume UDID: %(volume_uid)s"), - {'volume_uid': volume_udid, 'volume_id': volume_id, + {'volume_uid': udid, 'volume_id': volume_id, 'vios': vios_w.name}) return False @@ -267,7 +273,7 @@ class VscsiVolumeAdapter(v_driver.FibreChannelVolumeAdapter): LOG.warn(_LW("Disconnect Volume: Failed to find disk on Virtual " "I/O Server %(vios_name)s for volume %(volume_id)s. " "Volume UDID: %(volume_uid)s. Error: %(error)s"), - {'error': e, 'volume_uid': volume_udid, + {'error': e, 'volume_uid': udid, 'volume_id': volume_id, 'vios_name': vios_w.name}) return False @@ -275,7 +281,7 @@ class VscsiVolumeAdapter(v_driver.FibreChannelVolumeAdapter): LOG.info(_LI("Disconnect Volume: Discovered the device %(hdisk)s on " "Virtual I/O Server %(vios_name)s for volume " "%(volume_id)s. Volume UDID: %(volume_uid)s."), - {'volume_uid': volume_udid, 'volume_id': volume_id, + {'volume_uid': udid, 'volume_id': volume_id, 'vios_name': vios_w.name, 'hdisk': device_name}) # Add the action to remove the mapping when the stg_ftsk is run. @@ -405,6 +411,8 @@ class VscsiVolumeAdapter(v_driver.FibreChannelVolumeAdapter): try: return self.connection_info['data'][UDID_KEY] except (KeyError, ValueError): - LOG.warn(_LW(u'Failed to retrieve device_id key from BDM for ' + # It's common to lose our specific data in the BDM. The connection + # information can be 'refreshed' by operations like LPM and resize + LOG.info(_LI(u'Failed to retrieve device_id key from BDM for ' 'volume id %s'), self.volume_id) return None