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.
|
||||
|
||||
import ctypes
|
||||
import inspect
|
||||
from pkg_resources import parse_version
|
||||
import textwrap
|
||||
import time
|
||||
import types
|
||||
@ -29,6 +31,7 @@ from oslo_utils import excutils
|
||||
from oslo_utils import reflection
|
||||
import six
|
||||
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
|
||||
|
||||
@ -256,3 +259,43 @@ def hex_str_to_byte_array(string):
|
||||
|
||||
def byte_array_to_hex_str(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_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
|
||||
|
||||
SCSI_UID_SCSI_NAME_STRING = 8
|
||||
|
@ -104,6 +104,12 @@ class InvalidParameterValue(Invalid):
|
||||
"%(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):
|
||||
pass
|
||||
|
||||
|
@ -23,6 +23,7 @@ import mock
|
||||
from oslotest import base
|
||||
|
||||
from os_win import _utils
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
|
||||
|
||||
@ -286,3 +287,20 @@ class UtilsTestCase(base.BaseTestCase):
|
||||
expected_string = '000102'
|
||||
|
||||
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)
|
||||
|
||||
def test_set_snapshot_type(self):
|
||||
vmsettings = mock.Mock()
|
||||
vmsettings = mock.Mock(Version='6.2')
|
||||
|
||||
self._vmutils._set_vm_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,
|
||||
configuration_root_dir=None, snapshot_dir=None,
|
||||
host_shutdown_action=None, vnuma_enabled=None,
|
||||
snapshot_type=constants.VM_SNAPSHOT_TYPE_PROD_FALLBACK,
|
||||
snapshot_type=None,
|
||||
is_planned_vm=False):
|
||||
vmsetting = self._lookup_vm_check(vm_name, for_update=True)
|
||||
|
||||
@ -318,7 +318,8 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
||||
self._set_vm_vcpus(vmsetting, vcpus_num, vcpus_per_numa_node,
|
||||
limit_cpu_features)
|
||||
|
||||
self._set_vm_snapshot_type(vmsetting, snapshot_type)
|
||||
if snapshot_type:
|
||||
self._set_vm_snapshot_type(vmsetting, snapshot_type)
|
||||
|
||||
self._modify_virtual_system(vmsetting)
|
||||
|
||||
|
@ -19,6 +19,7 @@ from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from os_win._i18n import _
|
||||
from os_win import _utils
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
from os_win.utils import _wqlutils
|
||||
@ -289,6 +290,7 @@ class VMUtils10(vmutils.VMUtils):
|
||||
if 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):
|
||||
# We expect the caller to actually push the vmsettings update.
|
||||
vmsettings.UserSnapshotType = snapshot_type
|
||||
|
Loading…
Reference in New Issue
Block a user