diff --git a/nova_powervm/tests/virt/powervm/test_driver.py b/nova_powervm/tests/virt/powervm/test_driver.py index bb943297..2469d9b3 100644 --- a/nova_powervm/tests/virt/powervm/test_driver.py +++ b/nova_powervm/tests/virt/powervm/test_driver.py @@ -270,10 +270,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') @mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver._vol_drv_iter') - def test_spawn_ops( - self, mock_vdi, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img, - mock_conn_vol, mock_crt_cfg_drv): + def test_spawn_ops(self, mock_vdi, mock_pwron, mock_get_flv, mock_cfg_drv, + mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol, + mock_crt_disk_img, mock_conn_vol, mock_crt_cfg_drv): """Validates the 'typical' spawn flow of the spawn of an instance. Uses a basic disk image, attaching networks and powering on. @@ -315,9 +314,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_with_cfg( - self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_val_vopt, - mock_cfg_vopt, mock_plug_vifs, mock_plug_mgmt_vif): + def test_spawn_with_cfg(self, mock_pwron, mock_get_flv, mock_cfg_drv, + mock_val_vopt, mock_cfg_vopt, mock_plug_vifs, + mock_plug_mgmt_vif): """Validates the PowerVM spawn w/ config drive operations.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst.get_flavor() @@ -351,9 +350,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_with_bdms( - self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): + def test_spawn_with_bdms(self, mock_pwron, mock_get_flv, mock_cfg_drv, + mock_plug_vifs, mock_plug_mgmt_vif, + mock_boot_from_vol, mock_crt_img, mock_save): """Validates the PowerVM spawn. Specific Test: spawn of an image that has a disk image and block device @@ -406,9 +405,11 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_with_image_meta_root_bdm( - self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): + def test_spawn_with_image_meta_root_bdm(self, mock_pwron, mock_get_flv, + mock_cfg_drv, mock_plug_vifs, + mock_plug_mgmt_vif, + mock_boot_from_vol, + mock_crt_img, mock_save): """Validates the PowerVM spawn. @@ -465,9 +466,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_with_root_bdm( - self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): + def test_spawn_with_root_bdm(self, mock_pwron, mock_get_flv, mock_cfg_drv, + mock_plug_vifs, mock_plug_mgmt_vif, + mock_boot_from_vol, mock_crt_img, mock_save): """Validates the PowerVM spawn. Specific test: when no image is given and only block device mappings @@ -523,11 +524,11 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') @mock.patch('taskflow.patterns.linear_flow.Flow') @mock.patch('taskflow.engines.run') - def test_spawn_recreate( - self, mock_tf_run, mock_flow, mock_build_slot_mgr, mock_vol_drv_iter, - mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_find_disk, mock_conn_disk, - mock_conn_vol, mock_crt_cfg_drv): + def test_spawn_recreate(self, mock_tf_run, mock_flow, mock_build_slot_mgr, + mock_vol_drv_iter, mock_pwron, mock_get_flv, + mock_cfg_drv, mock_plug_vifs, mock_plug_mgmt_vif, + mock_boot_from_vol, mock_find_disk, mock_conn_disk, + mock_conn_vol, mock_crt_cfg_drv): """Validates the 'recreate' spawn flow. Uses a basic disk image, attaching networks and powering on. @@ -589,9 +590,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_off') - def test_spawn_ops_rollback( - self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt, - mock_plug_vifs, mock_plug_mgmt_vifs, mock_save): + def test_spawn_ops_rollback(self, mock_pwroff, mock_pwron, mock_get_flv, + mock_cfg_drv, mock_dlt, mock_plug_vifs, + mock_plug_mgmt_vifs, mock_save): """Validates the PowerVM driver operations. Will do a rollback.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst.get_flavor() @@ -633,11 +634,10 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.powervm.driver.PowerVMDriver.' '_get_boot_connectivity_type') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_ibmi( - self, mock_pwron, mock_boot_conn_type, - mock_update_lod_src, mock_get_flv, mock_cfg_drv, - mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol, - mock_crt_img, mock_save): + def test_spawn_ibmi(self, mock_pwron, mock_boot_conn_type, + mock_update_lod_src, mock_get_flv, mock_cfg_drv, + mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol, + mock_crt_img, mock_save): """Validates the PowerVM spawn to create an IBMi server.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst_ibmi.get_flavor() @@ -692,11 +692,12 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.virt.powervm.driver.PowerVMDriver.' '_get_boot_connectivity_type') @mock.patch('pypowervm.tasks.power.power_on') - def test_spawn_ibmi_without_bdms( - self, mock_pwron, mock_boot_conn_type, mock_update_lod_src, - mock_get_flv, mock_cfg_drv, mock_plug_vifs, - mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img, - mock_conn_vol, mock_crt_cfg_drv): + def test_spawn_ibmi_without_bdms(self, mock_pwron, mock_boot_conn_type, + mock_update_lod_src, mock_get_flv, + mock_cfg_drv, mock_plug_vifs, + mock_plug_mgmt_vif, mock_boot_from_vol, + mock_crt_disk_img, mock_conn_vol, + mock_crt_cfg_drv): """Validates the 'typical' spawn flow for IBMi Perform an UT using an image with local disk, attaching networks @@ -734,9 +735,10 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar') @mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.objects.flavor.Flavor.get_by_id') - def test_spawn_ops_rollback_disk( - self, mock_get_flv, mock_cfg_drv, mock_dlt, mock_plug_vifs, - mock_plug_mgmt_vifs, mock_crt_disk, mock_delete_disks): + def test_spawn_ops_rollback_disk(self, mock_get_flv, mock_cfg_drv, + mock_dlt, mock_plug_vifs, + mock_plug_mgmt_vifs, mock_crt_disk, + mock_delete_disks): """Validates the rollback if failure occurs on disk create.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst.get_flavor() @@ -767,9 +769,10 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_off') - def test_spawn_ops_rollback_on_vol_connect( - self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt, - mock_plug_vifs, mock_plug_mgmt_vifs, mock_save): + def test_spawn_ops_rollback_on_vol_connect(self, mock_pwroff, mock_pwron, + mock_get_flv, mock_cfg_drv, + mock_dlt, mock_plug_vifs, + mock_plug_mgmt_vifs, mock_save): """Validates the rollbacks on a volume connect failure.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst.get_flavor() @@ -899,10 +902,10 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') - def test_destroy_internal( - self, mock_bld_slot_mgr, mock_get_flv, mock_pvmuuid, - mock_val_vopt, mock_dlt_vopt, mock_pwroff, mock_dlt, - mock_boot_from_vol, mock_unplug_vifs): + def test_destroy_internal(self, mock_bld_slot_mgr, mock_get_flv, + mock_pvmuuid, mock_val_vopt, mock_dlt_vopt, + mock_pwroff, mock_dlt, mock_boot_from_vol, + mock_unplug_vifs): """Validates the basic PowerVM destroy.""" # NVRAM Manager self.drv.nvram_mgr = mock.Mock() @@ -1053,10 +1056,12 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid') @mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') - def test_destroy_internal_no_nvram_cleanup( - self, mock_bld_slot_mgr, mock_get_flv, mock_pvmuuid, - mock_val_vopt, mock_dlt_vopt, mock_pwroff, mock_dlt, - mock_boot_from_vol, mock_unplug_vifs): + def test_destroy_internal_no_nvram_cleanup(self, mock_bld_slot_mgr, + mock_get_flv, mock_pvmuuid, + mock_val_vopt, mock_dlt_vopt, + mock_pwroff, mock_dlt, + mock_boot_from_vol, + mock_unplug_vifs): """Validates the basic PowerVM destroy, without NVRAM cleanup. Used to validate the behavior when destroying evacuated instances. @@ -1210,9 +1215,9 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.' '_validate_vopt_vg') @mock.patch('nova.objects.flavor.Flavor.get_by_id') - def test_destroy_rollback( - self, mock_get_flv, mock_val_vopt, mock_dlt_vopt, - mock_pwroff, mock_dlt, mock_unplug_vifs): + def test_destroy_rollback(self, mock_get_flv, mock_val_vopt, + mock_dlt_vopt, mock_pwroff, mock_dlt, + mock_unplug_vifs): """Validates the basic PowerVM destroy rollback mechanism works.""" # Set up the mocks to the tasks. mock_get_flv.return_value = self.inst.get_flavor() @@ -1492,9 +1497,8 @@ class TestPowerVMDriver(test.TestCase): @mock.patch('nova_powervm.virt.powervm.vm.get_cnas') @mock.patch('nova_powervm.virt.powervm.vm.get_instance_wrapper') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') - def test_plug_vifs( - self, mock_bld_slot_mgr, mock_wrap, mock_vm_get, mock_plug_vif, - mock_get_rmc_vswitch, mock_plug_rmc_vif): + def test_plug_vifs(self, mock_bld_slot_mgr, mock_wrap, mock_vm_get, + mock_plug_vif, mock_get_rmc_vswitch, mock_plug_rmc_vif): # Mock up the CNA response cnas = [mock.MagicMock(), mock.MagicMock()] cnas[0].mac = 'AABBCCDDEEFF' @@ -1723,7 +1727,7 @@ class TestPowerVMDriver(test.TestCase): mapping_dict = {'source_type': 'volume', 'volume_id': volume_id, 'destination_type': 'volume', 'connection_info': - jsonutils.dumps(connection_info), + jsonutils.dumps(connection_info), } bdm_dict = nova_block_device.BlockDeviceDict(mapping_dict) bdm_obj = bdmobj.BlockDeviceMapping(**bdm_dict) @@ -1959,6 +1963,7 @@ class TestNovaEventHandler(test.TestCase): self.mock_driver = mock.Mock() self.handler = driver.NovaEventHandler(self.mock_driver) + @mock.patch('nova.context.get_admin_context', mock.MagicMock()) @mock.patch.object(vm, 'get_instance') @mock.patch.object(vm, 'get_vm_qp') def test_events(self, mock_qprops, mock_get_inst): @@ -2010,5 +2015,8 @@ class TestNovaEventHandler(test.TestCase): mock_get_inst.return_value = powervm.TEST_INST1 self.handler.process(event_data) + mock_get_inst.assert_called_once_with(mock.ANY, '794654F5-B6E9-' + '4A51-BEC2-A73E41EAA938') + self.assertTrue(self.mock_driver.emit_event.called) self.assertTrue(self.mock_driver.nvram_mgr.store.called) diff --git a/nova_powervm/virt/powervm/driver.py b/nova_powervm/virt/powervm/driver.py index 969c7b66..b85f9d55 100644 --- a/nova_powervm/virt/powervm/driver.py +++ b/nova_powervm/virt/powervm/driver.py @@ -1864,32 +1864,43 @@ class NovaEventHandler(pvm_apt.RawEventHandler): def __init__(self, driver): self._driver = driver - def _handle_event(self, uri, etype, details, eid): + def _handle_event(self, uri, etype, details, eid, inst=None): """Handle an individual event. :param uri: PowerVM event uri :param etype: PowerVM event type :param details: PowerVM event details :param eid: PowerVM event id + :param inst: (Optional, Default: None) The pypowervm wrapper object + that represents the VM instance. + If None we try to look it up based on UUID. + :return: returns the instance object or None (when it's not an + instance event or action is not partition state change + or NVRAM change) """ # See if this uri ends with a PowerVM UUID. if not pvm_util.is_instance_path(uri): - return + return None pvm_uuid = pvm_util.get_req_path_uuid( uri, preserve_case=True) # If a vm event and one we handle, call the inst handler. if (uri.endswith('LogicalPartition/' + pvm_uuid) and (self.inst_actions_handled & set(details))): - inst = vm.get_instance(ctx.get_admin_context(), - pvm_uuid) + if not inst: + LOG.debug('PowerVM Nova Event Handler: Getting inst ' + 'for id %s' % pvm_uuid) + inst = vm.get_instance(ctx.get_admin_context(), + pvm_uuid) if inst: LOG.debug('Handle action "%(action)s" event for instance: ' '%(inst)s' % dict(action=details, inst=inst.name)) self._handle_inst_event( inst, pvm_uuid, uri, etype, details, eid) + return inst + return None def _handle_inst_event(self, inst, pvm_uuid, uri, etype, details, eid): """Handle an instance event. @@ -1964,6 +1975,7 @@ class NovaEventHandler(pvm_apt.RawEventHandler): }, ] """ + inst_cache = {} for pvm_event in events: try: # Pull all the pieces of the event. @@ -1976,7 +1988,9 @@ class NovaEventHandler(pvm_apt.RawEventHandler): if etype not in ['NEW_CLIENT']: LOG.debug('PowerVM Event-Action: %s URI: %s Details %s' % (etype, uri, details)) - self._handle_event(uri, etype, details, eid) + inst_cache[uri] = self._handle_event(uri, etype, details, eid, + inst=inst_cache.get(uri, + None)) except Exception as e: LOG.exception(e) LOG.warning(_LW('Unable to parse event URI: %s from PowerVM.'),