From 18c61263a3c0344a79b3d86dcce9ac2a7824fca9 Mon Sep 17 00:00:00 2001 From: Kevin_Zheng Date: Tue, 25 Apr 2017 15:29:36 +0800 Subject: [PATCH] Support tag instances when boot(2/4) This is the second patch of the series, this patch adds tags to build_request object. Change-Id: I1121eaf24856dcb74f07c224531a821b3cdc46fc Part of blueprint support-tag-instance-when-boot --- nova/objects/build_request.py | 28 +++++++++++++++++-- .../tests/functional/db/test_build_request.py | 2 +- nova/tests/unit/fake_build_request.py | 6 ++++ nova/tests/unit/objects/test_build_request.py | 23 +++++++++++++++ nova/tests/unit/objects/test_objects.py | 2 +- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/nova/objects/build_request.py b/nova/objects/build_request.py index 86a4de1d0f5a..82c49c1741dd 100644 --- a/nova/objects/build_request.py +++ b/nova/objects/build_request.py @@ -36,7 +36,8 @@ class BuildRequest(base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added block_device_mappings # Version 1.2: Added save() method - VERSION = '1.2' + # Version 1.3: Added tags + VERSION = '1.3' fields = { 'id': fields.IntegerField(), @@ -49,6 +50,7 @@ class BuildRequest(base.NovaObject): # created_at/updated_at. There is no soft delete for this object. 'created_at': fields.DateTimeField(nullable=True), 'updated_at': fields.DateTimeField(nullable=True), + 'tags': fields.ObjectField('TagList'), } def obj_make_compatible(self, primitive, target_version): @@ -57,6 +59,8 @@ class BuildRequest(base.NovaObject): target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 1) and 'block_device_mappings' in primitive: del primitive['block_device_mappings'] + elif target_version < (1, 3) and 'tags' in primitive: + del primitive['tags'] def _load_instance(self, db_instance): # NOTE(alaski): Be very careful with instance loading because it @@ -104,7 +108,7 @@ class BuildRequest(base.NovaObject): # was never persisted. self.instance.created_at = self.created_at self.instance.updated_at = self.updated_at - self.instance.tags = objects.TagList([]) + self.instance.tags = self.tags def _load_block_device_mappings(self, db_bdms): # 'db_bdms' is a serialized BlockDeviceMappingList object. If it's None @@ -122,6 +126,22 @@ class BuildRequest(base.NovaObject): objects.BlockDeviceMappingList.obj_from_primitive( jsonutils.loads(db_bdms))) + def _load_tags(self, db_tags): + # 'db_tags' is a serialized TagList object. If it's None + # we're in a mixed version nova-api scenario and can't retrieve the + # actual list. Set it to an empty list here which will cause a + # temporary API inconsistency that will be resolved as soon as the + # instance is scheduled and on a compute. + if db_tags is None: + LOG.debug('Failed to load tags from BuildRequest ' + 'for instance %s because it is None', self.instance_uuid) + self.tags = objects.TagList() + return + + self.tags = ( + objects.TagList.obj_from_primitive( + jsonutils.loads(db_tags))) + @staticmethod def _from_db_object(context, req, db_req): # Set this up front so that it can be pulled for error messages or @@ -225,6 +245,8 @@ class BuildRequest(base.NovaObject): for field in self.instance.obj_fields: # NOTE(danms): Don't copy the defaulted tags field # as instance.create() won't handle it properly. + # TODO(zhengzhenyu): Handle this when the API supports creating + # servers with tags. if field == 'tags': continue if self.instance.obj_attr_is_set(field): @@ -344,6 +366,8 @@ class BuildRequestList(base.ObjectListBase, base.NovaObject): # Fortunately some filters do not apply here. # 'tags' can not be applied at boot time so will not be set for an # instance here. + # TODO(zhengzhenyu): Handle this when the API supports creating + # servers with tags. # 'changes-since' works off of the updated_at field which has not yet # been set at the point in the boot process where build_request still # exists. So it can be ignored. diff --git a/nova/tests/functional/db/test_build_request.py b/nova/tests/functional/db/test_build_request.py index c3a60f0e1bf9..b097f59bcd00 100644 --- a/nova/tests/functional/db/test_build_request.py +++ b/nova/tests/functional/db/test_build_request.py @@ -64,7 +64,7 @@ class BuildRequestTestCase(test.NoDBTestCase): if key == 'instance': objects.base.obj_equal_prims(expected, db_value) continue - elif key == 'block_device_mappings': + elif key in ('block_device_mappings', 'tags'): self.assertEqual(1, len(db_value)) # Can't compare list objects directly, just compare the single # item they contain. diff --git a/nova/tests/unit/fake_build_request.py b/nova/tests/unit/fake_build_request.py index 6bdf06c0d74b..955dbea3f2d3 100644 --- a/nova/tests/unit/fake_build_request.py +++ b/nova/tests/unit/fake_build_request.py @@ -40,6 +40,8 @@ def fake_db_req(**updates): snapshot_id=None, volume_id=None, volume_size=0, image_id='bar', no_device=False, connection_info=None, tag='', instance_uuid=uuids.instance))]) + tags = objects.TagList(objects=[objects.Tag(tag='tag1', + resource_id=instance_uuid)]) db_build_request = { 'id': 1, 'project_id': 'fake-project', @@ -47,6 +49,7 @@ def fake_db_req(**updates): 'instance': jsonutils.dumps(instance.obj_to_primitive()), 'block_device_mappings': jsonutils.dumps( block_devices.obj_to_primitive()), + 'tags': jsonutils.dumps(tags.obj_to_primitive()), 'created_at': datetime.datetime(2016, 1, 16), 'updated_at': datetime.datetime(2016, 1, 16), } @@ -85,6 +88,9 @@ def fake_req_obj(ctxt, db_req=None): req_obj.block_device_mappings = ( objects.BlockDeviceMappingList.obj_from_primitive( jsonutils.loads(value))) + elif field == 'tags': + req_obj.tags = objects.TagList.obj_from_primitive( + jsonutils.loads(value)) elif field == 'instance_metadata': setattr(req_obj, field, jsonutils.loads(value)) else: diff --git a/nova/tests/unit/objects/test_build_request.py b/nova/tests/unit/objects/test_build_request.py index 0812c98aee23..7f15e880747b 100644 --- a/nova/tests/unit/objects/test_build_request.py +++ b/nova/tests/unit/objects/test_build_request.py @@ -16,6 +16,7 @@ from oslo_versionedobjects import base as o_vo_base from nova import exception from nova import objects +from nova.objects import base from nova.objects import build_request from nova.tests.unit import fake_build_request from nova.tests.unit import fake_instance @@ -159,6 +160,28 @@ class _TestBuildRequestObject(object): mock_obj_set_defaults.assert_called_once_with('deleted') self.assertFalse(build_request.instance.deleted) + def test_obj_make_compatible_pre_1_3(self): + obj = fake_build_request.fake_req_obj(self.context) + build_request_obj = objects.BuildRequest(self.context) + obj_primitive = obj.obj_to_primitive() + build_request_obj.obj_make_compatible(obj_primitive, '1.2') + self.assertNotIn('tags', obj_primitive) + + def test_create_with_tags_set(self): + # Test that when we set tags on the build request, + # create it and reload it from the database that the + # build_request.instance.tags is the same thing. + build_request_obj = fake_build_request.fake_req_obj(self.context) + self.assertEqual(1, len(build_request_obj.tags)) + build_request_obj.create() + self.assertEqual(1, len(build_request_obj.tags)) + self.assertEqual(len(build_request_obj.tags), + len(build_request_obj.instance.tags)) + # Can't compare list objects directly, just compare the single + # item they contain. + base.obj_equal_prims(build_request_obj.tags[0], + build_request_obj.instance.tags[0]) + class TestBuildRequestObject(test_objects._LocalTest, _TestBuildRequestObject): diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index c93620be106f..52d7433d1b1d 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1068,7 +1068,7 @@ object_data = { 'BandwidthUsageList': '1.2-5fe7475ada6fe62413cbfcc06ec70746', 'BlockDeviceMapping': '1.18-ad87cece6f84c65f5ec21615755bc6d3', 'BlockDeviceMappingList': '1.17-1e568eecb91d06d4112db9fd656de235', - 'BuildRequest': '1.2-532d95a88c5fd33e85878e408e5d6e8d', + 'BuildRequest': '1.3-077dee42bed93f8a5b62be77657b7152', 'BuildRequestList': '1.0-cd95608eccb89fbc702c8b52f38ec738', 'CellMapping': '1.0-7f1a7e85a22bbb7559fc730ab658b9bd', 'CellMappingList': '1.0-4ee0d9efdfd681fed822da88376e04d2',