Xenapi: Allow windows builds with xentools 6.1 and 6.2

Building a windows VM with pre-installed xentools
6.1 or 6.2 requires device_id to be set on XEN VM record.

Requires xenapi_device_id to be set on the
image metadata, which is then used to set the device_id
on the vm record.

DocImpact

Implements: blueprint allow-windows-builds-with-xentools6-1-and-6-2
Change-Id: Id3cd9d569519f2df1afa046ddabf33f00d63c615
This commit is contained in:
Aditi Raveesh 2013-11-04 14:51:19 +05:30 committed by Michael Still
parent 4bc19d5e14
commit c5166aaf4a
6 changed files with 173 additions and 8 deletions

View File

@ -895,7 +895,7 @@ class GetImageFromSystemMetadataTestCase(test.NoDBTestCase):
self.assertNotIn("foo1", image)
class VersionTestCase(test.TestCase):
class VersionTestCase(test.NoDBTestCase):
def test_convert_version_to_int(self):
self.assertEqual(utils.convert_version_to_int('6.2.0'), 6002000)
self.assertEqual(utils.convert_version_to_int((6, 4, 3)), 6004003)
@ -909,3 +909,17 @@ class VersionTestCase(test.TestCase):
def test_convert_version_to_tuple(self):
self.assertEqual(utils.convert_version_to_tuple('6.7.0'), (6, 7, 0))
def test_get_major_minor_version_from_string(self):
self.assertEqual(utils.get_major_minor_version('6.1.3'), 6.1)
self.assertEqual(utils.get_major_minor_version('6.4'), 6.4)
self.assertEqual(utils.get_major_minor_version('6'), 6)
def test_get_major_minor_version_from_float(self):
self.assertEqual(utils.get_major_minor_version(6.1), 6.1)
self.assertEqual(utils.get_major_minor_version(6), 6.0)
def test_get_major_minor_version_raises_exception(self):
exc = self.assertRaises(exception.NovaException,
utils.get_major_minor_version, '5a.6b')
self.assertEqual("Version 5a.6b invalid", exc.message)

View File

@ -1816,3 +1816,81 @@ class StripBaseMirrorTestCase(VMUtilsTestBase):
self.assertEqual(expected, session.call_xenapi.call_args_list)
expected = [mock.call(session, "ref"), mock.call(session, "ref")]
self.assertEqual(expected, mock_strip.call_args_list)
class DeviceIdTestCase(VMUtilsTestBase):
def test_device_id_is_none_if_not_specified_in_meta_data(self):
image_meta = {}
session = mock.Mock()
session.product_version = '6.1'
self.assertIsNone(vm_utils.get_vm_device_id(session, image_meta))
def test_get_device_id_if_hypervisor_version_is_greater_than_6_1(self):
image_meta = {'xenapi_device_id': '0002'}
session = mock.Mock()
session.product_version = '6.2'
self.assertEqual('0002',
vm_utils.get_vm_device_id(session, image_meta))
session.product_version = '6.3.1'
self.assertEqual('0002',
vm_utils.get_vm_device_id(session, image_meta))
def test_raise_exception_if_device_id_not_supported_by_hyp_version(self):
image_meta = {'xenapi_device_id': '0002'}
session = mock.Mock()
session.product_version = '6.0'
exc = self.assertRaises(exception.NovaException,
vm_utils.get_vm_device_id, session, image_meta)
self.assertEqual("Device id 0002 specified is not supported by "
"hypervisor version 6.0", exc.message)
class CreateVmRecordTestCase(VMUtilsTestBase):
@mock.patch.object(flavors, 'extract_flavor')
def test_create_vm_record(self, mock_extract_flavor):
session = mock.Mock()
instance = {"uuid": "uuid123"}
instance_type = {"memory_mb": 1024, "vcpus": 1, "vcpu_weight": 2}
mock_extract_flavor.return_value = instance_type
vm_utils.create_vm(session, instance, "name", "kernel", "ramdisk",
device_id="0002")
expected_vm_rec = {
'VCPUs_params': {'cap': '0', 'weight': '2'},
'PV_args': '',
'memory_static_min': '0',
'ha_restart_priority': '',
'HVM_boot_policy': 'BIOS order',
'PV_bootloader': '',
'tags': [],
'VCPUs_max': '1',
'memory_static_max': '1073741824',
'actions_after_shutdown': 'destroy',
'memory_dynamic_max': '1073741824',
'user_version': '0',
'xenstore_data': {'vm-data/allowvssprovider': 'false'},
'blocked_operations': {},
'is_a_template': False,
'name_description': '',
'memory_dynamic_min': '1073741824',
'actions_after_crash': 'destroy',
'memory_target': '1073741824',
'PV_ramdisk': '',
'PV_bootloader_args': '',
'PCI_bus': '',
'other_config': {'nova_uuid': 'uuid123'},
'name_label': 'name',
'actions_after_reboot': 'restart',
'VCPUs_at_startup': '1',
'HVM_boot_params': {'order': 'dc'},
'platform': {'nx': 'true', 'pae': 'true', 'apic': 'true',
'timeoffset': '0', 'viridian': 'true', 'acpi': 'true',
'device_id': '0002'},
'PV_legacy_args': '',
'PV_kernel': '',
'affinity': '',
'recommendations': '',
'ha_always_run': False}
session.call_xenapi.assert_called_with('VM.create', expected_vm_rec)

