From 442a3a74a8af0b8585a1340d49103bf5304e4504 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 31 May 2012 20:18:34 +0000 Subject: Start removing unnecessary classes from XenAPI driver The XenAPI driver has the concept of helper classes. All methods have been classmethods and the classes themselves are never instantiated. As a result, they only add an extra namespace lookup and provide no value. The first step is to move some functions from HelperBase to the session class where it is better suited. Change-Id: I13d6d49509052d35339a0ef132647bd61179cdd8 --- nova/virt/xenapi/__init__.py | 24 ------------------------ nova/virt/xenapi/connection.py | 23 +++++++++++++++++++++++ nova/virt/xenapi/vm_utils.py | 10 +++++----- nova/virt/xenapi/volume_utils.py | 2 +- 4 files changed, 29 insertions(+), 30 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/__init__.py b/nova/virt/xenapi/__init__.py index 7eae5f767..0e93adcda 100644 --- a/nova/virt/xenapi/__init__.py +++ b/nova/virt/xenapi/__init__.py @@ -24,27 +24,3 @@ class HelperBase(object): """ The base for helper classes. """ - @classmethod - def get_rec(cls, session, record_type, ref): - try: - return session.call_xenapi('%s.get_record' % record_type, ref) - except session.XenAPI.Failure, e: - if e.details[0] != 'HANDLE_INVALID': - raise - - return None - - @classmethod - def get_all_refs_and_recs(cls, session, record_type): - """Retrieve all refs and recs for a Xen record type. - - Handles race-conditions where the record may be deleted between - the `get_all` call and the `get_record` call. - """ - - for ref in session.call_xenapi('%s.get_all' % record_type): - rec = cls.get_rec(session, record_type, ref) - # Check to make sure the record still exists. It may have - # been deleted between the get_all call and get_record call - if rec: - yield ref, rec diff --git a/nova/virt/xenapi/connection.py b/nova/virt/xenapi/connection.py index bd26e86a6..038a27cbf 100644 --- a/nova/virt/xenapi/connection.py +++ b/nova/virt/xenapi/connection.py @@ -632,3 +632,26 @@ class XenAPISession(object): except xmlrpclib.ProtocolError, exc: LOG.debug(_("Got exception: %s"), exc) raise + + def get_rec(self, record_type, ref): + try: + return self.call_xenapi('%s.get_record' % record_type, ref) + except self.XenAPI.Failure, e: + if e.details[0] != 'HANDLE_INVALID': + raise + + return None + + def get_all_refs_and_recs(self, record_type): + """Retrieve all refs and recs for a Xen record type. + + Handles race-conditions where the record may be deleted between + the `get_all` call and the `get_record` call. + """ + + for ref in self.call_xenapi('%s.get_all' % record_type): + rec = self.get_rec(record_type, ref) + # Check to make sure the record still exists. It may have + # been deleted between the get_all call and get_record call + if rec: + yield ref, rec diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e47002fa8..221735142 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -986,7 +986,7 @@ class VMHelper(xenapi.HelperBase): @classmethod def list_vms(cls, session): - for vm_ref, vm_rec in cls.get_all_refs_and_recs(session, 'VM'): + for vm_ref, vm_rec in session.get_all_refs_and_recs('VM'): if (vm_rec["resident_on"] != session.get_xenapi_host() or vm_rec["is_a_template"] or vm_rec["is_control_domain"]): continue @@ -1153,12 +1153,12 @@ class VMHelper(xenapi.HelperBase): if filter_criteria == 'other-config': key, value = filter_pattern.split('=', 1) - for sr_ref, sr_rec in cls.get_all_refs_and_recs(session, 'SR'): + for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): if not (key in sr_rec['other_config'] and sr_rec['other_config'][key] == value): continue for pbd_ref in sr_rec['PBDs']: - pbd_rec = cls.get_rec(session, 'PBD', pbd_ref) + pbd_rec = session.get_rec('PBD', pbd_ref) if pbd_rec and pbd_rec['host'] == host: return sr_ref elif filter_criteria == 'default-sr' and filter_pattern == 'true': @@ -1185,7 +1185,7 @@ class VMHelper(xenapi.HelperBase): def find_iso_sr(cls, session): """Return the storage repository to hold ISO images""" host = session.get_xenapi_host() - for sr_ref, sr_rec in cls.get_all_refs_and_recs(session, 'SR'): + for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): LOG.debug(_("ISO: looking at SR %(sr_rec)s") % locals()) if not sr_rec['content_type'] == 'iso': LOG.debug(_("ISO: not iso content")) @@ -1201,7 +1201,7 @@ class VMHelper(xenapi.HelperBase): LOG.debug(_("ISO: SR MATCHing our criteria")) for pbd_ref in sr_rec['PBDs']: LOG.debug(_("ISO: ISO, looking to see if it is host local")) - pbd_rec = cls.get_rec(session, 'PBD', pbd_ref) + pbd_rec = session.get_rec('PBD', pbd_ref) if not pbd_rec: LOG.debug(_("ISO: PBD %(pbd_ref)s disappeared") % locals()) continue diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index e93e1b81e..a5c1d572c 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -141,7 +141,7 @@ class VolumeHelper(xenapi.HelperBase): """ Return the storage repository given a uuid. """ - for sr_ref, sr_rec in cls.get_all_refs_and_recs(session, 'SR'): + for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): if sr_rec['uuid'] == sr_uuid: return sr_ref return None -- cgit From a3f33ca52c516e55dfbed13a1a6f4a9aef7aee96 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 31 May 2012 18:13:03 +0000 Subject: Remove vm_utils.VMHelper class The XenAPI driver has the concept of helper classes. All methods have been classmethods and the classes themselves are never instantiated. As a result, they only add an extra namespace lookup and provide no value. Change-Id: If8a20501c9a9353359234441580552e41471722b --- nova/tests/test_xenapi.py | 42 +- nova/tests/xenapi/stubs.py | 52 +- nova/virt/xenapi/host.py | 4 +- nova/virt/xenapi/vif.py | 4 +- nova/virt/xenapi/vm_utils.py | 1879 ++++++++++++++++++++--------------------- nova/virt/xenapi/vmops.py | 111 ++- nova/virt/xenapi/volumeops.py | 23 +- 7 files changed, 1034 insertions(+), 1081 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9314c947f..072be5b19 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -703,7 +703,7 @@ class XenAPIVMTestCase(test.TestCase): def test_rescue(self): instance = self._create_instance() session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') - vm_ref = vm_utils.VMHelper.lookup(session, instance.name) + vm_ref = vm_utils.lookup(session, instance.name) xenapi_fake.create_vbd(vm_ref, "swap", userdevice=1) xenapi_fake.create_vbd(vm_ref, "rootfs", userdevice=0) @@ -715,7 +715,7 @@ class XenAPIVMTestCase(test.TestCase): vm = xenapi_fake.get_record('VM', vm_ref) rescue_name = "%s-rescue" % vm["name_label"] - rescue_ref = vm_utils.VMHelper.lookup(session, rescue_name) + rescue_ref = vm_utils.lookup(session, rescue_name) rescue_vm = xenapi_fake.get_record('VM', rescue_ref) vdi_uuids = [] @@ -1035,7 +1035,7 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): self.fake_instance.architecture = 'x86-64' def assert_disk_type(self, image_meta, expected_disk_type): - actual = vm_utils.VMHelper.determine_disk_image_type(image_meta) + actual = vm_utils.determine_disk_image_type(image_meta) self.assertEqual(expected_disk_type, actual) def test_machine(self): @@ -1154,14 +1154,11 @@ class XenAPIAutoDiskConfigTestCase(test.TestCase): self.context = context.RequestContext(self.user_id, self.project_id) - @classmethod - def fake_create_vbd(cls, session, vm_ref, vdi_ref, userdevice, + def fake_create_vbd(session, vm_ref, vdi_ref, userdevice, vbd_type='disk', read_only=False, bootable=True): pass - self.stubs.Set(vm_utils.VMHelper, - "create_vbd", - fake_create_vbd) + self.stubs.Set(vm_utils, 'create_vbd', fake_create_vbd) def assertIsPartitionCalled(self, called): marker = {"partition_called": False} @@ -1251,14 +1248,11 @@ class XenAPIGenerateLocal(test.TestCase): self.context = context.RequestContext(self.user_id, self.project_id) - @classmethod - def fake_create_vbd(cls, session, vm_ref, vdi_ref, userdevice, + def fake_create_vbd(session, vm_ref, vdi_ref, userdevice, vbd_type='disk', read_only=False, bootable=True): pass - self.stubs.Set(vm_utils.VMHelper, - "create_vbd", - fake_create_vbd) + self.stubs.Set(vm_utils, 'create_vbd', fake_create_vbd) def assertCalled(self, instance): ctx = context.RequestContext(self.user_id, self.project_id) @@ -1281,11 +1275,9 @@ class XenAPIGenerateLocal(test.TestCase): instance = db.instance_update(self.context, instance['id'], {'instance_type_id': 5}) - @classmethod - def fake_generate_swap(cls, *args, **kwargs): + def fake_generate_swap(*args, **kwargs): self.called = True - self.stubs.Set(vm_utils.VMHelper, 'generate_swap', - fake_generate_swap) + self.stubs.Set(vm_utils, 'generate_swap', fake_generate_swap) self.assertCalled(instance) @@ -1295,11 +1287,9 @@ class XenAPIGenerateLocal(test.TestCase): instance = db.instance_update(self.context, instance['id'], {'instance_type_id': 4}) - @classmethod - def fake_generate_ephemeral(cls, *args): + def fake_generate_ephemeral(*args): self.called = True - self.stubs.Set(vm_utils.VMHelper, 'generate_ephemeral', - fake_generate_ephemeral) + self.stubs.Set(vm_utils, 'generate_ephemeral', fake_generate_ephemeral) self.assertCalled(instance) @@ -1307,7 +1297,7 @@ class XenAPIGenerateLocal(test.TestCase): class XenAPIBWUsageTestCase(test.TestCase): def setUp(self): super(XenAPIBWUsageTestCase, self).setUp() - self.stubs.Set(vm_utils.VMHelper, "compile_metrics", + self.stubs.Set(vm_utils, 'compile_metrics', XenAPIBWUsageTestCase._fake_compile_metrics) self.flags(target_host='127.0.0.1', xenapi_connection_url='test_url', @@ -1651,7 +1641,7 @@ class XenAPISRSelectionTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') self.assertRaises(exception.StorageRepositoryNotFound, - vm_utils.VMHelper.safe_find_sr, session) + vm_utils.safe_find_sr, session) def test_safe_find_sr_local_storage(self): """Ensure the default local-storage is found.""" @@ -1666,7 +1656,7 @@ class XenAPISRSelectionTestCase(test.TestCase): 'Local storage', 'i18n-key': 'local-storage'}, host_ref=host_ref) - expected = vm_utils.VMHelper.safe_find_sr(session) + expected = vm_utils.safe_find_sr(session) self.assertEqual(local_sr, expected) def test_safe_find_sr_by_other_criteria(self): @@ -1679,7 +1669,7 @@ class XenAPISRSelectionTestCase(test.TestCase): type='lvm', other_config={'my_fake_sr': 'true'}, host_ref=host_ref) - expected = vm_utils.VMHelper.safe_find_sr(session) + expected = vm_utils.safe_find_sr(session) self.assertEqual(local_sr, expected) def test_safe_find_sr_default(self): @@ -1688,7 +1678,7 @@ class XenAPISRSelectionTestCase(test.TestCase): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') pool_ref = xenapi_fake.create_pool('') - expected = vm_utils.VMHelper.safe_find_sr(session) + expected = vm_utils.safe_find_sr(session) self.assertEqual(session.call_xenapi('pool.get_default_SR', pool_ref), expected) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index e36fd40e9..0a87110e6 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -37,13 +37,12 @@ def stubout_firewall_driver(stubs, conn): def stubout_instance_snapshot(stubs): - @classmethod - def fake_fetch_image(cls, context, session, instance, image, type): + def fake_fetch_image(context, session, instance, image, type): return {'root': dict(uuid=_make_fake_vdi(), file=None), 'kernel': dict(uuid=_make_fake_vdi(), file=None), 'ramdisk': dict(uuid=_make_fake_vdi(), file=None)} - stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image) + stubs.Set(vm_utils, 'fetch_image', fake_fetch_image) def fake_wait_for_vhd_coalesce(*args): #TODO(sirp): Should we actually fake out the data here @@ -97,20 +96,18 @@ def stubout_is_vdi_pv(stubs): def stubout_determine_is_pv_objectstore(stubs): """Assumes VMs stu have PV kernels""" - @classmethod - def f(cls, *args): + def f(*args): return False - stubs.Set(vm_utils.VMHelper, '_determine_is_pv_objectstore', f) + stubs.Set(vm_utils, '_determine_is_pv_objectstore', f) def stubout_is_snapshot(stubs): """ Always returns true xenapi fake driver does not create vmrefs for snapshots """ - @classmethod - def f(cls, *args): + def f(*args): return True - stubs.Set(vm_utils.VMHelper, 'is_snapshot', f) + stubs.Set(vm_utils, 'is_snapshot', f) def stubout_lookup_image(stubs): @@ -123,8 +120,7 @@ def stubout_lookup_image(stubs): def stubout_fetch_image_glance_disk(stubs, raise_failure=False): """Simulates a failure in fetch image_glance_disk.""" - @classmethod - def _fake_fetch_image_glance_disk(cls, context, session, instance, image, + def _fake_fetch_image_glance_disk(context, session, instance, image, image_type): if raise_failure: raise fake.Failure("Test Exception raised by " @@ -139,18 +135,17 @@ def stubout_fetch_image_glance_disk(stubs, raise_failure=False): vdi_type = vm_utils.ImageType.to_string(image_type) return {vdi_type: dict(uuid=None, file=filename)} - stubs.Set(vm_utils.VMHelper, '_fetch_image_glance_disk', + stubs.Set(vm_utils, '_fetch_image_glance_disk', _fake_fetch_image_glance_disk) def stubout_create_vm(stubs): """Simulates a failure in create_vm.""" - @classmethod - def f(cls, *args): + def f(*args): raise fake.Failure("Test Exception raised by " + "fake create_vm") - stubs.Set(vm_utils.VMHelper, 'create_vm', f) + stubs.Set(vm_utils, 'create_vm', f) def _make_fake_vdi(): @@ -286,8 +281,7 @@ def stub_out_vm_methods(stubs): def fake_release_bootlock(self, vm): pass - @classmethod - def fake_generate_ephemeral(cls, *args): + def fake_generate_ephemeral(*args): pass def fake_wait_for_device(dev): @@ -295,7 +289,7 @@ def stub_out_vm_methods(stubs): stubs.Set(vmops.VMOps, "_acquire_bootlock", fake_acquire_bootlock) stubs.Set(vmops.VMOps, "_release_bootlock", fake_release_bootlock) - stubs.Set(vm_utils.VMHelper, 'generate_ephemeral', fake_generate_ephemeral) + stubs.Set(vm_utils, 'generate_ephemeral', fake_generate_ephemeral) stubs.Set(vm_utils, '_wait_for_device', fake_wait_for_device) @@ -344,32 +338,28 @@ def stub_out_migration_methods(stubs): vdi_rec['other_config']['nova_disk_type'] = 'root' return {'uuid': vdi_rec['uuid'], 'ref': vdi_ref} - @classmethod - def fake_get_vdi(cls, session, vm_ref): + def fake_get_vdi(session, vm_ref): vdi_ref = fake.create_vdi('derp', 'herp') vdi_rec = session.call_xenapi("VDI.get_record", vdi_ref) return vdi_ref, {'uuid': vdi_rec['uuid'], } - @classmethod - def fake_sr(cls, session, *args): + def fake_sr(session, *args): pass - @classmethod - def fake_get_sr_path(cls, *args): + def fake_get_sr_path(*args): return "fake" def fake_destroy(*args, **kwargs): pass - @classmethod - def fake_generate_ephemeral(cls, *args): + def fake_generate_ephemeral(*args): pass stubs.Set(vmops.VMOps, '_destroy', fake_destroy) stubs.Set(vmops.VMOps, '_move_disks', fake_move_disks) - stubs.Set(vm_utils.VMHelper, 'scan_default_sr', fake_sr) - stubs.Set(vm_utils.VMHelper, 'scan_sr', fake_sr) + stubs.Set(vm_utils, 'scan_default_sr', fake_sr) + stubs.Set(vm_utils, 'scan_sr', fake_sr) stubs.Set(vmops.VMOps, '_create_snapshot', fake_create_snapshot) - stubs.Set(vm_utils.VMHelper, 'get_vdi_for_vm_safely', fake_get_vdi) - stubs.Set(vm_utils.VMHelper, 'get_sr_path', fake_get_sr_path) - stubs.Set(vm_utils.VMHelper, 'generate_ephemeral', fake_generate_ephemeral) + stubs.Set(vm_utils, 'get_vdi_for_vm_safely', fake_get_vdi) + stubs.Set(vm_utils, 'get_sr_path', fake_get_sr_path) + stubs.Set(vm_utils, 'generate_ephemeral', fake_generate_ephemeral) diff --git a/nova/virt/xenapi/host.py b/nova/virt/xenapi/host.py index a6fb4a92e..044173a03 100644 --- a/nova/virt/xenapi/host.py +++ b/nova/virt/xenapi/host.py @@ -57,7 +57,7 @@ class Host(object): if host_ref != self._session.get_xenapi_host()] migrations_counter = vm_counter = 0 ctxt = context.get_admin_context() - for vm_ref, vm_rec in vm_utils.VMHelper.list_vms(self._session): + for vm_ref, vm_rec in vm_utils.list_vms(self._session): for host_ref in host_list: try: # Ensure only guest instances are migrated @@ -144,7 +144,7 @@ class HostState(object): if data: try: # Get the SR usage - sr_ref = vm_utils.VMHelper.safe_find_sr(self._session) + sr_ref = vm_utils.safe_find_sr(self._session) except exception.NotFound as e: # No SR configured LOG.error(_("Unable to get SR for this host: %s") % e) diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index 05bbc33c1..c2885cc4d 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -46,7 +46,7 @@ class XenAPIBridgeDriver(XenVIFDriver): def plug(self, instance, vif, vm_ref=None, device=None): if not vm_ref: - vm_ref = vm_utils.VMHelper.lookup(self._session, instance.name) + vm_ref = vm_utils.lookup(self._session, instance.name) if not device: device = 0 @@ -136,7 +136,7 @@ class XenAPIOpenVswitchDriver(XenVIFDriver): def plug(self, instance, vif, vm_ref=None, device=None): if not vm_ref: - vm_ref = vm_utils.VMHelper.lookup(self._session, instance.name) + vm_ref = vm_utils.lookup(self._session, instance.name) if not device: device = 0 diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 221735142..570fccb1c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -136,1082 +136,1058 @@ class ImageType: return dict(zip(ImageType._strs, ImageType._ids)).get(image_type_str) -class VMHelper(xenapi.HelperBase): - """ - The class that wraps the helper methods together. - """ +def create_vm(session, instance, kernel, ramdisk, use_pv_kernel=False): + """Create a VM record. Returns new VM reference. + the use_pv_kernel flag indicates whether the guest is HVM or PV - @classmethod - def create_vm(cls, session, instance, kernel, ramdisk, - use_pv_kernel=False): - """Create a VM record. Returns new VM reference. - the use_pv_kernel flag indicates whether the guest is HVM or PV - - There are 3 scenarios: - - 1. Using paravirtualization, kernel passed in - - 2. Using paravirtualization, kernel within the image - - 3. Using hardware virtualization - """ - inst_type_id = instance.instance_type_id - instance_type = instance_types.get_instance_type(inst_type_id) - mem = str(long(instance_type['memory_mb']) * 1024 * 1024) - vcpus = str(instance_type['vcpus']) - - rec = { - 'actions_after_crash': 'destroy', - 'actions_after_reboot': 'restart', - 'actions_after_shutdown': 'destroy', - 'affinity': '', - 'blocked_operations': {}, - 'ha_always_run': False, - 'ha_restart_priority': '', - 'HVM_boot_params': {}, - 'HVM_boot_policy': '', - 'is_a_template': False, - 'memory_dynamic_min': mem, - 'memory_dynamic_max': mem, - 'memory_static_min': '0', - 'memory_static_max': mem, - 'memory_target': mem, - 'name_description': '', - 'name_label': instance.name, - 'other_config': {'allowvssprovider': str(False), - 'nova_uuid': str(instance.uuid), }, - 'PCI_bus': '', - 'platform': {'acpi': 'true', 'apic': 'true', 'pae': 'true', - 'viridian': 'true', 'timeoffset': '0'}, - 'PV_args': '', - 'PV_bootloader': '', - 'PV_bootloader_args': '', - 'PV_kernel': '', - 'PV_legacy_args': '', - 'PV_ramdisk': '', - 'recommendations': '', - 'tags': [], - 'user_version': '0', - 'VCPUs_at_startup': vcpus, - 'VCPUs_max': vcpus, - 'VCPUs_params': {}, - 'xenstore_data': {}} - - # Complete VM configuration record according to the image type - # non-raw/raw with PV kernel/raw in HVM mode - if use_pv_kernel: - rec['platform']['nx'] = 'false' - if instance.kernel_id: - # 1. Kernel explicitly passed in, use that - rec['PV_args'] = 'root=/dev/xvda1' - rec['PV_kernel'] = kernel - rec['PV_ramdisk'] = ramdisk - else: - # 2. Use kernel within the image - rec['PV_bootloader'] = 'pygrub' - else: - # 3. Using hardware virtualization - rec['platform']['nx'] = 'true' - rec['HVM_boot_params'] = {'order': 'dc'} - rec['HVM_boot_policy'] = 'BIOS order' - - vm_ref = session.call_xenapi('VM.create', rec) - LOG.debug(_('Created VM'), instance=instance) - return vm_ref + There are 3 scenarios: - @classmethod - def ensure_free_mem(cls, session, instance): - inst_type_id = instance.instance_type_id - instance_type = instance_types.get_instance_type(inst_type_id) - mem = long(instance_type['memory_mb']) * 1024 * 1024 - #get free memory from host - host = session.get_xenapi_host() - host_free_mem = long(session.call_xenapi("host.compute_free_memory", - host)) - return host_free_mem >= mem + 1. Using paravirtualization, kernel passed in - @classmethod - def find_vbd_by_number(cls, session, vm_ref, number): - """Get the VBD reference from the device number""" - vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) - if vbd_refs: - for vbd_ref in vbd_refs: - try: - vbd_rec = session.call_xenapi("VBD.get_record", vbd_ref) - if vbd_rec['userdevice'] == str(number): - return vbd_ref - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise volume_utils.StorageError( - _('VBD not found in instance %s') % vm_ref) + 2. Using paravirtualization, kernel within the image - @classmethod - def unplug_vbd(cls, session, vbd_ref): - """Unplug VBD from VM""" - # Call VBD.unplug on the given VBD, with a retry if we get - # DEVICE_DETACH_REJECTED. For reasons which we don't understand, - # we're seeing the device still in use, even when all processes - # using the device should be dead. - max_attempts = FLAGS.xenapi_num_vbd_unplug_retries + 1 - for num_attempt in xrange(1, max_attempts + 1): + 3. Using hardware virtualization + """ + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) + mem = str(long(instance_type['memory_mb']) * 1024 * 1024) + vcpus = str(instance_type['vcpus']) + + rec = { + 'actions_after_crash': 'destroy', + 'actions_after_reboot': 'restart', + 'actions_after_shutdown': 'destroy', + 'affinity': '', + 'blocked_operations': {}, + 'ha_always_run': False, + 'ha_restart_priority': '', + 'HVM_boot_params': {}, + 'HVM_boot_policy': '', + 'is_a_template': False, + 'memory_dynamic_min': mem, + 'memory_dynamic_max': mem, + 'memory_static_min': '0', + 'memory_static_max': mem, + 'memory_target': mem, + 'name_description': '', + 'name_label': instance.name, + 'other_config': {'allowvssprovider': str(False), + 'nova_uuid': str(instance.uuid)}, + 'PCI_bus': '', + 'platform': {'acpi': 'true', 'apic': 'true', 'pae': 'true', + 'viridian': 'true', 'timeoffset': '0'}, + 'PV_args': '', + 'PV_bootloader': '', + 'PV_bootloader_args': '', + 'PV_kernel': '', + 'PV_legacy_args': '', + 'PV_ramdisk': '', + 'recommendations': '', + 'tags': [], + 'user_version': '0', + 'VCPUs_at_startup': vcpus, + 'VCPUs_max': vcpus, + 'VCPUs_params': {}, + 'xenstore_data': {}} + + # Complete VM configuration record according to the image type + # non-raw/raw with PV kernel/raw in HVM mode + if use_pv_kernel: + rec['platform']['nx'] = 'false' + if instance.kernel_id: + # 1. Kernel explicitly passed in, use that + rec['PV_args'] = 'root=/dev/xvda1' + rec['PV_kernel'] = kernel + rec['PV_ramdisk'] = ramdisk + else: + # 2. Use kernel within the image + rec['PV_bootloader'] = 'pygrub' + else: + # 3. Using hardware virtualization + rec['platform']['nx'] = 'true' + rec['HVM_boot_params'] = {'order': 'dc'} + rec['HVM_boot_policy'] = 'BIOS order' + + vm_ref = session.call_xenapi('VM.create', rec) + LOG.debug(_('Created VM'), instance=instance) + return vm_ref + + +def ensure_free_mem(session, instance): + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) + mem = long(instance_type['memory_mb']) * 1024 * 1024 + #get free memory from host + host = session.get_xenapi_host() + host_free_mem = long(session.call_xenapi("host.compute_free_memory", + host)) + return host_free_mem >= mem + + +def find_vbd_by_number(session, vm_ref, number): + """Get the VBD reference from the device number""" + vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) + if vbd_refs: + for vbd_ref in vbd_refs: try: - session.call_xenapi('VBD.unplug', vbd_ref) - return + vbd_rec = session.call_xenapi("VBD.get_record", vbd_ref) + if vbd_rec['userdevice'] == str(number): + return vbd_ref except session.XenAPI.Failure, exc: - err = len(exc.details) > 0 and exc.details[0] - if err == 'DEVICE_ALREADY_DETACHED': - LOG.info(_('VBD %s already detached'), vbd_ref) - return - elif err == 'DEVICE_DETACH_REJECTED': - LOG.info(_('VBD %(vbd_ref)s detach rejected, attempt' - ' %(num_attempt)d/%(max_attempts)d'), locals()) - else: - LOG.exception(exc) - raise volume_utils.StorageError( - _('Unable to unplug VBD %s') % vbd_ref) - - greenthread.sleep(1) + LOG.exception(exc) + raise volume_utils.StorageError( + _('VBD not found in instance %s') % vm_ref) - raise volume_utils.StorageError( - _('Reached maximum number of retries trying to unplug VBD %s') - % vbd_ref) - @classmethod - def destroy_vbd(cls, session, vbd_ref): - """Destroy VBD from host database""" +def unplug_vbd(session, vbd_ref): + """Unplug VBD from VM""" + # Call VBD.unplug on the given VBD, with a retry if we get + # DEVICE_DETACH_REJECTED. For reasons which we don't understand, + # we're seeing the device still in use, even when all processes + # using the device should be dead. + max_attempts = FLAGS.xenapi_num_vbd_unplug_retries + 1 + for num_attempt in xrange(1, max_attempts + 1): try: - session.call_xenapi('VBD.destroy', vbd_ref) + session.call_xenapi('VBD.unplug', vbd_ref) + return except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise volume_utils.StorageError( - _('Unable to destroy VBD %s') % vbd_ref) + err = len(exc.details) > 0 and exc.details[0] + if err == 'DEVICE_ALREADY_DETACHED': + LOG.info(_('VBD %s already detached'), vbd_ref) + return + elif err == 'DEVICE_DETACH_REJECTED': + LOG.info(_('VBD %(vbd_ref)s detach rejected, attempt' + ' %(num_attempt)d/%(max_attempts)d'), locals()) + else: + LOG.exception(exc) + raise volume_utils.StorageError( + _('Unable to unplug VBD %s') % vbd_ref) - @classmethod - def create_vbd(cls, session, vm_ref, vdi_ref, userdevice, - vbd_type='disk', read_only=False, bootable=False): - """Create a VBD record and returns its reference.""" - vbd_rec = {} - vbd_rec['VM'] = vm_ref - vbd_rec['VDI'] = vdi_ref - vbd_rec['userdevice'] = str(userdevice) - vbd_rec['bootable'] = bootable - vbd_rec['mode'] = read_only and 'RO' or 'RW' - vbd_rec['type'] = vbd_type - vbd_rec['unpluggable'] = True - vbd_rec['empty'] = False - vbd_rec['other_config'] = {} - vbd_rec['qos_algorithm_type'] = '' - vbd_rec['qos_algorithm_params'] = {} - vbd_rec['qos_supported_algorithms'] = [] - LOG.debug(_('Creating %(vbd_type)s-type VBD for VM %(vm_ref)s,' - ' VDI %(vdi_ref)s ... '), locals()) - vbd_ref = session.call_xenapi('VBD.create', vbd_rec) - LOG.debug(_('Created VBD %(vbd_ref)s for VM %(vm_ref)s,' - ' VDI %(vdi_ref)s.'), locals()) - return vbd_ref + greenthread.sleep(1) - @classmethod - def destroy_vdi(cls, session, vdi_ref): - try: - session.call_xenapi('VDI.destroy', vdi_ref) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise volume_utils.StorageError( - _('Unable to destroy VDI %s') % vdi_ref) + raise volume_utils.StorageError( + _('Reached maximum number of retries trying to unplug VBD %s') + % vbd_ref) - @classmethod - def create_vdi(cls, session, sr_ref, instance, disk_type, - virtual_size, read_only=False): - """Create a VDI record and returns its reference.""" - name_label = instance['name'] - vdi_ref = session.call_xenapi("VDI.create", - {'name_label': name_label, - 'name_description': disk_type, - 'SR': sr_ref, - 'virtual_size': str(virtual_size), - 'type': 'User', - 'sharable': False, - 'read_only': read_only, - 'xenstore_data': {}, - 'other_config': {'nova_instance_uuid': instance['uuid'], - 'nova_disk_type': disk_type}, - 'sm_config': {}, - 'tags': []}) - LOG.debug(_('Created VDI %(vdi_ref)s (%(name_label)s,' - ' %(virtual_size)s, %(read_only)s) on %(sr_ref)s.'), - locals()) - return vdi_ref - @classmethod - def copy_vdi(cls, session, sr_ref, vdi_to_copy_ref): - """Copy a VDI and return the new VDIs reference.""" - vdi_ref = session.call_xenapi('VDI.copy', vdi_to_copy_ref, sr_ref) - LOG.debug(_('Copied VDI %(vdi_ref)s from VDI ' - '%(vdi_to_copy_ref)s on %(sr_ref)s.') % locals()) - return vdi_ref +def destroy_vbd(session, vbd_ref): + """Destroy VBD from host database""" + try: + session.call_xenapi('VBD.destroy', vbd_ref) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise volume_utils.StorageError( + _('Unable to destroy VBD %s') % vbd_ref) + + +def create_vbd(session, vm_ref, vdi_ref, userdevice, vbd_type='disk', + read_only=False, bootable=False): + """Create a VBD record and returns its reference.""" + vbd_rec = {} + vbd_rec['VM'] = vm_ref + vbd_rec['VDI'] = vdi_ref + vbd_rec['userdevice'] = str(userdevice) + vbd_rec['bootable'] = bootable + vbd_rec['mode'] = read_only and 'RO' or 'RW' + vbd_rec['type'] = vbd_type + vbd_rec['unpluggable'] = True + vbd_rec['empty'] = False + vbd_rec['other_config'] = {} + vbd_rec['qos_algorithm_type'] = '' + vbd_rec['qos_algorithm_params'] = {} + vbd_rec['qos_supported_algorithms'] = [] + LOG.debug(_('Creating %(vbd_type)s-type VBD for VM %(vm_ref)s,' + ' VDI %(vdi_ref)s ... '), locals()) + vbd_ref = session.call_xenapi('VBD.create', vbd_rec) + LOG.debug(_('Created VBD %(vbd_ref)s for VM %(vm_ref)s,' + ' VDI %(vdi_ref)s.'), locals()) + return vbd_ref + + +def destroy_vdi(session, vdi_ref): + try: + session.call_xenapi('VDI.destroy', vdi_ref) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise volume_utils.StorageError( + _('Unable to destroy VDI %s') % vdi_ref) + + +def create_vdi(session, sr_ref, instance, disk_type, virtual_size, + read_only=False): + """Create a VDI record and returns its reference.""" + name_label = instance['name'] + vdi_ref = session.call_xenapi("VDI.create", + {'name_label': name_label, + 'name_description': disk_type, + 'SR': sr_ref, + 'virtual_size': str(virtual_size), + 'type': 'User', + 'sharable': False, + 'read_only': read_only, + 'xenstore_data': {}, + 'other_config': {'nova_instance_uuid': instance['uuid'], + 'nova_disk_type': disk_type}, + 'sm_config': {}, + 'tags': []}) + LOG.debug(_('Created VDI %(vdi_ref)s (%(name_label)s,' + ' %(virtual_size)s, %(read_only)s) on %(sr_ref)s.'), + locals()) + return vdi_ref - @classmethod - def clone_vdi(cls, session, vdi_to_clone_ref): - """Clones a VDI and return the new VDIs reference.""" - vdi_ref = session.call_xenapi('VDI.clone', vdi_to_clone_ref) - LOG.debug(_('Cloned VDI %(vdi_ref)s from VDI ' - '%(vdi_to_clone_ref)s') % locals()) - return vdi_ref - @classmethod - def set_vdi_name(cls, session, vdi_uuid, label, description, - vdi_ref=None): - vdi_ref = vdi_ref or session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - session.call_xenapi('VDI.set_name_label', vdi_ref, label) - session.call_xenapi('VDI.set_name_description', vdi_ref, description) +def copy_vdi(session, sr_ref, vdi_to_copy_ref): + """Copy a VDI and return the new VDIs reference.""" + vdi_ref = session.call_xenapi('VDI.copy', vdi_to_copy_ref, sr_ref) + LOG.debug(_('Copied VDI %(vdi_ref)s from VDI ' + '%(vdi_to_copy_ref)s on %(sr_ref)s.') % locals()) + return vdi_ref - @classmethod - def get_vdi_for_vm_safely(cls, session, vm_ref): - """Retrieves the primary VDI for a VM""" - vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) - for vbd in vbd_refs: - vbd_rec = session.call_xenapi("VBD.get_record", vbd) - # Convention dictates the primary VDI will be userdevice 0 - if vbd_rec['userdevice'] == '0': - vdi_rec = session.call_xenapi("VDI.get_record", vbd_rec['VDI']) - return vbd_rec['VDI'], vdi_rec - raise exception.NovaException(_("No primary VDI found for %(vm_ref)s") - % locals()) - @classmethod - def create_snapshot(cls, session, instance, vm_ref, label): - """Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, - Snapshot VHD""" - LOG.debug(_("Snapshotting with label '%(label)s'"), locals(), - instance=instance) +def clone_vdi(session, vdi_to_clone_ref): + """Clones a VDI and return the new VDIs reference.""" + vdi_ref = session.call_xenapi('VDI.clone', vdi_to_clone_ref) + LOG.debug(_('Cloned VDI %(vdi_ref)s from VDI ' + '%(vdi_to_clone_ref)s') % locals()) + return vdi_ref + + +def set_vdi_name(session, vdi_uuid, label, description, vdi_ref=None): + vdi_ref = vdi_ref or session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + session.call_xenapi('VDI.set_name_label', vdi_ref, label) + session.call_xenapi('VDI.set_name_description', vdi_ref, description) + + +def get_vdi_for_vm_safely(session, vm_ref): + """Retrieves the primary VDI for a VM""" + vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) + for vbd in vbd_refs: + vbd_rec = session.call_xenapi("VBD.get_record", vbd) + # Convention dictates the primary VDI will be userdevice 0 + if vbd_rec['userdevice'] == '0': + vdi_rec = session.call_xenapi("VDI.get_record", vbd_rec['VDI']) + return vbd_rec['VDI'], vdi_rec + raise exception.NovaException(_("No primary VDI found for %(vm_ref)s") + % locals()) + - vm_vdi_ref, vm_vdi_rec = cls.get_vdi_for_vm_safely(session, vm_ref) - sr_ref = vm_vdi_rec["SR"] +def create_snapshot(session, instance, vm_ref, label): + """Creates Snapshot (Template) VM, Snapshot VBD, Snapshot VDI, + Snapshot VHD""" + LOG.debug(_("Snapshotting with label '%(label)s'"), locals(), + instance=instance) - original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) + vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref) + sr_ref = vm_vdi_rec["SR"] - template_vm_ref = session.call_xenapi('VM.snapshot', vm_ref, label) - template_vdi_rec = cls.get_vdi_for_vm_safely(session, - template_vm_ref)[1] - template_vdi_uuid = template_vdi_rec["uuid"] + original_parent_uuid = get_vhd_parent_uuid(session, vm_vdi_ref) - LOG.debug(_('Created snapshot %(template_vm_ref)s'), locals(), - instance=instance) + template_vm_ref = session.call_xenapi('VM.snapshot', vm_ref, label) + template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1] + template_vdi_uuid = template_vdi_rec["uuid"] - parent_uuid, base_uuid = _wait_for_vhd_coalesce( + LOG.debug(_('Created snapshot %(template_vm_ref)s'), locals(), + instance=instance) + + parent_uuid, base_uuid = _wait_for_vhd_coalesce( session, instance, sr_ref, vm_vdi_ref, original_parent_uuid) - template_vdi_uuids = {'base': base_uuid, - 'image': parent_uuid, - 'snap': template_vdi_uuid} - return template_vm_ref, template_vdi_uuids + template_vdi_uuids = {'base': base_uuid, + 'image': parent_uuid, + 'snap': template_vdi_uuid} + return template_vm_ref, template_vdi_uuids - @classmethod - def get_sr_path(cls, session): - """Return the path to our storage repository - This is used when we're dealing with VHDs directly, either by taking - snapshots or by restoring an image in the DISK_VHD format. - """ - sr_ref = cls.safe_find_sr(session) - sr_rec = session.call_xenapi("SR.get_record", sr_ref) - sr_uuid = sr_rec["uuid"] - return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) +def get_sr_path(session): + """Return the path to our storage repository - @classmethod - def find_cached_image(cls, session, image_id, sr_ref): - """Returns the vdi-ref of the cached image.""" - for vdi_ref, vdi_rec in _get_all_vdis_in_sr(session, sr_ref): - if ('image-id' in vdi_rec['other_config'] and - vdi_rec['other_config']['image-id'] == image_id): - return vdi_ref - return None + This is used when we're dealing with VHDs directly, either by taking + snapshots or by restoring an image in the DISK_VHD format. + """ + sr_ref = safe_find_sr(session) + sr_rec = session.call_xenapi("SR.get_record", sr_ref) + sr_uuid = sr_rec["uuid"] + return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) - @classmethod - def upload_image(cls, context, session, instance, vdi_uuids, image_id): - """ Requests that the Glance plugin bundle the specified VDIs and - push them into Glance using the specified human-friendly name. - """ - # NOTE(sirp): Currently we only support uploading images as VHD, there - # is no RAW equivalent (yet) - LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as" - " ID %(image_id)s"), locals(), instance=instance) - glance_host, glance_port = glance.pick_glance_api_server() +def find_cached_image(session, image_id, sr_ref): + """Returns the vdi-ref of the cached image.""" + for vdi_ref, vdi_rec in _get_all_vdis_in_sr(session, sr_ref): + if ('image-id' in vdi_rec['other_config'] and + vdi_rec['other_config']['image-id'] == image_id): + return vdi_ref + return None - properties = {} - properties['auto_disk_config'] = instance.auto_disk_config - properties['os_type'] = instance.os_type or FLAGS.default_os_type - params = {'vdi_uuids': vdi_uuids, - 'image_id': image_id, - 'glance_host': glance_host, - 'glance_port': glance_port, - 'sr_path': cls.get_sr_path(session), - 'auth_token': getattr(context, 'auth_token', None), - 'properties': properties} +def upload_image(context, session, instance, vdi_uuids, image_id): + """Requests that the Glance plugin bundle the specified VDIs and + push them into Glance using the specified human-friendly name. + """ + # NOTE(sirp): Currently we only support uploading images as VHD, there + # is no RAW equivalent (yet) + LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as" + " ID %(image_id)s"), locals(), instance=instance) - kwargs = {'params': pickle.dumps(params)} - session.call_plugin('glance', 'upload_vhd', kwargs) + glance_host, glance_port = glance.pick_glance_api_server() - @classmethod - def resize_disk(cls, session, instance, vdi_ref, instance_type): - # Copy VDI over to something we can resize - # NOTE(jerdfelt): Would be nice to just set vdi_ref to read/write - sr_ref = cls.safe_find_sr(session) - copy_ref = session.call_xenapi('VDI.copy', vdi_ref, sr_ref) + properties = {} + properties['auto_disk_config'] = instance.auto_disk_config + properties['os_type'] = instance.os_type or FLAGS.default_os_type - try: - # Resize partition and filesystem down - cls.auto_configure_disk(session=session, - vdi_ref=copy_ref, - new_gb=instance_type['root_gb']) + params = {'vdi_uuids': vdi_uuids, + 'image_id': image_id, + 'glance_host': glance_host, + 'glance_port': glance_port, + 'sr_path': get_sr_path(session), + 'auth_token': getattr(context, 'auth_token', None), + 'properties': properties} - # Create new VDI - vdi_size = instance_type['root_gb'] * 1024 * 1024 * 1024 - new_ref = cls.create_vdi(session, sr_ref, instance, 'root', - vdi_size) + kwargs = {'params': pickle.dumps(params)} + session.call_plugin('glance', 'upload_vhd', kwargs) - new_uuid = session.call_xenapi('VDI.get_uuid', new_ref) - # Manually copy contents over - virtual_size = instance_type['root_gb'] * 1024 * 1024 * 1024 - _copy_partition(session, copy_ref, new_ref, 1, virtual_size) +def resize_disk(session, instance, vdi_ref, instance_type): + # Copy VDI over to something we can resize + # NOTE(jerdfelt): Would be nice to just set vdi_ref to read/write + sr_ref = safe_find_sr(session) + copy_ref = session.call_xenapi('VDI.copy', vdi_ref, sr_ref) - return new_ref, new_uuid - finally: - cls.destroy_vdi(session, copy_ref) + try: + # Resize partition and filesystem down + auto_configure_disk(session, copy_ref, instance_type['root_gb']) - @classmethod - def auto_configure_disk(cls, session, vdi_ref, new_gb): - """Partition and resize FS to match the size specified by - instance_types.root_gb. + # Create new VDI + vdi_size = instance_type['root_gb'] * 1024 * 1024 * 1024 + new_ref = create_vdi(session, sr_ref, instance, 'root', vdi_size) - This is a fail-safe to prevent accidentally destroying data on a disk - erroneously marked as auto_disk_config=True. + new_uuid = session.call_xenapi('VDI.get_uuid', new_ref) - The criteria for allowing resize are: + # Manually copy contents over + virtual_size = instance_type['root_gb'] * 1024 * 1024 * 1024 + _copy_partition(session, copy_ref, new_ref, 1, virtual_size) - 1. 'auto_disk_config' must be true for the instance (and image). - (If we've made it here, then auto_disk_config=True.) + return new_ref, new_uuid + finally: + destroy_vdi(session, copy_ref) - 2. The disk must have only one partition. - 3. The file-system on the one partition must be ext3 or ext4. - """ - with vdi_attached_here(session, vdi_ref, read_only=False) as dev: - partitions = _get_partitions(dev) +def auto_configure_disk(session, vdi_ref, new_gb): + """Partition and resize FS to match the size specified by + instance_types.root_gb. - if len(partitions) != 1: - return + This is a fail-safe to prevent accidentally destroying data on a disk + erroneously marked as auto_disk_config=True. - _num, start, old_sectors, ptype = partitions[0] - if ptype in ('ext3', 'ext4'): - new_sectors = new_gb * 1024 * 1024 * 1024 / SECTOR_SIZE - _resize_part_and_fs(dev, start, old_sectors, new_sectors) + The criteria for allowing resize are: - @classmethod - def _generate_disk(cls, session, instance, vm_ref, userdevice, name, - size_mb, fs_type): - """ - Steps to programmatically generate a disk: + 1. 'auto_disk_config' must be true for the instance (and image). + (If we've made it here, then auto_disk_config=True.) - 1. Create VDI of desired size + 2. The disk must have only one partition. - 2. Attach VDI to compute worker + 3. The file-system on the one partition must be ext3 or ext4. + """ + with vdi_attached_here(session, vdi_ref, read_only=False) as dev: + partitions = _get_partitions(dev) + + if len(partitions) != 1: + return - 3. Create partition + _num, start, old_sectors, ptype = partitions[0] + if ptype in ('ext3', 'ext4'): + new_sectors = new_gb * 1024 * 1024 * 1024 / SECTOR_SIZE + _resize_part_and_fs(dev, start, old_sectors, new_sectors) - 4. Create VBD between instance VM and VDI - """ - # 1. Create VDI - sr_ref = cls.safe_find_sr(session) - ONE_MEG = 1024 * 1024 - virtual_size = size_mb * ONE_MEG - vdi_ref = cls.create_vdi(session, sr_ref, instance, name, - virtual_size) - try: - # 2. Attach VDI to compute worker (VBD hotplug) - with vdi_attached_here(session, vdi_ref, read_only=False) as dev: - # 3. Create partition - dev_path = utils.make_dev_path(dev) - utils.execute('parted', '--script', dev_path, - 'mklabel', 'msdos', run_as_root=True) - - partition_start = 0 - partition_end = size_mb - utils.execute('parted', '--script', dev_path, - 'mkpart', 'primary', - str(partition_start), - str(partition_end), +def _generate_disk(session, instance, vm_ref, userdevice, name, size_mb, + fs_type): + """ + Steps to programmatically generate a disk: + + 1. Create VDI of desired size + + 2. Attach VDI to compute worker + + 3. Create partition + + 4. Create VBD between instance VM and VDI + """ + # 1. Create VDI + sr_ref = safe_find_sr(session) + ONE_MEG = 1024 * 1024 + virtual_size = size_mb * ONE_MEG + vdi_ref = create_vdi(session, sr_ref, instance, name, virtual_size) + + try: + # 2. Attach VDI to compute worker (VBD hotplug) + with vdi_attached_here(session, vdi_ref, read_only=False) as dev: + # 3. Create partition + dev_path = utils.make_dev_path(dev) + utils.execute('parted', '--script', dev_path, + 'mklabel', 'msdos', run_as_root=True) + + partition_start = 0 + partition_end = size_mb + utils.execute('parted', '--script', dev_path, + 'mkpart', 'primary', + str(partition_start), + str(partition_end), + run_as_root=True) + + partition_path = utils.make_dev_path(dev, partition=1) + + if fs_type == 'linux-swap': + utils.execute('mkswap', partition_path, run_as_root=True) + elif fs_type is not None: + utils.execute('mkfs', '-t', fs_type, partition_path, run_as_root=True) - partition_path = utils.make_dev_path(dev, partition=1) + # 4. Create VBD between instance VM and swap VDI + create_vbd(session, vm_ref, vdi_ref, userdevice, bootable=False) + except Exception: + with excutils.save_and_reraise_exception(): + destroy_vdi(session, vdi_ref) - if fs_type == 'linux-swap': - utils.execute('mkswap', partition_path, - run_as_root=True) - elif fs_type is not None: - utils.execute('mkfs', '-t', fs_type, partition_path, - run_as_root=True) - # 4. Create VBD between instance VM and swap VDI - cls.create_vbd(session, vm_ref, vdi_ref, userdevice, - bootable=False) - except Exception: - with excutils.save_and_reraise_exception(): - cls.destroy_vdi(session, vdi_ref) +def generate_swap(session, instance, vm_ref, userdevice, swap_mb): + # NOTE(jk0): We use a FAT32 filesystem for the Windows swap + # partition because that is what parted supports. + is_windows = instance.os_type == "windows" + fs_type = "vfat" if is_windows else "linux-swap" - @classmethod - def generate_swap(cls, session, instance, vm_ref, userdevice, swap_mb): - # NOTE(jk0): We use a FAT32 filesystem for the Windows swap - # partition because that is what parted supports. - is_windows = instance.os_type == "windows" - fs_type = "vfat" if is_windows else "linux-swap" + _generate_disk(session, instance, vm_ref, userdevice, 'swap', swap_mb, + fs_type) - cls._generate_disk(session, instance, vm_ref, userdevice, - 'swap', swap_mb, fs_type) - @classmethod - def generate_ephemeral(cls, session, instance, vm_ref, userdevice, - size_gb): - cls._generate_disk(session, instance, vm_ref, userdevice, - 'ephemeral', size_gb * 1024, - FLAGS.default_ephemeral_format) +def generate_ephemeral(session, instance, vm_ref, userdevice, size_gb): + _generate_disk(session, instance, vm_ref, userdevice, 'ephemeral', + size_gb * 1024, FLAGS.default_ephemeral_format) - @classmethod - def create_kernel_image(cls, context, session, instance, image, user_id, - project_id, image_type): - """Creates kernel/ramdisk file from the image stored in the cache. - If the image is not present in the cache, it streams it from glance. - - Returns: A list of dictionaries that describe VDIs - """ - filename = "" - if FLAGS.cache_images: - args = {} - args['cached-image'] = image - args['new-image-uuid'] = str(uuid.uuid4()) - filename = session.call_plugin('glance', 'create_kernel_ramdisk', - args) - if filename == "": - return cls.fetch_image(context, session, instance, image, - image_type) - else: - vdi_type = ImageType.to_string(image_type) - return {vdi_type: dict(uuid=None, file=filename)} +def create_kernel_image(context, session, instance, image, user_id, + project_id, image_type): + """Creates kernel/ramdisk file from the image stored in the cache. + If the image is not present in the cache, it streams it from glance. - @classmethod - def _create_cached_image(cls, context, session, instance, image, - image_type): - sr_ref = cls.safe_find_sr(session) - sr_type = session.call_xenapi('SR.get_record', sr_ref)["type"] - vdis = {} - - if FLAGS.use_cow_images and sr_type != "ext": - LOG.warning(_("Fast cloning is only supported on default local SR " - "of type ext. SR on this system was found to be of " - "type %(sr_type)s. Ignoring the cow flag.") - % locals()) - - root_vdi_ref = cls.find_cached_image(session, image, sr_ref) - if root_vdi_ref is None: - fetched_vdis = cls.fetch_image(context, session, instance, image, - image_type) - root_vdi = fetched_vdis['root'] - root_vdi_ref = session.call_xenapi('VDI.get_by_uuid', - root_vdi['uuid']) - cls.set_vdi_name(session, root_vdi['uuid'], - 'Glance Image %s' % image, 'root', - vdi_ref=root_vdi_ref) + Returns: A list of dictionaries that describe VDIs + """ + filename = "" + if FLAGS.cache_images: + args = {} + args['cached-image'] = image + args['new-image-uuid'] = str(uuid.uuid4()) + filename = session.call_plugin('glance', 'create_kernel_ramdisk', + args) + + if filename == "": + return fetch_image(context, session, instance, image, image_type) + else: + vdi_type = ImageType.to_string(image_type) + return {vdi_type: dict(uuid=None, file=filename)} + + +def _create_cached_image(context, session, instance, image, image_type): + sr_ref = safe_find_sr(session) + sr_type = session.call_xenapi('SR.get_record', sr_ref)["type"] + vdis = {} + + if FLAGS.use_cow_images and sr_type != "ext": + LOG.warning(_("Fast cloning is only supported on default local SR " + "of type ext. SR on this system was found to be of " + "type %(sr_type)s. Ignoring the cow flag.") + % locals()) + + root_vdi_ref = find_cached_image(session, image, sr_ref) + if root_vdi_ref is None: + fetched_vdis = fetch_image(context, session, instance, image, + image_type) + root_vdi = fetched_vdis['root'] + root_vdi_ref = session.call_xenapi('VDI.get_by_uuid', + root_vdi['uuid']) + set_vdi_name(session, root_vdi['uuid'], 'Glance Image %s' % image, + 'root', vdi_ref=root_vdi_ref) + session.call_xenapi('VDI.add_to_other_config', + root_vdi_ref, 'image-id', str(image)) + + for vdi_type, vdi in fetched_vdis.iteritems(): + vdi_ref = session.call_xenapi('VDI.get_by_uuid', + vdi['uuid']) session.call_xenapi('VDI.add_to_other_config', - root_vdi_ref, 'image-id', str(image)) + vdi_ref, 'nova_disk_type', + vdi_type) - for vdi_type, vdi in fetched_vdis.iteritems(): - vdi_ref = session.call_xenapi('VDI.get_by_uuid', - vdi['uuid']) + if vdi_type == 'swap': session.call_xenapi('VDI.add_to_other_config', - vdi_ref, 'nova_disk_type', - vdi_type) + root_vdi_ref, 'swap-disk', + str(vdi['uuid'])) - if vdi_type == 'swap': - session.call_xenapi('VDI.add_to_other_config', - root_vdi_ref, 'swap-disk', - str(vdi['uuid'])) + if FLAGS.use_cow_images and sr_type == 'ext': + new_vdi_ref = clone_vdi(session, root_vdi_ref) + else: + new_vdi_ref = copy_vdi(session, sr_ref, root_vdi_ref) - if FLAGS.use_cow_images and sr_type == 'ext': - new_vdi_ref = cls.clone_vdi(session, root_vdi_ref) - else: - new_vdi_ref = cls.copy_vdi(session, sr_ref, root_vdi_ref) - - # Set the name label for the image we just created and remove image id - # field from other-config. - session.call_xenapi('VDI.remove_from_other_config', - new_vdi_ref, 'image-id') - - vdi_type = ("root" if image_type == ImageType.DISK_VHD - else ImageType.to_string(image_type)) - vdi_uuid = session.call_xenapi('VDI.get_uuid', new_vdi_ref) - vdis[vdi_type] = dict(uuid=vdi_uuid, file=None) - - # Create a swap disk if the glance image had one associated with it. - vdi_rec = session.call_xenapi('VDI.get_record', root_vdi_ref) - if 'swap-disk' in vdi_rec['other_config']: - swap_disk_uuid = vdi_rec['other_config']['swap-disk'] - swap_vdi_ref = session.call_xenapi('VDI.get_by_uuid', - swap_disk_uuid) - new_swap_vdi_ref = cls.copy_vdi(session, sr_ref, swap_vdi_ref) - new_swap_vdi_uuid = session.call_xenapi('VDI.get_uuid', - new_swap_vdi_ref) - vdis['swap'] = dict(uuid=new_swap_vdi_uuid, file=None) - - return vdis + # Set the name label for the image we just created and remove image id + # field from other-config. + session.call_xenapi('VDI.remove_from_other_config', + new_vdi_ref, 'image-id') - @classmethod - def create_image(cls, context, session, instance, image, image_type): - """Creates VDI from the image stored in the local cache. If the image - is not present in the cache, it streams it from glance. - - Returns: A list of dictionaries that describe VDIs - """ - if FLAGS.cache_images is True and image_type != ImageType.DISK_ISO: - vdis = cls._create_cached_image(context, session, instance, - image, image_type) - else: - # If caching is disabled, we do not have to keep a copy of the - # image. Fetch the image from glance. - vdis = cls.fetch_image(context, session, instance, - instance.image_ref, image_type) + vdi_type = ("root" if image_type == ImageType.DISK_VHD + else ImageType.to_string(image_type)) + vdi_uuid = session.call_xenapi('VDI.get_uuid', new_vdi_ref) + vdis[vdi_type] = dict(uuid=vdi_uuid, file=None) - # Set the name label and description to easily identify what - # instance and disk it's for - for vdi_type, vdi in vdis.iteritems(): - cls.set_vdi_name(session, vdi['uuid'], instance.name, vdi_type) + # Create a swap disk if the glance image had one associated with it. + vdi_rec = session.call_xenapi('VDI.get_record', root_vdi_ref) + if 'swap-disk' in vdi_rec['other_config']: + swap_disk_uuid = vdi_rec['other_config']['swap-disk'] + swap_vdi_ref = session.call_xenapi('VDI.get_by_uuid', + swap_disk_uuid) + new_swap_vdi_ref = copy_vdi(session, sr_ref, swap_vdi_ref) + new_swap_vdi_uuid = session.call_xenapi('VDI.get_uuid', + new_swap_vdi_ref) + vdis['swap'] = dict(uuid=new_swap_vdi_uuid, file=None) - return vdis + return vdis - @classmethod - def fetch_image(cls, context, session, instance, image, image_type): - """Fetch image from glance based on image type. - - Returns: A single filename if image_type is KERNEL or RAMDISK - A list of dictionaries that describe VDIs, otherwise - """ - if image_type == ImageType.DISK_VHD: - return cls._fetch_image_glance_vhd(context, session, instance, - image) - else: - return cls._fetch_image_glance_disk(context, session, instance, - image, image_type) - @classmethod - def _retry_glance_download_vhd(cls, context, session, image): - # NOTE(sirp): The Glance plugin runs under Python 2.4 - # which does not have the `uuid` module. To work around this, - # we generate the uuids here (under Python 2.6+) and - # pass them as arguments - uuid_stack = [str(uuid.uuid4()) for i in xrange(3)] - - max_attempts = FLAGS.glance_num_retries + 1 - sleep_time = 0.5 - for attempt_num in xrange(1, max_attempts + 1): - glance_host, glance_port = glance.pick_glance_api_server() - params = {'image_id': image, - 'glance_host': glance_host, - 'glance_port': glance_port, - 'uuid_stack': uuid_stack, - 'sr_path': cls.get_sr_path(session), - 'auth_token': getattr(context, 'auth_token', None)} - kwargs = {'params': pickle.dumps(params)} - - LOG.info(_('download_vhd %(image)s ' - 'attempt %(attempt_num)d/%(max_attempts)d ' - 'from %(glance_host)s:%(glance_port)s') % locals()) +def create_image(context, session, instance, image, image_type): + """Creates VDI from the image stored in the local cache. If the image + is not present in the cache, it streams it from glance. - try: - result = session.call_plugin('glance', 'download_vhd', kwargs) - return json.loads(result) - except session.XenAPI.Failure as exc: - _type, _method, error = exc.details[:3] - if error == 'RetryableError': - LOG.error(_('download_vhd failed: %r') % - (exc.details[3:],)) - else: - raise + Returns: A list of dictionaries that describe VDIs + """ + if FLAGS.cache_images is True and image_type != ImageType.DISK_ISO: + vdis = _create_cached_image(context, session, instance, image, + image_type) + else: + # If caching is disabled, we do not have to keep a copy of the + # image. Fetch the image from glance. + vdis = fetch_image(context, session, instance, + instance.image_ref, image_type) - time.sleep(sleep_time) - sleep_time = min(2 * sleep_time, 15) + # Set the name label and description to easily identify what + # instance and disk it's for + for vdi_type, vdi in vdis.iteritems(): + set_vdi_name(session, vdi['uuid'], instance.name, vdi_type) - raise exception.CouldNotFetchImage(image=image) + return vdis - @classmethod - def _fetch_image_glance_vhd(cls, context, session, instance, image): - """Tell glance to download an image and put the VHDs into the SR - Returns: A list of dictionaries that describe VDIs - """ - LOG.debug(_("Asking xapi to fetch vhd image %(image)s"), locals(), - instance=instance) - sr_ref = cls.safe_find_sr(session) +def fetch_image(context, session, instance, image, image_type): + """Fetch image from glance based on image type. + + Returns: A single filename if image_type is KERNEL or RAMDISK + A list of dictionaries that describe VDIs, otherwise + """ + if image_type == ImageType.DISK_VHD: + return _fetch_image_glance_vhd(context, session, instance, image) + else: + return _fetch_image_glance_disk(context, session, instance, image, + image_type) + + +def _retry_glance_download_vhd(context, session, image): + # NOTE(sirp): The Glance plugin runs under Python 2.4 + # which does not have the `uuid` module. To work around this, + # we generate the uuids here (under Python 2.6+) and + # pass them as arguments + uuid_stack = [str(uuid.uuid4()) for i in xrange(3)] + + max_attempts = FLAGS.glance_num_retries + 1 + sleep_time = 0.5 + for attempt_num in xrange(1, max_attempts + 1): + glance_host, glance_port = glance.pick_glance_api_server() + params = {'image_id': image, + 'glance_host': glance_host, + 'glance_port': glance_port, + 'uuid_stack': uuid_stack, + 'sr_path': get_sr_path(session), + 'auth_token': getattr(context, 'auth_token', None)} + kwargs = {'params': pickle.dumps(params)} + + LOG.info(_('download_vhd %(image)s ' + 'attempt %(attempt_num)d/%(max_attempts)d ' + 'from %(glance_host)s:%(glance_port)s') % locals()) + + try: + result = session.call_plugin('glance', 'download_vhd', kwargs) + return json.loads(result) + except session.XenAPI.Failure as exc: + _type, _method, error = exc.details[:3] + if error == 'RetryableError': + LOG.error(_('download_vhd failed: %r') % + (exc.details[3:],)) + else: + raise + + time.sleep(sleep_time) + sleep_time = min(2 * sleep_time, 15) - fetched_vdis = cls._retry_glance_download_vhd(context, session, image) + raise exception.CouldNotFetchImage(image=image) - # 'download_vhd' will return a list of dictionaries describing VDIs. - # The dictionary will contain 'vdi_type' and 'vdi_uuid' keys. - # 'vdi_type' can be 'root' or 'swap' right now. - for vdi in fetched_vdis: - LOG.debug(_("xapi 'download_vhd' returned VDI of " - "type '%(vdi_type)s' with UUID '%(vdi_uuid)s'"), - vdi, instance=instance) - cls.scan_sr(session, sr_ref) +def _fetch_image_glance_vhd(context, session, instance, image): + """Tell glance to download an image and put the VHDs into the SR - vdis = {} - for vdi in fetched_vdis: - vdis[vdi['vdi_type']] = dict(uuid=vdi['vdi_uuid'], file=None) + Returns: A list of dictionaries that describe VDIs + """ + LOG.debug(_("Asking xapi to fetch vhd image %(image)s"), locals(), + instance=instance) + sr_ref = safe_find_sr(session) - # Pull out the UUID of the root VDI - root_vdi_uuid = vdis['root']['uuid'] + fetched_vdis = _retry_glance_download_vhd(context, session, image) - # Set the name-label to ease debugging - cls.set_vdi_name(session, root_vdi_uuid, instance.name, 'root') + # 'download_vhd' will return a list of dictionaries describing VDIs. + # The dictionary will contain 'vdi_type' and 'vdi_uuid' keys. + # 'vdi_type' can be 'root' or 'swap' right now. + for vdi in fetched_vdis: + LOG.debug(_("xapi 'download_vhd' returned VDI of " + "type '%(vdi_type)s' with UUID '%(vdi_uuid)s'"), + vdi, instance=instance) - cls._check_vdi_size(context, session, instance, root_vdi_uuid) - return vdis + scan_sr(session, sr_ref) - @classmethod - def _get_vdi_chain_size(cls, session, vdi_uuid): - """Compute the total size of a VDI chain, starting with the specified - VDI UUID. - - This will walk the VDI chain to the root, add the size of each VDI into - the total. - """ - size_bytes = 0 - for vdi_rec in walk_vdi_chain(session, vdi_uuid): - cur_vdi_uuid = vdi_rec['uuid'] - vdi_size_bytes = int(vdi_rec['physical_utilisation']) - LOG.debug(_('vdi_uuid=%(cur_vdi_uuid)s vdi_size_bytes=' - '%(vdi_size_bytes)d'), locals()) - size_bytes += vdi_size_bytes - return size_bytes + vdis = {} + for vdi in fetched_vdis: + vdis[vdi['vdi_type']] = dict(uuid=vdi['vdi_uuid'], file=None) - @classmethod - def _check_vdi_size(cls, context, session, instance, vdi_uuid): - size_bytes = cls._get_vdi_chain_size(session, vdi_uuid) + # Pull out the UUID of the root VDI + root_vdi_uuid = vdis['root']['uuid'] - # FIXME(jk0): this was copied directly from compute.manager.py, let's - # refactor this to a common area - instance_type_id = instance['instance_type_id'] - instance_type = instance_types.get_instance_type(instance_type_id) - allowed_size_gb = instance_type['root_gb'] - allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 + # Set the name-label to ease debugging + set_vdi_name(session, root_vdi_uuid, instance.name, 'root') - LOG.debug(_("image_size_bytes=%(size_bytes)d, allowed_size_bytes=" - "%(allowed_size_bytes)d"), locals(), instance=instance) + _check_vdi_size(context, session, instance, root_vdi_uuid) + return vdis - if size_bytes > allowed_size_bytes: - LOG.info(_("Image size %(size_bytes)d exceeded instance_type " - "allowed size %(allowed_size_bytes)d"), - locals(), instance=instance) - raise exception.ImageTooLarge() - @classmethod - def _fetch_image_glance_disk(cls, context, session, instance, image, - image_type): - """Fetch the image from Glance - - NOTE: - Unlike _fetch_image_glance_vhd, this method does not use the Glance - plugin; instead, it streams the disks through domU to the VDI - directly. - - Returns: A single filename if image_type is KERNEL_RAMDISK - A list of dictionaries that describe VDIs, otherwise - """ - # FIXME(sirp): Since the Glance plugin seems to be required for the - # VHD disk, it may be worth using the plugin for both VHD and RAW and - # DISK restores - image_type_str = ImageType.to_string(image_type) - LOG.debug(_("Fetching image %(image)s, type %(image_type_str)s"), - locals(), instance=instance) - - if image_type == ImageType.DISK_ISO: - sr_ref = cls.safe_find_iso_sr(session) +def _get_vdi_chain_size(session, vdi_uuid): + """Compute the total size of a VDI chain, starting with the specified + VDI UUID. + + This will walk the VDI chain to the root, add the size of each VDI into + the total. + """ + size_bytes = 0 + for vdi_rec in walk_vdi_chain(session, vdi_uuid): + cur_vdi_uuid = vdi_rec['uuid'] + vdi_size_bytes = int(vdi_rec['physical_utilisation']) + LOG.debug(_('vdi_uuid=%(cur_vdi_uuid)s vdi_size_bytes=' + '%(vdi_size_bytes)d'), locals()) + size_bytes += vdi_size_bytes + return size_bytes + + +def _check_vdi_size(context, session, instance, vdi_uuid): + size_bytes = _get_vdi_chain_size(session, vdi_uuid) + + # FIXME(jk0): this was copied directly from compute.manager.py, let's + # refactor this to a common area + instance_type_id = instance['instance_type_id'] + instance_type = instance_types.get_instance_type(instance_type_id) + allowed_size_gb = instance_type['root_gb'] + allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 + + LOG.debug(_("image_size_bytes=%(size_bytes)d, allowed_size_bytes=" + "%(allowed_size_bytes)d"), locals(), instance=instance) + + if size_bytes > allowed_size_bytes: + LOG.info(_("Image size %(size_bytes)d exceeded instance_type " + "allowed size %(allowed_size_bytes)d"), + locals(), instance=instance) + raise exception.ImageTooLarge() + + +def _fetch_image_glance_disk(context, session, instance, image, image_type): + """Fetch the image from Glance + + NOTE: + Unlike _fetch_image_glance_vhd, this method does not use the Glance + plugin; instead, it streams the disks through domU to the VDI + directly. + + Returns: A single filename if image_type is KERNEL_RAMDISK + A list of dictionaries that describe VDIs, otherwise + """ + # FIXME(sirp): Since the Glance plugin seems to be required for the + # VHD disk, it may be worth using the plugin for both VHD and RAW and + # DISK restores + image_type_str = ImageType.to_string(image_type) + LOG.debug(_("Fetching image %(image)s, type %(image_type_str)s"), + locals(), instance=instance) + + if image_type == ImageType.DISK_ISO: + sr_ref = safe_find_iso_sr(session) + else: + sr_ref = safe_find_sr(session) + + glance_client, image_id = glance.get_glance_client(context, image) + glance_client.set_auth_token(getattr(context, 'auth_token', None)) + meta, image_file = glance_client.get_image(image_id) + virtual_size = int(meta['size']) + vdi_size = virtual_size + LOG.debug(_("Size for image %(image)s: %(virtual_size)d"), locals(), + instance=instance) + if image_type == ImageType.DISK: + # Make room for MBR. + vdi_size += MBR_SIZE_BYTES + elif (image_type in (ImageType.KERNEL, ImageType.RAMDISK) and + vdi_size > FLAGS.max_kernel_ramdisk_size): + max_size = FLAGS.max_kernel_ramdisk_size + raise exception.NovaException( + _("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, " + "max %(max_size)d bytes") % locals()) + + vdi_ref = create_vdi(session, sr_ref, instance, image_type_str, vdi_size) + # From this point we have a VDI on Xen host; + # If anything goes wrong, we need to remember its uuid. + try: + filename = None + vdi_uuid = session.call_xenapi("VDI.get_uuid", vdi_ref) + + with vdi_attached_here(session, vdi_ref, read_only=False) as dev: + _stream_disk(dev, image_type, virtual_size, image_file) + + if image_type in (ImageType.KERNEL, ImageType.RAMDISK): + # We need to invoke a plugin for copying the + # content of the VDI into the proper path. + LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), + vdi_ref, instance=instance) + fn = "copy_kernel_vdi" + args = {} + args['vdi-ref'] = vdi_ref + + # Let the plugin copy the correct number of bytes. + args['image-size'] = str(vdi_size) + if FLAGS.cache_images: + args['cached-image'] = image + filename = session.call_plugin('glance', fn, args) + + # Remove the VDI as it is not needed anymore. + destroy_vdi(session, vdi_ref) + LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi_ref, + instance=instance) + vdi_type = ImageType.to_string(image_type) + return {vdi_type: dict(uuid=None, file=filename)} else: - sr_ref = cls.safe_find_sr(session) - - glance_client, image_id = glance.get_glance_client(context, image) - glance_client.set_auth_token(getattr(context, 'auth_token', None)) - meta, image_file = glance_client.get_image(image_id) - virtual_size = int(meta['size']) - vdi_size = virtual_size - LOG.debug(_("Size for image %(image)s: %(virtual_size)d"), locals(), - instance=instance) - if image_type == ImageType.DISK: - # Make room for MBR. - vdi_size += MBR_SIZE_BYTES - elif (image_type in (ImageType.KERNEL, ImageType.RAMDISK) and - vdi_size > FLAGS.max_kernel_ramdisk_size): - max_size = FLAGS.max_kernel_ramdisk_size - raise exception.NovaException( - _("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, " - "max %(max_size)d bytes") % locals()) - - vdi_ref = cls.create_vdi(session, sr_ref, instance, image_type_str, - vdi_size) - # From this point we have a VDI on Xen host; - # If anything goes wrong, we need to remember its uuid. - try: - filename = None - vdi_uuid = session.call_xenapi("VDI.get_uuid", vdi_ref) - - with vdi_attached_here(session, vdi_ref, read_only=False) as dev: - _stream_disk(dev, image_type, virtual_size, image_file) - - if image_type in (ImageType.KERNEL, ImageType.RAMDISK): - # We need to invoke a plugin for copying the - # content of the VDI into the proper path. - LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), - vdi_ref, instance=instance) - fn = "copy_kernel_vdi" - args = {} - args['vdi-ref'] = vdi_ref - - # Let the plugin copy the correct number of bytes. - args['image-size'] = str(vdi_size) - if FLAGS.cache_images: - args['cached-image'] = image - filename = session.call_plugin('glance', fn, args) - - # Remove the VDI as it is not needed anymore. - cls.destroy_vdi(session, vdi_ref) - LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi_ref, - instance=instance) - vdi_type = ImageType.to_string(image_type) - return {vdi_type: dict(uuid=None, file=filename)} - else: - vdi_type = ImageType.to_string(image_type) - return {vdi_type: dict(uuid=vdi_uuid, file=None)} - except (session.XenAPI.Failure, IOError, OSError) as e: - # We look for XenAPI and OS failures. - LOG.exception(_("Failed to fetch glance image"), - instance=instance) - e.args = e.args + ([dict(vdi_type=ImageType. - to_string(image_type), - vdi_uuid=vdi_uuid, - file=filename)],) - raise e + vdi_type = ImageType.to_string(image_type) + return {vdi_type: dict(uuid=vdi_uuid, file=None)} + except (session.XenAPI.Failure, IOError, OSError) as e: + # We look for XenAPI and OS failures. + LOG.exception(_("Failed to fetch glance image"), + instance=instance) + e.args = e.args + ([dict(type=ImageType.to_string(image_type), + uuid=vdi_uuid, + file=filename)],) + raise e + + +def determine_disk_image_type(image_meta): + """Disk Image Types are used to determine where the kernel will reside + within an image. To figure out which type we're dealing with, we use + the following rules: + + 1. If we're using Glance, we can use the image_type field to + determine the image_type + + 2. If we're not using Glance, then we need to deduce this based on + whether a kernel_id is specified. + """ + disk_format = image_meta['disk_format'] - @classmethod - def determine_disk_image_type(cls, image_meta): - """Disk Image Types are used to determine where the kernel will reside - within an image. To figure out which type we're dealing with, we use - the following rules: - - 1. If we're using Glance, we can use the image_type field to - determine the image_type - - 2. If we're not using Glance, then we need to deduce this based on - whether a kernel_id is specified. - """ - disk_format = image_meta['disk_format'] - - disk_format_map = { - 'ami': 'DISK', - 'aki': 'KERNEL', - 'ari': 'RAMDISK', - 'raw': 'DISK_RAW', - 'vhd': 'DISK_VHD', - 'iso': 'DISK_ISO', - } + disk_format_map = { + 'ami': 'DISK', + 'aki': 'KERNEL', + 'ari': 'RAMDISK', + 'raw': 'DISK_RAW', + 'vhd': 'DISK_VHD', + 'iso': 'DISK_ISO', + } - try: - image_type_str = disk_format_map[disk_format] - except KeyError: - raise exception.InvalidDiskFormat(disk_format=disk_format) + try: + image_type_str = disk_format_map[disk_format] + except KeyError: + raise exception.InvalidDiskFormat(disk_format=disk_format) - image_type = getattr(ImageType, image_type_str) + image_type = getattr(ImageType, image_type_str) - image_ref = image_meta['id'] - msg = _("Detected %(image_type_str)s format for image %(image_ref)s") - LOG.debug(msg % locals()) + image_ref = image_meta['id'] + msg = _("Detected %(image_type_str)s format for image %(image_ref)s") + LOG.debug(msg % locals()) - return image_type + return image_type - @classmethod - def determine_is_pv(cls, session, vdi_ref, disk_image_type, os_type): - """ - Determine whether the VM will use a paravirtualized kernel or if it - will use hardware virtualization. - 1. Glance (VHD): then we use `os_type`, raise if not set +def determine_is_pv(session, vdi_ref, disk_image_type, os_type): + """ + Determine whether the VM will use a paravirtualized kernel or if it + will use hardware virtualization. - 2. Glance (DISK_RAW): use Pygrub to figure out if pv kernel is - available + 1. Glance (VHD): then we use `os_type`, raise if not set - 3. Glance (DISK): pv is assumed + 2. Glance (DISK_RAW): use Pygrub to figure out if pv kernel is + available - 4. Glance (DISK_ISO): no pv is assumed - """ + 3. Glance (DISK): pv is assumed - LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref) - if disk_image_type == ImageType.DISK_VHD: - # 1. VHD - if os_type == 'windows': - is_pv = False - else: - is_pv = True - elif disk_image_type == ImageType.DISK_RAW: - # 2. RAW - with vdi_attached_here(session, vdi_ref, read_only=True) as dev: - is_pv = _is_vdi_pv(dev) - elif disk_image_type == ImageType.DISK: - # 3. Disk - is_pv = True - elif disk_image_type == ImageType.DISK_ISO: - # 4. ISO + 4. Glance (DISK_ISO): no pv is assumed + """ + + LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref) + if disk_image_type == ImageType.DISK_VHD: + # 1. VHD + if os_type == 'windows': is_pv = False else: - msg = _("Unknown image format %(disk_image_type)s") % locals() - raise exception.NovaException(msg) + is_pv = True + elif disk_image_type == ImageType.DISK_RAW: + # 2. RAW + with vdi_attached_here(session, vdi_ref, read_only=True) as dev: + is_pv = _is_vdi_pv(dev) + elif disk_image_type == ImageType.DISK: + # 3. Disk + is_pv = True + elif disk_image_type == ImageType.DISK_ISO: + # 4. ISO + is_pv = False + else: + msg = _("Unknown image format %(disk_image_type)s") % locals() + raise exception.NovaException(msg) - return is_pv + return is_pv - @classmethod - def set_vm_name_label(cls, session, vm_ref, name_label): - session.call_xenapi("VM.set_name_label", vm_ref, name_label) - @classmethod - def list_vms(cls, session): - for vm_ref, vm_rec in session.get_all_refs_and_recs('VM'): - if (vm_rec["resident_on"] != session.get_xenapi_host() or - vm_rec["is_a_template"] or vm_rec["is_control_domain"]): - continue - else: - yield vm_ref, vm_rec +def set_vm_name_label(session, vm_ref, name_label): + session.call_xenapi("VM.set_name_label", vm_ref, name_label) - @classmethod - def lookup(cls, session, name_label): - """Look the instance up and return it if available""" - vm_refs = session.call_xenapi("VM.get_by_name_label", name_label) - n = len(vm_refs) - if n == 0: - return None - elif n > 1: - raise exception.InstanceExists(name=name_label) + +def list_vms(session): + for vm_ref, vm_rec in session.get_all_refs_and_recs('VM'): + if (vm_rec["resident_on"] != session.get_xenapi_host() or + vm_rec["is_a_template"] or vm_rec["is_control_domain"]): + continue else: - return vm_refs[0] + yield vm_ref, vm_rec - @classmethod - def lookup_vm_vdis(cls, session, vm_ref): - """Look for the VDIs that are attached to the VM""" - # Firstly we get the VBDs, then the VDIs. - # TODO(Armando): do we leave the read-only devices? - vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) - vdi_refs = [] - if vbd_refs: - for vbd_ref in vbd_refs: - try: - vdi_ref = session.call_xenapi("VBD.get_VDI", vbd_ref) - # Test valid VDI - record = session.call_xenapi("VDI.get_record", vdi_ref) - LOG.debug(_('VDI %s is still available'), record['uuid']) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - else: - vdi_refs.append(vdi_ref) - return vdi_refs - @classmethod - def preconfigure_instance(cls, session, instance, vdi_ref, network_info): - """Makes alterations to the image before launching as part of spawn. - """ - - # As mounting the image VDI is expensive, we only want do do it once, - # if at all, so determine whether it's required first, and then do - # everything - mount_required = False - key, net, metadata = _prepare_injectables(instance, network_info) - mount_required = key or net or metadata - if not mount_required: - return +def lookup(session, name_label): + """Look the instance up and return it if available""" + vm_refs = session.call_xenapi("VM.get_by_name_label", name_label) + n = len(vm_refs) + if n == 0: + return None + elif n > 1: + raise exception.InstanceExists(name=name_label) + else: + return vm_refs[0] - with vdi_attached_here(session, vdi_ref, read_only=False) as dev: - _mounted_processing(dev, key, net, metadata) - @classmethod - def lookup_kernel_ramdisk(cls, session, vm): - vm_rec = session.call_xenapi("VM.get_record", vm) - if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec: - return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk']) - else: - return (None, None) +def lookup_vm_vdis(session, vm_ref): + """Look for the VDIs that are attached to the VM""" + # Firstly we get the VBDs, then the VDIs. + # TODO(Armando): do we leave the read-only devices? + vbd_refs = session.call_xenapi("VM.get_VBDs", vm_ref) + vdi_refs = [] + if vbd_refs: + for vbd_ref in vbd_refs: + try: + vdi_ref = session.call_xenapi("VBD.get_VDI", vbd_ref) + # Test valid VDI + record = session.call_xenapi("VDI.get_record", vdi_ref) + LOG.debug(_('VDI %s is still available'), record['uuid']) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + else: + vdi_refs.append(vdi_ref) + return vdi_refs - @classmethod - def is_snapshot(cls, session, vm): - vm_rec = session.call_xenapi("VM.get_record", vm) - if 'is_a_template' in vm_rec and 'is_a_snapshot' in vm_rec: - return vm_rec['is_a_template'] and vm_rec['is_a_snapshot'] - else: - return False - @classmethod - def compile_info(cls, record): - """Fill record with VM status information""" - return {'state': XENAPI_POWER_STATE[record['power_state']], - 'max_mem': long(record['memory_static_max']) >> 10, - 'mem': long(record['memory_dynamic_max']) >> 10, - 'num_cpu': record['VCPUs_max'], - 'cpu_time': 0} +def preconfigure_instance(session, instance, vdi_ref, network_info): + """Makes alterations to the image before launching as part of spawn. + """ - @classmethod - def compile_diagnostics(cls, record): - """Compile VM diagnostics data""" - try: - keys = [] - diags = {} - vm_uuid = record["uuid"] - xml = get_rrd(get_rrd_server(), vm_uuid) - if xml: - rrd = minidom.parseString(xml) - for i, node in enumerate(rrd.firstChild.childNodes): - # Provide the last update of the information - if node.localName == 'lastupdate': - diags['last_update'] = node.firstChild.data - - # Create a list of the diagnostic keys (in their order) - if node.localName == 'ds': - ref = node.childNodes - # Name and Value - if len(ref) > 6: - keys.append(ref[0].firstChild.data) - - # Read the last row of the first RRA to get the latest info - if node.localName == 'rra': - rows = node.childNodes[4].childNodes - last_row = rows[rows.length - 1].childNodes - for j, value in enumerate(last_row): - diags[keys[j]] = value.firstChild.data - break - - return diags - except expat.ExpatError as e: - LOG.exception(_('Unable to parse rrd of %(vm_uuid)s') % locals()) - return {"Unable to retrieve diagnostics": e} + # As mounting the image VDI is expensive, we only want do do it once, + # if at all, so determine whether it's required first, and then do + # everything + mount_required = False + key, net, metadata = _prepare_injectables(instance, network_info) + mount_required = key or net or metadata + if not mount_required: + return - @classmethod - def compile_metrics(cls, start_time, stop_time=None): - """Compile bandwidth usage, cpu, and disk metrics for all VMs on - this host""" - start_time = int(start_time) + with vdi_attached_here(session, vdi_ref, read_only=False) as dev: + _mounted_processing(dev, key, net, metadata) + + +def lookup_kernel_ramdisk(session, vm): + vm_rec = session.call_xenapi("VM.get_record", vm) + if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec: + return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk']) + else: + return (None, None) - xml = get_rrd_updates(get_rrd_server(), start_time) + +def is_snapshot(session, vm): + vm_rec = session.call_xenapi("VM.get_record", vm) + if 'is_a_template' in vm_rec and 'is_a_snapshot' in vm_rec: + return vm_rec['is_a_template'] and vm_rec['is_a_snapshot'] + else: + return False + + +def compile_info(record): + """Fill record with VM status information""" + return {'state': XENAPI_POWER_STATE[record['power_state']], + 'max_mem': long(record['memory_static_max']) >> 10, + 'mem': long(record['memory_dynamic_max']) >> 10, + 'num_cpu': record['VCPUs_max'], + 'cpu_time': 0} + + +def compile_diagnostics(record): + """Compile VM diagnostics data""" + try: + keys = [] + diags = {} + vm_uuid = record["uuid"] + xml = get_rrd(get_rrd_server(), vm_uuid) if xml: - doc = minidom.parseString(xml) - return parse_rrd_update(doc, start_time, stop_time) + rrd = minidom.parseString(xml) + for i, node in enumerate(rrd.firstChild.childNodes): + # Provide the last update of the information + if node.localName == 'lastupdate': + diags['last_update'] = node.firstChild.data - raise exception.CouldNotFetchMetrics() + # Create a list of the diagnostic keys (in their order) + if node.localName == 'ds': + ref = node.childNodes + # Name and Value + if len(ref) > 6: + keys.append(ref[0].firstChild.data) - @classmethod - def scan_sr(cls, session, sr_ref=None): - """Scans the SR specified by sr_ref""" - if sr_ref: - LOG.debug(_("Re-scanning SR %s"), sr_ref) - session.call_xenapi('SR.scan', sr_ref) + # Read the last row of the first RRA to get the latest info + if node.localName == 'rra': + rows = node.childNodes[4].childNodes + last_row = rows[rows.length - 1].childNodes + for j, value in enumerate(last_row): + diags[keys[j]] = value.firstChild.data + break - @classmethod - def scan_default_sr(cls, session): - """Looks for the system default SR and triggers a re-scan""" - cls.scan_sr(session, cls.find_sr(session)) + return diags + except expat.ExpatError as e: + LOG.exception(_('Unable to parse rrd of %(vm_uuid)s') % locals()) + return {"Unable to retrieve diagnostics": e} - @classmethod - def safe_find_sr(cls, session): - """Same as find_sr except raises a NotFound exception if SR cannot be - determined - """ - sr_ref = cls.find_sr(session) - if sr_ref is None: - raise exception.StorageRepositoryNotFound() - return sr_ref - @classmethod - def find_sr(cls, session): - """Return the storage repository to hold VM images""" - host = session.get_xenapi_host() - try: - tokens = FLAGS.sr_matching_filter.split(':') - filter_criteria = tokens[0] - filter_pattern = tokens[1] - except IndexError: - # oops, flag is invalid - LOG.warning(_("Flag sr_matching_filter '%s' does not respect " - "formatting convention"), FLAGS.sr_matching_filter) - return None - - if filter_criteria == 'other-config': - key, value = filter_pattern.split('=', 1) - for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): - if not (key in sr_rec['other_config'] and - sr_rec['other_config'][key] == value): - continue - for pbd_ref in sr_rec['PBDs']: - pbd_rec = session.get_rec('PBD', pbd_ref) - if pbd_rec and pbd_rec['host'] == host: - return sr_ref - elif filter_criteria == 'default-sr' and filter_pattern == 'true': - pool_ref = session.call_xenapi('pool.get_all')[0] - return session.call_xenapi('pool.get_default_SR', pool_ref) - # No SR found! - LOG.warning(_("XenAPI is unable to find a Storage Repository to " - "install guest instances on. Please check your " - "configuration and/or configure the flag " - "'sr_matching_filter'")) - return None +def compile_metrics(start_time, stop_time=None): + """Compile bandwidth usage, cpu, and disk metrics for all VMs on + this host""" + start_time = int(start_time) - @classmethod - def safe_find_iso_sr(cls, session): - """Same as find_iso_sr except raises a NotFound exception if SR - cannot be determined - """ - sr_ref = cls.find_iso_sr(session) - if sr_ref is None: - raise exception.NotFound(_('Cannot find SR of content-type ISO')) - return sr_ref + xml = get_rrd_updates(get_rrd_server(), start_time) + if xml: + doc = minidom.parseString(xml) + return parse_rrd_update(doc, start_time, stop_time) - @classmethod - def find_iso_sr(cls, session): - """Return the storage repository to hold ISO images""" - host = session.get_xenapi_host() + raise exception.CouldNotFetchMetrics() + + +def scan_sr(session, sr_ref=None): + """Scans the SR specified by sr_ref""" + if sr_ref: + LOG.debug(_("Re-scanning SR %s"), sr_ref) + session.call_xenapi('SR.scan', sr_ref) + + +def scan_default_sr(session): + """Looks for the system default SR and triggers a re-scan""" + scan_sr(session, find_sr(session)) + + +def safe_find_sr(session): + """Same as find_sr except raises a NotFound exception if SR cannot be + determined + """ + sr_ref = find_sr(session) + if sr_ref is None: + raise exception.StorageRepositoryNotFound() + return sr_ref + + +def find_sr(session): + """Return the storage repository to hold VM images""" + host = session.get_xenapi_host() + try: + tokens = FLAGS.sr_matching_filter.split(':') + filter_criteria = tokens[0] + filter_pattern = tokens[1] + except IndexError: + # oops, flag is invalid + LOG.warning(_("Flag sr_matching_filter '%s' does not respect " + "formatting convention"), FLAGS.sr_matching_filter) + return None + + if filter_criteria == 'other-config': + key, value = filter_pattern.split('=', 1) for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): - LOG.debug(_("ISO: looking at SR %(sr_rec)s") % locals()) - if not sr_rec['content_type'] == 'iso': - LOG.debug(_("ISO: not iso content")) - continue - if not 'i18n-key' in sr_rec['other_config']: - LOG.debug(_("ISO: iso content_type, no 'i18n-key' key")) - continue - if not sr_rec['other_config']['i18n-key'] == 'local-storage-iso': - LOG.debug(_("ISO: iso content_type, i18n-key value not " - "'local-storage-iso'")) + if not (key in sr_rec['other_config'] and + sr_rec['other_config'][key] == value): continue - - LOG.debug(_("ISO: SR MATCHing our criteria")) for pbd_ref in sr_rec['PBDs']: - LOG.debug(_("ISO: ISO, looking to see if it is host local")) pbd_rec = session.get_rec('PBD', pbd_ref) - if not pbd_rec: - LOG.debug(_("ISO: PBD %(pbd_ref)s disappeared") % locals()) - continue - pbd_rec_host = pbd_rec['host'] - LOG.debug(_("ISO: PBD matching, want %(pbd_rec)s, " - "have %(host)s") % locals()) - if pbd_rec_host == host: - LOG.debug(_("ISO: SR with local PBD")) + if pbd_rec and pbd_rec['host'] == host: return sr_ref - return None + elif filter_criteria == 'default-sr' and filter_pattern == 'true': + pool_ref = session.call_xenapi('pool.get_all')[0] + return session.call_xenapi('pool.get_default_SR', pool_ref) + # No SR found! + LOG.warning(_("XenAPI is unable to find a Storage Repository to " + "install guest instances on. Please check your " + "configuration and/or configure the flag " + "'sr_matching_filter'")) + return None + + +def safe_find_iso_sr(session): + """Same as find_iso_sr except raises a NotFound exception if SR + cannot be determined + """ + sr_ref = find_iso_sr(session) + if sr_ref is None: + raise exception.NotFound(_('Cannot find SR of content-type ISO')) + return sr_ref + + +def find_iso_sr(session): + """Return the storage repository to hold ISO images""" + host = session.get_xenapi_host() + for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): + LOG.debug(_("ISO: looking at SR %(sr_rec)s") % locals()) + if not sr_rec['content_type'] == 'iso': + LOG.debug(_("ISO: not iso content")) + continue + if not 'i18n-key' in sr_rec['other_config']: + LOG.debug(_("ISO: iso content_type, no 'i18n-key' key")) + continue + if not sr_rec['other_config']['i18n-key'] == 'local-storage-iso': + LOG.debug(_("ISO: iso content_type, i18n-key value not " + "'local-storage-iso'")) + continue + + LOG.debug(_("ISO: SR MATCHing our criteria")) + for pbd_ref in sr_rec['PBDs']: + LOG.debug(_("ISO: ISO, looking to see if it is host local")) + pbd_rec = session.get_rec('PBD', pbd_ref) + if not pbd_rec: + LOG.debug(_("ISO: PBD %(pbd_ref)s disappeared") % locals()) + continue + pbd_rec_host = pbd_rec['host'] + LOG.debug(_("ISO: PBD matching, want %(pbd_rec)s, " + "have %(host)s") % locals()) + if pbd_rec_host == host: + LOG.debug(_("ISO: SR with local PBD")) + return sr_ref + return None def get_rrd_server(): @@ -1425,7 +1401,7 @@ def _wait_for_vhd_coalesce(session, instance, sr_ref, vdi_ref, max_attempts = FLAGS.xenapi_vhd_coalesce_max_attempts for i in xrange(max_attempts): - VMHelper.scan_sr(session, sr_ref) + scan_sr(session, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): LOG.debug(_("Parent %(parent_uuid)s doesn't match original parent" @@ -1495,17 +1471,16 @@ def cleanup_attached_vdis(session): # unclean restart LOG.info(_('Disconnecting stale VDI %s from compute domU'), vdi_rec['uuid']) - VMHelper.unplug_vbd(session, vbd_ref) - VMHelper.destroy_vbd(session, vbd_ref) + unplug_vbd(session, vbd_ref) + destroy_vbd(session, vbd_ref) @contextlib.contextmanager def vdi_attached_here(session, vdi_ref, read_only=False): this_vm_ref = get_this_vm_ref(session) - vbd_ref = VMHelper.create_vbd(session, this_vm_ref, vdi_ref, - 'autodetect', read_only=read_only, - bootable=False) + vbd_ref = create_vbd(session, this_vm_ref, vdi_ref, 'autodetect', + read_only=read_only, bootable=False) try: LOG.debug(_('Plugging VBD %s ... '), vbd_ref) session.call_xenapi("VBD.plug", vbd_ref) @@ -1521,10 +1496,10 @@ def vdi_attached_here(session, vdi_ref, read_only=False): yield dev finally: LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref) - VMHelper.unplug_vbd(session, vbd_ref) + unplug_vbd(session, vbd_ref) finally: try: - VMHelper.destroy_vbd(session, vbd_ref) + destroy_vbd(session, vbd_ref) except volume_utils.StorageError: # destroy_vbd() will log error pass diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 253540260..8692ba574 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -51,7 +51,6 @@ from nova.virt.xenapi import volume_utils VolumeHelper = volume_utils.VolumeHelper NetworkHelper = network_utils.NetworkHelper -VMHelper = vm_utils.VMHelper LOG = logging.getLogger(__name__) xenapi_vmops_opts = [ @@ -173,7 +172,7 @@ class VMOps(object): # TODO(justinsb): Should we just always use the details method? # Seems to be the same number of API calls.. name_labels = [] - for vm_ref, vm_rec in VMHelper.list_vms(self._session): + for vm_ref, vm_rec in vm_utils.list_vms(self._session): name_labels.append(vm_rec["name_label"]) return name_labels @@ -181,11 +180,11 @@ class VMOps(object): def list_instances_detail(self): """List VM instances, returning InstanceInfo objects.""" details = [] - for vm_ref, vm_rec in VMHelper.list_vms(self._session): + for vm_ref, vm_rec in vm_utils.list_vms(self._session): name = vm_rec["name_label"] # TODO(justinsb): This a roundabout way to map the state - openstack_format = VMHelper.compile_info(vm_rec) + openstack_format = vm_utils.compile_info(vm_rec) state = openstack_format['state'] instance_info = driver.InstanceInfo(name, state) @@ -195,19 +194,19 @@ class VMOps(object): def confirm_migration(self, migration, instance, network_info): name_label = self._get_orig_vm_name_label(instance) - vm_ref = VMHelper.lookup(self._session, name_label) + vm_ref = vm_utils.lookup(self._session, name_label) return self._destroy(instance, vm_ref, network_info) def finish_revert_migration(self, instance): # NOTE(sirp): the original vm was suffixed with '-orig'; find it using # the old suffix, remove the suffix, then power it back on. name_label = self._get_orig_vm_name_label(instance) - vm_ref = VMHelper.lookup(self._session, name_label) + vm_ref = vm_utils.lookup(self._session, name_label) # Remove the '-orig' suffix (which was added in case the resized VM # ends up on the source host, common during testing) name_label = instance.name - VMHelper.set_vm_name_label(self._session, vm_ref, name_label) + vm_utils.set_vm_name_label(self._session, vm_ref, name_label) self._start(instance, vm_ref) @@ -237,8 +236,8 @@ class VMOps(object): False, False) def _create_disks(self, context, instance, image_meta): - disk_image_type = VMHelper.determine_disk_image_type(image_meta) - vdis = VMHelper.create_image(context, self._session, + disk_image_type = vm_utils.determine_disk_image_type(image_meta) + vdis = vm_utils.create_image(context, self._session, instance, instance.image_ref, disk_image_type) @@ -282,13 +281,13 @@ class VMOps(object): ramdisk_file = None if instance.kernel_id: - vdis = VMHelper.create_kernel_image(context, self._session, + vdis = vm_utils.create_kernel_image(context, self._session, instance, instance.kernel_id, instance.user_id, instance.project_id, vm_utils.ImageType.KERNEL) kernel_file = vdis['kernel'].get('file') if instance.ramdisk_id: - vdis = VMHelper.create_kernel_image(context, self._session, + vdis = vm_utils.create_kernel_image(context, self._session, instance, instance.ramdisk_id, instance.user_id, instance.project_id, vm_utils.ImageType.RAMDISK) ramdisk_file = vdis['ramdisk'].get('file') @@ -365,15 +364,15 @@ class VMOps(object): kernel_file=None, ramdisk_file=None): """Create VM instance.""" instance_name = instance.name - vm_ref = VMHelper.lookup(self._session, instance_name) + vm_ref = vm_utils.lookup(self._session, instance_name) if vm_ref is not None: raise exception.InstanceExists(name=instance_name) # Ensure enough free memory is available - if not VMHelper.ensure_free_mem(self._session, instance): + if not vm_utils.ensure_free_mem(self._session, instance): raise exception.InsufficientFreeMemory(uuid=instance.uuid) - disk_image_type = VMHelper.determine_disk_image_type(image_meta) + disk_image_type = vm_utils.determine_disk_image_type(image_meta) vm_mode = instance.vm_mode and instance.vm_mode.lower() if vm_mode == 'pv': @@ -382,7 +381,7 @@ class VMOps(object): use_pv_kernel = False vm_mode = 'hvm' # Normalize else: - use_pv_kernel = VMHelper.determine_is_pv(self._session, + use_pv_kernel = vm_utils.determine_is_pv(self._session, vdis['root']['ref'], disk_image_type, instance.os_type) vm_mode = use_pv_kernel and 'pv' or 'hvm' @@ -391,7 +390,7 @@ class VMOps(object): db.instance_update(nova_context.get_admin_context(), instance['id'], {'vm_mode': vm_mode}) - vm_ref = VMHelper.create_vm( + vm_ref = vm_utils.create_vm( self._session, instance, kernel_file, ramdisk_file, use_pv_kernel) @@ -400,7 +399,7 @@ class VMOps(object): # Alter the image before VM start for network injection. if FLAGS.flat_injected: - VMHelper.preconfigure_instance(self._session, instance, + vm_utils.preconfigure_instance(self._session, instance, vdis['root']['ref'], network_info) self._create_vifs(vm_ref, instance, network_info) @@ -420,14 +419,14 @@ class VMOps(object): "for install"), instance=instance) cd_vdi = vdis.pop('root') - root_vdi = VMHelper.fetch_blank_disk(self._session, + root_vdi = vm_utils.fetch_blank_disk(self._session, instance.instance_type_id) vdis['root'] = root_vdi - VMHelper.create_vbd(self._session, vm_ref, root_vdi['ref'], + vm_utils.create_vbd(self._session, vm_ref, root_vdi['ref'], DEVICE_ROOT, bootable=False) - VMHelper.create_vbd(self._session, vm_ref, cd_vdi['ref'], + vm_utils.create_vbd(self._session, vm_ref, cd_vdi['ref'], DEVICE_CD, vbd_type='CD', bootable=True) else: root_vdi = vdis['root'] @@ -437,11 +436,11 @@ class VMOps(object): "resize partition..."), instance=instance) instance_type = db.instance_type_get(ctx, instance.instance_type_id) - VMHelper.auto_configure_disk(self._session, + vm_utils.auto_configure_disk(self._session, root_vdi['ref'], instance_type['root_gb']) - VMHelper.create_vbd(self._session, vm_ref, root_vdi['ref'], + vm_utils.create_vbd(self._session, vm_ref, root_vdi['ref'], DEVICE_ROOT, bootable=True) # Attach (optional) swap disk @@ -451,21 +450,21 @@ class VMOps(object): swap_mb = instance_type['swap'] generate_swap = swap_mb and FLAGS.xenapi_generate_swap if generate_swap: - VMHelper.generate_swap(self._session, instance, vm_ref, + vm_utils.generate_swap(self._session, instance, vm_ref, DEVICE_SWAP, swap_mb) if swap_vdi: # We won't be using packaged swap VDI, so destroy it - VMHelper.destroy_vdi(self._session, swap_vdi['ref']) + vm_utils.destroy_vdi(self._session, swap_vdi['ref']) elif swap_vdi: # Attach packaged swap VDI to VM - VMHelper.create_vbd(self._session, vm_ref, swap_vdi['ref'], + vm_utils.create_vbd(self._session, vm_ref, swap_vdi['ref'], DEVICE_SWAP, bootable=False) # Attach (optional) ephemeral disk ephemeral_gb = instance_type['ephemeral_gb'] if ephemeral_gb: - VMHelper.generate_ephemeral(self._session, instance, vm_ref, + vm_utils.generate_ephemeral(self._session, instance, vm_ref, DEVICE_EPHEMERAL, ephemeral_gb) def _boot_new_instance(self, instance, vm_ref): @@ -552,7 +551,7 @@ class VMOps(object): 'weight', str(vcpu_weight)) def _get_vm_opaque_ref(self, instance): - vm_ref = VMHelper.lookup(self._session, instance['name']) + vm_ref = vm_utils.lookup(self._session, instance['name']) if vm_ref is None: raise exception.NotFound(_('Could not find VM with name %s') % instance['name']) @@ -601,7 +600,7 @@ class VMOps(object): _snapshot_info = self._create_snapshot(instance) template_vm_ref, template_vdi_uuids = _snapshot_info # call plugin to ship snapshot off to glance - VMHelper.upload_image(context, + vm_utils.upload_image(context, self._session, instance, template_vdi_uuids, image_id) finally: if template_vm_ref: @@ -620,7 +619,7 @@ class VMOps(object): label = "%s-snapshot" % instance.name try: - template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot( + template_vm_ref, template_vdi_uuids = vm_utils.create_snapshot( self._session, instance, vm_ref, label) return template_vm_ref, template_vdi_uuids except self.XenAPI.Failure, exc: @@ -696,11 +695,11 @@ class VMOps(object): total_steps=RESIZE_TOTAL_STEPS) base_copy_uuid = template_vdi_uuids['image'] - _vdi_info = VMHelper.get_vdi_for_vm_safely(self._session, vm_ref) + _vdi_info = vm_utils.get_vdi_for_vm_safely(self._session, vm_ref) vdi_ref, vm_vdi_rec = _vdi_info cow_uuid = vm_vdi_rec['uuid'] - sr_path = VMHelper.get_sr_path(self._session) + sr_path = vm_utils.get_sr_path(self._session) if (instance['auto_disk_config'] and instance['root_gb'] > instance_type['root_gb']): @@ -720,7 +719,7 @@ class VMOps(object): # 3. Copy VDI, resize partition and filesystem, forget VDI, # truncate VHD - new_ref, new_uuid = VMHelper.resize_disk(self._session, + new_ref, new_uuid = vm_utils.resize_disk(self._session, instance, vdi_ref, instance_type) @@ -735,7 +734,7 @@ class VMOps(object): total_steps=RESIZE_TOTAL_STEPS) # Clean up VDI now that it's been copied - VMHelper.destroy_vdi(self._session, new_ref) + vm_utils.destroy_vdi(self._session, new_ref) vdis = {'base_copy': new_uuid} else: @@ -772,7 +771,7 @@ class VMOps(object): # purposes), apply a suffix to name-label so the two VM records # extant until a confirm_resize don't collide. name_label = self._get_orig_vm_name_label(instance) - VMHelper.set_vm_name_label(self._session, vm_ref, name_label) + vm_utils.set_vm_name_label(self._session, vm_ref, name_label) finally: if template_vm_ref: self._destroy(instance, template_vm_ref, @@ -786,7 +785,7 @@ class VMOps(object): new_base_copy_uuid = str(uuid.uuid4()) params = {'instance_uuid': instance['uuid'], - 'sr_path': VMHelper.get_sr_path(self._session), + 'sr_path': vm_utils.get_sr_path(self._session), 'old_base_copy_uuid': base_copy_uuid, 'new_base_copy_uuid': new_base_copy_uuid} @@ -804,11 +803,11 @@ class VMOps(object): {'params': pickle.dumps(params)}) # Now we rescan the SR so we find the VHDs - VMHelper.scan_default_sr(self._session) + vm_utils.scan_default_sr(self._session) # Set name-label so we can find if we need to clean up a failed # migration - VMHelper.set_vdi_name(self._session, new_uuid, instance.name, 'root') + vm_utils.set_vdi_name(self._session, new_uuid, instance.name, 'root') new_ref = self._session.call_xenapi('VDI.get_by_uuid', new_uuid) @@ -970,7 +969,7 @@ class VMOps(object): def _shutdown(self, instance, vm_ref, hard=True): """Shutdown an instance.""" vm_rec = self._session.call_xenapi("VM.get_record", vm_ref) - state = VMHelper.compile_info(vm_rec)['state'] + state = vm_utils.compile_info(vm_rec)['state'] if state == power_state.SHUTDOWN: LOG.warn(_("VM already halted, skipping shutdown..."), instance=instance) @@ -1003,7 +1002,7 @@ class VMOps(object): """Destroys the requested VDIs, logging any StorageError exceptions.""" for vdi_ref in vdi_refs: try: - VMHelper.destroy_vdi(self._session, vdi_ref) + vm_utils.destroy_vdi(self._session, vdi_ref) except volume_utils.StorageError as exc: LOG.error(exc) @@ -1041,7 +1040,7 @@ class VMOps(object): reason=_("instance has a kernel or ramdisk but not both")) # 3. We have both kernel and ramdisk - (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk(self._session, + (kernel, ramdisk) = vm_utils.lookup_kernel_ramdisk(self._session, vm_ref) self._destroy_kernel_ramdisk_plugin_call(kernel, ramdisk) @@ -1061,12 +1060,12 @@ class VMOps(object): """Destroy a rescue instance.""" # Shutdown Rescue VM vm_rec = self._session.call_xenapi("VM.get_record", rescue_vm_ref) - state = VMHelper.compile_info(vm_rec)['state'] + state = vm_utils.compile_info(vm_rec)['state'] if state != power_state.SHUTDOWN: self._session.call_xenapi("VM.hard_shutdown", rescue_vm_ref) # Destroy Rescue VDIs - vdi_refs = VMHelper.lookup_vm_vdis(self._session, rescue_vm_ref) + vdi_refs = vm_utils.lookup_vm_vdis(self._session, rescue_vm_ref) root_vdi_ref = self._find_root_vdi_ref(original_vm_ref) vdi_refs = [vdi_ref for vdi_ref in vdi_refs if vdi_ref != root_vdi_ref] self._safe_destroy_vdis(vdi_refs) @@ -1086,9 +1085,9 @@ class VMOps(object): # We don't use _get_vm_opaque_ref because the instance may # truly not exist because of a failure during build. A valid # vm_ref is checked correctly where necessary. - vm_ref = VMHelper.lookup(self._session, instance['name']) + vm_ref = vm_utils.lookup(self._session, instance['name']) - rescue_vm_ref = VMHelper.lookup(self._session, + rescue_vm_ref = vm_utils.lookup(self._session, "%s-rescue" % instance.name) if rescue_vm_ref: self._destroy_rescue_instance(rescue_vm_ref, vm_ref) @@ -1109,11 +1108,11 @@ class VMOps(object): LOG.warning(_("VM is not present, skipping destroy..."), instance=instance) return - is_snapshot = VMHelper.is_snapshot(self._session, vm_ref) + is_snapshot = vm_utils.is_snapshot(self._session, vm_ref) self._shutdown(instance, vm_ref) # Destroy VDIs - vdi_refs = VMHelper.lookup_vm_vdis(self._session, vm_ref) + vdi_refs = vm_utils.lookup_vm_vdis(self._session, vm_ref) self._safe_destroy_vdis(vdi_refs) if destroy_kernel_ramdisk: @@ -1155,7 +1154,7 @@ class VMOps(object): - spawn a rescue VM (the vm name-label will be instance-N-rescue). """ - rescue_vm_ref = VMHelper.lookup(self._session, + rescue_vm_ref = vm_utils.lookup(self._session, "%s-rescue" % instance.name) if rescue_vm_ref: raise RuntimeError(_("Instance is already in Rescue Mode: %s") @@ -1167,10 +1166,10 @@ class VMOps(object): instance._rescue = True self.spawn(context, instance, image_meta, network_info) # instance.name now has -rescue appended because of magic - rescue_vm_ref = VMHelper.lookup(self._session, instance.name) + rescue_vm_ref = vm_utils.lookup(self._session, instance.name) vdi_ref = self._find_root_vdi_ref(vm_ref) - rescue_vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref, + rescue_vbd_ref = vm_utils.create_vbd(self._session, rescue_vm_ref, vdi_ref, DEVICE_RESCUE, bootable=False) self._session.call_xenapi('VBD.plug', rescue_vbd_ref) @@ -1183,7 +1182,7 @@ class VMOps(object): - release the bootlock to allow the instance VM to start. """ - rescue_vm_ref = VMHelper.lookup(self._session, + rescue_vm_ref = vm_utils.lookup(self._session, "%s-rescue" % instance.name) if not rescue_vm_ref: raise exception.InstanceNotInRescueMode(instance_id=instance.uuid) @@ -1264,14 +1263,14 @@ class VMOps(object): for instance in self.list_instances(): if instance.endswith("-rescue"): rescue_vms.append(dict(name=instance, - vm_ref=VMHelper.lookup(self._session, + vm_ref=vm_utils.lookup(self._session, instance))) for vm in rescue_vms: rescue_vm_ref = vm["vm_ref"] original_name = vm["name"].split("-rescue", 1)[0] - original_vm_ref = VMHelper.lookup(self._session, original_name) + original_vm_ref = vm_utils.lookup(self._session, original_name) self._destroy_rescue_instance(rescue_vm_ref, original_vm_ref) @@ -1283,19 +1282,19 @@ class VMOps(object): """Return data about VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) vm_rec = self._session.call_xenapi("VM.get_record", vm_ref) - return VMHelper.compile_info(vm_rec) + return vm_utils.compile_info(vm_rec) def get_diagnostics(self, instance): """Return data about VM diagnostics.""" vm_ref = self._get_vm_opaque_ref(instance) vm_rec = self._session.call_xenapi("VM.get_record", vm_ref) - return VMHelper.compile_diagnostics(vm_rec) + return vm_utils.compile_diagnostics(vm_rec) def get_all_bw_usage(self, start_time, stop_time=None): """Return bandwidth usage info for each interface on each running VM""" try: - metrics = VMHelper.compile_metrics(start_time, stop_time) + metrics = vm_utils.compile_metrics(start_time, stop_time) except exception.CouldNotFetchMetrics: LOG.exception(_("Could not get bandwidth info.")) return {} @@ -1405,7 +1404,7 @@ class VMOps(object): Generate the network info and make calls to place it into the xenstore and the xenstore param list. vm_ref can be passed in because it will sometimes be different than - what VMHelper.lookup(session, instance.name) will find (ex: rescue) + what vm_utils.lookup(session, instance.name) will find (ex: rescue) """ vm_ref = vm_ref or self._get_vm_opaque_ref(instance) LOG.debug(_("Injecting network info to xenstore"), instance=instance) diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index 1bf62bfa7..fa8539e70 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -49,8 +49,8 @@ class VolumeOps(object): label = 'vol-' + hex(volume['id'])[:-1] # size presented to xenapi is in bytes, while euca api is in GB vdi_size = volume['size'] * 1024 * 1024 * 1024 - vdi_ref = vm_utils.VMHelper.create_vdi(self._session, - sr_ref, label, vdi_size, False) + vdi_ref = vm_utils.create_vdi(self._session, sr_ref, label, + vdi_size, False) vdi_rec = self._session.call_xenapi("VDI.get_record", vdi_ref) sm_vol_rec['vdi_uuid'] = vdi_rec['uuid'] return sm_vol_rec @@ -60,7 +60,7 @@ class VolumeOps(object): if vdi_ref is None: raise exception.NovaException(_('Could not find VDI ref')) - vm_utils.VMHelper.destroy_vdi(self._session, vdi_ref) + vm_utils.destroy_vdi(self._session, vdi_ref) def create_sr(self, label, params): LOG.debug(_("Creating SR %s") % label) @@ -111,7 +111,7 @@ class VolumeOps(object): def attach_volume(self, connection_info, instance_name, mountpoint): """Attach volume storage to VM instance""" # Before we start, check that the VM exists - vm_ref = vm_utils.VMHelper.lookup(self._session, instance_name) + vm_ref = vm_utils.lookup(self._session, instance_name) if vm_ref is None: raise exception.InstanceNotFound(instance_id=instance_name) # NOTE: No Resource Pool concept so far @@ -177,9 +177,8 @@ class VolumeOps(object): dev_number = volume_utils.VolumeHelper.mountpoint_to_number(mountpoint) try: - vbd_ref = vm_utils.VMHelper.create_vbd(self._session, vm_ref, - vdi_ref, dev_number, - bootable=False) + vbd_ref = vm_utils.create_vbd(self._session, vm_ref, vdi_ref, + dev_number, bootable=False) except self.XenAPI.Failure, exc: LOG.exception(exc) self.forget_sr(uuid) @@ -200,7 +199,7 @@ class VolumeOps(object): def detach_volume(self, connection_info, instance_name, mountpoint): """Detach volume storage to VM instance""" # Before we start, check that the VM exists - vm_ref = vm_utils.VMHelper.lookup(self._session, instance_name) + vm_ref = vm_utils.lookup(self._session, instance_name) if vm_ref is None: raise exception.InstanceNotFound(instance_id=instance_name) # Detach VBD from VM @@ -209,8 +208,8 @@ class VolumeOps(object): device_number = volume_utils.VolumeHelper.mountpoint_to_number( mountpoint) try: - vbd_ref = vm_utils.VMHelper.find_vbd_by_number(self._session, - vm_ref, device_number) + vbd_ref = vm_utils.find_vbd_by_number(self._session, vm_ref, + device_number) except volume_utils.StorageError, exc: LOG.exception(exc) raise Exception(_('Unable to locate volume %s') % mountpoint) @@ -218,12 +217,12 @@ class VolumeOps(object): try: sr_ref = volume_utils.VolumeHelper.find_sr_from_vbd(self._session, vbd_ref) - vm_utils.VMHelper.unplug_vbd(self._session, vbd_ref) + vm_utils.unplug_vbd(self._session, vbd_ref) except volume_utils.StorageError, exc: LOG.exception(exc) raise Exception(_('Unable to detach volume %s') % mountpoint) try: - vm_utils.VMHelper.destroy_vbd(self._session, vbd_ref) + vm_utils.destroy_vbd(self._session, vbd_ref) except volume_utils.StorageError, exc: LOG.exception(exc) raise Exception(_('Unable to destroy vbd %s') % mountpoint) -- cgit From 791ea7e3b7b4155257b588a2827cdd2c86735c1b Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 31 May 2012 21:36:15 +0000 Subject: Remove volume_util.VolumeHelper class The XenAPI driver has the concept of helper classes. All methods have been classmethods and the classes themselves are never instantiated. As a result, they only add an extra namespace lookup and provide no value. Change-Id: I26d1adfd378adc0728388185ba81fc4c71a41bdc --- nova/tests/test_xenapi.py | 5 +- nova/virt/xenapi/vmops.py | 1 - nova/virt/xenapi/volume_utils.py | 549 +++++++++++++++++++-------------------- nova/virt/xenapi/volumeops.py | 36 ++- 4 files changed, 288 insertions(+), 303 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 072be5b19..d30ad6420 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -157,8 +157,7 @@ class XenAPIVolumeTestCase(test.TestCase): } for (input, expected) in cases.iteritems(): - func = volume_utils.VolumeHelper.mountpoint_to_number - actual = func(input) + actual = volume_utils.mountpoint_to_number(input) self.assertEqual(actual, expected, '%s yielded %s, not %s' % (input, actual, expected)) @@ -169,7 +168,7 @@ class XenAPIVolumeTestCase(test.TestCase): vol = self._create_volume() # oops, wrong mount point! self.assertRaises(volume_utils.StorageError, - volume_utils.VolumeHelper.parse_volume_info, + volume_utils.parse_volume_info, self._make_info(), 'dev/sd' ) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8692ba574..031668d29 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -49,7 +49,6 @@ from nova.virt.xenapi import vm_utils from nova.virt.xenapi import volume_utils -VolumeHelper = volume_utils.VolumeHelper NetworkHelper = network_utils.NetworkHelper LOG = logging.getLogger(__name__) diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index a5c1d572c..eadc6cf5c 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -41,305 +41,298 @@ class StorageError(Exception): super(StorageError, self).__init__(message) -class VolumeHelper(xenapi.HelperBase): - """ - The class that wraps the helper methods together. - """ +def create_sr(session, label, params): + LOG.debug(_("creating sr within volume_utils")) + type = params['sr_type'] + del params['sr_type'] + LOG.debug(_('type is = %s') % type) + if 'name_description' in params: + desc = params['name_description'] + LOG.debug(_('name = %s') % desc) + del params['name_description'] + else: + desc = '' + if 'id' in params: + del params['id'] + LOG.debug(params) + + try: + sr_ref = session.call_xenapi("SR.create", + session.get_xenapi_host(), + params, + '0', label, desc, type, '', False, {}) + LOG.debug(_('Created %(label)s as %(sr_ref)s.') % locals()) + return sr_ref - @classmethod - def create_sr(cls, session, label, params): - - LOG.debug(_("creating sr within volume_utils")) - type = params['sr_type'] - del params['sr_type'] - LOG.debug(_('type is = %s') % type) - if 'name_description' in params: - desc = params['name_description'] - LOG.debug(_('name = %s') % desc) - del params['name_description'] - else: - desc = '' - if 'id' in params: - del params['id'] - LOG.debug(params) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to create Storage Repository')) - try: - sr_ref = session.call_xenapi("SR.create", - session.get_xenapi_host(), - params, - '0', label, desc, type, '', False, {}) - LOG.debug(_('Created %(label)s as %(sr_ref)s.') % locals()) - return sr_ref - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to create Storage Repository')) +def introduce_sr(session, sr_uuid, label, params): + LOG.debug(_("introducing sr within volume_utils")) + type = params['sr_type'] + del params['sr_type'] + LOG.debug(_('type is = %s') % type) + if 'name_description' in params: + desc = params['name_description'] + LOG.debug(_('name = %s') % desc) + del params['name_description'] + else: + desc = '' + if 'id' in params: + del params['id'] + LOG.debug(params) + + try: + sr_ref = session.call_xenapi("SR.introduce", + sr_uuid, + label, + desc, + type, + '', + False, + params,) + LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals()) + + #Create pbd + LOG.debug(_('Creating pbd for SR')) + pbd_ref = create_pbd(session, sr_ref, params) + LOG.debug(_('Plugging SR')) + #Plug pbd + session.call_xenapi("PBD.plug", pbd_ref) + session.call_xenapi("SR.scan", sr_ref) + return sr_ref - @classmethod - def introduce_sr(cls, session, sr_uuid, label, params): - LOG.debug(_("introducing sr within volume_utils")) - type = params['sr_type'] - del params['sr_type'] - LOG.debug(_('type is = %s') % type) - if 'name_description' in params: - desc = params['name_description'] - LOG.debug(_('name = %s') % desc) - del params['name_description'] - else: - desc = '' - if 'id' in params: - del params['id'] - LOG.debug(params) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to introduce Storage Repository')) - try: - sr_ref = session.call_xenapi("SR.introduce", - sr_uuid, - label, - desc, - type, - '', - False, - params,) - LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals()) - #Create pbd - LOG.debug(_('Creating pbd for SR')) - pbd_ref = cls.create_pbd(session, sr_ref, params) - LOG.debug(_('Plugging SR')) - #Plug pbd - session.call_xenapi("PBD.plug", pbd_ref) - session.call_xenapi("SR.scan", sr_ref) - return sr_ref +def forget_sr(session, sr_uuid): + """ + Forgets the storage repository without destroying the VDIs within + """ + try: + sr_ref = session.call_xenapi("SR.get_by_uuid", sr_uuid) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to get SR using uuid')) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to introduce Storage Repository')) + LOG.debug(_('Forgetting SR %s...') % sr_ref) - @classmethod - def forget_sr(cls, session, sr_uuid): - """ - Forgets the storage repository without destroying the VDIs within - """ - try: - sr_ref = session.call_xenapi("SR.get_by_uuid", sr_uuid) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to get SR using uuid')) + try: + unplug_pbds(session, sr_ref) + sr_ref = session.call_xenapi("SR.forget", sr_ref) - LOG.debug(_('Forgetting SR %s...') % sr_ref) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to forget Storage Repository')) - try: - cls.unplug_pbds(session, sr_ref) - sr_ref = session.call_xenapi("SR.forget", sr_ref) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to forget Storage Repository')) - - @classmethod - def find_sr_by_uuid(cls, session, sr_uuid): - """ - Return the storage repository given a uuid. - """ - for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): - if sr_rec['uuid'] == sr_uuid: - return sr_ref - return None - - @classmethod - def create_iscsi_storage(cls, session, info, label, description): - """ - Create an iSCSI storage repository that will be used to mount - the volume for the specified instance - """ - sr_ref = session.call_xenapi("SR.get_by_name_label", label) - if len(sr_ref) == 0: - LOG.debug(_('Introducing %s...'), label) - record = {} - if 'chapuser' in info and 'chappassword' in info: - record = {'target': info['targetHost'], - 'port': info['targetPort'], - 'targetIQN': info['targetIQN'], - 'chapuser': info['chapuser'], - 'chappassword': info['chappassword']} - else: - record = {'target': info['targetHost'], - 'port': info['targetPort'], - 'targetIQN': info['targetIQN']} - try: - LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals()) - return sr_ref - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to create Storage Repository')) - else: - return sr_ref[0] - - @classmethod - def find_sr_from_vbd(cls, session, vbd_ref): - """Find the SR reference from the VBD reference""" - try: - vdi_ref = session.call_xenapi("VBD.get_VDI", vbd_ref) - sr_ref = session.call_xenapi("VDI.get_SR", vdi_ref) - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to find SR from VBD %s') % vbd_ref) - return sr_ref +def find_sr_by_uuid(session, sr_uuid): + """ + Return the storage repository given a uuid. + """ + for sr_ref, sr_rec in session.get_all_refs_and_recs('SR'): + if sr_rec['uuid'] == sr_uuid: + return sr_ref + return None - @classmethod - def create_pbd(cls, session, sr_ref, params): - pbd_rec = {} - pbd_rec['host'] = session.get_xenapi_host() - pbd_rec['SR'] = sr_ref - pbd_rec['device_config'] = params - pbd_ref = session.call_xenapi("PBD.create", pbd_rec) - return pbd_ref - - @classmethod - def unplug_pbds(cls, session, sr_ref): - pbds = [] - try: - pbds = session.call_xenapi("SR.get_PBDs", sr_ref) - except session.XenAPI.Failure, exc: - LOG.warn(_('Ignoring exception %(exc)s when getting PBDs' - ' for %(sr_ref)s') % locals()) - for pbd in pbds: - try: - session.call_xenapi("PBD.unplug", pbd) - except session.XenAPI.Failure, exc: - LOG.warn(_('Ignoring exception %(exc)s when unplugging' - ' PBD %(pbd)s') % locals()) - - @classmethod - def introduce_vdi(cls, session, sr_ref, vdi_uuid=None, target_lun=None): - """Introduce VDI in the host""" - try: - session.call_xenapi("SR.scan", sr_ref) - if vdi_uuid: - LOG.debug("vdi_uuid: %s" % vdi_uuid) - vdi_ref = session.call_xenapi("VDI.get_by_uuid", vdi_uuid) - elif target_lun: - vdi_refs = session.call_xenapi("SR.get_VDIs", sr_ref) - for curr_ref in vdi_refs: - curr_rec = session.call_xenapi("VDI.get_record", curr_ref) - if ('sm_config' in curr_rec and - 'LUNid' in curr_rec['sm_config'] and - curr_rec['sm_config']['LUNid'] == str(target_lun)): - vdi_ref = curr_ref - break - else: - vdi_ref = (session.call_xenapi("SR.get_VDIs", sr_ref))[0] - except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to introduce VDI on SR %s') % sr_ref) +def create_iscsi_storage(session, info, label, description): + """ + Create an iSCSI storage repository that will be used to mount + the volume for the specified instance + """ + sr_ref = session.call_xenapi("SR.get_by_name_label", label) + if len(sr_ref) == 0: + LOG.debug(_('Introducing %s...'), label) + record = {} + if 'chapuser' in info and 'chappassword' in info: + record = {'target': info['targetHost'], + 'port': info['targetPort'], + 'targetIQN': info['targetIQN'], + 'chapuser': info['chapuser'], + 'chappassword': info['chappassword']} + else: + record = {'target': info['targetHost'], + 'port': info['targetPort'], + 'targetIQN': info['targetIQN']} try: - vdi_rec = session.call_xenapi("VDI.get_record", vdi_ref) - LOG.debug(vdi_rec) - LOG.debug(type(vdi_rec)) + LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals()) + return sr_ref except session.XenAPI.Failure, exc: LOG.exception(exc) - raise StorageError(_('Unable to get record' - ' of VDI %s on') % vdi_ref) - - if vdi_rec['managed']: - # We do not need to introduce the vdi - return vdi_ref - + raise StorageError(_('Unable to create Storage Repository')) + else: + return sr_ref[0] + + +def find_sr_from_vbd(session, vbd_ref): + """Find the SR reference from the VBD reference""" + try: + vdi_ref = session.call_xenapi("VBD.get_VDI", vbd_ref) + sr_ref = session.call_xenapi("VDI.get_SR", vdi_ref) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to find SR from VBD %s') % vbd_ref) + return sr_ref + + +def create_pbd(session, sr_ref, params): + pbd_rec = {} + pbd_rec['host'] = session.get_xenapi_host() + pbd_rec['SR'] = sr_ref + pbd_rec['device_config'] = params + pbd_ref = session.call_xenapi("PBD.create", pbd_rec) + return pbd_ref + + +def unplug_pbds(session, sr_ref): + pbds = [] + try: + pbds = session.call_xenapi("SR.get_PBDs", sr_ref) + except session.XenAPI.Failure, exc: + LOG.warn(_('Ignoring exception %(exc)s when getting PBDs' + ' for %(sr_ref)s') % locals()) + for pbd in pbds: try: - return session.call_xenapi("VDI.introduce", - vdi_rec['uuid'], - vdi_rec['name_label'], - vdi_rec['name_description'], - vdi_rec['SR'], - vdi_rec['type'], - vdi_rec['sharable'], - vdi_rec['read_only'], - vdi_rec['other_config'], - vdi_rec['location'], - vdi_rec['xenstore_data'], - vdi_rec['sm_config']) + session.call_xenapi("PBD.unplug", pbd) except session.XenAPI.Failure, exc: - LOG.exception(exc) - raise StorageError(_('Unable to introduce VDI for SR %s') - % sr_ref) - - @classmethod - def purge_sr(cls, session, sr_ref): - try: - sr_rec = session.call_xenapi("SR.get_record", sr_ref) + LOG.warn(_('Ignoring exception %(exc)s when unplugging' + ' PBD %(pbd)s') % locals()) + + +def introduce_vdi(session, sr_ref, vdi_uuid=None, target_lun=None): + """Introduce VDI in the host""" + try: + session.call_xenapi("SR.scan", sr_ref) + if vdi_uuid: + LOG.debug("vdi_uuid: %s" % vdi_uuid) + vdi_ref = session.call_xenapi("VDI.get_by_uuid", vdi_uuid) + elif target_lun: vdi_refs = session.call_xenapi("SR.get_VDIs", sr_ref) + for curr_ref in vdi_refs: + curr_rec = session.call_xenapi("VDI.get_record", curr_ref) + if ('sm_config' in curr_rec and + 'LUNid' in curr_rec['sm_config'] and + curr_rec['sm_config']['LUNid'] == str(target_lun)): + vdi_ref = curr_ref + break + else: + vdi_ref = (session.call_xenapi("SR.get_VDIs", sr_ref))[0] + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to introduce VDI on SR %s') % sr_ref) + + try: + vdi_rec = session.call_xenapi("VDI.get_record", vdi_ref) + LOG.debug(vdi_rec) + LOG.debug(type(vdi_rec)) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to get record' + ' of VDI %s on') % vdi_ref) + + if vdi_rec['managed']: + # We do not need to introduce the vdi + return vdi_ref + + try: + return session.call_xenapi("VDI.introduce", + vdi_rec['uuid'], + vdi_rec['name_label'], + vdi_rec['name_description'], + vdi_rec['SR'], + vdi_rec['type'], + vdi_rec['sharable'], + vdi_rec['read_only'], + vdi_rec['other_config'], + vdi_rec['location'], + vdi_rec['xenstore_data'], + vdi_rec['sm_config']) + except session.XenAPI.Failure, exc: + LOG.exception(exc) + raise StorageError(_('Unable to introduce VDI for SR %s') + % sr_ref) + + +def purge_sr(session, sr_ref): + try: + sr_rec = session.call_xenapi("SR.get_record", sr_ref) + vdi_refs = session.call_xenapi("SR.get_VDIs", sr_ref) + except StorageError, ex: + LOG.exception(ex) + raise StorageError(_('Error finding vdis in SR %s') % sr_ref) + + for vdi_ref in vdi_refs: + try: + vbd_refs = session.call_xenapi("VDI.get_VBDs", vdi_ref) except StorageError, ex: LOG.exception(ex) - raise StorageError(_('Error finding vdis in SR %s') % sr_ref) - - for vdi_ref in vdi_refs: - try: - vbd_refs = session.call_xenapi("VDI.get_VBDs", vdi_ref) - except StorageError, ex: - LOG.exception(ex) - raise StorageError(_('Unable to find vbd for vdi %s') % - vdi_ref) - if len(vbd_refs) > 0: - return - - cls.forget_sr(session, sr_rec['uuid']) - - @classmethod - def parse_volume_info(cls, connection_info, mountpoint): - """ - Parse device_path and mountpoint as they can be used by XenAPI. - In particular, the mountpoint (e.g. /dev/sdc) must be translated - into a numeric literal. - FIXME(armando): - As for device_path, currently cannot be used as it is, - because it does not contain target information. As for interim - solution, target details are passed either via Flags or obtained - by iscsiadm. Long-term solution is to add a few more fields to the - db in the iscsi_target table with the necessary info and modify - the iscsi driver to set them. - """ - device_number = VolumeHelper.mountpoint_to_number(mountpoint) - data = connection_info['data'] - volume_id = data['volume_id'] - target_portal = data['target_portal'] - target_host = _get_target_host(target_portal) - target_port = _get_target_port(target_portal) - target_iqn = data['target_iqn'] - LOG.debug('(vol_id,number,host,port,iqn): (%s,%s,%s,%s)', - volume_id, target_host, target_port, target_iqn) - if (device_number < 0 or - volume_id is None or - target_host is None or - target_iqn is None): - raise StorageError(_('Unable to obtain target information' - ' %(data)s, %(mountpoint)s') % locals()) - volume_info = {} - volume_info['id'] = volume_id - volume_info['target'] = target_host - volume_info['port'] = target_port - volume_info['targetIQN'] = target_iqn - if ('auth_method' in connection_info and - connection_info['auth_method'] == 'CHAP'): - volume_info['chapuser'] = connection_info['auth_username'] - volume_info['chappassword'] = connection_info['auth_password'] - - return volume_info - - @classmethod - def mountpoint_to_number(cls, mountpoint): - """Translate a mountpoint like /dev/sdc into a numeric""" - if mountpoint.startswith('/dev/'): - mountpoint = mountpoint[5:] - if re.match('^[hs]d[a-p]$', mountpoint): - return (ord(mountpoint[2:3]) - ord('a')) - elif re.match('^x?vd[a-p]$', mountpoint): - return (ord(mountpoint[-1]) - ord('a')) - elif re.match('^[0-9]+$', mountpoint): - return string.atoi(mountpoint, 10) - else: - LOG.warn(_('Mountpoint cannot be translated: %s'), mountpoint) - return -1 + raise StorageError(_('Unable to find vbd for vdi %s') % + vdi_ref) + if len(vbd_refs) > 0: + return + + forget_sr(session, sr_rec['uuid']) + + +def parse_volume_info(connection_info, mountpoint): + """ + Parse device_path and mountpoint as they can be used by XenAPI. + In particular, the mountpoint (e.g. /dev/sdc) must be translated + into a numeric literal. + FIXME(armando): + As for device_path, currently cannot be used as it is, + because it does not contain target information. As for interim + solution, target details are passed either via Flags or obtained + by iscsiadm. Long-term solution is to add a few more fields to the + db in the iscsi_target table with the necessary info and modify + the iscsi driver to set them. + """ + device_number = mountpoint_to_number(mountpoint) + data = connection_info['data'] + volume_id = data['volume_id'] + target_portal = data['target_portal'] + target_host = _get_target_host(target_portal) + target_port = _get_target_port(target_portal) + target_iqn = data['target_iqn'] + LOG.debug('(vol_id,number,host,port,iqn): (%s,%s,%s,%s)', + volume_id, target_host, target_port, target_iqn) + if (device_number < 0 or + volume_id is None or + target_host is None or + target_iqn is None): + raise StorageError(_('Unable to obtain target information' + ' %(data)s, %(mountpoint)s') % locals()) + volume_info = {} + volume_info['id'] = volume_id + volume_info['target'] = target_host + volume_info['port'] = target_port + volume_info['targetIQN'] = target_iqn + if ('auth_method' in connection_info and + connection_info['auth_method'] == 'CHAP'): + volume_info['chapuser'] = connection_info['auth_username'] + volume_info['chappassword'] = connection_info['auth_password'] + + return volume_info + + +def mountpoint_to_number(mountpoint): + """Translate a mountpoint like /dev/sdc into a numeric""" + if mountpoint.startswith('/dev/'): + mountpoint = mountpoint[5:] + if re.match('^[hs]d[a-p]$', mountpoint): + return (ord(mountpoint[2:3]) - ord('a')) + elif re.match('^x?vd[a-p]$', mountpoint): + return (ord(mountpoint[-1]) - ord('a')) + elif re.match('^[0-9]+$', mountpoint): + return string.atoi(mountpoint, 10) + else: + LOG.warn(_('Mountpoint cannot be translated: %s'), mountpoint) + return -1 def _get_volume_id(path_or_id): diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index fa8539e70..bab0b4d2c 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -64,8 +64,7 @@ class VolumeOps(object): def create_sr(self, label, params): LOG.debug(_("Creating SR %s") % label) - sr_ref = volume_utils.VolumeHelper.create_sr(self._session, - label, params) + sr_ref = volume_utils.create_sr(self._session, label, params) if sr_ref is None: raise exception.NovaException(_('Could not create SR')) sr_rec = self._session.call_xenapi("SR.get_record", sr_ref) @@ -76,34 +75,31 @@ class VolumeOps(object): # Checks if sr has already been introduced to this host def introduce_sr(self, sr_uuid, label, params): LOG.debug(_("Introducing SR %s") % label) - sr_ref = volume_utils.VolumeHelper.find_sr_by_uuid(self._session, - sr_uuid) + sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid) if sr_ref: LOG.debug(_('SR found in xapi database. No need to introduce')) return sr_ref - sr_ref = volume_utils.VolumeHelper.introduce_sr(self._session, - sr_uuid, label, params) + sr_ref = volume_utils.introduce_sr(self._session, sr_uuid, label, + params) if sr_ref is None: raise exception.NovaException(_('Could not introduce SR')) return sr_ref def is_sr_on_host(self, sr_uuid): LOG.debug(_('Checking for SR %s') % sr_uuid) - sr_ref = volume_utils.VolumeHelper.find_sr_by_uuid(self._session, - sr_uuid) + sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid) if sr_ref: return True return False # Checks if sr has been introduced def forget_sr(self, sr_uuid): - sr_ref = volume_utils.VolumeHelper.find_sr_by_uuid(self._session, - sr_uuid) + sr_ref = volume_utils.find_sr_by_uuid(self._session, sr_uuid) if sr_ref is None: LOG.INFO(_('SR %s not found in the xapi database') % sr_uuid) return try: - volume_utils.VolumeHelper.forget_sr(self._session, sr_uuid) + volume_utils.forget_sr(self._session, sr_uuid) except volume_utils.StorageError, exc: LOG.exception(exc) raise exception.NovaException(_('Could not forget SR')) @@ -136,8 +132,8 @@ class VolumeOps(object): LOG.debug(connection_info) sr_params = {} if u'sr_uuid' not in data: - sr_params = volume_utils.VolumeHelper.parse_volume_info( - connection_info, mountpoint) + sr_params = volume_utils.parse_volume_info(connection_info, + mountpoint) uuid = "FA15E-D15C-" + str(sr_params['id']) sr_params['sr_type'] = 'iscsi' else: @@ -167,15 +163,15 @@ class VolumeOps(object): # Introduce VDI and attach VBD to VM try: - vdi_ref = volume_utils.VolumeHelper.introduce_vdi(self._session, - sr_ref, vdi_uuid, target_lun) + vdi_ref = volume_utils.introduce_vdi(self._session, sr_ref, + vdi_uuid, target_lun) except volume_utils.StorageError, exc: LOG.exception(exc) self.forget_sr(uuid) raise Exception(_('Unable to create VDI on SR %(sr_ref)s for' ' instance %(instance_name)s') % locals()) - dev_number = volume_utils.VolumeHelper.mountpoint_to_number(mountpoint) + dev_number = volume_utils.mountpoint_to_number(mountpoint) try: vbd_ref = vm_utils.create_vbd(self._session, vm_ref, vdi_ref, dev_number, bootable=False) @@ -205,8 +201,7 @@ class VolumeOps(object): # Detach VBD from VM LOG.debug(_("Detach_volume: %(instance_name)s, %(mountpoint)s") % locals()) - device_number = volume_utils.VolumeHelper.mountpoint_to_number( - mountpoint) + device_number = volume_utils.mountpoint_to_number(mountpoint) try: vbd_ref = vm_utils.find_vbd_by_number(self._session, vm_ref, device_number) @@ -215,8 +210,7 @@ class VolumeOps(object): raise Exception(_('Unable to locate volume %s') % mountpoint) try: - sr_ref = volume_utils.VolumeHelper.find_sr_from_vbd(self._session, - vbd_ref) + sr_ref = volume_utils.find_sr_from_vbd(self._session, vbd_ref) vm_utils.unplug_vbd(self._session, vbd_ref) except volume_utils.StorageError, exc: LOG.exception(exc) @@ -229,7 +223,7 @@ class VolumeOps(object): # Forget SR only if no other volumes on this host are using it try: - volume_utils.VolumeHelper.purge_sr(self._session, sr_ref) + volume_utils.purge_sr(self._session, sr_ref) except volume_utils.StorageError, exc: LOG.exception(exc) raise Exception(_('Error purging SR %s') % sr_ref) -- cgit