Merge "Fixed PortBindingLevelDbObjectTestCase"

This commit is contained in:
Zuul 2018-05-24 02:01:23 +00:00 committed by Gerrit Code Review
commit 0fbf6691c5
5 changed files with 91 additions and 12 deletions

View File

@ -107,7 +107,8 @@ class DistributedPortBinding(PortBindingBase):
@base.NeutronObjectRegistry.register
class PortBindingLevel(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
# Version 1.1: Added segment_id
VERSION = '1.1'
db_model = ml2_models.PortBindingLevel
@ -121,6 +122,9 @@ class PortBindingLevel(base.NeutronDbObject):
'segment': obj_fields.ObjectField(
'NetworkSegment', nullable=True
),
# arguably redundant but allows us to define foreign key for 'segment'
# synthetic field inside NetworkSegment definition
'segment_id': common_types.UUIDField(nullable=True),
}
synthetic_fields = ['segment']
@ -140,6 +144,11 @@ class PortBindingLevel(base.NeutronDbObject):
return super(PortBindingLevel, cls).get_objects(
context, _pager, validate_filters, **kwargs)
def obj_make_compatible(self, primitive, target_version):
_target_version = versionutils.convert_version_to_tuple(target_version)
if _target_version < (1, 1):
primitive.pop('segment_id', None)
@base.NeutronObjectRegistry.register
class IPAllocation(base.NeutronDbObject):
@ -253,7 +262,8 @@ class SecurityGroupPortBinding(base.NeutronDbObject):
class Port(base.NeutronDbObject):
# Version 1.0: Initial version
# Version 1.1: Add data_plane_status field
VERSION = '1.1'
# Version 1.2: Added segment_id to binding_levels
VERSION = '1.2'
db_model = models_v2.Port
@ -450,9 +460,13 @@ class Port(base.NeutronDbObject):
def obj_make_compatible(self, primitive, target_version):
_target_version = versionutils.convert_version_to_tuple(target_version)
if _target_version < (1, 1):
primitive.pop('data_plane_status', None)
if _target_version < (1, 2):
binding_levels = primitive.get('binding_levels', [])
for lvl in binding_levels:
lvl['versioned_object.version'] = '1.0'
lvl['versioned_object.data'].pop('segment_id', None)
@classmethod
def get_ports_by_router(cls, context, router_id, owner, subnet):

View File

@ -19,6 +19,7 @@ from neutron_lib.db import model_base
import sqlalchemy as sa
from sqlalchemy import orm
from neutron.db.models import segment as segment_models
from neutron.db import models_v2
BINDING_PROFILE_LEN = 4095
@ -90,6 +91,9 @@ class PortBindingLevel(model_base.BASEV2):
load_on_pending=True,
backref=orm.backref("binding_levels", lazy='subquery',
cascade='delete'))
segment = orm.relationship(
segment_models.NetworkSegment,
load_on_pending=True)
revises_on_change = ('port', )

View File

@ -1816,15 +1816,42 @@ class BaseDbObjectTestCase(_BaseObjectTestCase,
for local_field, foreign_key in foreign_keys.items():
objclass_fields[local_field] = obj.get(foreign_key)
# remember which fields were nullified so that later we know what
# to assert for each child field
nullified_fields = set()
# cut off more depth levels to simplify object field generation
# (for example, nullify segment field for PortBindingLevel objects
# to avoid creating a Segment object (and back-linking it to the
# original network of the port)
for child_field in self._get_object_synthetic_fields(objclass):
if objclass.fields[child_field].nullable:
objclass_fields[child_field] = None
nullified_fields.add(child_field)
# initialize the child object
synth_field_obj = objclass(self.context, **objclass_fields)
# nullify nullable UUID fields since they may otherwise trigger
# foreign key violations
for field_name in get_obj_persistent_fields(synth_field_obj):
child_field = objclass.fields[field_name]
if child_field.nullable:
if isinstance(child_field, common_types.UUIDField):
synth_field_obj[field_name] = None
nullified_fields.add(field_name)
synth_field_obj.create()
# reload the parent object under test
obj = cls_.get_object(self.context, **obj._get_composite_keys())
# check that the stored database model now has filled relationships
# check that the stored database model now has correct attr values
dbattr = obj.fields_need_translation.get(field, field)
self.assertTrue(getattr(obj.db_obj, dbattr, None))
if field in nullified_fields:
self.assertIsNone(getattr(obj.db_obj, dbattr, None))
else:
self.assertIsNotNone(getattr(obj.db_obj, dbattr, None))
# reset the object so that we can compare it to other clean objects
obj.obj_reset_changes([field])

View File

@ -62,9 +62,9 @@ object_data = {
'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
'NetworkRBAC': '1.0-c8a67f39809c5a3c8c7f26f2f2c620b2',
'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8',
'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5',
'Port': '1.2-5bf48d12a7bf7f5b7a319e8003b437a5',
'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578',
'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060',
'PortBindingLevel': '1.1-50d47f63218f87581b6cd9a62db574e5',
'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2',
'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023',
'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',

View File

@ -194,10 +194,6 @@ class PortBindingLevelIfaceObjTestCase(
def setUp(self):
super(PortBindingLevelIfaceObjTestCase, self).setUp()
# for this object, the model contains segment_id but we expose it
# through an ObjectField that is loaded without a relationship
for obj in self.db_objs:
obj['segment_id'] = None
self.pager_map[self._test_class.obj_name()] = (
obj_base.Pager(sorts=[('port_id', True), ('level', True)]))
self.pager_map[network.NetworkSegment.obj_name()] = (
@ -206,10 +202,16 @@ class PortBindingLevelIfaceObjTestCase(
class PortBindingLevelDbObjectTestCase(
obj_test_base.BaseDbObjectTestCase):
obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase):
_test_class = ports.PortBindingLevel
def setUp(self):
super(PortBindingLevelDbObjectTestCase, self).setUp()
self.update_obj_fields(
{'port_id': lambda: self._create_test_port_id(),
'segment_id': lambda: self._create_test_segment_id()})
class PortIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase):
@ -359,3 +361,35 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
port_v1_0 = port_new.obj_to_primitive(target_version='1.0')
self.assertNotIn('data_plane_status',
port_v1_0['versioned_object.data'])
def test_v1_2_to_v1_1_drops_segment_id_in_binding_levels(self):
port_new = self._create_test_port()
segment = network.NetworkSegment(
self.context,
# TODO(ihrachys) we should be able to create a segment object
# without explicitly specifying id, but it's currently not working
id=uuidutils.generate_uuid(),
network_id=port_new.network_id,
network_type='vxlan')
segment.create()
# TODO(ihrachys) we should be able to create / update level objects via
# Port object, but it's currently not working
binding = ports.PortBindingLevel(
self.context, port_id=port_new.id,
host='host1', level=0, segment_id=segment.id)
binding.create()
port_new = ports.Port.get_object(self.context, id=port_new.id)
port_v1_1 = port_new.obj_to_primitive(target_version='1.1')
lvl = port_v1_1['versioned_object.data']['binding_levels'][0]
self.assertNotIn('segment_id', lvl['versioned_object.data'])
# check that we also downgraded level object version
self.assertEqual('1.0', lvl['versioned_object.version'])
# finally, prove that binding primitive is now identical to direct
# downgrade of the binding object
binding_v1_0 = binding.obj_to_primitive(target_version='1.0')
self.assertEqual(binding_v1_0, lvl)