Merge "objects: add support for per parent type foreign keys"
This commit is contained in:
commit
c4f7f4f30d
|
@ -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:
|
||||||
|
|
|
@ -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'}}
|
||||||
|
|
|
@ -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']
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue