Merge "objects: add support for per parent type foreign keys"

This commit is contained in:
Jenkins 2016-08-25 05:45:11 +00:00 committed by Gerrit Code Review
commit c4f7f4f30d
6 changed files with 100 additions and 14 deletions

View File

@ -68,6 +68,10 @@ class NeutronSyntheticFieldMultipleForeignKeys(exceptions.NeutronException):
"foreign key") "foreign key")
class NeutronSyntheticFieldsForeignKeysNotFound(exceptions.NeutronException):
message = _("%(child)s does not define a foreign key for %(parent)s")
def get_updatable_fields(cls, fields): def get_updatable_fields(cls, fields):
fields = fields.copy() fields = fields.copy()
for field in cls.fields_no_update: for field in cls.fields_no_update:
@ -445,6 +449,7 @@ class NeutronDbObject(NeutronObject):
This method doesn't take care of loading synthetic fields that aren't This method doesn't take care of loading synthetic fields that aren't
stored in the DB, e.g. 'shared' in RBAC policy. stored in the DB, e.g. 'shared' in RBAC policy.
""" """
clsname = self.__class__.__name__
# TODO(rossella_s) Find a way to handle ObjectFields with # TODO(rossella_s) Find a way to handle ObjectFields with
# subclasses=True # subclasses=True
@ -462,7 +467,11 @@ class NeutronDbObject(NeutronObject):
# QosRule # QosRule
continue continue
objclass = objclasses[0] objclass = objclasses[0]
if len(objclass.foreign_keys.keys()) > 1: foreign_keys = objclass.foreign_keys.get(clsname)
if not foreign_keys:
raise NeutronSyntheticFieldsForeignKeysNotFound(
parent=clsname, child=objclass.__name__)
if len(foreign_keys.keys()) > 1:
raise NeutronSyntheticFieldMultipleForeignKeys(field=field) raise NeutronSyntheticFieldMultipleForeignKeys(field=field)
synthetic_field_db_name = ( synthetic_field_db_name = (
@ -482,7 +491,7 @@ class NeutronDbObject(NeutronObject):
synth_objs = objclass.get_objects( synth_objs = objclass.get_objects(
self.obj_context, **{ self.obj_context, **{
k: getattr(self, v) k: getattr(self, v)
for k, v in objclass.foreign_keys.items()}) for k, v in foreign_keys.items()})
if isinstance(self.fields[field], obj_fields.ObjectField): if isinstance(self.fields[field], obj_fields.ObjectField):
setattr(self, field, synth_objs[0] if synth_objs else None) setattr(self, field, synth_objs[0] if synth_objs else None)
else: else:

View File

@ -34,4 +34,4 @@ class NetworkSegment(base.NeutronDbObject):
'segment_index': obj_fields.IntegerField(default=0) 'segment_index': obj_fields.IntegerField(default=0)
} }
foreign_keys = {'network_id': 'id'} foreign_keys = {'Network': {'network_id': 'id'}}

View File

@ -106,7 +106,7 @@ class SecurityGroupRule(base.NeutronDbObject):
'remote_ip_prefix': obj_fields.IPNetworkField(nullable=True), 'remote_ip_prefix': obj_fields.IPNetworkField(nullable=True),
} }
foreign_keys = {'security_group_id': 'id'} foreign_keys = {'SecurityGroup': {'security_group_id': 'id'}}
fields_no_update = ['project_id', 'security_group_id'] fields_no_update = ['project_id', 'security_group_id']

View File

