check minimum VM version when setting VM snapshots
Some Hyper-V operations require a minimum VM version in order to succeed. For example, Production Checkpoints are supported on VM Versions 6.2 and newer. Clustering Hyper-V compute nodes may change the list of supported VM versions list and the default VM version on that host. https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/deploy/upgrade-virtual-machine-version-in-hyper-v-on-windows-or-windows-server Partial-Bug: #1707605 Change-Id: Iac795f570649ce28847b88c5a21e575daefefc49
This commit is contained in:
parent
742c6d1bbb
commit
e0d7032dfb
@ -15,6 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import inspect
|
||||||
|
from pkg_resources import parse_version
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
@ -29,6 +31,7 @@ from oslo_utils import excutils
|
|||||||
from oslo_utils import reflection
|
from oslo_utils import reflection
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from os_win import constants
|
||||||
from os_win import exceptions
|
from os_win import exceptions
|
||||||
|
|
||||||
|
|
||||||
@ -256,3 +259,43 @@ def hex_str_to_byte_array(string):
|
|||||||
|
|
||||||
def byte_array_to_hex_str(byte_aray):
|
def byte_array_to_hex_str(byte_aray):
|
||||||
return ''.join('{:02X}'.format(b) for b in byte_aray)
|
return ''.join('{:02X}'.format(b) for b in byte_aray)
|
||||||
|
|
||||||
|
|
||||||
|
def required_vm_version(min_version=constants.VM_VERSION_5_0,
|
||||||
|
max_version=constants.VM_VERSION_254_0):
|
||||||
|
"""Ensures that the wrapped method's VM meets the version requirements.
|
||||||
|
|
||||||
|
Some Hyper-V operations require a minimum VM version in order to succeed.
|
||||||
|
For example, Production Checkpoints are supported on VM Versions 6.2 and
|
||||||
|
newer.
|
||||||
|
|
||||||
|
Clustering Hyper-V compute nodes may change the list of supported VM
|
||||||
|
versions list and the default VM version on that host.
|
||||||
|
|
||||||
|
:param min_version: string, the VM's minimum version required for the
|
||||||
|
operation to succeed.
|
||||||
|
:param max_version: string, the VM's maximum version required for the
|
||||||
|
operation to succeed.
|
||||||
|
:raises exceptions.InvalidVMVersion: if the VM's version does not meet the
|
||||||
|
given requirements.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def wrapper(func):
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
all_args = inspect.getcallargs(func, *args, **kwargs)
|
||||||
|
vmsettings = all_args['vmsettings']
|
||||||
|
|
||||||
|
# NOTE(claudiub): VMs on Windows / Hyper-V Server 2012 do not have
|
||||||
|
# a Version field, but they are 4.0.
|
||||||
|
vm_version_str = getattr(vmsettings, 'Version', '4.0')
|
||||||
|
vm_version = parse_version(vm_version_str)
|
||||||
|
if (vm_version >= parse_version(min_version) and
|
||||||
|
vm_version <= parse_version(max_version)):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
raise exceptions.InvalidVMVersion(
|
||||||
|
vm_name=vmsettings.ElementName, version=vm_version_str,
|
||||||
|
min_version=min_version, max_version=max_version)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
return wrapper
|
||||||
|
@ -211,6 +211,11 @@ VM_SNAPSHOT_TYPE_PROD_FALLBACK = 3
|
|||||||
VM_SNAPSHOT_TYPE_PROD_ENFORCED = 4
|
VM_SNAPSHOT_TYPE_PROD_ENFORCED = 4
|
||||||
VM_SNAPSHOT_TYPE_STANDARD = 5
|
VM_SNAPSHOT_TYPE_STANDARD = 5
|
||||||
|
|
||||||
|
VM_VERSION_5_0 = '5.0'
|
||||||
|
VM_VERSION_6_2 = '6.2'
|
||||||
|
VM_VERSION_8_0 = '8.0'
|
||||||
|
VM_VERSION_254_0 = '254.0'
|
||||||
|
|
||||||
DEFAULT_WMI_EVENT_TIMEOUT_MS = 2000
|
DEFAULT_WMI_EVENT_TIMEOUT_MS = 2000
|
||||||
|
|
||||||
SCSI_UID_SCSI_NAME_STRING = 8
|
SCSI_UID_SCSI_NAME_STRING = 8
|
||||||
|
@ -104,6 +104,12 @@ class InvalidParameterValue(Invalid):
|
|||||||
"%(param_name)s=%(param_value)s")
|
"%(param_name)s=%(param_value)s")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidVMVersion(Invalid):
|
||||||
|
msg_fmt = _("VM '%(vm_name)s' has an invalid version for this operation: "
|
||||||
|
"%(version)s. Version is expected to be between: "
|
||||||
|
"%(min_version)s and %(max_version)s.")
|
||||||
|
|
||||||
|
|
||||||
class SMBException(OSWinException):
|
class SMBException(OSWinException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import mock
|
|||||||
from oslotest import base
|
from oslotest import base
|
||||||
|
|
||||||
from os_win import _utils
|
from os_win import _utils
|
||||||
|
from os_win import constants
|
||||||
from os_win import exceptions
|
from os_win import exceptions
|
||||||
|
|
||||||
|
|
||||||
@ -286,3 +287,20 @@ class UtilsTestCase(base.BaseTestCase):
|
|||||||
expected_string = '000102'
|
expected_string = '000102'
|
||||||
|
|
||||||
self.assertEqual(expected_string, resulted_string)
|
self.assertEqual(expected_string, resulted_string)
|
||||||
|
|
||||||
|
def test_required_vm_version(self):
|
||||||
|
@_utils.required_vm_version()
|
||||||
|
def foo(bar, vmsettings):
|
||||||
|
pass
|
||||||
|
|
||||||
|
mock_vmsettings = mock.Mock()
|
||||||
|
|
||||||
|
for good_version in [constants.VM_VERSION_5_0,
|
||||||
|
constants.VM_VERSION_254_0]:
|
||||||
|
mock_vmsettings.Version = good_version
|
||||||
|
foo(mock.sentinel.bar, mock_vmsettings)
|
||||||
|
|
||||||
|
for bad_version in ['4.99', '254.1']:
|
||||||
|
mock_vmsettings.Version = bad_version
|
||||||
|
self.assertRaises(exceptions.InvalidVMVersion, foo,
|
||||||
|
mock.sentinel.bar, mock_vmsettings)
|
||||||
|
@ -351,7 +351,7 @@ class VMUtils10TestCase(test_base.OsWinBaseTestCase):
|
|||||||
mock_get_element_associated_class.return_value)
|
mock_get_element_associated_class.return_value)
|
||||||
|
|
||||||
def test_set_snapshot_type(self):
|
def test_set_snapshot_type(self):
|
||||||
vmsettings = mock.Mock()
|
vmsettings = mock.Mock(Version='6.2')
|
||||||
|
|
||||||
self._vmutils._set_vm_snapshot_type(
|
self._vmutils._set_vm_snapshot_type(
|
||||||
vmsettings, mock.sentinel.snapshot_type)
|
vmsettings, mock.sentinel.snapshot_type)
|
||||||
|
@ -296,7 +296,7 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||||||
vcpus_per_numa_node, limit_cpu_features, dynamic_mem_ratio,
|
vcpus_per_numa_node, limit_cpu_features, dynamic_mem_ratio,
|
||||||
configuration_root_dir=None, snapshot_dir=None,
|
configuration_root_dir=None, snapshot_dir=None,
|
||||||
host_shutdown_action=None, vnuma_enabled=None,
|
host_shutdown_action=None, vnuma_enabled=None,
|
||||||
snapshot_type=constants.VM_SNAPSHOT_TYPE_PROD_FALLBACK,
|
snapshot_type=None,
|
||||||
is_planned_vm=False):
|
is_planned_vm=False):
|
||||||
vmsetting = self._lookup_vm_check(vm_name, for_update=True)
|
vmsetting = self._lookup_vm_check(vm_name, for_update=True)
|
||||||
|
|
||||||
@ -318,6 +318,7 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||||||
self._set_vm_vcpus(vmsetting, vcpus_num, vcpus_per_numa_node,
|
self._set_vm_vcpus(vmsetting, vcpus_num, vcpus_per_numa_node,
|
||||||
limit_cpu_features)
|
limit_cpu_features)
|
||||||
|
|
||||||
|
if snapshot_type:
|
||||||
self._set_vm_snapshot_type(vmsetting, snapshot_type)
|
self._set_vm_snapshot_type(vmsetting, snapshot_type)
|
||||||
|
|
||||||
self._modify_virtual_system(vmsetting)
|
self._modify_virtual_system(vmsetting)
|
||||||
|
@ -19,6 +19,7 @@ from oslo_log import log as logging
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from os_win._i18n import _
|
from os_win._i18n import _
|
||||||
|
from os_win import _utils
|
||||||
from os_win import constants
|
from os_win import constants
|
||||||
from os_win import exceptions
|
from os_win import exceptions
|
||||||
from os_win.utils import _wqlutils
|
from os_win.utils import _wqlutils
|
||||||
@ -289,6 +290,7 @@ class VMUtils10(vmutils.VMUtils):
|
|||||||
if pci_sds:
|
if pci_sds:
|
||||||
self._jobutils.remove_multiple_virt_resources(pci_sds)
|
self._jobutils.remove_multiple_virt_resources(pci_sds)
|
||||||
|
|
||||||
|
@_utils.required_vm_version(min_version=constants.VM_VERSION_6_2)
|
||||||
def _set_vm_snapshot_type(self, vmsettings, snapshot_type):
|
def _set_vm_snapshot_type(self, vmsettings, snapshot_type):
|
||||||
# We expect the caller to actually push the vmsettings update.
|
# We expect the caller to actually push the vmsettings update.
|
||||||
vmsettings.UserSnapshotType = snapshot_type
|
vmsettings.UserSnapshotType = snapshot_type
|
||||||
|
Loading…
x
Reference in New Issue
Block a user