Avoid handling Hyper-V instance event objects by compute-hyperv

At the moment, the instance event handler retrieves an event
listener from os-win and polls for events, handling Hyper-V specific
instance objects, as well as WMI exceptions.

One of the goals that os-win tries to achieve is hiding Hyper-V
specific details such as those mentioned above.

This patch slightly changes the way events are handled, meaning
that we'll let os-win do the polling, which will invoke a callback
specifying the instance name and the new power state when an instance
power state change occurs.

Also, the base test class has been updated not to mock the wmi lib
as we don't directly use it anymore.

Change-Id: I5cdfb5f8ab2ca4b66cc4db56056df4537551c9bb
Depends-On: Iddc52878bcc84bf3d8c31edbcd178eac4b59092d
This commit is contained in:
Lucian Petrut 2016-02-29 17:45:48 +02:00
parent a501139931
commit 9adae8daca
3 changed files with 15 additions and 82 deletions

View File

@ -15,11 +15,6 @@
import eventlet
import sys
if sys.platform == 'win32':
import wmi
from nova.i18n import _LW
from nova.virt import event as virtevent
from os_win import constants
@ -48,10 +43,6 @@ CONF.register_opts(hyperv_opts, 'hyperv')
class InstanceEventHandler(object):
# The event listener timeout is set to 0 in order to return immediately
# and avoid blocking the thread.
_WAIT_TIMEOUT = 0
_TRANSITION_MAP = {
constants.HYPERV_VM_STATE_ENABLED: virtevent.EVENT_LIFECYCLE_STARTED,
constants.HYPERV_VM_STATE_DISABLED: virtevent.EVENT_LIFECYCLE_STOPPED,
@ -64,40 +55,24 @@ class InstanceEventHandler(object):
self._vmutils = utilsfactory.get_vmutils()
self._listener = self._vmutils.get_vm_power_state_change_listener(
timeframe=CONF.hyperv.power_state_check_timeframe,
filtered_states=list(self._TRANSITION_MAP.keys()))
event_timeout=CONF.hyperv.power_state_event_polling_interval,
filtered_states=list(self._TRANSITION_MAP.keys()),
get_handler=True)
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
self._polling_interval = CONF.hyperv.power_state_event_polling_interval
self._state_change_callback = state_change_callback
def start_listener(self):
eventlet.spawn_n(self._poll_events)
def _poll_events(self):
while True:
try:
# Retrieve one by one all the events that occurred in
# the checked interval.
event = self._listener(self._WAIT_TIMEOUT)
self._dispatch_event(event)
continue
except wmi.x_wmi_timed_out:
# If no events were triggered in the checked interval,
# a timeout exception is raised. We'll just ignore it.
pass
eventlet.sleep(self._polling_interval)
def _dispatch_event(self, event):
instance_state = self._vmutils.get_vm_power_state(event.EnabledState)
instance_name = event.ElementName
eventlet.spawn_n(self._listener, self._event_callback)
def _event_callback(self, instance_name, instance_power_state):
# Instance uuid set by Nova. If this is missing, we assume that
# the instance was not created by Nova and ignore the event.
instance_uuid = self._get_instance_uuid(instance_name)
if instance_uuid:
self._emit_event(instance_name, instance_uuid, instance_state)
self._emit_event(instance_name,
instance_uuid,
instance_power_state)
def _emit_event(self, instance_name, instance_uuid, instance_state):
virt_event = self._get_virt_event(instance_uuid,

View File

@ -16,7 +16,6 @@
import mock
from os_win import utilsfactory
from six.moves import builtins
from hyperv.tests import test
@ -25,16 +24,6 @@ class HyperVBaseTestCase(test.NoDBTestCase):
def setUp(self):
super(HyperVBaseTestCase, self).setUp()
self._mock_wmi = mock.MagicMock()
wmi_patcher = mock.patch.object(builtins, 'wmi', create=True,
new=self._mock_wmi)
platform_patcher = mock.patch('sys.platform', 'win32')
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
platform_patcher.start()
wmi_patcher.start()
utilsfactory_patcher.start()
self.addCleanup(wmi_patcher.stop)
self.addCleanup(platform_patcher.stop)
self.addCleanup(utilsfactory_patcher.stop)

View File

@ -45,49 +45,18 @@ class EventHandlerTestCase(test_base.HyperVBaseTestCase):
self._state_change_callback)
self._event_handler._serial_console_ops = mock.Mock()
@mock.patch.object(eventhandler, 'wmi', create=True)
@mock.patch.object(eventhandler.InstanceEventHandler, '_dispatch_event')
@mock.patch.object(eventlet, 'sleep')
def _test_poll_events(self, mock_sleep, mock_dispatch,
mock_wmi, event_found=True):
fake_listener = mock.Mock()
mock_wmi.x_wmi_timed_out = Exception
fake_listener.side_effect = (mock.sentinel.event if event_found
else mock_wmi.x_wmi_timed_out,
KeyboardInterrupt)
self._event_handler._listener = fake_listener
# This is supposed to run as a daemon, so we'll just cause an exception
# in order to be able to test the method.
self.assertRaises(KeyboardInterrupt,
self._event_handler._poll_events)
if event_found:
mock_dispatch.assert_called_once_with(mock.sentinel.event)
else:
mock_sleep.assert_called_once_with(self._FAKE_POLLING_INTERVAL)
def test_poll_having_events(self):
# Test case in which events were found in the checked interval
self._test_poll_events()
def test_poll_no_event_found(self):
self._test_poll_events(event_found=False)
@mock.patch.object(eventhandler.InstanceEventHandler,
'_get_instance_uuid')
@mock.patch.object(eventhandler.InstanceEventHandler, '_emit_event')
def _test_dispatch_event(self, mock_emit_event, mock_get_uuid,
def _test_event_callback(self, mock_emit_event, mock_get_uuid,
missing_uuid=False):
mock_get_uuid.return_value = (
mock.sentinel.instance_uuid if not missing_uuid else None)
self._event_handler._vmutils.get_vm_power_state.return_value = (
mock.sentinel.power_state)
event = mock.Mock()
event.ElementName = mock.sentinel.instance_name
event.EnabledState = mock.sentinel.enabled_state
self._event_handler._dispatch_event(event)
self._event_handler._event_callback(mock.sentinel.instance_name,
mock.sentinel.power_state)
if not missing_uuid:
mock_emit_event.assert_called_once_with(
@ -97,11 +66,11 @@ class EventHandlerTestCase(test_base.HyperVBaseTestCase):
else:
self.assertFalse(mock_emit_event.called)
def test_dispatch_event_new_final_state(self):
self._test_dispatch_event()
def test_event_callback_uuid_present(self):
self._test_event_callback()
def test_dispatch_event_missing_uuid(self):
self._test_dispatch_event(missing_uuid=True)
def test_event_callback_missing_uuid(self):
self._test_event_callback(missing_uuid=True)
@mock.patch.object(eventhandler.InstanceEventHandler, '_get_virt_event')
@mock.patch.object(eventlet, 'spawn_n')