diff options
| author | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2012-07-25 15:31:11 -0500 |
|---|---|---|
| committer | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2012-07-25 15:31:34 -0500 |
| commit | 82afe7ad5eac668aaefc79e16bbf2226eddde97d (patch) | |
| tree | 2f6fbe30e9e5e1b1246c64027be1a4b0415cb6c7 /nova | |
| parent | 9a40e9e9a4538f6ba87451137bf0d6d2598f2319 (diff) | |
| download | nova-82afe7ad5eac668aaefc79e16bbf2226eddde97d.tar.gz nova-82afe7ad5eac668aaefc79e16bbf2226eddde97d.tar.xz nova-82afe7ad5eac668aaefc79e16bbf2226eddde97d.zip | |
Inject instance metadata into xenstore
When using Xenserver, inject instance metadata into the xenstore, for
the use of the instance. Implements blueprint xenstore-metadata.
Change-Id: I88a59f1b783eaaaef6ba5efd8bd448aece2f869c
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/compute/api.py | 10 | ||||
| -rw-r--r-- | nova/compute/manager.py | 12 | ||||
| -rw-r--r-- | nova/compute/rpcapi.py | 7 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 1 | ||||
| -rw-r--r-- | nova/tests/api/openstack/compute/test_server_metadata.py | 8 | ||||
| -rw-r--r-- | nova/tests/compute/test_compute.py | 15 | ||||
| -rw-r--r-- | nova/tests/compute/test_rpcapi.py | 5 | ||||
| -rw-r--r-- | nova/tests/test_utils.py | 32 | ||||
| -rw-r--r-- | nova/tests/test_xenapi.py | 217 | ||||
| -rw-r--r-- | nova/utils.py | 18 | ||||
| -rw-r--r-- | nova/virt/driver.py | 11 | ||||
| -rw-r--r-- | nova/virt/xenapi/driver.py | 4 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 49 |
13 files changed, 386 insertions, 3 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index eb23a2412..e5b6fb1b3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1595,6 +1595,9 @@ class API(base.Base): def delete_instance_metadata(self, context, instance, key): """Delete the given metadata item from an instance.""" self.db.instance_metadata_delete(context, instance['uuid'], key) + self.compute_rpcapi.change_instance_metadata(context, + instance=instance, + diff={key: ['-']}) @wrap_check_policy def update_instance_metadata(self, context, instance, @@ -1605,15 +1608,20 @@ class API(base.Base): `metadata` argument will be deleted. """ + orig = self.get_instance_metadata(context, instance) if delete: _metadata = metadata else: - _metadata = self.get_instance_metadata(context, instance) + _metadata = orig.copy() _metadata.update(metadata) self._check_metadata_properties_quota(context, _metadata) self.db.instance_metadata_update(context, instance['uuid'], _metadata, True) + diff = utils.diff_dict(orig, _metadata) + self.compute_rpcapi.change_instance_metadata(context, + instance=instance, + diff=diff) return _metadata def get_instance_faults(self, context, instances): diff --git a/nova/compute/manager.py b/nova/compute/manager.py index ecebf2d9c..e19a4ec90 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -235,7 +235,7 @@ def _get_additional_capabilities(): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '1.2' + RPC_API_VERSION = '1.3' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -1327,6 +1327,16 @@ class ComputeManager(manager.SchedulerDependentManager): @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @checks_instance_lock @wrap_instance_fault + def change_instance_metadata(self, context, instance_uuid, diff): + """Update the metadata published to the instance.""" + instance_ref = self.db.instance_get_by_uuid(context, instance_uuid) + LOG.debug(_("Changing instance metadata according to %(diff)r") % + locals(), instance=instance_ref) + self.driver.change_instance_metadata(context, instance_ref, diff) + + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + @checks_instance_lock + @wrap_instance_fault def confirm_resize(self, context, instance_uuid, migration_id): """Destroys the source instance.""" migration_ref = self.db.migration_get(context, migration_id) diff --git a/nova/compute/rpcapi.py b/nova/compute/rpcapi.py index 332b8153e..e637c9f64 100644 --- a/nova/compute/rpcapi.py +++ b/nova/compute/rpcapi.py @@ -57,6 +57,7 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.0 - Initial version. 1.1 - Adds get_host_uptime() 1.2 - Adds check_can_live_migrate_[destination|source] + 1.3 - Adds change_instance_metadata() ''' BASE_RPC_API_VERSION = '1.0' @@ -375,6 +376,12 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): instance_uuid=instance['uuid']), topic=_compute_topic(self.topic, ctxt, None, instance)) + def change_instance_metadata(self, ctxt, instance, diff): + self.cast(ctxt, self.make_msg('change_instance_metadata', + instance_uuid=instance['uuid'], diff=diff), + topic=_compute_topic(self.topic, ctxt, None, instance), + version='1.3') + class SecurityGroupAPI(nova.openstack.common.rpc.proxy.RpcProxy): '''Client side of the security group rpc API. diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7b258c42d..527c10a61 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1470,6 +1470,7 @@ def _build_instance_get(context, session=None): options(joinedload_all('security_groups.rules')).\ options(joinedload('info_cache')).\ options(joinedload('metadata')).\ + options(joinedload('system_metadata')).\ options(joinedload('instance_type')) diff --git a/nova/tests/api/openstack/compute/test_server_metadata.py b/nova/tests/api/openstack/compute/test_server_metadata.py index 37d54e09b..29eb60cad 100644 --- a/nova/tests/api/openstack/compute/test_server_metadata.py +++ b/nova/tests/api/openstack/compute/test_server_metadata.py @@ -18,6 +18,7 @@ import webob from nova.api.openstack.compute import server_metadata +from nova.compute import rpcapi as compute_rpcapi import nova.db from nova import exception from nova import flags @@ -85,6 +86,10 @@ def return_server_nonexistant(context, server_id): raise exception.InstanceNotFound() +def fake_change_instance_metadata(self, context, instance, diff): + pass + + class ServerMetaDataTest(test.TestCase): def setUp(self): @@ -97,6 +102,9 @@ class ServerMetaDataTest(test.TestCase): self.stubs.Set(nova.db, 'instance_metadata_get', return_server_metadata) + self.stubs.Set(compute_rpcapi.ComputeAPI, 'change_instance_metadata', + fake_change_instance_metadata) + self.controller = server_metadata.Controller() self.uuid = str(utils.gen_uuid()) self.url = '/v1.1/fake/servers/%s/metadata' % self.uuid diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 6dacd7459..f6bc0ee4f 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -3339,7 +3339,13 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(c, instance4['uuid']) def test_instance_metadata(self): - """Test searching instances by state""" + meta_changes = [None] + + def fake_change_instance_metadata(inst, ctxt, instance, diff): + meta_changes[0] = diff + self.stubs.Set(compute_rpcapi.ComputeAPI, 'change_instance_metadata', + fake_change_instance_metadata) + _context = context.get_admin_context() instance = self._create_fake_instance({'metadata': {'key1': 'value1'}}) @@ -3350,16 +3356,23 @@ class ComputeAPITestCase(BaseTestCase): {'key2': 'value2'}) metadata = self.compute_api.get_instance_metadata(_context, instance) self.assertEqual(metadata, {'key1': 'value1', 'key2': 'value2'}) + self.assertEqual(meta_changes, [{'key2': ['+', 'value2']}]) new_metadata = {'key2': 'bah', 'key3': 'value3'} self.compute_api.update_instance_metadata(_context, instance, new_metadata, delete=True) metadata = self.compute_api.get_instance_metadata(_context, instance) self.assertEqual(metadata, new_metadata) + self.assertEqual(meta_changes, [{ + 'key1': ['-'], + 'key2': ['+', 'bah'], + 'key3': ['+', 'value3'], + }]) self.compute_api.delete_instance_metadata(_context, instance, 'key2') metadata = self.compute_api.get_instance_metadata(_context, instance) self.assertEqual(metadata, {'key3': 'value3'}) + self.assertEqual(meta_changes, [{'key2': ['-']}]) db.instance_destroy(_context, instance['uuid']) diff --git a/nova/tests/compute/test_rpcapi.py b/nova/tests/compute/test_rpcapi.py index 40998b593..d1d633023 100644 --- a/nova/tests/compute/test_rpcapi.py +++ b/nova/tests/compute/test_rpcapi.py @@ -322,3 +322,8 @@ class ComputeRpcAPITestCase(test.TestCase): def test_unrescue_instance(self): self._test_compute_api('unrescue_instance', 'cast', instance=self.fake_instance) + + def test_change_instance_metadata(self): + self._test_compute_api('change_instance_metadata', 'cast', + instance=self.fake_instance, diff={}, + version='1.3') diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index 6506bde1c..24c2e04fb 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -884,3 +884,35 @@ class AuditPeriodTest(test.TestCase): day=1, month=6, year=2011)) + + +class DiffDict(test.TestCase): + """Unit tests for diff_dict()""" + + def test_no_change(self): + old = dict(a=1, b=2, c=3) + new = dict(a=1, b=2, c=3) + diff = utils.diff_dict(old, new) + + self.assertEqual(diff, {}) + + def test_new_key(self): + old = dict(a=1, b=2, c=3) + new = dict(a=1, b=2, c=3, d=4) + diff = utils.diff_dict(old, new) + + self.assertEqual(diff, dict(d=['+', 4])) + + def test_changed_key(self): + old = dict(a=1, b=2, c=3) + new = dict(a=1, b=4, c=3) + diff = utils.diff_dict(old, new) + + self.assertEqual(diff, dict(b=['+', 4])) + + def test_removed_key(self): + old = dict(a=1, b=2, c=3) + new = dict(a=1, c=3) + diff = utils.diff_dict(old, new) + + self.assertEqual(diff, dict(b=['-'])) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index dcb0bf4ed..df35d54ba 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -33,6 +33,7 @@ from nova import exception from nova import flags from nova.image import glance from nova.openstack.common import importutils +from nova.openstack.common import jsonutils from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import test @@ -284,6 +285,11 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): stubs.stubout_image_service_download(self.stubs) stubs.stubout_stream_disk(self.stubs) + def fake_inject_instance_metadata(self, instance, vm): + pass + self.stubs.Set(vmops.VMOps, 'inject_instance_metadata', + fake_inject_instance_metadata) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() fake_image.FakeImageService_reset() @@ -511,6 +517,12 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): hostname="test", architecture="x86-64", instance_id=1, check_injection=False, create_record=True, empty_dns=False): + # Fake out inject_instance_metadata + def fake_inject_instance_metadata(self, instance, vm): + pass + self.stubs.Set(vmops.VMOps, 'inject_instance_metadata', + fake_inject_instance_metadata) + if create_record: instance_values = {'id': instance_id, 'project_id': self.project_id, @@ -943,6 +955,11 @@ class XenAPIMigrateInstance(stubs.XenAPITestBase): stubs.stub_out_migration_methods(self.stubs) stubs.stubout_get_this_vm_uuid(self.stubs) + def fake_inject_instance_metadata(self, instance, vm): + pass + self.stubs.Set(vmops.VMOps, 'inject_instance_metadata', + fake_inject_instance_metadata) + def test_resize_xenserver_6(self): instance = db.instance_create(self.context, self.instance_values) called = {'resize': False} @@ -2186,3 +2203,203 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase): self.assertRaises(NotImplementedError, self.conn.live_migration, self.conn, None, None, None, recover_method) self.assertTrue(recover_method.called, "recover_method.called") + + +class XenAPIInjectMetadataTestCase(stubs.XenAPITestBase): + def setUp(self): + super(XenAPIInjectMetadataTestCase, self).setUp() + self.flags(xenapi_connection_url='test_url', + xenapi_connection_password='test_pass', + firewall_driver='nova.virt.xenapi.firewall.' + 'Dom0IptablesFirewallDriver') + stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) + self.conn = xenapi_conn.XenAPIDriver(False) + + self.xenstore = dict(persist={}, ephem={}) + + def fake_get_vm_opaque_ref(inst, instance): + self.assertEqual(instance, 'instance') + return 'vm_ref' + + def fake_add_to_param_xenstore(inst, vm_ref, key, val): + self.assertEqual(vm_ref, 'vm_ref') + self.xenstore['persist'][key] = val + + def fake_remove_from_param_xenstore(inst, vm_ref, key): + self.assertEqual(vm_ref, 'vm_ref') + if key in self.xenstore['persist']: + del self.xenstore['persist'][key] + + def fake_write_to_xenstore(inst, instance, path, value, vm_ref=None): + self.assertEqual(instance, 'instance') + self.assertEqual(vm_ref, 'vm_ref') + self.xenstore['ephem'][path] = jsonutils.dumps(value) + + def fake_delete_from_xenstore(inst, instance, path, vm_ref=None): + self.assertEqual(instance, 'instance') + self.assertEqual(vm_ref, 'vm_ref') + if path in self.xenstore['ephem']: + del self.xenstore['ephem'][path] + + self.stubs.Set(vmops.VMOps, '_get_vm_opaque_ref', + fake_get_vm_opaque_ref) + self.stubs.Set(vmops.VMOps, '_add_to_param_xenstore', + fake_add_to_param_xenstore) + self.stubs.Set(vmops.VMOps, '_remove_from_param_xenstore', + fake_remove_from_param_xenstore) + self.stubs.Set(vmops.VMOps, '_write_to_xenstore', + fake_write_to_xenstore) + self.stubs.Set(vmops.VMOps, '_delete_from_xenstore', + fake_delete_from_xenstore) + + def test_inject_instance_metadata(self): + class FakeMetaItem(object): + def __init__(self, key, value): + self.key = key + self.value = value + + instance = dict(metadata=[FakeMetaItem("a", 1), + FakeMetaItem("b", 2), + FakeMetaItem("c", 3)], + system_metadata=[FakeMetaItem("sys_a", 1), + FakeMetaItem("sys_b", 2), + FakeMetaItem("sys_c", 3)]) + self.conn._vmops.inject_instance_metadata(instance, 'vm_ref') + + self.assertEqual(self.xenstore, { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': {}, + }) + + def test_change_instance_metadata_add(self): + diff = dict(d=['+', 4]) + self.xenstore = { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + } + + self.conn._vmops.change_instance_metadata('instance', diff) + + self.assertEqual(self.xenstore, { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/user-metadata/d': '4', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/user-metadata/d': '4', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + }) + + def test_change_instance_metadata_update(self): + diff = dict(b=['+', 4]) + self.xenstore = { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + } + + self.conn._vmops.change_instance_metadata('instance', diff) + + self.assertEqual(self.xenstore, { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '4', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '4', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + }) + + def test_change_instance_metadata_delete(self): + diff = dict(b=['-']) + self.xenstore = { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/b': '2', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + } + + self.conn._vmops.change_instance_metadata('instance', diff) + + self.assertEqual(self.xenstore, { + 'persist': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + 'ephem': { + 'vm-data/user-metadata/a': '1', + 'vm-data/user-metadata/c': '3', + 'vm-data/system-metadata/sys_a': '1', + 'vm-data/system-metadata/sys_b': '2', + 'vm-data/system-metadata/sys_c': '3', + }, + }) diff --git a/nova/utils.py b/nova/utils.py index 9cfb6d06d..adb1cf45c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -861,6 +861,24 @@ def subset_dict(dict_, keys): return subset +def diff_dict(orig, new): + """ + Return a dict describing how to change orig to new. The keys + correspond to values that have changed; the value will be a list + of one or two elements. The first element of the list will be + either '+' or '-', indicating whether the key was updated or + deleted; if the key was updated, the list will contain a second + element, giving the updated value. + """ + # Figure out what keys went away + result = dict((k, ['-']) for k in set(orig.keys()) - set(new.keys())) + # Compute the updates + for key, value in new.items(): + if key not in orig or value != orig[key]: + result[key] = ['+', value] + return result + + def check_isinstance(obj, cls): """Checks that obj is of type cls, and lets PyLint infer types.""" if isinstance(obj, cls): diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 6c370ffbd..4e821eab2 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -528,6 +528,17 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() + def change_instance_metadata(self, context, instance, diff): + """ + Applies a diff to the instance metadata. + + This is an optional driver method which is used to publish + changes to the instance's metadata to the hypervisor. If the + hypervisor has no means of publishing the instance metadata to + the instance, then this method should not be implemented. + """ + pass + def agent_update(self, instance, url, md5hash): """ Update agent on the specified instance. diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py index 2a86277e5..9a81311cb 100644 --- a/nova/virt/xenapi/driver.py +++ b/nova/virt/xenapi/driver.py @@ -214,6 +214,10 @@ class XenAPIDriver(driver.ComputeDriver): """ self._vmops.inject_file(instance, b64_path, b64_contents) + def change_instance_metadata(self, context, instance, diff): + """Apply a diff to the instance metadata.""" + self._vmops.change_instance_metadata(instance, diff) + def destroy(self, instance, network_info, block_device_info=None): """Destroy VM instance""" self._vmops.destroy(instance, network_info, block_device_info) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 83970e861..5dcc53b94 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -415,6 +415,8 @@ class VMOps(object): hostname = self._generate_hostname(instance) self.inject_hostname(instance, vm_ref, hostname) + self.inject_instance_metadata(instance, vm_ref) + return vm_ref def _attach_disks(self, instance, disk_image_type, vm_ref, vdis): @@ -841,6 +843,44 @@ class VMOps(object): vm_ref = self._get_vm_opaque_ref(instance) agent.inject_file(self._session, instance, vm_ref, path, contents) + def inject_instance_metadata(self, instance, vm_ref): + """Inject instance metadata into xenstore.""" + def store_meta(topdir, data_list): + for item in data_list: + key = item.key + value = item.value or '' + self._add_to_param_xenstore(vm_ref, '%s/%s' % (topdir, key), + jsonutils.dumps(value)) + + # Store user metadata + store_meta('vm-data/user-metadata', instance['metadata']) + + # Store system metadata + store_meta('vm-data/system-metadata', instance['system_metadata']) + + def change_instance_metadata(self, instance, diff): + """Apply changes to instance metadata to xenstore.""" + vm_ref = self._get_vm_opaque_ref(instance) + for key, change in diff.items(): + location = 'vm-data/user-metadata/%s' % key + if change[0] == '-': + self._remove_from_param_xenstore(vm_ref, location) + try: + self._delete_from_xenstore(instance, location, + vm_ref=vm_ref) + except KeyError: + # catch KeyError for domid if instance isn't running + pass + elif change[0] == '+': + self._add_to_param_xenstore(vm_ref, location, + jsonutils.dumps(change[1])) + try: + self._write_to_xenstore(instance, location, change[1], + vm_ref=vm_ref) + except KeyError: + # catch KeyError for domid if instance isn't running + pass + def _find_root_vdi_ref(self, vm_ref): """Find and return the root vdi ref for a VM.""" if not vm_ref: @@ -1328,6 +1368,15 @@ class VMOps(object): vm_ref=vm_ref, path=path, value=jsonutils.dumps(value)) + def _delete_from_xenstore(self, instance, path, vm_ref=None): + """ + Deletes the value from the xenstore record for the given VM at + the specified location. A XenAPIPlugin.PluginError will be + raised if any error is encountered in the delete process. + """ + return self._make_plugin_call('xenstore.py', 'delete_record', instance, + vm_ref=vm_ref, path=path) + def _make_plugin_call(self, plugin, method, instance, vm_ref=None, **addl_args): """ |
