Use an instance cache in NovaEventHandler

This changes set adds an instance cache in the NovaEventHandler so that
a new instance object is not created everytime while processing multiple
events for the same VM.

Change-Id: I984f936624ae4867b89059883948d228da781d85
Closes-Bug: #1578649
This commit is contained in:
manasmandlekar
2016-05-26 07:43:39 -04:00
committed by Eric Fried
parent 84f0e8026a
commit f560b59133
2 changed files with 82 additions and 60 deletions

View File

@@ -270,10 +270,9 @@ class TestPowerVMDriver(test.TestCase):
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver._vol_drv_iter') @mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver._vol_drv_iter')
def test_spawn_ops( def test_spawn_ops(self, mock_vdi, mock_pwron, mock_get_flv, mock_cfg_drv,
self, mock_vdi, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img, mock_crt_disk_img, mock_conn_vol, mock_crt_cfg_drv):
mock_conn_vol, mock_crt_cfg_drv):
"""Validates the 'typical' spawn flow of the spawn of an instance. """Validates the 'typical' spawn flow of the spawn of an instance.
Uses a basic disk image, attaching networks and powering on. 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.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_cfg( def test_spawn_with_cfg(self, mock_pwron, mock_get_flv, mock_cfg_drv,
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_val_vopt, mock_val_vopt, mock_cfg_vopt, mock_plug_vifs,
mock_cfg_vopt, mock_plug_vifs, mock_plug_mgmt_vif): mock_plug_mgmt_vif):
"""Validates the PowerVM spawn w/ config drive operations.""" """Validates the PowerVM spawn w/ config drive operations."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor() 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.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_bdms( def test_spawn_with_bdms(self, mock_pwron, mock_get_flv, mock_cfg_drv,
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_plug_vifs, mock_plug_mgmt_vif,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): mock_boot_from_vol, mock_crt_img, mock_save):
"""Validates the PowerVM spawn. """Validates the PowerVM spawn.
Specific Test: spawn of an image that has a disk image and block device 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.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_image_meta_root_bdm( def test_spawn_with_image_meta_root_bdm(self, mock_pwron, mock_get_flv,
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): mock_plug_mgmt_vif,
mock_boot_from_vol,
mock_crt_img, mock_save):
"""Validates the PowerVM spawn. """Validates the PowerVM spawn.
@@ -465,9 +466,9 @@ class TestPowerVMDriver(test.TestCase):
@mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_root_bdm( def test_spawn_with_root_bdm(self, mock_pwron, mock_get_flv, mock_cfg_drv,
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_plug_vifs, mock_plug_mgmt_vif,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save): mock_boot_from_vol, mock_crt_img, mock_save):
"""Validates the PowerVM spawn. """Validates the PowerVM spawn.
Specific test: when no image is given and only block device mappings 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('nova_powervm.virt.powervm.slot.build_slot_mgr')
@mock.patch('taskflow.patterns.linear_flow.Flow') @mock.patch('taskflow.patterns.linear_flow.Flow')
@mock.patch('taskflow.engines.run') @mock.patch('taskflow.engines.run')
def test_spawn_recreate( def test_spawn_recreate(self, mock_tf_run, mock_flow, mock_build_slot_mgr,
self, mock_tf_run, mock_flow, mock_build_slot_mgr, mock_vol_drv_iter, mock_vol_drv_iter, mock_pwron, mock_get_flv,
mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_cfg_drv, mock_plug_vifs, mock_plug_mgmt_vif,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_find_disk, mock_conn_disk, mock_boot_from_vol, mock_find_disk, mock_conn_disk,
mock_conn_vol, mock_crt_cfg_drv): mock_conn_vol, mock_crt_cfg_drv):
"""Validates the 'recreate' spawn flow. """Validates the 'recreate' spawn flow.
Uses a basic disk image, attaching networks and powering on. 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('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('pypowervm.tasks.power.power_off') @mock.patch('pypowervm.tasks.power.power_off')
def test_spawn_ops_rollback( def test_spawn_ops_rollback(self, mock_pwroff, mock_pwron, mock_get_flv,
self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt, mock_cfg_drv, mock_dlt, mock_plug_vifs,
mock_plug_vifs, mock_plug_mgmt_vifs, mock_save): mock_plug_mgmt_vifs, mock_save):
"""Validates the PowerVM driver operations. Will do a rollback.""" """Validates the PowerVM driver operations. Will do a rollback."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor() mock_get_flv.return_value = self.inst.get_flavor()
@@ -633,11 +634,10 @@ class TestPowerVMDriver(test.TestCase):
@mock.patch('nova.virt.powervm.driver.PowerVMDriver.' @mock.patch('nova.virt.powervm.driver.PowerVMDriver.'
'_get_boot_connectivity_type') '_get_boot_connectivity_type')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_ibmi( def test_spawn_ibmi(self, mock_pwron, mock_boot_conn_type,
self, mock_pwron, mock_boot_conn_type, mock_update_lod_src, mock_get_flv, mock_cfg_drv,
mock_update_lod_src, mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol,
mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save):
mock_crt_img, mock_save):
"""Validates the PowerVM spawn to create an IBMi server.""" """Validates the PowerVM spawn to create an IBMi server."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst_ibmi.get_flavor() 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.' @mock.patch('nova.virt.powervm.driver.PowerVMDriver.'
'_get_boot_connectivity_type') '_get_boot_connectivity_type')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_ibmi_without_bdms( def test_spawn_ibmi_without_bdms(self, mock_pwron, mock_boot_conn_type,
self, mock_pwron, mock_boot_conn_type, mock_update_lod_src, mock_update_lod_src, mock_get_flv,
mock_get_flv, mock_cfg_drv, mock_plug_vifs, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img, mock_plug_mgmt_vif, mock_boot_from_vol,
mock_conn_vol, mock_crt_cfg_drv): mock_crt_disk_img, mock_conn_vol,
mock_crt_cfg_drv):
"""Validates the 'typical' spawn flow for IBMi """Validates the 'typical' spawn flow for IBMi
Perform an UT using an image with local disk, attaching networks 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_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova.virt.configdrive.required_by') @mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_spawn_ops_rollback_disk( def test_spawn_ops_rollback_disk(self, mock_get_flv, mock_cfg_drv,
self, mock_get_flv, mock_cfg_drv, mock_dlt, mock_plug_vifs, mock_dlt, mock_plug_vifs,
mock_plug_mgmt_vifs, mock_crt_disk, mock_delete_disks): mock_plug_mgmt_vifs, mock_crt_disk,
mock_delete_disks):
"""Validates the rollback if failure occurs on disk create.""" """Validates the rollback if failure occurs on disk create."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor() 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('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on') @mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('pypowervm.tasks.power.power_off') @mock.patch('pypowervm.tasks.power.power_off')
def test_spawn_ops_rollback_on_vol_connect( def test_spawn_ops_rollback_on_vol_connect(self, mock_pwroff, mock_pwron,
self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt, mock_get_flv, mock_cfg_drv,
mock_plug_vifs, mock_plug_mgmt_vifs, mock_save): mock_dlt, mock_plug_vifs,
mock_plug_mgmt_vifs, mock_save):
"""Validates the rollbacks on a volume connect failure.""" """Validates the rollbacks on a volume connect failure."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor() 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_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr')
def test_destroy_internal( def test_destroy_internal(self, mock_bld_slot_mgr, mock_get_flv,
self, mock_bld_slot_mgr, mock_get_flv, mock_pvmuuid, mock_pvmuuid, mock_val_vopt, mock_dlt_vopt,
mock_val_vopt, mock_dlt_vopt, mock_pwroff, mock_dlt, mock_pwroff, mock_dlt, mock_boot_from_vol,
mock_boot_from_vol, mock_unplug_vifs): mock_unplug_vifs):
"""Validates the basic PowerVM destroy.""" """Validates the basic PowerVM destroy."""
# NVRAM Manager # NVRAM Manager
self.drv.nvram_mgr = mock.Mock() 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_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr')
def test_destroy_internal_no_nvram_cleanup( def test_destroy_internal_no_nvram_cleanup(self, mock_bld_slot_mgr,
self, mock_bld_slot_mgr, mock_get_flv, mock_pvmuuid, mock_get_flv, mock_pvmuuid,
mock_val_vopt, mock_dlt_vopt, mock_pwroff, mock_dlt, mock_val_vopt, mock_dlt_vopt,
mock_boot_from_vol, mock_unplug_vifs): mock_pwroff, mock_dlt,
mock_boot_from_vol,
mock_unplug_vifs):
"""Validates the basic PowerVM destroy, without NVRAM cleanup. """Validates the basic PowerVM destroy, without NVRAM cleanup.
Used to validate the behavior when destroying evacuated instances. 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.' @mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'_validate_vopt_vg') '_validate_vopt_vg')
@mock.patch('nova.objects.flavor.Flavor.get_by_id') @mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_destroy_rollback( def test_destroy_rollback(self, mock_get_flv, mock_val_vopt,
self, mock_get_flv, mock_val_vopt, mock_dlt_vopt, mock_dlt_vopt, mock_pwroff, mock_dlt,
mock_pwroff, mock_dlt, mock_unplug_vifs): mock_unplug_vifs):
"""Validates the basic PowerVM destroy rollback mechanism works.""" """Validates the basic PowerVM destroy rollback mechanism works."""
# Set up the mocks to the tasks. # Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor() 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_cnas')
@mock.patch('nova_powervm.virt.powervm.vm.get_instance_wrapper') @mock.patch('nova_powervm.virt.powervm.vm.get_instance_wrapper')
@mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr') @mock.patch('nova_powervm.virt.powervm.slot.build_slot_mgr')
def test_plug_vifs( def test_plug_vifs(self, mock_bld_slot_mgr, mock_wrap, mock_vm_get,
self, mock_bld_slot_mgr, mock_wrap, mock_vm_get, mock_plug_vif, mock_plug_vif, mock_get_rmc_vswitch, mock_plug_rmc_vif):
mock_get_rmc_vswitch, mock_plug_rmc_vif):
# Mock up the CNA response # Mock up the CNA response
cnas = [mock.MagicMock(), mock.MagicMock()] cnas = [mock.MagicMock(), mock.MagicMock()]
cnas[0].mac = 'AABBCCDDEEFF' cnas[0].mac = 'AABBCCDDEEFF'
@@ -1723,7 +1727,7 @@ class TestPowerVMDriver(test.TestCase):
mapping_dict = {'source_type': 'volume', 'volume_id': volume_id, mapping_dict = {'source_type': 'volume', 'volume_id': volume_id,
'destination_type': 'volume', 'destination_type': 'volume',
'connection_info': 'connection_info':
jsonutils.dumps(connection_info), jsonutils.dumps(connection_info),
} }
bdm_dict = nova_block_device.BlockDeviceDict(mapping_dict) bdm_dict = nova_block_device.BlockDeviceDict(mapping_dict)
bdm_obj = bdmobj.BlockDeviceMapping(**bdm_dict) bdm_obj = bdmobj.BlockDeviceMapping(**bdm_dict)
@@ -1959,6 +1963,7 @@ class TestNovaEventHandler(test.TestCase):
self.mock_driver = mock.Mock() self.mock_driver = mock.Mock()
self.handler = driver.NovaEventHandler(self.mock_driver) 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_instance')
@mock.patch.object(vm, 'get_vm_qp') @mock.patch.object(vm, 'get_vm_qp')
def test_events(self, mock_qprops, mock_get_inst): 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 mock_get_inst.return_value = powervm.TEST_INST1
self.handler.process(event_data) 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.emit_event.called)
self.assertTrue(self.mock_driver.nvram_mgr.store.called) self.assertTrue(self.mock_driver.nvram_mgr.store.called)

View File

@@ -1864,32 +1864,43 @@ class NovaEventHandler(pvm_apt.RawEventHandler):
def __init__(self, driver): def __init__(self, driver):
self._driver = 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. """Handle an individual event.
:param uri: PowerVM event uri :param uri: PowerVM event uri
:param etype: PowerVM event type :param etype: PowerVM event type
:param details: PowerVM event details :param details: PowerVM event details
:param eid: PowerVM event id :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. # See if this uri ends with a PowerVM UUID.
if not pvm_util.is_instance_path(uri): if not pvm_util.is_instance_path(uri):
return return None
pvm_uuid = pvm_util.get_req_path_uuid( pvm_uuid = pvm_util.get_req_path_uuid(
uri, preserve_case=True) uri, preserve_case=True)
# If a vm event and one we handle, call the inst handler. # If a vm event and one we handle, call the inst handler.
if (uri.endswith('LogicalPartition/' + pvm_uuid) and if (uri.endswith('LogicalPartition/' + pvm_uuid) and
(self.inst_actions_handled & set(details))): (self.inst_actions_handled & set(details))):
inst = vm.get_instance(ctx.get_admin_context(), if not inst:
pvm_uuid) 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: if inst:
LOG.debug('Handle action "%(action)s" event for instance: ' LOG.debug('Handle action "%(action)s" event for instance: '
'%(inst)s' % '%(inst)s' %
dict(action=details, inst=inst.name)) dict(action=details, inst=inst.name))
self._handle_inst_event( self._handle_inst_event(
inst, pvm_uuid, uri, etype, details, eid) inst, pvm_uuid, uri, etype, details, eid)
return inst
return None
def _handle_inst_event(self, inst, pvm_uuid, uri, etype, details, eid): def _handle_inst_event(self, inst, pvm_uuid, uri, etype, details, eid):
"""Handle an instance event. """Handle an instance event.
@@ -1964,6 +1975,7 @@ class NovaEventHandler(pvm_apt.RawEventHandler):
}, },
] ]
""" """
inst_cache = {}
for pvm_event in events: for pvm_event in events:
try: try:
# Pull all the pieces of the event. # Pull all the pieces of the event.
@@ -1976,7 +1988,9 @@ class NovaEventHandler(pvm_apt.RawEventHandler):
if etype not in ['NEW_CLIENT']: if etype not in ['NEW_CLIENT']:
LOG.debug('PowerVM Event-Action: %s URI: %s Details %s' % LOG.debug('PowerVM Event-Action: %s URI: %s Details %s' %
(etype, uri, details)) (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: except Exception as e:
LOG.exception(e) LOG.exception(e)
LOG.warning(_LW('Unable to parse event URI: %s from PowerVM.'), LOG.warning(_LW('Unable to parse event URI: %s from PowerVM.'),