Merge "Hyper-V: restart serial console workers after instance power change"
This commit is contained in:
commit
25b378bf5c
|
@ -0,0 +1,158 @@
|
|||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
# 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 eventlet
|
||||
import mock
|
||||
|
||||
from nova import exception
|
||||
from nova.tests.unit.virt.hyperv import test_base
|
||||
from nova.virt.hyperv import constants
|
||||
from nova.virt.hyperv import eventhandler
|
||||
from nova.virt.hyperv import utilsfactory
|
||||
|
||||
|
||||
class EventHandlerTestCase(test_base.HyperVBaseTestCase):
|
||||
_FAKE_POLLING_INTERVAL = 3
|
||||
_FAKE_EVENT_CHECK_TIMEFRAME = 15
|
||||
|
||||
@mock.patch.object(utilsfactory, 'get_vmutils')
|
||||
def setUp(self, mock_get_vmutils):
|
||||
super(EventHandlerTestCase, self).setUp()
|
||||
|
||||
self._state_change_callback = mock.Mock()
|
||||
self._running_state_callback = mock.Mock()
|
||||
self.flags(
|
||||
power_state_check_timeframe=self._FAKE_EVENT_CHECK_TIMEFRAME,
|
||||
group='hyperv')
|
||||
self.flags(
|
||||
power_state_event_polling_interval=self._FAKE_POLLING_INTERVAL,
|
||||
group='hyperv')
|
||||
|
||||
self._event_handler = eventhandler.InstanceEventHandler(
|
||||
self._state_change_callback,
|
||||
self._running_state_callback)
|
||||
|
||||
@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,
|
||||
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)
|
||||
|
||||
if not missing_uuid:
|
||||
mock_emit_event.assert_called_once_with(
|
||||
mock.sentinel.instance_name,
|
||||
mock.sentinel.instance_uuid,
|
||||
mock.sentinel.power_state)
|
||||
else:
|
||||
self.assertFalse(mock_emit_event.called)
|
||||
|
||||
def test_dispatch_event_new_final_state(self):
|
||||
self._test_dispatch_event()
|
||||
|
||||
def test_dispatch_event_missing_uuid(self):
|
||||
self._test_dispatch_event(missing_uuid=True)
|
||||
|
||||
@mock.patch.object(eventhandler.InstanceEventHandler, '_get_virt_event')
|
||||
@mock.patch.object(eventlet, 'spawn_n')
|
||||
def test_emit_event(self, mock_spawn, mock_get_event):
|
||||
self._event_handler._emit_event(mock.sentinel.instance_name,
|
||||
mock.sentinel.instance_uuid,
|
||||
constants.HYPERV_VM_STATE_ENABLED)
|
||||
|
||||
virt_event = mock_get_event.return_value
|
||||
mock_spawn.assert_has_calls(
|
||||
[mock.call(self._state_change_callback, virt_event),
|
||||
mock.call(self._running_state_callback,
|
||||
mock.sentinel.instance_name,
|
||||
mock.sentinel.instance_uuid)])
|
||||
|
||||
def _test_get_instance_uuid(self, instance_found=True,
|
||||
missing_uuid=False):
|
||||
if instance_found:
|
||||
side_effect = (mock.sentinel.instance_uuid
|
||||
if not missing_uuid else None, )
|
||||
else:
|
||||
side_effect = exception.NotFound
|
||||
mock_get_uuid = self._event_handler._vmutils.get_instance_uuid
|
||||
mock_get_uuid.side_effect = side_effect
|
||||
|
||||
instance_uuid = self._event_handler._get_instance_uuid(
|
||||
mock.sentinel.instance_name)
|
||||
|
||||
expected_uuid = (mock.sentinel.instance_uuid
|
||||
if instance_found and not missing_uuid else None)
|
||||
self.assertEqual(expected_uuid, instance_uuid)
|
||||
|
||||
def test_get_nova_created_instance_uuid(self):
|
||||
self._test_get_instance_uuid()
|
||||
|
||||
def test_get_deleted_instance_uuid(self):
|
||||
self._test_get_instance_uuid(instance_found=False)
|
||||
|
||||
def test_get_instance_uuid_missing_notes(self):
|
||||
self._test_get_instance_uuid(missing_uuid=True)
|
||||
|
||||
@mock.patch('nova.virt.event.LifecycleEvent')
|
||||
def test_get_virt_event(self, mock_lifecycle_event):
|
||||
instance_state = constants.HYPERV_VM_STATE_ENABLED
|
||||
expected_transition = self._event_handler._TRANSITION_MAP[
|
||||
instance_state]
|
||||
|
||||
virt_event = self._event_handler._get_virt_event(
|
||||
mock.sentinel.instance_uuid, instance_state)
|
||||
|
||||
self.assertEqual(mock_lifecycle_event.return_value,
|
||||
virt_event)
|
||||
mock_lifecycle_event.assert_called_once_with(
|
||||
uuid=mock.sentinel.instance_uuid,
|
||||
transition=expected_transition)
|
|
@ -781,13 +781,14 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||
mock_set_vm_state.assert_called_once_with(
|
||||
mock_instance, constants.HYPERV_VM_STATE_ENABLED)
|
||||
|
||||
def _test_power_off(self, timeout):
|
||||
def _test_power_off(self, timeout, set_state_expected=True):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
with mock.patch.object(self._vmops, '_set_vm_state') as mock_set_state:
|
||||
self._vmops.power_off(instance, timeout)
|
||||
|
||||
mock_set_state.assert_called_once_with(
|
||||
instance, constants.HYPERV_VM_STATE_DISABLED)
|
||||
if set_state_expected:
|
||||
mock_set_state.assert_called_once_with(
|
||||
instance, constants.HYPERV_VM_STATE_DISABLED)
|
||||
|
||||
def test_power_off_hard(self):
|
||||
self._test_power_off(timeout=0)
|
||||
|
@ -809,6 +810,11 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||
instance, 1, vmops.SHUTDOWN_TIME_INCREMENT)
|
||||
self.assertFalse(mock_set_state.called)
|
||||
|
||||
@mock.patch("nova.virt.hyperv.vmops.VMOps._soft_shutdown")
|
||||
def test_power_off_unexisting_instance(self, mock_soft_shutdown):
|
||||
mock_soft_shutdown.side_effect = exception.NotFound
|
||||
self._test_power_off(timeout=1, set_state_expected=False)
|
||||
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._set_vm_state')
|
||||
def test_power_on(self, mock_set_vm_state):
|
||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||
|
|
|
@ -787,3 +787,64 @@ class VMUtilsTestCase(test.NoDBTestCase):
|
|||
|
||||
self._vmutils._conn.query.assert_called_once_with(expected_query)
|
||||
self.assertEqual(expected_disks, ret_disks)
|
||||
|
||||
def _get_fake_instance_notes(self):
|
||||
return self._FAKE_VM_UUID
|
||||
|
||||
def test_instance_notes(self):
|
||||
self._lookup_vm()
|
||||
mock_vm_settings = mock.Mock()
|
||||
mock_vm_settings.Notes = self._get_fake_instance_notes()
|
||||
self._vmutils._get_vm_setting_data = mock.Mock(
|
||||
return_value=mock_vm_settings)
|
||||
|
||||
notes = self._vmutils._get_instance_notes(mock.sentinel.vm_name)
|
||||
|
||||
self.assertEqual(notes[0], self._FAKE_VM_UUID)
|
||||
|
||||
def test_get_event_wql_query(self):
|
||||
cls = self._vmutils._COMPUTER_SYSTEM_CLASS
|
||||
field = self._vmutils._VM_ENABLED_STATE_PROP
|
||||
timeframe = 10
|
||||
filtered_states = [constants.HYPERV_VM_STATE_ENABLED,
|
||||
constants.HYPERV_VM_STATE_DISABLED]
|
||||
|
||||
expected_checks = ' OR '.join(
|
||||
["TargetInstance.%s = '%s'" % (field, state)
|
||||
for state in filtered_states])
|
||||
expected_query = (
|
||||
"SELECT %(field)s, TargetInstance "
|
||||
"FROM __InstanceModificationEvent "
|
||||
"WITHIN %(timeframe)s "
|
||||
"WHERE TargetInstance ISA '%(class)s' "
|
||||
"AND TargetInstance.%(field)s != "
|
||||
"PreviousInstance.%(field)s "
|
||||
"AND (%(checks)s)" %
|
||||
{'class': cls,
|
||||
'field': field,
|
||||
'timeframe': timeframe,
|
||||
'checks': expected_checks})
|
||||
|
||||
query = self._vmutils._get_event_wql_query(
|
||||
cls=cls, field=field, timeframe=timeframe,
|
||||
filtered_states=filtered_states)
|
||||
self.assertEqual(expected_query, query)
|
||||
|
||||
def test_get_vm_power_state_change_listener(self):
|
||||
with mock.patch.object(self._vmutils,
|
||||
'_get_event_wql_query') as mock_get_query:
|
||||
listener = self._vmutils.get_vm_power_state_change_listener(
|
||||
mock.sentinel.timeframe,
|
||||
mock.sentinel.filtered_states)
|
||||
|
||||
mock_get_query.assert_called_once_with(
|
||||
cls=self._vmutils._COMPUTER_SYSTEM_CLASS,
|
||||
field=self._vmutils._VM_ENABLED_STATE_PROP,
|
||||
timeframe=mock.sentinel.timeframe,
|
||||
filtered_states=mock.sentinel.filtered_states)
|
||||
watcher = self._vmutils._conn.Msvm_ComputerSystem.watch_for
|
||||
watcher.assert_called_once_with(
|
||||
raw_wql=mock_get_query.return_value,
|
||||
fields=[self._vmutils._VM_ENABLED_STATE_PROP])
|
||||
|
||||
self.assertEqual(watcher.return_value, listener)
|
||||
|
|
|
@ -150,6 +150,9 @@ class VMUtilsV2TestCase(test_vmutils.VMUtilsTestCase):
|
|||
['ElementName', 'Notes'],
|
||||
VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
|
||||
def _get_fake_instance_notes(self):
|
||||
return [self._FAKE_VM_UUID]
|
||||
|
||||
@mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2.check_ret_val')
|
||||
@mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2._get_wmi_obj')
|
||||
def _test_create_vm_obj(self, mock_get_wmi_obj, mock_check_ret_val,
|
||||
|
|
|
@ -20,6 +20,7 @@ Constants used in ops classes
|
|||
from nova.compute import arch
|
||||
from nova.compute import power_state
|
||||
|
||||
HYPERV_VM_STATE_OTHER = 1
|
||||
HYPERV_VM_STATE_ENABLED = 2
|
||||
HYPERV_VM_STATE_DISABLED = 3
|
||||
HYPERV_VM_STATE_SHUTTING_DOWN = 4
|
||||
|
|
|
@ -23,6 +23,7 @@ from oslo_log import log as logging
|
|||
|
||||
from nova.i18n import _
|
||||
from nova.virt import driver
|
||||
from nova.virt.hyperv import eventhandler
|
||||
from nova.virt.hyperv import hostops
|
||||
from nova.virt.hyperv import livemigrationops
|
||||
from nova.virt.hyperv import migrationops
|
||||
|
@ -54,6 +55,10 @@ class HyperVDriver(driver.ComputeDriver):
|
|||
|
||||
def init_host(self, host):
|
||||
self._vmops.restart_vm_log_writers()
|
||||
event_handler = eventhandler.InstanceEventHandler(
|
||||
state_change_callback=self.emit_event,
|
||||
running_state_callback=self._vmops.log_vm_serial_output)
|
||||
event_handler.start_listener()
|
||||
|
||||
def list_instance_uuids(self):
|
||||
return self._vmops.list_instance_uuids()
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
# 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 eventlet
|
||||
|
||||
import sys
|
||||
|
||||
if sys.platform == 'win32':
|
||||
import wmi
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _LW
|
||||
from nova.virt import event as virtevent
|
||||
from nova.virt.hyperv import constants
|
||||
from nova.virt.hyperv import utilsfactory
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
hyperv_opts = [
|
||||
cfg.IntOpt('power_state_check_timeframe',
|
||||
default=60,
|
||||
help='The timeframe to be checked for instance power '
|
||||
'state changes.'),
|
||||
cfg.IntOpt('power_state_event_polling_interval',
|
||||
default=2,
|
||||
help='Instance power state change event polling frequency.'),
|
||||
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
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,
|
||||
constants.HYPERV_VM_STATE_PAUSED: virtevent.EVENT_LIFECYCLE_PAUSED,
|
||||
constants.HYPERV_VM_STATE_SUSPENDED:
|
||||
virtevent.EVENT_LIFECYCLE_SUSPENDED
|
||||
}
|
||||
|
||||
def __init__(self, state_change_callback=None,
|
||||
running_state_callback=None):
|
||||
self._vmutils = utilsfactory.get_vmutils()
|
||||
self._listener = self._vmutils.get_vm_power_state_change_listener(
|
||||
timeframe=CONF.hyperv.power_state_check_timeframe,
|
||||
filtered_states=self._TRANSITION_MAP.keys())
|
||||
|
||||
self._polling_interval = CONF.hyperv.power_state_event_polling_interval
|
||||
self._state_change_callback = state_change_callback
|
||||
self._running_state_callback = running_state_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 occured 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
|
||||
|
||||
# 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)
|
||||
|
||||
def _emit_event(self, instance_name, instance_uuid, instance_state):
|
||||
virt_event = self._get_virt_event(instance_uuid,
|
||||
instance_state)
|
||||
eventlet.spawn_n(self._state_change_callback, virt_event)
|
||||
|
||||
if instance_state == constants.HYPERV_VM_STATE_ENABLED:
|
||||
eventlet.spawn_n(self._running_state_callback,
|
||||
instance_name, instance_uuid)
|
||||
|
||||
def _get_instance_uuid(self, instance_name):
|
||||
try:
|
||||
instance_uuid = self._vmutils.get_instance_uuid(instance_name)
|
||||
if not instance_uuid:
|
||||
LOG.warn(_LW("Instance uuid could not be retrieved for "
|
||||
"instance %s. Instance state change event "
|
||||
"will be ignored."),
|
||||
instance_name)
|
||||
return instance_uuid
|
||||
except exception.NotFound:
|
||||
# The instance has been deleted.
|
||||
pass
|
||||
|
||||
def _get_virt_event(self, instance_uuid, instance_state):
|
||||
transition = self._TRANSITION_MAP[instance_state]
|
||||
return virtevent.LifecycleEvent(uuid=instance_uuid,
|
||||
transition=transition)
|
|
@ -537,11 +537,22 @@ class VMOps(object):
|
|||
LOG.debug("Power off instance", instance=instance)
|
||||
if retry_interval <= 0:
|
||||
retry_interval = SHUTDOWN_TIME_INCREMENT
|
||||
if timeout and self._soft_shutdown(instance, timeout, retry_interval):
|
||||
return
|
||||
|
||||
self._set_vm_state(instance,
|
||||
constants.HYPERV_VM_STATE_DISABLED)
|
||||
try:
|
||||
if timeout and self._soft_shutdown(instance,
|
||||
timeout,
|
||||
retry_interval):
|
||||
return
|
||||
|
||||
self._set_vm_state(instance,
|
||||
constants.HYPERV_VM_STATE_DISABLED)
|
||||
except exception.NotFound:
|
||||
# The manager can call the stop API after recieving instance
|
||||
# power off events. If this is triggered when the instance
|
||||
# is being deleted, it might attempt to power off an unexisting
|
||||
# instance. We'll just pass in this case.
|
||||
LOG.debug("Instance not found. Skipping power off",
|
||||
instance=instance)
|
||||
|
||||
def power_on(self, instance, block_device_info=None):
|
||||
"""Power on the specified instance."""
|
||||
|
|
|
@ -27,6 +27,7 @@ if sys.platform == 'win32':
|
|||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
|
@ -83,6 +84,9 @@ class VMUtils(object):
|
|||
_SYNTHETIC_ETHERNET_PORT_SETTING_DATA_CLASS = \
|
||||
'Msvm_SyntheticEthernetPortSettingData'
|
||||
_AFFECTED_JOB_ELEMENT_CLASS = "Msvm_AffectedJobElement"
|
||||
_COMPUTER_SYSTEM_CLASS = "Msvm_ComputerSystem"
|
||||
|
||||
_VM_ENABLED_STATE_PROP = "EnabledState"
|
||||
|
||||
_SHUTDOWN_COMPONENT = "Msvm_ShutdownComponent"
|
||||
_VIRTUAL_SYSTEM_CURRENT_SETTINGS = 3
|
||||
|
@ -766,3 +770,53 @@ class VMUtils(object):
|
|||
if v.EnabledState == constants.HYPERV_VM_STATE_ENABLED]
|
||||
|
||||
return active_vm_names
|
||||
|
||||
def get_vm_power_state_change_listener(self, timeframe, filtered_states):
|
||||
field = self._VM_ENABLED_STATE_PROP
|
||||
query = self._get_event_wql_query(cls=self._COMPUTER_SYSTEM_CLASS,
|
||||
field=field,
|
||||
timeframe=timeframe,
|
||||
filtered_states=filtered_states)
|
||||
return self._conn.Msvm_ComputerSystem.watch_for(raw_wql=query,
|
||||
fields=[field])
|
||||
|
||||
def _get_event_wql_query(self, cls, field,
|
||||
timeframe, filtered_states=None):
|
||||
"""Return a WQL query used for polling WMI events.
|
||||
|
||||
:param cls: the WMI class polled for events
|
||||
:param field: the field checked
|
||||
:param timeframe: check for events that occurred in
|
||||
the specified timeframe
|
||||
:param filtered_states: only catch events triggered when a WMI
|
||||
object transitioned into one of those
|
||||
states.
|
||||
"""
|
||||
query = ("SELECT %(field)s, TargetInstance "
|
||||
"FROM __InstanceModificationEvent "
|
||||
"WITHIN %(timeframe)s "
|
||||
"WHERE TargetInstance ISA '%(class)s' "
|
||||
"AND TargetInstance.%(field)s != "
|
||||
"PreviousInstance.%(field)s" %
|
||||
{'class': cls,
|
||||
'field': field,
|
||||
'timeframe': timeframe})
|
||||
if filtered_states:
|
||||
checks = ["TargetInstance.%s = '%s'" % (field, state)
|
||||
for state in filtered_states]
|
||||
query += " AND (%s)" % " OR ".join(checks)
|
||||
return query
|
||||
|
||||
def _get_instance_notes(self, vm_name):
|
||||
vm = self._lookup_vm_check(vm_name)
|
||||
vmsettings = self._get_vm_setting_data(vm)
|
||||
return [note for note in vmsettings.Notes.split('\n') if note]
|
||||
|
||||
def get_instance_uuid(self, vm_name):
|
||||
instance_notes = self._get_instance_notes(vm_name)
|
||||
if instance_notes and uuidutils.is_uuid_like(instance_notes[0]):
|
||||
return instance_notes[0]
|
||||
|
||||
def get_vm_power_state(self, vm_enabled_state):
|
||||
return self._enabled_states_map.get(vm_enabled_state,
|
||||
constants.HYPERV_VM_STATE_OTHER)
|
||||
|
|
|
@ -321,3 +321,8 @@ class VMUtilsV2(vmutils.VMUtils):
|
|||
if sasd.ResourceSubType == self._DVD_DISK_RES_SUB_TYPE]
|
||||
|
||||
return dvd_paths
|
||||
|
||||
def _get_instance_notes(self, vm_name):
|
||||
vm = self._lookup_vm_check(vm_name)
|
||||
vmsettings = self._get_vm_setting_data(vm)
|
||||
return [note for note in vmsettings.Notes if note]
|
||||
|
|
Loading…
Reference in New Issue