View File

@ -294,7 +294,8 @@ class SpawnTestCase(VMOpsTestBase):
self.vmops._ensure_instance_name_unique(name_label)
self.vmops._ensure_enough_free_mem(instance)
self.vmops._create_vm_record(context, instance, name_label,
di_type, kernel_file, ramdisk_file).AndReturn(vm_ref)
di_type, kernel_file,
ramdisk_file, image_meta).AndReturn(vm_ref)
step += 1
self.vmops._update_instance_progress(context, instance, step, steps)
@ -400,7 +401,8 @@ class SpawnTestCase(VMOpsTestBase):
vm_ref = "fake_vm_ref"
self.vmops._create_vm_record(context, instance, name_label,
di_type, kernel_file, ramdisk_file).AndReturn(vm_ref)
di_type, kernel_file,
ramdisk_file, image_meta).AndReturn(vm_ref)
if resize_instance:
self.vmops._resize_up_root_vdi(instance, root_vdi)
@ -746,3 +748,31 @@ class MigrateDiskResizingUpTestCase(VMOpsTestBase):
mock_restore.assert_called_once_with(instance)
mock_migrate_vhd.assert_called_once_with(self.vmops._session,
instance, "parent", dest, sr_path, 1)
class CreateVMRecordTestCase(VMOpsTestBase):
@mock.patch.object(vm_utils, 'determine_vm_mode')
@mock.patch.object(vm_utils, 'get_vm_device_id')
@mock.patch.object(vm_utils, 'create_vm')
def test_create_vm_record_with_vm_device_id(self, mock_create_vm,
mock_get_vm_device_id, mock_determine_vm_mode):
context = "context"
instance = {"vm_mode": "vm_mode", "uuid": "uuid123"}
name_label = "dummy"
disk_image_type = "vhd"
kernel_file = "kernel"
ramdisk_file = "ram"
image_meta = "image_meta"
device_id = "0002"
session = "session"
self.vmops._session = session
mock_get_vm_device_id.return_value = device_id
mock_determine_vm_mode.return_value = "vm_mode"
self.vmops._create_vm_record(context, instance, name_label,
disk_image_type, kernel_file, ramdisk_file, image_meta)
vm_utils.get_vm_device_id.assert_called_with(session, image_meta)
mock_create_vm.assert_called_with(session, instance, name_label,
kernel_file, ramdisk_file, False, device_id)

