nova-powervm/nova_powervm/tests/virt/powervm/test_event.py

394 lines
17 KiB
Python

# Copyright 2014, 2016 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import mock
from nova.compute import power_state
from nova import exception
from nova import test
from pypowervm.wrappers import event as pvm_evt
from nova_powervm.virt.powervm import event
class TestGetInstance(test.NoDBTestCase):
@mock.patch('nova.context.get_admin_context')
@mock.patch('nova_powervm.virt.powervm.vm.get_instance')
def test_get_instance(self, mock_get_inst, mock_get_context):
# If instance provided, vm.get_instance not called
self.assertEqual('inst', event._get_instance('inst', 'uuid'))
mock_get_inst.assert_not_called()
# Note that we can only guarantee get_admin_context wasn't called
# because _get_instance is mocked everywhere else in this suite.
# Otherwise it could run from another test case executing in parallel.
mock_get_context.assert_not_called()
# If instance not provided, vm.get_instance is called
mock_get_inst.return_value = 'inst2'
for _ in range(2):
# Doing it the second time doesn't call get_admin_context() again.
self.assertEqual('inst2', event._get_instance(None, 'uuid'))
mock_get_context.assert_called_once_with()
mock_get_inst.assert_called_once_with(
mock_get_context.return_value, 'uuid')
mock_get_inst.reset_mock()
# Don't reset mock_get_context
class TestPowerVMNovaEventHandler(test.NoDBTestCase):
def setUp(self):
super(TestPowerVMNovaEventHandler, self).setUp()
lceh_process_p = mock.patch(
'nova_powervm.virt.powervm.event.PowerVMLifecycleEventHandler.'
'process')
self.addCleanup(lceh_process_p.stop)
self.mock_lceh_process = lceh_process_p.start()
self.mock_driver = mock.Mock()
self.handler = event.PowerVMNovaEventHandler(self.mock_driver)
@mock.patch('nova_powervm.virt.powervm.event._get_instance')
def test_get_inst_uuid(self, mock_get_instance):
fake_inst1 = mock.Mock(uuid='uuid1')
fake_inst2 = mock.Mock(uuid='uuid2')
mock_get_instance.side_effect = lambda i, u: {
'fake_pvm_uuid1': fake_inst1,
'fake_pvm_uuid2': fake_inst2}.get(u)
self.assertEqual(
(fake_inst1, 'uuid1'),
self.handler._get_inst_uuid(fake_inst1, 'fake_pvm_uuid1'))
self.assertEqual(
(fake_inst2, 'uuid2'),
self.handler._get_inst_uuid(fake_inst2, 'fake_pvm_uuid2'))
self.assertEqual(
(None, 'uuid1'),
self.handler._get_inst_uuid(None, 'fake_pvm_uuid1'))
self.assertEqual(
(fake_inst2, 'uuid2'),
self.handler._get_inst_uuid(fake_inst2, 'fake_pvm_uuid2'))
self.assertEqual(
(fake_inst1, 'uuid1'),
self.handler._get_inst_uuid(fake_inst1, 'fake_pvm_uuid1'))
mock_get_instance.assert_has_calls(
[mock.call(fake_inst1, 'fake_pvm_uuid1'),
mock.call(fake_inst2, 'fake_pvm_uuid2')])
@mock.patch('nova_powervm.virt.powervm.event._get_instance')
def test_handle_inst_event(self, mock_get_instance):
# If no event we care about, or NVRAM but no nvram_mgr, nothing happens
self.mock_driver.nvram_mgr = None
for dets in ([], ['foo', 'bar', 'baz'], ['NVRAM']):
self.assertEqual('inst', self.handler._handle_inst_event(
'inst', 'uuid', dets))
mock_get_instance.assert_not_called()
self.mock_lceh_process.assert_not_called()
self.mock_driver.nvram_mgr = mock.Mock()
# PartitionState only: no NVRAM handling, and inst is passed through.
self.assertEqual('inst', self.handler._handle_inst_event(
'inst', 'uuid', ['foo', 'PartitionState', 'bar']))
mock_get_instance.assert_not_called()
self.mock_driver.nvram_mgr.store.assert_not_called()
self.mock_lceh_process.assert_called_once_with('inst', 'uuid')
self.mock_lceh_process.reset_mock()
# No instance; nothing happens (we skip PartitionState handling too)
mock_get_instance.return_value = None
self.assertIsNone(self.handler._handle_inst_event(
'inst', 'uuid', ['NVRAM', 'PartitionState']))
mock_get_instance.assert_called_once_with('inst', 'uuid')
self.mock_driver.nvram_mgr.store.assert_not_called()
self.mock_lceh_process.assert_not_called()
mock_get_instance.reset_mock()
fake_inst = mock.Mock(uuid='fake-uuid')
mock_get_instance.return_value = fake_inst
# NVRAM only - no PartitionState handling, instance is returned
self.assertEqual(fake_inst, self.handler._handle_inst_event(
None, 'uuid', ['NVRAM', 'baz']))
mock_get_instance.assert_called_once_with(None, 'uuid')
self.mock_driver.nvram_mgr.store.assert_called_once_with('fake-uuid')
self.mock_lceh_process.assert_not_called()
mock_get_instance.reset_mock()
self.mock_driver.nvram_mgr.store.reset_mock()
self.handler._uuid_cache.clear()
# Both event types
self.assertEqual(fake_inst, self.handler._handle_inst_event(
None, 'uuid', ['PartitionState', 'NVRAM']))
mock_get_instance.assert_called_once_with(None, 'uuid')
self.mock_driver.nvram_mgr.store.assert_called_once_with('fake-uuid')
self.mock_lceh_process.assert_called_once_with(fake_inst, 'uuid')
mock_get_instance.reset_mock()
self.mock_driver.nvram_mgr.store.reset_mock()
self.handler._uuid_cache.clear()
# Handle multiple NVRAM and PartitionState events
self.assertEqual(fake_inst, self.handler._handle_inst_event(
None, 'uuid', ['NVRAM']))
self.assertEqual(None, self.handler._handle_inst_event(
None, 'uuid', ['NVRAM']))
self.assertEqual(None, self.handler._handle_inst_event(
None, 'uuid', ['PartitionState']))
self.assertEqual(fake_inst, self.handler._handle_inst_event(
fake_inst, 'uuid', ['NVRAM']))
self.assertEqual(fake_inst, self.handler._handle_inst_event(
fake_inst, 'uuid', ['NVRAM', 'PartitionState']))
mock_get_instance.assert_called_once_with(None, 'uuid')
self.mock_driver.nvram_mgr.store.assert_has_calls(
[mock.call('fake-uuid')] * 4)
self.mock_lceh_process.assert_has_calls(
[mock.call(None, 'uuid'),
mock.call(fake_inst, 'uuid')])
@mock.patch('nova_powervm.virt.powervm.event.PowerVMNovaEventHandler.'
'_handle_inst_event')
@mock.patch('pypowervm.util.get_req_path_uuid', autospec=True)
def test_process(self, mock_get_rpu, mock_handle):
# NEW_CLIENT/CACHE_CLEARED events are ignored
events = [mock.Mock(etype=pvm_evt.EventType.NEW_CLIENT),
mock.Mock(etype=pvm_evt.EventType.CACHE_CLEARED)]
self.handler.process(events)
self.assertEqual(0, mock_get_rpu.call_count)
mock_handle.assert_not_called()
moduri = pvm_evt.EventType.MODIFY_URI
# If get_req_path_uuid doesn't find a UUID, or not a LogicalPartition
# URI, or details is empty, or has no actions we care about, no action
# is taken.
mock_get_rpu.side_effect = [None, 'uuid1', 'uuid2', 'uuid3']
events = [
mock.Mock(etype=moduri, data='foo/LogicalPartition/None',
details='NVRAM,PartitionState'),
mock.Mock(etype=moduri, data='bar/VirtualIOServer/uuid1',
details='NVRAM,PartitionState'),
mock.Mock(etype=moduri, data='baz/LogicalPartition/uuid2',
detail=''),
mock.Mock(etype=moduri, data='blah/LogicalPartition/uuid3',
detail='do,not,care')]
self.handler.process(events)
mock_get_rpu.assert_has_calls(
[mock.call(uri, preserve_case=True)
for uri in ('bar/VirtualIOServer/uuid1',
'baz/LogicalPartition/uuid2',
'blah/LogicalPartition/uuid3')])
mock_handle.assert_not_called()
mock_get_rpu.reset_mock()
# The stars align, and we handle some events.
uuid_det = (('uuid1', 'NVRAM'),
('uuid2', 'this,one,ignored'),
('uuid3', 'PartitionState,baz,NVRAM'),
# Repeat uuid1 to test the cache
('uuid1', 'blah,PartitionState'),
('uuid5', 'also,ignored'))
mock_get_rpu.side_effect = [ud[0] for ud in uuid_det]
events = [
mock.Mock(etype=moduri, data='LogicalPartition/' + uuid,
detail=detail) for uuid, detail in uuid_det]
# Set up _handle_inst_event to test the cache and the exception path
mock_handle.side_effect = ['inst1', None, ValueError]
# Run it!
self.handler.process(events)
mock_get_rpu.assert_has_calls(
[mock.call(uri, preserve_case=True) for uri in
('LogicalPartition/' + ud[0] for ud in uuid_det)])
mock_handle.assert_has_calls(
[mock.call(None, 'uuid1', ['NVRAM']),
mock.call(None, 'uuid3', ['PartitionState', 'baz', 'NVRAM']),
# inst1 pulled from the cache based on uuid1
mock.call('inst1', 'uuid1', ['blah', 'PartitionState'])])
@mock.patch('nova_powervm.virt.powervm.event._get_instance')
@mock.patch('pypowervm.util.get_req_path_uuid', autospec=True)
def test_uuid_cache(self, mock_get_rpu, mock_get_instance):
deluri = pvm_evt.EventType.DELETE_URI
moduri = pvm_evt.EventType.MODIFY_URI
fake_inst1 = mock.Mock(uuid='uuid1')
fake_inst2 = mock.Mock(uuid='uuid2')
fake_inst4 = mock.Mock(uuid='uuid4')
mock_get_instance.side_effect = lambda i, u: {
'fake_pvm_uuid1': fake_inst1,
'fake_pvm_uuid2': fake_inst2,
'fake_pvm_uuid4': fake_inst4}.get(u)
mock_get_rpu.side_effect = lambda d, **k: d.split('/')[1]
uuid_det = (('fake_pvm_uuid1', 'NVRAM', moduri),
('fake_pvm_uuid2', 'NVRAM', moduri),
('fake_pvm_uuid4', 'NVRAM', moduri),
('fake_pvm_uuid1', 'NVRAM', moduri),
('fake_pvm_uuid2', '', deluri),
('fake_pvm_uuid2', 'NVRAM', moduri),
('fake_pvm_uuid1', '', deluri),
('fake_pvm_uuid3', '', deluri))
events = [
mock.Mock(etype=etype, data='LogicalPartition/' + uuid,
detail=detail) for uuid, detail, etype in uuid_det]
self.handler.process(events[0:4])
mock_get_instance.assert_has_calls([
mock.call(None, 'fake_pvm_uuid1'),
mock.call(None, 'fake_pvm_uuid2'),
mock.call(None, 'fake_pvm_uuid4')])
self.assertEqual({
'fake_pvm_uuid1': 'uuid1',
'fake_pvm_uuid2': 'uuid2',
'fake_pvm_uuid4': 'uuid4'}, self.handler._uuid_cache)
mock_get_instance.reset_mock()
# Test the cache with a second process call
self.handler.process(events[4:7])
mock_get_instance.assert_has_calls([
mock.call(None, 'fake_pvm_uuid2')])
self.assertEqual({
'fake_pvm_uuid2': 'uuid2',
'fake_pvm_uuid4': 'uuid4'}, self.handler._uuid_cache)
mock_get_instance.reset_mock()
# Make sure a delete to a non-cached UUID doesn't blow up
self.handler.process([events[7]])
mock_get_instance.assert_not_called()
mock_get_rpu.reset_mock()
mock_get_instance.reset_mock()
clear_events = [mock.Mock(etype=pvm_evt.EventType.NEW_CLIENT),
mock.Mock(etype=pvm_evt.EventType.CACHE_CLEARED)]
# This should clear the cache
self.handler.process(clear_events)
self.assertEqual(dict(), self.handler._uuid_cache)
self.assertEqual(0, mock_get_rpu.call_count)
mock_get_instance.assert_not_called()
class TestPowerVMLifecycleEventHandler(test.NoDBTestCase):
def setUp(self):
super(TestPowerVMLifecycleEventHandler, self).setUp()
self.mock_driver = mock.MagicMock()
self.handler = event.PowerVMLifecycleEventHandler(self.mock_driver)
@mock.patch('nova_powervm.virt.powervm.vm.get_vm_qp')
@mock.patch('nova_powervm.virt.powervm.event._get_instance')
@mock.patch('nova_powervm.virt.powervm.vm.translate_event')
@mock.patch('nova.virt.event.LifecycleEvent')
def test_emit_event(self, mock_lce, mock_tx_evt, mock_get_inst, mock_qp):
def assert_qp():
mock_qp.assert_called_once_with(
self.mock_driver.adapter, 'uuid', 'PartitionState')
mock_qp.reset_mock()
def assert_get_inst():
mock_get_inst.assert_called_once_with('inst', 'uuid')
mock_get_inst.reset_mock()
# Ignore if LPAR is gone
mock_qp.side_effect = exception.InstanceNotFound(instance_id='uuid')
self.handler._emit_event('uuid', None)
assert_qp()
mock_get_inst.assert_not_called()
mock_tx_evt.assert_not_called()
mock_lce.assert_not_called()
self.mock_driver.emit_event.assert_not_called()
# Let get_vm_qp return its usual mock from now on
mock_qp.side_effect = None
# Ignore if instance is gone
mock_get_inst.return_value = None
self.handler._emit_event('uuid', 'inst')
assert_qp()
assert_get_inst()
mock_tx_evt.assert_not_called()
mock_lce.assert_not_called()
self.mock_driver.emit_event.assert_not_called()
# Ignore if task_state isn't one we care about
for task_state in event._NO_EVENT_TASK_STATES:
mock_get_inst.return_value = mock.Mock(task_state=task_state)
self.handler._emit_event('uuid', 'inst')
assert_qp()
assert_get_inst()
mock_tx_evt.assert_not_called()
mock_lce.assert_not_called()
self.mock_driver.emit_event.assert_not_called()
# Task state we care about from now on
inst = mock.Mock(task_state='scheduling',
power_state=power_state.RUNNING)
mock_get_inst.return_value = inst
# Ignore if not a transition we care about
mock_tx_evt.return_value = None
self.handler._emit_event('uuid', 'inst')
assert_qp()
assert_get_inst()
mock_tx_evt.assert_called_once_with(
mock_qp.return_value, power_state.RUNNING)
mock_lce.assert_not_called()
self.mock_driver.emit_event.assert_not_called()
mock_tx_evt.reset_mock()
# Good path
mock_tx_evt.return_value = 'transition'
self.handler._delayed_event_threads = {'uuid': 'thread1',
'uuid2': 'thread2'}
self.handler._emit_event('uuid', 'inst')
assert_qp()
assert_get_inst()
mock_tx_evt.assert_called_once_with(
mock_qp.return_value, power_state.RUNNING)
mock_lce.assert_called_once_with(inst.uuid, 'transition')
self.mock_driver.emit_event.assert_called_once_with(
mock_lce.return_value)
# The thread was removed
self.assertEqual({'uuid2': 'thread2'},
self.handler._delayed_event_threads)
@mock.patch('eventlet.greenthread.spawn_after')
def test_process(self, mock_spawn):
thread1 = mock.Mock()
thread2 = mock.Mock()
mock_spawn.side_effect = [thread1, thread2]
# First call populates the delay queue
self.assertEqual({}, self.handler._delayed_event_threads)
self.handler.process(None, 'uuid')
mock_spawn.assert_called_once_with(15, self.handler._emit_event,
'uuid', None)
self.assertEqual({'uuid': thread1},
self.handler._delayed_event_threads)
thread1.cancel.assert_not_called()
thread2.cancel.assert_not_called()
mock_spawn.reset_mock()
# Second call cancels the first thread and replaces it in delay queue
self.handler.process('inst', 'uuid')
mock_spawn.assert_called_once_with(15, self.handler._emit_event,
'uuid', 'inst')
self.assertEqual({'uuid': thread2},
self.handler._delayed_event_threads)
thread1.cancel.assert_called_once_with()
thread2.cancel.assert_not_called()