@ -32,7 +32,7 @@ class DNSNameServer(base.NeutronDbObject):
primary_keys = ['address', 'subnet_id'] primary_keys = ['address', 'subnet_id']
foreign_keys = {'subnet_id': 'id'} foreign_keys = {'Subnet': {'subnet_id': 'id'}}
fields = { fields = {
'address': obj_fields.StringField(), 'address': obj_fields.StringField(),
@ -62,7 +62,7 @@ class Route(base.NeutronDbObject):
primary_keys = ['destination', 'nexthop', 'subnet_id'] primary_keys = ['destination', 'nexthop', 'subnet_id']
foreign_keys = {'subnet_id': 'id'} foreign_keys = {'Subnet': {'subnet_id': 'id'}}
fields = { fields = {
'subnet_id': obj_fields.UUIDField(), 'subnet_id': obj_fields.UUIDField(),
@ -99,7 +99,7 @@ class IPAllocationPool(base.NeutronDbObject):
db_model = models_v2.IPAllocationPool db_model = models_v2.IPAllocationPool
foreign_keys = {'subnet_id': 'id'} foreign_keys = {'Subnet': {'subnet_id': 'id'}}
fields_need_translation = { fields_need_translation = {
'start': 'first_ip', 'start': 'first_ip',
@ -172,7 +172,7 @@ class Subnet(base.NeutronDbObject):
synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes', synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes',
'shared'] 'shared']
foreign_keys = {'network_id': 'id'} foreign_keys = {'Network': {'network_id': 'id'}}
fields_no_update = ['project_id'] fields_no_update = ['project_id']

View File

@ -32,7 +32,7 @@ class SubPort(base.NeutronDbObject):
db_model = models.SubPort db_model = models.SubPort
primary_keys = ['port_id'] primary_keys = ['port_id']
foreign_keys = {'trunk_id': 'id'} foreign_keys = {'Trunk': {'trunk_id': 'id'}}
fields = { fields = {
'port_id': obj_fields.UUIDField(), 'port_id': obj_fields.UUIDField(),

View File

@ -67,7 +67,32 @@ class FakeSmallNeutronObject(base.NeutronDbObject):
primary_keys = ['field1'] primary_keys = ['field1']
foreign_keys = {'field1': 'id'} foreign_keys = {
'FakeNeutronObjectCompositePrimaryKeyWithId': {'field1': 'id'},
'FakeNeutronDbObject': {'field2': 'id'},
'FakeNeutronObjectUniqueKey': {'field3': 'id'},
}
fields = {
'field1': obj_fields.UUIDField(),
'field2': obj_fields.UUIDField(),
'field3': obj_fields.UUIDField(),
}
@obj_base.VersionedObjectRegistry.register_if(False)
class FakeSmallNeutronObjectWithMultipleParents(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = ObjectFieldsModel
primary_keys = ['field1', 'field2']
foreign_keys = {
'FakeParent': {'field1': 'id'},
'FakeParent2': {'field2': 'id'},
}
fields = { fields = {
'field1': obj_fields.UUIDField(), 'field1': obj_fields.UUIDField(),
@ -75,6 +100,25 @@ class FakeSmallNeutronObject(base.NeutronDbObject):
} }
@obj_base.VersionedObjectRegistry.register_if(False)
class FakeParent(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = ObjectFieldsModel
primary_keys = ['field1', 'field2']
fields = {
'id': obj_fields.UUIDField(),
'children': obj_fields.ListOfObjectsField(
'FakeSmallNeutronObjectWithMultipleParents',
nullable=True)
}
synthetic_fields = ['children']
@obj_base.VersionedObjectRegistry.register_if(False) @obj_base.VersionedObjectRegistry.register_if(False)
class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject): class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject):
# Version 1.0: Initial version # Version 1.0: Initial version
@ -84,7 +128,10 @@ class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject):
primary_keys = ['field1'] primary_keys = ['field1']
foreign_keys = {'field1': 'weird_key'} foreign_keys = {
'FakeNeutronObjectNonStandardPrimaryKey': {'field1': 'weird_key'},
'FakeNeutronObjectCompositePrimaryKey': {'field2': 'weird_key'},
}
fields = { fields = {
'field1': obj_fields.UUIDField(), 'field1': obj_fields.UUIDField(),
@ -225,7 +272,9 @@ class FakeNeutronObjectMultipleForeignKeys(base.NeutronDbObject):
db_model = ObjectFieldsModel db_model = ObjectFieldsModel
foreign_keys = {'field1': 'id', 'field2': 'id'} foreign_keys = {
'FakeNeutronObjectSyntheticField': {'field1': 'id', 'field2': 'id'},
}
fields = { fields = {
'field1': obj_fields.UUIDField(), 'field1': obj_fields.UUIDField(),
@ -494,12 +543,14 @@ class BaseObjectIfaceTestCase(_BaseObjectTestCase, test_base.BaseTestCase):
if self._test_class.is_object_field(field): if self._test_class.is_object_field(field):
obj_class = self._get_ovo_object_class(self._test_class, obj_class = self._get_ovo_object_class(self._test_class,
field) field)
foreign_keys = obj_class.foreign_keys.get(
self._test_class.__name__)
mock_calls.append( mock_calls.append(
mock.call( mock.call(
self.context, obj_class.db_model, self.context, obj_class.db_model,
_pager=self.pager_map[obj_class.obj_name()], _pager=self.pager_map[obj_class.obj_name()],
**{k: db_obj[v] **{k: db_obj[v]
for k, v in obj_class.foreign_keys.items()})) for k, v in foreign_keys.items()}))
return mock_calls return mock_calls
def test_get_objects(self): def test_get_objects(self):
@ -904,6 +955,31 @@ class BaseDbObjectMultipleForeignKeysTestCase(_BaseObjectTestCase,
obj.load_synthetic_db_fields) obj.load_synthetic_db_fields)
class BaseDbObjectMultipleParentsForForeignKeysTestCase(
_BaseObjectTestCase,
test_base.BaseTestCase):
_test_class = FakeParent
def test_load_synthetic_db_fields_with_multiple_parents(self):
child_cls = FakeSmallNeutronObjectWithMultipleParents
self.obj_registry.register(child_cls)
self.obj_registry.register(FakeParent)
obj = self._test_class(self.context, **self.obj_fields[0])
fake_children = [
child_cls(
self.context, **child_cls.modify_fields_from_db(
self.get_random_fields(obj_cls=child_cls))
)
for _ in range(5)
]
with mock.patch.object(child_cls, 'get_objects',
return_value=fake_children) as get_objects:
obj.load_synthetic_db_fields()
get_objects.assert_called_once_with(self.context, field1=obj.id)
self.assertEqual(fake_children, obj.children)
class BaseDbObjectTestCase(_BaseObjectTestCase, class BaseDbObjectTestCase(_BaseObjectTestCase,
test_db_base_plugin_v2.DbOperationBoundMixin): test_db_base_plugin_v2.DbOperationBoundMixin):
def setUp(self): def setUp(self):
@ -1130,7 +1206,8 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
db_obj[field][0]) db_obj[field][0])
# make sure children point to the base object # make sure children point to the base object
for local_field, foreign_key in objclass.foreign_keys.items(): foreign_keys = objclass.foreign_keys.get(obj.__class__.__name__)
for local_field, foreign_key in foreign_keys.items():
objclass_fields[local_field] = obj.get(foreign_key) objclass_fields[local_field] = obj.get(foreign_key)
synth_field_obj = objclass(self.context, **objclass_fields) synth_field_obj = objclass(self.context, **objclass_fields)