View File

@ -1033,6 +1033,18 @@ def convert_version_to_tuple(version_str):
return tuple(int(part) for part in version_str.split('.'))
def get_major_minor_version(version):
try:
if type(version) == int or type(version) == float:
return version
if type(version) == str:
major_minor_versions = version.split(".")[0:2]
version_as_float = float(".".join(major_minor_versions))
return version_as_float
except Exception:
raise exception.NovaException(_("Version %s invalid") % version)
def is_neutron():
global _IS_NEUTRON_ATTEMPTED
global _IS_NEUTRON

View File

@ -216,8 +216,33 @@ class ImageType(object):
}.get(image_type_id)
def get_vm_device_id(session, image_meta):
# NOTE: device_id should be 2 for windows VMs which run new xentools
# (>=6.1). Refer to http://support.citrix.com/article/CTX135099 for more
# information.
if image_meta is None:
image_meta = {}
device_id = image_meta.get('xenapi_device_id')
# The device_id is required to be set for hypervisor version 6.1 and above
if device_id:
hypervisor_version = session.product_version
if _hypervisor_supports_device_id(hypervisor_version):
return device_id
else:
msg = _("Device id %(id)s specified is not supported by "
"hypervisor version %(version)s") % {'id': device_id,
'version': hypervisor_version}
raise exception.NovaException(msg)
def _hypervisor_supports_device_id(version):
hypervisor_major_minor_version = utils.get_major_minor_version(version)
return(hypervisor_major_minor_version >= 6.1)
def create_vm(session, instance, name_label, kernel, ramdisk,
use_pv_kernel=False):
use_pv_kernel=False, device_id=None):
"""Create a VM record. Returns new VM reference.
the use_pv_kernel flag indicates whether the guest is HVM or PV
@ -294,6 +319,9 @@ def create_vm(session, instance, name_label, kernel, ramdisk,
rec['HVM_boot_params'] = {'order': 'dc'}
rec['HVM_boot_policy'] = 'BIOS order'
if device_id:
rec['platform']['device_id'] = device_id
vm_ref = session.call_xenapi('VM.create', rec)
LOG.debug(_('Created VM'), instance=instance)
return vm_ref

View File

@ -387,7 +387,8 @@ class VMOps(object):
def create_vm_record_step(undo_mgr, disk_image_type,
kernel_file, ramdisk_file):
vm_ref = self._create_vm_record(context, instance, name_label,
disk_image_type, kernel_file, ramdisk_file)
disk_image_type, kernel_file,
ramdisk_file, image_meta)
def undo_create_vm():
self._destroy(instance, vm_ref, network_info=network_info)
@ -524,8 +525,8 @@ class VMOps(object):
if not vm_utils.is_enough_free_mem(self._session, instance):
raise exception.InsufficientFreeMemory(uuid=instance['uuid'])
def _create_vm_record(self, context, instance, name_label,
disk_image_type, kernel_file, ramdisk_file):
def _create_vm_record(self, context, instance, name_label, disk_image_type,
kernel_file, ramdisk_file, image_meta):
"""Create the VM record in Xen, making sure that we do not create
a duplicate name-label. Also do a rough sanity check on memory
to try to short-circuit a potential failure later. (The memory
@ -538,10 +539,12 @@ class VMOps(object):
self._virtapi.instance_update(context,
instance['uuid'], {'vm_mode': mode})
device_id = vm_utils.get_vm_device_id(self._session, image_meta)
use_pv_kernel = (mode == vm_mode.XEN)
LOG.debug(_("Using PV kernel: %s") % use_pv_kernel, instance=instance)
vm_ref = vm_utils.create_vm(self._session, instance, name_label,
kernel_file, ramdisk_file, use_pv_kernel)
kernel_file, ramdisk_file,
use_pv_kernel, device_id)
return vm_ref
def _attach_disks(self, instance, vm_ref, name_label, vdis,