Merge "Adds Hyper-V dynamic memory support"
This commit is contained in:
@@ -2591,6 +2591,14 @@
|
|||||||
# / Windows Server 2012 and above (boolean value)
|
# / Windows Server 2012 and above (boolean value)
|
||||||
#enable_instance_metrics_collection=false
|
#enable_instance_metrics_collection=false
|
||||||
|
|
||||||
|
# Enables dynamic memory allocation (ballooning) when set to a
|
||||||
|
# value greater than 1. The value expresses the ratio between
|
||||||
|
# the total RAM assigned to an instance and its startup RAM
|
||||||
|
# amount. For example a ratio of 2.0 for an instance with
|
||||||
|
# 1024MB of RAM implies 512MB of RAM allocated at startup
|
||||||
|
# (floating point value)
|
||||||
|
#dynamic_memory_ratio=1.0
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Options defined in nova.virt.hyperv.volumeops
|
# Options defined in nova.virt.hyperv.volumeops
|
||||||
|
|||||||
@@ -363,6 +363,10 @@ class HyperVAPITestCase(test.TestCase):
|
|||||||
def test_spawn_no_cow_image(self):
|
def test_spawn_no_cow_image(self):
|
||||||
self._test_spawn_instance(False)
|
self._test_spawn_instance(False)
|
||||||
|
|
||||||
|
def test_spawn_dynamic_memory(self):
|
||||||
|
CONF.set_override('dynamic_memory_ratio', 2.0, 'hyperv')
|
||||||
|
self._test_spawn_instance()
|
||||||
|
|
||||||
def _setup_spawn_config_drive_mocks(self, use_cdrom):
|
def _setup_spawn_config_drive_mocks(self, use_cdrom):
|
||||||
im = instance_metadata.InstanceMetadata(mox.IgnoreArg(),
|
im = instance_metadata.InstanceMetadata(mox.IgnoreArg(),
|
||||||
content=mox.IsA(list),
|
content=mox.IsA(list),
|
||||||
@@ -871,7 +875,8 @@ class HyperVAPITestCase(test.TestCase):
|
|||||||
block_device_info=None,
|
block_device_info=None,
|
||||||
admin_permissions=True):
|
admin_permissions=True):
|
||||||
vmutils.VMUtils.create_vm(mox.Func(self._check_vm_name), mox.IsA(int),
|
vmutils.VMUtils.create_vm(mox.Func(self._check_vm_name), mox.IsA(int),
|
||||||
mox.IsA(int), mox.IsA(bool))
|
mox.IsA(int), mox.IsA(bool),
|
||||||
|
CONF.hyperv.dynamic_memory_ratio)
|
||||||
|
|
||||||
if not boot_from_volume:
|
if not boot_from_volume:
|
||||||
m = vmutils.VMUtils.attach_ide_drive(mox.Func(self._check_vm_name),
|
m = vmutils.VMUtils.attach_ide_drive(mox.Func(self._check_vm_name),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2013 Cloudbase Solutions Srl
|
# Copyright 2013 Cloudbase Solutions Srl
|
||||||
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@@ -25,6 +26,8 @@ class VMUtilsTestCase(test.TestCase):
|
|||||||
"""Unit tests for the Hyper-V VMUtils class."""
|
"""Unit tests for the Hyper-V VMUtils class."""
|
||||||
|
|
||||||
_FAKE_VM_NAME = 'fake_vm'
|
_FAKE_VM_NAME = 'fake_vm'
|
||||||
|
_FAKE_MEMORY_MB = 2
|
||||||
|
_FAKE_VM_PATH = "fake_vm_path"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._vmutils = vmutils.VMUtils()
|
self._vmutils = vmutils.VMUtils()
|
||||||
@@ -36,3 +39,39 @@ class VMUtilsTestCase(test.TestCase):
|
|||||||
self.assertRaises(NotImplementedError,
|
self.assertRaises(NotImplementedError,
|
||||||
self._vmutils.enable_vm_metrics_collection,
|
self._vmutils.enable_vm_metrics_collection,
|
||||||
self._FAKE_VM_NAME)
|
self._FAKE_VM_NAME)
|
||||||
|
|
||||||
|
def _lookup_vm(self):
|
||||||
|
mock_vm = mock.MagicMock()
|
||||||
|
self._vmutils._lookup_vm_check = mock.MagicMock(
|
||||||
|
return_value=mock_vm)
|
||||||
|
mock_vm.path_.return_value = self._FAKE_VM_PATH
|
||||||
|
return mock_vm
|
||||||
|
|
||||||
|
def test_set_vm_memory_static(self):
|
||||||
|
self._test_set_vm_memory_dynamic(1.0)
|
||||||
|
|
||||||
|
def test_set_vm_memory_dynamic(self):
|
||||||
|
self._test_set_vm_memory_dynamic(2.0)
|
||||||
|
|
||||||
|
def _test_set_vm_memory_dynamic(self, dynamic_memory_ratio):
|
||||||
|
mock_vm = self._lookup_vm()
|
||||||
|
|
||||||
|
mock_s = self._vmutils._conn.Msvm_VirtualSystemSettingData()[0]
|
||||||
|
mock_s.SystemType = 3
|
||||||
|
|
||||||
|
mock_vmsetting = mock.MagicMock()
|
||||||
|
mock_vmsetting.associators.return_value = [mock_s]
|
||||||
|
|
||||||
|
self._vmutils._modify_virt_resource = mock.MagicMock()
|
||||||
|
|
||||||
|
self._vmutils._set_vm_memory(mock_vm, mock_vmsetting,
|
||||||
|
self._FAKE_MEMORY_MB,
|
||||||
|
dynamic_memory_ratio)
|
||||||
|
|
||||||
|
self._vmutils._modify_virt_resource.assert_called_with(
|
||||||
|
mock_s, self._FAKE_VM_PATH)
|
||||||
|
|
||||||
|
if dynamic_memory_ratio > 1:
|
||||||
|
self.assertTrue(mock_s.DynamicMemoryEnabled)
|
||||||
|
else:
|
||||||
|
self.assertFalse(mock_s.DynamicMemoryEnabled)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class VMUtilsV2TestCase(test.TestCase):
|
|||||||
_FAKE_SNAPSHOT_PATH = "_FAKE_SNAPSHOT_PATH"
|
_FAKE_SNAPSHOT_PATH = "_FAKE_SNAPSHOT_PATH"
|
||||||
_FAKE_RES_DATA = "fake_res_data"
|
_FAKE_RES_DATA = "fake_res_data"
|
||||||
_FAKE_RES_PATH = "fake_res_path"
|
_FAKE_RES_PATH = "fake_res_path"
|
||||||
|
_FAKE_DYNAMIC_MEMORY_RATIO = 1.0
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._vmutils = vmutilsv2.VMUtilsV2()
|
self._vmutils = vmutilsv2.VMUtilsV2()
|
||||||
@@ -68,11 +69,14 @@ class VMUtilsV2TestCase(test.TestCase):
|
|||||||
self._vmutils._set_vm_vcpus = mock.MagicMock()
|
self._vmutils._set_vm_vcpus = mock.MagicMock()
|
||||||
|
|
||||||
self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB,
|
self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB,
|
||||||
self._FAKE_VCPUS_NUM, False)
|
self._FAKE_VCPUS_NUM, False,
|
||||||
|
self._FAKE_DYNAMIC_MEMORY_RATIO)
|
||||||
|
|
||||||
self.assertTrue(mock_svc.DefineSystem.called)
|
self.assertTrue(mock_svc.DefineSystem.called)
|
||||||
self._vmutils._set_vm_memory.assert_called_with(mock_vm, mock_s,
|
self._vmutils._set_vm_memory.assert_called_with(
|
||||||
self._FAKE_MEMORY_MB)
|
mock_vm, mock_s, self._FAKE_MEMORY_MB,
|
||||||
|
self._FAKE_DYNAMIC_MEMORY_RATIO)
|
||||||
|
|
||||||
self._vmutils._set_vm_vcpus.assert_called_with(mock_vm, mock_s,
|
self._vmutils._set_vm_vcpus.assert_called_with(mock_vm, mock_s,
|
||||||
self._FAKE_VCPUS_NUM,
|
self._FAKE_VCPUS_NUM,
|
||||||
False)
|
False)
|
||||||
|
|||||||
@@ -62,7 +62,15 @@ hyperv_opts = [
|
|||||||
help='Enables metrics collections for an instance by using '
|
help='Enables metrics collections for an instance by using '
|
||||||
'Hyper-V\'s metric APIs. Collected data can by retrieved '
|
'Hyper-V\'s metric APIs. Collected data can by retrieved '
|
||||||
'by other apps and services, e.g.: Ceilometer. '
|
'by other apps and services, e.g.: Ceilometer. '
|
||||||
'Requires Hyper-V / Windows Server 2012 and above')
|
'Requires Hyper-V / Windows Server 2012 and above'),
|
||||||
|
cfg.FloatOpt('dynamic_memory_ratio',
|
||||||
|
default=1.0,
|
||||||
|
help='Enables dynamic memory allocation (ballooning) when '
|
||||||
|
'set to a value greater than 1. The value expresses '
|
||||||
|
'the ratio between the total RAM assigned to an '
|
||||||
|
'instance and its startup RAM amount. For example a '
|
||||||
|
'ratio of 2.0 for an instance with 1024MB of RAM '
|
||||||
|
'implies 512MB of RAM allocated at startup')
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -205,7 +213,8 @@ class VMOps(object):
|
|||||||
self._vmutils.create_vm(instance_name,
|
self._vmutils.create_vm(instance_name,
|
||||||
instance['memory_mb'],
|
instance['memory_mb'],
|
||||||
instance['vcpus'],
|
instance['vcpus'],
|
||||||
CONF.hyperv.limit_cpu_features)
|
CONF.hyperv.limit_cpu_features,
|
||||||
|
CONF.hyperv.dynamic_memory_ratio)
|
||||||
|
|
||||||
if root_vhd_path:
|
if root_vhd_path:
|
||||||
self._vmutils.attach_ide_drive(instance_name,
|
self._vmutils.attach_ide_drive(instance_name,
|
||||||
|
|||||||
@@ -150,16 +150,28 @@ class VMUtils(object):
|
|||||||
# Avoid snapshots
|
# Avoid snapshots
|
||||||
return [s for s in vmsettings if s.SettingType == 3][0]
|
return [s for s in vmsettings if s.SettingType == 3][0]
|
||||||
|
|
||||||
def _set_vm_memory(self, vm, vmsetting, memory_mb):
|
def _set_vm_memory(self, vm, vmsetting, memory_mb, dynamic_memory_ratio):
|
||||||
memsetting = vmsetting.associators(
|
mem_settings = vmsetting.associators(
|
||||||
wmi_result_class='Msvm_MemorySettingData')[0]
|
wmi_result_class='Msvm_MemorySettingData')[0]
|
||||||
#No Dynamic Memory, so reservation, limit and quantity are identical.
|
|
||||||
mem = long(memory_mb)
|
|
||||||
memsetting.VirtualQuantity = mem
|
|
||||||
memsetting.Reservation = mem
|
|
||||||
memsetting.Limit = mem
|
|
||||||
|
|
||||||
self._modify_virt_resource(memsetting, vm.path_())
|
max_mem = long(memory_mb)
|
||||||
|
mem_settings.Limit = max_mem
|
||||||
|
|
||||||
|
if dynamic_memory_ratio > 1:
|
||||||
|
mem_settings.DynamicMemoryEnabled = True
|
||||||
|
# Must be a multiple of 2
|
||||||
|
reserved_mem = min(
|
||||||
|
long(max_mem / dynamic_memory_ratio) >> 1 << 1,
|
||||||
|
max_mem)
|
||||||
|
else:
|
||||||
|
mem_settings.DynamicMemoryEnabled = False
|
||||||
|
reserved_mem = max_mem
|
||||||
|
|
||||||
|
mem_settings.Reservation = reserved_mem
|
||||||
|
# Start with the minimum memory
|
||||||
|
mem_settings.VirtualQuantity = reserved_mem
|
||||||
|
|
||||||
|
self._modify_virt_resource(mem_settings, vm.path_())
|
||||||
|
|
||||||
def _set_vm_vcpus(self, vm, vmsetting, vcpus_num, limit_cpu_features):
|
def _set_vm_vcpus(self, vm, vmsetting, vcpus_num, limit_cpu_features):
|
||||||
procsetting = vmsetting.associators(
|
procsetting = vmsetting.associators(
|
||||||
@@ -172,10 +184,11 @@ class VMUtils(object):
|
|||||||
|
|
||||||
self._modify_virt_resource(procsetting, vm.path_())
|
self._modify_virt_resource(procsetting, vm.path_())
|
||||||
|
|
||||||
def update_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features):
|
def update_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features,
|
||||||
|
dynamic_memory_ratio):
|
||||||
vm = self._lookup_vm_check(vm_name)
|
vm = self._lookup_vm_check(vm_name)
|
||||||
vmsetting = self._get_vm_setting_data(vm)
|
vmsetting = self._get_vm_setting_data(vm)
|
||||||
self._set_vm_memory(vm, vmsetting, memory_mb)
|
self._set_vm_memory(vm, vmsetting, memory_mb, dynamic_memory_ratio)
|
||||||
self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
|
self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
|
||||||
|
|
||||||
def check_admin_permissions(self):
|
def check_admin_permissions(self):
|
||||||
@@ -185,7 +198,8 @@ class VMUtils(object):
|
|||||||
" operate the virtual machine.")
|
" operate the virtual machine.")
|
||||||
raise HyperVAuthorizationException(msg)
|
raise HyperVAuthorizationException(msg)
|
||||||
|
|
||||||
def create_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features):
|
def create_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features,
|
||||||
|
dynamic_memory_ratio):
|
||||||
"""Creates a VM."""
|
"""Creates a VM."""
|
||||||
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
||||||
|
|
||||||
@@ -195,7 +209,7 @@ class VMUtils(object):
|
|||||||
vmsetting = self._get_vm_setting_data(vm)
|
vmsetting = self._get_vm_setting_data(vm)
|
||||||
|
|
||||||
LOG.debug(_('Setting memory for vm %s'), vm_name)
|
LOG.debug(_('Setting memory for vm %s'), vm_name)
|
||||||
self._set_vm_memory(vm, vmsetting, memory_mb)
|
self._set_vm_memory(vm, vmsetting, memory_mb, dynamic_memory_ratio)
|
||||||
|
|
||||||
LOG.debug(_('Set vCPUs for vm %s'), vm_name)
|
LOG.debug(_('Set vCPUs for vm %s'), vm_name)
|
||||||
self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
|
self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
|
||||||
|
|||||||
Reference in New Issue
Block a user