Added objects Tag and TagList
Also field 'tags' was added to the instance object. These objects will be used in new instance-tags API extension. Implements: blueprint tag-instances Change-Id: I34ec32f0b3704b01aaca9c0cb2b34eb6d3107513
This commit is contained in:
parent
9ccad00922
commit
5783c04cd2
|
@ -49,6 +49,7 @@ def register_all():
|
|||
__import__('nova.objects.network_request')
|
||||
__import__('nova.objects.numa')
|
||||
__import__('nova.objects.pci_device')
|
||||
__import__('nova.objects.tag')
|
||||
__import__('nova.objects.quotas')
|
||||
__import__('nova.objects.security_group')
|
||||
__import__('nova.objects.security_group_rule')
|
||||
|
|
|
@ -43,7 +43,8 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject):
|
|||
# Version 1.2: Instance version 1.14
|
||||
# Version 1.3: Instance version 1.15
|
||||
# Version 1.4: Instance version 1.16
|
||||
VERSION = '1.4'
|
||||
# Version 1.5: Instance version 1.17
|
||||
VERSION = '1.5'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
|
@ -211,7 +212,8 @@ class BlockDeviceMappingList(base.ObjectListBase, base.NovaObject):
|
|||
# Version 1.3: BlockDeviceMapping <= version 1.2
|
||||
# Version 1.4: BlockDeviceMapping <= version 1.3
|
||||
# Version 1.5: BlockDeviceMapping <= version 1.4
|
||||
VERSION = '1.5'
|
||||
# Version 1.6: BlockDeviceMapping <= version 1.5
|
||||
VERSION = '1.6'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('BlockDeviceMapping'),
|
||||
|
@ -223,6 +225,7 @@ class BlockDeviceMappingList(base.ObjectListBase, base.NovaObject):
|
|||
'1.3': '1.2',
|
||||
'1.4': '1.3',
|
||||
'1.5': '1.4',
|
||||
'1.6': '1.5',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
|
|
|
@ -34,7 +34,8 @@ class FixedIP(obj_base.NovaPersistentObject, obj_base.NovaObject):
|
|||
# Version 1.4: Added default_route field
|
||||
# Version 1.5: Added floating_ips field
|
||||
# Version 1.6: Instance 1.16
|
||||
VERSION = '1.6'
|
||||
# Version 1.7: Instance 1.17
|
||||
VERSION = '1.7'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
|
@ -209,7 +210,8 @@ class FixedIPList(obj_base.ObjectListBase, obj_base.NovaObject):
|
|||
# Version 1.4: FixedIP <= version 1.4
|
||||
# Version 1.5: FixedIP <= version 1.5, added expected attrs to gets
|
||||
# Version 1.6: FixedIP <= version 1.6
|
||||
VERSION = '1.6'
|
||||
# Version 1.7: FixedIP <= version 1.7
|
||||
VERSION = '1.7'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('FixedIP'),
|
||||
|
@ -222,6 +224,7 @@ class FixedIPList(obj_base.ObjectListBase, obj_base.NovaObject):
|
|||
'1.4': '1.4',
|
||||
'1.5': '1.5',
|
||||
'1.6': '1.6',
|
||||
'1.7': '1.7',
|
||||
}
|
||||
|
||||
@obj_base.remotable_classmethod
|
||||
|
|
|
@ -37,7 +37,7 @@ LOG = logging.getLogger(__name__)
|
|||
# List of fields that can be joined in DB layer.
|
||||
_INSTANCE_OPTIONAL_JOINED_FIELDS = ['metadata', 'system_metadata',
|
||||
'info_cache', 'security_groups',
|
||||
'pci_devices']
|
||||
'pci_devices', 'tags']
|
||||
# These are fields that are optional but don't translate to db columns
|
||||
_INSTANCE_OPTIONAL_NON_COLUMN_FIELDS = ['fault']
|
||||
# These are fields that are optional and in instance_extra
|
||||
|
@ -86,7 +86,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
|||
# Version 1.14: Added numa_topology
|
||||
# Version 1.15: PciDeviceList 1.1
|
||||
# Version 1.16: Added pci_requests
|
||||
VERSION = '1.16'
|
||||
# Version 1.17: Added tags
|
||||
VERSION = '1.17'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
|
@ -176,6 +177,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
|||
nullable=True),
|
||||
'pci_requests': fields.ObjectField('InstancePCIRequests',
|
||||
nullable=True),
|
||||
'tags': fields.ObjectField('TagList'),
|
||||
}
|
||||
|
||||
obj_extra_fields = ['name']
|
||||
|
@ -248,6 +250,10 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
|||
del primitive['pci_devices']
|
||||
if target_version < (1, 16) and 'pci_requests' in primitive:
|
||||
del primitive['pci_requests']
|
||||
if target_version < (1, 17):
|
||||
# NOTE(snikitin): Before 1.17 there was no tags list
|
||||
if 'tags' in primitive:
|
||||
del primitive['tags']
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -333,6 +339,12 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
|
|||
objects.SecurityGroup, db_inst['security_groups'])
|
||||
instance['security_groups'] = sec_groups
|
||||
|
||||
if 'tags' in expected_attrs:
|
||||
tags = base.obj_make_list(
|
||||
context, objects.TagList(context),
|
||||
objects.Tag, db_inst['tags'])
|
||||
instance['tags'] = tags
|
||||
|
||||
instance.obj_reset_changes()
|
||||
return instance
|
||||
|
||||
|
@ -715,7 +727,8 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
|
|||
# Version 1.10: Instance <= version 1.16
|
||||
# Version 1.11: Added sort_keys and sort_dirs to get_by_filters
|
||||
# Version 1.12: Pass expected_attrs to instance_get_active_by_window_joined
|
||||
VERSION = '1.12'
|
||||
# Version 1.13: Instance <= version 1.17
|
||||
VERSION = '1.13'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Instance'),
|
||||
|
@ -734,6 +747,7 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
|
|||
'1.10': '1.16',
|
||||
'1.11': '1.16',
|
||||
'1.12': '1.16',
|
||||
'1.13': '1.17',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova import db
|
||||
from nova import objects
|
||||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
class Tag(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'resource_id': fields.StringField(),
|
||||
'tag': fields.StringField(),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(context, tag, db_tag):
|
||||
for key in tag.fields:
|
||||
tag[key] = db_tag[key]
|
||||
tag.obj_reset_changes()
|
||||
tag._context = context
|
||||
return tag
|
||||
|
||||
@base.remotable
|
||||
def create(self, context):
|
||||
db_tag = db.instance_tag_add(context, self.resource_id, self.tag)
|
||||
self._from_db_object(context, self, db_tag)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def destroy(cls, context, resource_id, name):
|
||||
db.instance_tag_delete(context, resource_id, name)
|
||||
|
||||
|
||||
class TagList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('Tag'),
|
||||
}
|
||||
child_versions = {
|
||||
'1.0': '1.0',
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_resource_id(cls, context, resource_id):
|
||||
db_tags = db.instance_tag_get_by_instance_uuid(context, resource_id)
|
||||
return base.obj_make_list(context, cls(), objects.Tag, db_tags)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def create(cls, context, resource_id, tags):
|
||||
db_tags = db.instance_tag_set(context, resource_id, tags)
|
||||
return base.obj_make_list(context, cls(), objects.Tag, db_tags)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def destroy(cls, context, resource_id):
|
||||
db.instance_tag_delete_all(context, resource_id)
|
|
@ -57,6 +57,7 @@ def fake_db_instance(**updates):
|
|||
'ephemeral_gb': 0,
|
||||
'extra': {'pci_requests': None,
|
||||
'numa_topology': None},
|
||||
'tags': []
|
||||
}
|
||||
|
||||
for name, field in objects.Instance.fields.items():
|
||||
|
|
|
@ -65,6 +65,7 @@ class _TestInstanceObject(object):
|
|||
fake_instance['pci_devices'] = []
|
||||
fake_instance['user_id'] = self.context.user_id
|
||||
fake_instance['project_id'] = self.context.project_id
|
||||
fake_instance['tags'] = []
|
||||
return fake_instance
|
||||
|
||||
def test_datetime_deserialization(self):
|
||||
|
@ -74,7 +75,7 @@ class _TestInstanceObject(object):
|
|||
primitive = inst.obj_to_primitive()
|
||||
expected = {'nova_object.name': 'Instance',
|
||||
'nova_object.namespace': 'nova',
|
||||
'nova_object.version': '1.16',
|
||||
'nova_object.version': '1.17',
|
||||
'nova_object.data':
|
||||
{'uuid': 'fake-uuid',
|
||||
'launched_at': '1955-11-05T00:00:00Z'},
|
||||
|
@ -90,7 +91,7 @@ class _TestInstanceObject(object):
|
|||
primitive = inst.obj_to_primitive()
|
||||
expected = {'nova_object.name': 'Instance',
|
||||
'nova_object.namespace': 'nova',
|
||||
'nova_object.version': '1.16',
|
||||
'nova_object.version': '1.17',
|
||||
'nova_object.data':
|
||||
{'uuid': 'fake-uuid',
|
||||
'access_ip_v4': '1.2.3.4',
|
||||
|
|
|
@ -1075,8 +1075,8 @@ object_data = {
|
|||
'AggregateList': '1.2-4b02a285b8612bfb86a96ff80052fb0a',
|
||||
'BandwidthUsage': '1.2-a9d7c2ba54995e48ce38688c51c9416d',
|
||||
'BandwidthUsageList': '1.2-5b564cbfd5ae6e106443c086938e7602',
|
||||
'BlockDeviceMapping': '1.4-9968ffe513e7672484b0f528b034cd0f',
|
||||
'BlockDeviceMappingList': '1.5-83767968de6e91e9705bddaae02bc649',
|
||||
'BlockDeviceMapping': '1.5-9968ffe513e7672484b0f528b034cd0f',
|
||||
'BlockDeviceMappingList': '1.6-ee2ed2eb3f3f2f54d573ccea0ff2eeaa',
|
||||
'ComputeNode': '1.6-d2ea9b8f4a6e95ff6a683266eebddbff',
|
||||
'ComputeNodeList': '1.6-205aa2ea08d49f6ce87df1fcd2407b4e',
|
||||
'DNSDomain': '1.0-5bdc288d7c3b723ce86ede998fd5c9ba',
|
||||
|
@ -1084,14 +1084,14 @@ object_data = {
|
|||
'EC2InstanceMapping': '1.0-627baaf4b12c9067200979bdc4558a99',
|
||||
'EC2SnapshotMapping': '1.0-26cf315be1f8abab4289d4147671c836',
|
||||
'EC2VolumeMapping': '1.0-2f8c3bf077c65a425294ec2b361c9143',
|
||||
'FixedIP': '1.6-2472964d39e50da67202109eb85cd173',
|
||||
'FixedIPList': '1.6-f2f740de66bc2d90627004bd311690ad',
|
||||
'FixedIP': '1.7-2472964d39e50da67202109eb85cd173',
|
||||
'FixedIPList': '1.7-125de790b58cfb8c84ffc8c34db4a81e',
|
||||
'Flavor': '1.1-096cfd023c35d07542cf732fb29b45e4',
|
||||
'FlavorList': '1.1-a3d5551267cb8f62ff38ded125900721',
|
||||
'FloatingIP': '1.6-27eb68b7c9c620dd5f0561b5a3be0e82',
|
||||
'FloatingIPList': '1.7-f376f63ed99243f9d90841b7f6732bbf',
|
||||
'HVSpec': '1.0-c4d8377cc4fe519930e60c1d8265a142',
|
||||
'Instance': '1.16-b00c09fb92ae80b393943f56e84abd9c',
|
||||
'Instance': '1.17-972cae223db35e88bb184bdf8c197229',
|
||||
'InstanceAction': '1.1-6b1d0a6dbd522b5a83c20757ec659663',
|
||||
'InstanceActionEvent': '1.1-42dbdba74bd06e0619ca75cd3397cd1b',
|
||||
'InstanceActionEventList': '1.0-1d5cc958171d6ce07383c2ad6208318e',
|
||||
|
@ -1102,7 +1102,7 @@ object_data = {
|
|||
'InstanceGroup': '1.9-95ece99f092e8f4f88327cdbb44162c9',
|
||||
'InstanceGroupList': '1.6-c6b78f3c9d9080d33c08667e80589817',
|
||||
'InstanceInfoCache': '1.5-ef64b604498bfa505a8c93747a9d8b2f',
|
||||
'InstanceList': '1.12-f044d117e39478498e0d3124c5c4ef54',
|
||||
'InstanceList': '1.13-179093360c48747a41694cc2f326d75d',
|
||||
'InstanceNUMACell': '1.1-8d2a13c8360cc9ea1b68c9c6c4476857',
|
||||
'InstanceNUMATopology': '1.1-86b95d263c4c68411d44c6741b8d2bb0',
|
||||
'InstancePCIRequest': '1.1-e082d174f4643e5756ba098c47c1510f',
|
||||
|
@ -1131,6 +1131,8 @@ object_data = {
|
|||
'Service': '1.5-82bbfd46a744a9c89bc44b47a1b81683',
|
||||
'ServiceList': '1.3-4a1a5822dea268d0d7f892f5106bb2e1',
|
||||
'TestSubclassedObject': '1.6-b9be83b5587fbca3c8570aab67cb3d02',
|
||||
'Tag': '1.0-a11531f4e4e3166eef6243d6d58a18bd',
|
||||
'TagList': '1.0-e89bf8c8055f1f1d654fb44f0abf1f53',
|
||||
'VirtualInterface': '1.0-10fdac4c704102b6d57d6936d6d790d2',
|
||||
'VirtualInterfaceList': '1.0-accbf02628a8063c1d885077a2bf49b6',
|
||||
'VirtCPUTopology': '1.0-fc694de72e20298f7c6bab1083fd4563',
|
||||
|
@ -1138,15 +1140,16 @@ object_data = {
|
|||
|
||||
|
||||
object_relationships = {
|
||||
'BlockDeviceMapping': {'Instance': '1.16'},
|
||||
'FixedIP': {'Instance': '1.16', 'Network': '1.2',
|
||||
'BlockDeviceMapping': {'Instance': '1.17'},
|
||||
'FixedIP': {'Instance': '1.17', 'Network': '1.2',
|
||||
'VirtualInterface': '1.0',
|
||||
'FloatingIPList': '1.7'},
|
||||
'FloatingIP': {'FixedIP': '1.6'},
|
||||
'FloatingIP': {'FixedIP': '1.7'},
|
||||
'Instance': {'InstanceFault': '1.2',
|
||||
'InstanceInfoCache': '1.5',
|
||||
'InstanceNUMATopology': '1.1',
|
||||
'PciDeviceList': '1.1',
|
||||
'TagList': '1.0',
|
||||
'SecurityGroupList': '1.0',
|
||||
'InstancePCIRequests': '1.1'},
|
||||
'MyObj': {'MyOwnedObject': '1.0'},
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from nova.objects import tag
|
||||
from nova.tests.unit.objects import test_objects
|
||||
|
||||
RESOURCE_ID = '123'
|
||||
TAG_NAME1 = 'fake-tag1'
|
||||
TAG_NAME2 = 'fake-tag2'
|
||||
|
||||
fake_tag1 = {
|
||||
'resource_id': RESOURCE_ID,
|
||||
'tag': TAG_NAME1,
|
||||
}
|
||||
|
||||
fake_tag2 = {
|
||||
'resource_id': RESOURCE_ID,
|
||||
'tag': TAG_NAME1,
|
||||
}
|
||||
|
||||
fake_tag_list = [fake_tag1, fake_tag2]
|
||||
|
||||
|
||||
def _get_tag(resource_id, tag_name):
|
||||
t = tag.Tag()
|
||||
t.resource_id = resource_id
|
||||
t.tag = tag_name
|
||||
return t
|
||||
|
||||
|
||||
class _TestTagObject(object):
|
||||
@mock.patch('nova.db.instance_tag_add')
|
||||
def test_create(self, tag_add):
|
||||
tag_add.return_value = fake_tag1
|
||||
tag_obj = _get_tag(RESOURCE_ID, TAG_NAME1)
|
||||
tag_obj.create(self.context)
|
||||
|
||||
tag_add.assert_called_once_with(self.context, RESOURCE_ID, TAG_NAME1)
|
||||
self.compare_obj(tag_obj, fake_tag1)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_delete')
|
||||
def test_destroy(self, tag_delete):
|
||||
tag.Tag.destroy(self.context, RESOURCE_ID, TAG_NAME1)
|
||||
tag_delete.assert_called_once_with(self.context,
|
||||
RESOURCE_ID, TAG_NAME1)
|
||||
|
||||
|
||||
class TestMigrationObject(test_objects._LocalTest,
|
||||
_TestTagObject):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteMigrationObject(test_objects._RemoteTest,
|
||||
_TestTagObject):
|
||||
pass
|
||||
|
||||
|
||||
class _TestTagList(object):
|
||||
def _compare_tag_list(self, tag_list, tag_list_obj):
|
||||
self.assertEqual(len(tag_list), len(tag_list_obj))
|
||||
|
||||
for obj, fake in zip(tag_list_obj, tag_list):
|
||||
self.assertIsInstance(obj, tag.Tag)
|
||||
self.assertEqual(obj['tag'], fake['tag'])
|
||||
self.assertEqual(obj['resource_id'], fake['resource_id'])
|
||||
|
||||
@mock.patch('nova.db.instance_tag_get_by_instance_uuid')
|
||||
def test_get_by_resource_id(self, get_by_inst):
|
||||
get_by_inst.return_value = fake_tag_list
|
||||
|
||||
tag_list_obj = tag.TagList.get_by_resource_id(
|
||||
self.context, RESOURCE_ID)
|
||||
|
||||
get_by_inst.assert_called_once_with(self.context, RESOURCE_ID)
|
||||
self._compare_tag_list(fake_tag_list, tag_list_obj)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_set')
|
||||
def test_create(self, tag_set):
|
||||
tag_set.return_value = fake_tag_list
|
||||
tag_list_obj = tag.TagList.create(
|
||||
self.context, RESOURCE_ID, [TAG_NAME1, TAG_NAME2])
|
||||
|
||||
tag_set.assert_called_once_with(self.context,
|
||||
RESOURCE_ID, [TAG_NAME1, TAG_NAME2])
|
||||
self._compare_tag_list(fake_tag_list, tag_list_obj)
|
||||
|
||||
@mock.patch('nova.db.instance_tag_delete_all')
|
||||
def test_destroy(self, tag_delete_all):
|
||||
tag.TagList.destroy(self.context, RESOURCE_ID)
|
||||
tag_delete_all.assert_called_once_with(self.context, RESOURCE_ID)
|
||||
|
||||
|
||||
class TestTagList(test_objects._LocalTest, _TestTagList):
|
||||
pass
|
||||
|
||||
|
||||
class TestTagListRemote(test_objects._RemoteTest, _TestTagList):
|
||||
pass
|
Loading…
Reference in New Issue