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:
parent
a501139931
commit
9adae8daca
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue