Adds Hyper-V dynamic memory support
Blueprint: hyper-v-dynamic-memory Memory overcommit in Hyper-V is handled with a feature called dynamic memory (memory ballooning). This change adds a configuration option to enable it. The value of dynamic_memory_ratio 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. Unit tests have been added for this feature as well. DocImpact Change-Id: I036eaa3e739d995c6a5bcfb022bf2ed0a999085e
This commit is contained in:
@@ -2591,6 +2591,14 @@
|
||||
# / Windows Server 2012 and above (boolean value)
|
||||
#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
|
||||
|
||||
@@ -363,6 +363,10 @@ class HyperVAPITestCase(test.TestCase):
|
||||
def test_spawn_no_cow_image(self):
|
||||
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):
|
||||
im = instance_metadata.InstanceMetadata(mox.IgnoreArg(),
|
||||
content=mox.IsA(list),
|
||||
@@ -871,7 +875,8 @@ class HyperVAPITestCase(test.TestCase):
|
||||
block_device_info=None,
|
||||
admin_permissions=True):
|
||||
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:
|
||||
m = vmutils.VMUtils.attach_ide_drive(mox.Func(self._check_vm_name),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# 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
|
||||
# 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."""
|
||||
|
||||
_FAKE_VM_NAME = 'fake_vm'
|
||||
_FAKE_MEMORY_MB = 2
|
||||
_FAKE_VM_PATH = "fake_vm_path"
|
||||
|
||||
def setUp(self):
|
||||
self._vmutils = vmutils.VMUtils()
|
||||
@@ -36,3 +39,39 @@ class VMUtilsTestCase(test.TestCase):
|
||||
self.assertRaises(NotImplementedError,
|
||||
self._vmutils.enable_vm_metrics_collection,
|
||||
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_RES_DATA = "fake_res_data"
|
||||
_FAKE_RES_PATH = "fake_res_path"
|
||||
_FAKE_DYNAMIC_MEMORY_RATIO = 1.0
|
||||
|
||||
def setUp(self):
|
||||
self._vmutils = vmutilsv2.VMUtilsV2()
|
||||
@@ -68,11 +69,14 @@ class VMUtilsV2TestCase(test.TestCase):
|
||||
self._vmutils._set_vm_vcpus = mock.MagicMock()
|
||||
|
||||
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._vmutils._set_vm_memory.assert_called_with(mock_vm, mock_s,
|
||||
self._FAKE_MEMORY_MB)
|
||||
self._vmutils._set_vm_memory.assert_called_with(
|
||||
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._FAKE_VCPUS_NUM,
|
||||
False)
|
||||
|
||||
@@ -62,7 +62,15 @@ hyperv_opts = [
|
||||
help='Enables metrics collections for an instance by using '
|
||||
'Hyper-V\'s metric APIs. Collected data can by retrieved '
|
||||
'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
|
||||
@@ -205,7 +213,8 @@ class VMOps(object):
|
||||
self._vmutils.create_vm(instance_name,
|
||||
instance['memory_mb'],
|
||||
instance['vcpus'],
|
||||
CONF.hyperv.limit_cpu_features)
|
||||
CONF.hyperv.limit_cpu_features,
|
||||
CONF.hyperv.dynamic_memory_ratio)
|
||||
|
||||
if root_vhd_path:
|
||||
self._vmutils.attach_ide_drive(instance_name,
|
||||
|
||||
@@ -150,16 +150,28 @@ class VMUtils(object):
|
||||
# Avoid snapshots
|
||||
return [s for s in vmsettings if s.SettingType == 3][0]
|
||||
|
||||
def _set_vm_memory(self, vm, vmsetting, memory_mb):
|
||||
memsetting = vmsetting.associators(
|
||||
def _set_vm_memory(self, vm, vmsetting, memory_mb, dynamic_memory_ratio):
|
||||
mem_settings = vmsetting.associators(
|
||||
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):
|
||||
procsetting = vmsetting.associators(
|
||||
@@ -172,10 +184,11 @@ class VMUtils(object):
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
def check_admin_permissions(self):
|
||||
@@ -185,7 +198,8 @@ class VMUtils(object):
|
||||
" operate the virtual machine.")
|
||||
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."""
|
||||
vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
||||
|
||||
@@ -195,7 +209,7 @@ class VMUtils(object):
|
||||
vmsetting = self._get_vm_setting_data(vm)
|
||||
|
||||
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)
|
||||
self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
|
||||
|
||||
Reference in New Issue
Block a user