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('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)

View File

@@ -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.'),