From 82afe7ad5eac668aaefc79e16bbf2226eddde97d Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Wed, 25 Jul 2012 15:31:11 -0500 Subject: 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 --- .../api/openstack/compute/test_server_metadata.py | 8 + nova/tests/compute/test_compute.py | 15 +- nova/tests/compute/test_rpcapi.py | 5 + nova/tests/test_utils.py | 32 +++ nova/tests/test_xenapi.py | 217 +++++++++++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) (limited to 'nova/tests') 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', + }, + }) -- cgit