Merge "Adds Hyper-V dynamic memory support"

This commit is contained in:
Jenkins
2013-09-05 01:09:56 +00:00
committed by Gerrit Code Review
6 changed files with 98 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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