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:
parent
4bc19d5e14
commit
c5166aaf4a
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue