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:
Robert Tingirica
2013-07-26 13:07:07 +03:00
parent d27895aab8
commit a1b6ea5d03
6 changed files with 98 additions and 19 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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)