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")
class NeutronSyntheticFieldsForeignKeysNotFound(exceptions.NeutronException):
message = _("%(child)s does not define a foreign key for %(parent)s")
def get_updatable_fields(cls, fields):
fields = fields.copy()
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
stored in the DB, e.g. 'shared' in RBAC policy.
"""
clsname = self.__class__.__name__
# TODO(rossella_s) Find a way to handle ObjectFields with
# subclasses=True
@ -462,7 +467,11 @@ class NeutronDbObject(NeutronObject):
# QosRule
continue
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)
synthetic_field_db_name = (
@ -482,7 +491,7 @@ class NeutronDbObject(NeutronObject):
synth_objs = objclass.get_objects(
self.obj_context, **{
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):
setattr(self, field, synth_objs[0] if synth_objs else None)
else:

View File

@ -34,4 +34,4 @@ class NetworkSegment(base.NeutronDbObject):
'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),
}
foreign_keys = {'security_group_id': 'id'}
foreign_keys = {'SecurityGroup': {'security_group_id': 'id'}}
fields_no_update = ['project_id', 'security_group_id']

View File

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

View File

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

View File

@ -67,7 +67,32 @@ class FakeSmallNeutronObject(base.NeutronDbObject):
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 = {
'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)
class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject):
# Version 1.0: Initial version
@ -84,7 +128,10 @@ class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject):
primary_keys = ['field1']
foreign_keys = {'field1': 'weird_key'}
foreign_keys = {
'FakeNeutronObjectNonStandardPrimaryKey': {'field1': 'weird_key'},
'FakeNeutronObjectCompositePrimaryKey': {'field2': 'weird_key'},
}
fields = {
'field1': obj_fields.UUIDField(),
@ -225,7 +272,9 @@ class FakeNeutronObjectMultipleForeignKeys(base.NeutronDbObject):
db_model = ObjectFieldsModel
foreign_keys = {'field1': 'id', 'field2': 'id'}
foreign_keys = {
'FakeNeutronObjectSyntheticField': {'field1': 'id', 'field2': 'id'},
}
fields = {
'field1': obj_fields.UUIDField(),
@ -494,12 +543,14 @@ class BaseObjectIfaceTestCase(_BaseObjectTestCase, test_base.BaseTestCase):
if self._test_class.is_object_field(field):
obj_class = self._get_ovo_object_class(self._test_class,
field)
foreign_keys = obj_class.foreign_keys.get(
self._test_class.__name__)
mock_calls.append(
mock.call(
self.context, obj_class.db_model,
_pager=self.pager_map[obj_class.obj_name()],
**{k: db_obj[v]
for k, v in obj_class.foreign_keys.items()}))
for k, v in foreign_keys.items()}))
return mock_calls
def test_get_objects(self):
@ -904,6 +955,31 @@ class BaseDbObjectMultipleForeignKeysTestCase(_BaseObjectTestCase,
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,
test_db_base_plugin_v2.DbOperationBoundMixin):
def setUp(self):
@ -1130,7 +1206,8 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
db_obj[field][0])
# 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)
synth_field_obj = objclass(self.context, **objclass_fields)