summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLujin <luo.lujin@jp.fujitsu.com>2016-12-07 07:16:00 +0100
committerLujin <luo.lujin@jp.fujitsu.com>2017-12-25 14:03:42 +0900
commitfebeaf5d4016157d0cca41023e64b91e550453e8 (patch)
tree08040a0ffc4609518a00358f9d821911fc160c11
parent3f1a9846d23198f4a89f89bac73ba80ef201dea0 (diff)
Integration of (Distributed) Port Binding OVO
This patch integrates Port Binding OVO in /plugins/ml2/db.py and /plugins/ml2/plugin.py. Co-Authored-By: Artur Korzeniewski <artur.korzeniewski@intel.com> Change-Id: Idb76c0cb2a4d66690c9aca5ba338d5df814cd21e Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db
Notes
Notes (review): Verified+1: Arista CI <arista-openstack-test@aristanetworks.com> Code-Review+2: Slawek Kaplonski <slawek@kaplonski.pl> Code-Review+2: Ihar Hrachyshka <ihrachys@redhat.com> Workflow+1: Ihar Hrachyshka <ihrachys@redhat.com> Code-Review+1: Vu Cong Tuan <tuanvc@vn.fujitsu.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Thu, 18 Jan 2018 19:21:14 +0000 Reviewed-on: https://review.openstack.org/407868 Project: openstack/neutron Branch: refs/heads/master
-rw-r--r--neutron/objects/base.py16
-rw-r--r--neutron/objects/ports.py18
-rw-r--r--neutron/plugins/ml2/db.py85
-rw-r--r--neutron/plugins/ml2/driver_context.py7
-rw-r--r--neutron/plugins/ml2/models.py2
-rw-r--r--neutron/plugins/ml2/plugin.py72
-rw-r--r--neutron/tests/unit/objects/test_objects.py2
-rw-r--r--neutron/tests/unit/plugins/ml2/test_db.py119
-rw-r--r--neutron/tests/unit/plugins/ml2/test_plugin.py124
-rw-r--r--neutron/tests/unit/plugins/ml2/test_port_binding.py19
10 files changed, 264 insertions, 200 deletions
diff --git a/neutron/objects/base.py b/neutron/objects/base.py
index 7ccc529..8e4fcd1 100644
--- a/neutron/objects/base.py
+++ b/neutron/objects/base.py
@@ -27,6 +27,7 @@ from oslo_versionedobjects import base as obj_base
27from oslo_versionedobjects import exception as obj_exception 27from oslo_versionedobjects import exception as obj_exception
28from oslo_versionedobjects import fields as obj_fields 28from oslo_versionedobjects import fields as obj_fields
29import six 29import six
30from sqlalchemy import exc as sql_exc
30 31
31from neutron._i18n import _ 32from neutron._i18n import _
32from neutron.db import api as db_api 33from neutron.db import api as db_api
@@ -303,7 +304,11 @@ def _detach_db_obj(func):
303 # TODO(ihrachys) consider refreshing just changed attributes 304 # TODO(ihrachys) consider refreshing just changed attributes
304 self.obj_context.session.refresh(self.db_obj) 305 self.obj_context.session.refresh(self.db_obj)
305 # detach the model so that consequent fetches don't reuse it 306 # detach the model so that consequent fetches don't reuse it
306 self.obj_context.session.expunge(self.db_obj) 307 try:
308 self.obj_context.session.expunge(self.db_obj)
309 except sql_exc.InvalidRequestError:
310 # already detached
311 pass
307 return res 312 return res
308 return decorator 313 return decorator
309 314
@@ -330,6 +335,8 @@ class DeclarativeObject(abc.ABCMeta):
330 if key in cls.fields or key in cls.obj_extra_fields: 335 if key in cls.fields or key in cls.obj_extra_fields:
331 fields_no_update_set.add(key) 336 fields_no_update_set.add(key)
332 cls.fields_no_update = list(fields_no_update_set) 337 cls.fields_no_update = list(fields_no_update_set)
338 if name in ('PortBinding', 'DistributedPortBinding'):
339 cls.fields_no_update.remove('host')
333 340
334 model = getattr(cls, 'db_model', None) 341 model = getattr(cls, 'db_model', None)
335 if model: 342 if model:
@@ -480,7 +487,12 @@ class NeutronDbObject(NeutronObject):
480 obj = cls(context) 487 obj = cls(context)
481 obj.from_db_object(db_obj) 488 obj.from_db_object(db_obj)
482 # detach the model so that consequent fetches don't reuse it 489 # detach the model so that consequent fetches don't reuse it
483 context.session.expunge(obj.db_obj) 490 # TODO(lujinluo): remove the try block when Port OVO is in place.
491 try:
492 context.session.expunge(obj.db_obj)
493 except sql_exc.InvalidRequestError:
494 # already detached
495 pass
484 return obj 496 return obj
485 497
486 def obj_load_attr(self, attrname): 498 def obj_load_attr(self, attrname):
diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py
index 3810ebc..205892a 100644
--- a/neutron/objects/ports.py
+++ b/neutron/objects/ports.py
@@ -36,6 +36,22 @@ class PortBindingBase(base.NeutronDbObject):
36 'Port': {'port_id': 'id'}, 36 'Port': {'port_id': 'id'},
37 } 37 }
38 38
39 def update(self):
40 """Override to handle host update in Port Binding.
41 Delete old Port Binding entry, update the hostname and create new
42 Port Binding with all values saved in DB.
43 This is done due to host being a primary key, and OVO is not able
44 to update primary key fields.
45 """
46 if self.db_obj and self.host != self.db_obj.host:
47 with self.obj_context.session.begin(subtransactions=True):
48 old_obj = self._load_object(self.obj_context, self.db_obj)
49 old_obj.delete()
50 self._changed_fields = set(self.fields.keys())
51 self.create()
52 else:
53 super(PortBindingBase, self).update()
54
39 @classmethod 55 @classmethod
40 def modify_fields_to_db(cls, fields): 56 def modify_fields_to_db(cls, fields):
41 result = super(PortBindingBase, cls).modify_fields_to_db(fields) 57 result = super(PortBindingBase, cls).modify_fields_to_db(fields)
@@ -69,7 +85,7 @@ class PortBinding(PortBindingBase):
69 85
70 fields = { 86 fields = {
71 'port_id': common_types.UUIDField(), 87 'port_id': common_types.UUIDField(),
72 'host': obj_fields.StringField(), 88 'host': obj_fields.StringField(default=''),
73 'profile': common_types.DictOfMiscValuesField(), 89 'profile': common_types.DictOfMiscValuesField(),
74 'vif_type': obj_fields.StringField(), 90 'vif_type': obj_fields.StringField(),
75 'vif_details': common_types.DictOfMiscValuesField(nullable=True), 91 'vif_details': common_types.DictOfMiscValuesField(nullable=True),
diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py
index 7694efb..aa4e574 100644
--- a/neutron/plugins/ml2/db.py
+++ b/neutron/plugins/ml2/db.py
@@ -18,8 +18,8 @@ from neutron_lib.callbacks import events
18from neutron_lib.callbacks import registry 18from neutron_lib.callbacks import registry
19from neutron_lib.callbacks import resources 19from neutron_lib.callbacks import resources
20from neutron_lib import constants as n_const 20from neutron_lib import constants as n_const
21from neutron_lib.objects import exceptions
21from neutron_lib.plugins import directory 22from neutron_lib.plugins import directory
22from oslo_db import exception as db_exc
23from oslo_log import log 23from oslo_log import log
24from oslo_utils import uuidutils 24from oslo_utils import uuidutils
25import six 25import six
@@ -31,6 +31,7 @@ from neutron.db import api as db_api
31from neutron.db.models import securitygroup as sg_models 31from neutron.db.models import securitygroup as sg_models
32from neutron.db import models_v2 32from neutron.db import models_v2
33from neutron.objects import ports as port_obj 33from neutron.objects import ports as port_obj
34from neutron.objects import utils as obj_utils
34from neutron.plugins.ml2 import models 35from neutron.plugins.ml2 import models
35from neutron.services.segments import exceptions as seg_exc 36from neutron.services.segments import exceptions as seg_exc
36 37
@@ -42,11 +43,10 @@ MAX_PORTS_PER_QUERY = 500
42 43
43@db_api.context_manager.writer 44@db_api.context_manager.writer
44def add_port_binding(context, port_id): 45def add_port_binding(context, port_id):
45 record = models.PortBinding( 46 binding = port_obj.PortBinding(
46 port_id=port_id, 47 context, port_id=port_id, vif_type=portbindings.VIF_TYPE_UNBOUND)
47 vif_type=portbindings.VIF_TYPE_UNBOUND) 48 binding.create()
48 context.session.add(record) 49 return binding
49 return record
50 50
51 51
52@db_api.context_manager.writer 52@db_api.context_manager.writer
@@ -91,35 +91,32 @@ def clear_binding_levels(context, port_id, host):
91 91
92 92
93def ensure_distributed_port_binding(context, port_id, host, router_id=None): 93def ensure_distributed_port_binding(context, port_id, host, router_id=None):
94 with db_api.context_manager.reader.using(context): 94 binding_obj = port_obj.DistributedPortBinding.get_object(
95 record = (context.session.query(models.DistributedPortBinding). 95 context, port_id=port_id, host=host)
96 filter_by(port_id=port_id, host=host).first()) 96 if binding_obj:
97 if record: 97 return binding_obj
98 return record
99 98
100 try: 99 try:
101 with db_api.context_manager.writer.using(context): 100 binding_obj = port_obj.DistributedPortBinding(
102 record = models.DistributedPortBinding( 101 context,
103 port_id=port_id, 102 port_id=port_id,
104 host=host, 103 host=host,
105 router_id=router_id, 104 router_id=router_id,
106 vif_type=portbindings.VIF_TYPE_UNBOUND, 105 vif_type=portbindings.VIF_TYPE_UNBOUND,
107 vnic_type=portbindings.VNIC_NORMAL, 106 vnic_type=portbindings.VNIC_NORMAL,
108 status=n_const.PORT_STATUS_DOWN) 107 status=n_const.PORT_STATUS_DOWN)
109 context.session.add(record) 108 binding_obj.create()
110 return record 109 return binding_obj
111 except db_exc.DBDuplicateEntry: 110 except exceptions.NeutronDbObjectDuplicateEntry:
112 LOG.debug("Distributed Port %s already bound", port_id) 111 LOG.debug("Distributed Port %s already bound", port_id)
113 with db_api.context_manager.reader.using(context): 112 return port_obj.DistributedPortBinding.get_object(
114 return (context.session.query(models.DistributedPortBinding). 113 context, port_id=port_id, host=host)
115 filter_by(port_id=port_id, host=host).one())
116 114
117 115
118def delete_distributed_port_binding_if_stale(context, binding): 116def delete_distributed_port_binding_if_stale(context, binding):
119 if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN: 117 if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN:
120 with db_api.context_manager.writer.using(context): 118 LOG.debug("Distributed port: Deleting binding %s", binding)
121 LOG.debug("Distributed port: Deleting binding %s", binding) 119 binding.delete()
122 context.session.delete(binding)
123 120
124 121
125def get_port(context, port_id): 122def get_port(context, port_id):
@@ -212,29 +209,27 @@ def make_port_dict_with_security_groups(port, sec_groups):
212 209
213 210
214def get_port_binding_host(context, port_id): 211def get_port_binding_host(context, port_id):
215 try: 212 binding = port_obj.PortBinding.get_objects(
216 with db_api.context_manager.reader.using(context): 213 context, port_id=obj_utils.StringStarts(port_id))
217 query = (context.session.query(models.PortBinding). 214 if not binding:
218 filter(models.PortBinding.port_id.startswith(port_id)).
219 one())
220 except exc.NoResultFound:
221 LOG.debug("No binding found for port %(port_id)s", 215 LOG.debug("No binding found for port %(port_id)s",
222 {'port_id': port_id}) 216 {'port_id': port_id})
223 return 217 return
224 except exc.MultipleResultsFound: 218 if len(binding) > 1:
225 LOG.error("Multiple ports have port_id starting with %s", 219 LOG.error("Multiple ports have port_id starting with %s",
226 port_id) 220 port_id)
227 return 221 return
228 return query.host 222 return binding[0].host
229 223
230 224
231@db_api.context_manager.reader 225@db_api.context_manager.reader
232def generate_distributed_port_status(context, port_id): 226def generate_distributed_port_status(context, port_id):
233 # an OR'ed value of status assigned to parent port from the 227 # an OR'ed value of status assigned to parent port from the
234 # distributedportbinding bucket 228 # distributedportbinding bucket
235 query = context.session.query(models.DistributedPortBinding)
236 final_status = n_const.PORT_STATUS_BUILD 229 final_status = n_const.PORT_STATUS_BUILD
237 for bind in query.filter(models.DistributedPortBinding.port_id == port_id): 230 bindings = port_obj.DistributedPortBinding.get_objects(context,
231 port_id=port_id)
232 for bind in bindings:
238 if bind.status == n_const.PORT_STATUS_ACTIVE: 233 if bind.status == n_const.PORT_STATUS_ACTIVE:
239 return bind.status 234 return bind.status
240 elif bind.status == n_const.PORT_STATUS_DOWN: 235 elif bind.status == n_const.PORT_STATUS_DOWN:
@@ -243,10 +238,10 @@ def generate_distributed_port_status(context, port_id):
243 238
244 239
245def get_distributed_port_binding_by_host(context, port_id, host): 240def get_distributed_port_binding_by_host(context, port_id, host):
246 with db_api.context_manager.reader.using(context): 241 bindings = port_obj.DistributedPortBinding.get_objects(
247 binding = (context.session.query(models.DistributedPortBinding). 242 context, port_id=obj_utils.StringStarts(port_id), host=host)
248 filter(models.DistributedPortBinding.port_id.startswith(port_id), 243 binding = bindings.pop() if bindings else None
249 models.DistributedPortBinding.host == host).first()) 244
250 if not binding: 245 if not binding:
251 LOG.debug("No binding for distributed port %(port_id)s with host " 246 LOG.debug("No binding for distributed port %(port_id)s with host "
252 "%(host)s", {'port_id': port_id, 'host': host}) 247 "%(host)s", {'port_id': port_id, 'host': host})
@@ -254,10 +249,8 @@ def get_distributed_port_binding_by_host(context, port_id, host):
254 249
255 250
256def get_distributed_port_bindings(context, port_id): 251def get_distributed_port_bindings(context, port_id):
257 with db_api.context_manager.reader.using(context): 252 bindings = port_obj.DistributedPortBinding.get_objects(
258 bindings = (context.session.query(models.DistributedPortBinding). 253 context, port_id=obj_utils.StringStarts(port_id))
259 filter(models.DistributedPortBinding.port_id.startswith(
260 port_id)).all())
261 if not bindings: 254 if not bindings:
262 LOG.debug("No bindings for distributed port %s", port_id) 255 LOG.debug("No bindings for distributed port %s", port_id)
263 return bindings 256 return bindings
diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py
index 68da335..593a08f 100644
--- a/neutron/plugins/ml2/driver_context.py
+++ b/neutron/plugins/ml2/driver_context.py
@@ -17,7 +17,6 @@ from neutron_lib.api.definitions import portbindings
17from neutron_lib import constants 17from neutron_lib import constants
18from neutron_lib.plugins.ml2 import api 18from neutron_lib.plugins.ml2 import api
19from oslo_log import log 19from oslo_log import log
20from oslo_serialization import jsonutils
21import sqlalchemy 20import sqlalchemy
22 21
23from neutron.db import segments_db 22from neutron.db import segments_db
@@ -124,9 +123,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
124 else: 123 else:
125 self._network_context = NetworkContext( 124 self._network_context = NetworkContext(
126 plugin, plugin_context, network) if network else None 125 plugin, plugin_context, network) if network else None
127 # NOTE(kevinbenton): InstanceSnapshot can go away once we are working 126 self._binding = binding
128 # with OVO objects instead of native SQLA objects.
129 self._binding = InstanceSnapshot(binding)
130 self._binding_levels = [InstanceSnapshot(l) 127 self._binding_levels = [InstanceSnapshot(l)
131 for l in (binding_levels or [])] 128 for l in (binding_levels or [])]
132 self._segments_to_bind = None 129 self._segments_to_bind = None
@@ -295,7 +292,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
295 # TODO(rkukura) Verify binding allowed, segment in network 292 # TODO(rkukura) Verify binding allowed, segment in network
296 self._new_bound_segment = segment_id 293 self._new_bound_segment = segment_id
297 self._binding.vif_type = vif_type 294 self._binding.vif_type = vif_type
298 self._binding.vif_details = jsonutils.dumps(vif_details) 295 self._binding.vif_details = vif_details
299 self._new_port_status = status 296 self._new_port_status = status
300 297
301 def continue_binding(self, segment_id, next_segments_to_bind): 298 def continue_binding(self, segment_id, next_segments_to_bind):
diff --git a/neutron/plugins/ml2/models.py b/neutron/plugins/ml2/models.py
index 8922e21..9964048 100644
--- a/neutron/plugins/ml2/models.py
+++ b/neutron/plugins/ml2/models.py
@@ -125,6 +125,6 @@ class DistributedPortBinding(model_base.BASEV2):
125 models_v2.Port, 125 models_v2.Port,
126 load_on_pending=True, 126 load_on_pending=True,
127 backref=orm.backref("distributed_port_binding", 127 backref=orm.backref("distributed_port_binding",
128 lazy='subquery', 128 lazy='joined',
129 cascade='delete')) 129 cascade='delete'))
130 revises_on_change = ('port', ) 130 revises_on_change = ('port', )
diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py
index 3619e79..018fad3 100644
--- a/neutron/plugins/ml2/plugin.py
+++ b/neutron/plugins/ml2/plugin.py
@@ -82,6 +82,7 @@ from neutron.db import subnet_service_type_db_models as service_type_db
82from neutron.db import vlantransparent_db 82from neutron.db import vlantransparent_db
83from neutron.extensions import providernet as provider 83from neutron.extensions import providernet as provider
84from neutron.extensions import vlantransparent 84from neutron.extensions import vlantransparent
85from neutron.objects import ports as obj_port
85from neutron.plugins.common import utils as p_utils 86from neutron.plugins.common import utils as p_utils
86from neutron.plugins.ml2.common import exceptions as ml2_exc 87from neutron.plugins.ml2.common import exceptions as ml2_exc
87from neutron.plugins.ml2 import db 88from neutron.plugins.ml2 import db
@@ -314,7 +315,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
314 port = mech_context.current 315 port = mech_context.current
315 port_id = port['id'] 316 port_id = port['id']
316 changes = False 317 changes = False
317
318 host = const.ATTR_NOT_SPECIFIED 318 host = const.ATTR_NOT_SPECIFIED
319 if attrs and portbindings.HOST_ID in attrs: 319 if attrs and portbindings.HOST_ID in attrs:
320 host = attrs.get(portbindings.HOST_ID) or '' 320 host = attrs.get(portbindings.HOST_ID) or ''
@@ -338,8 +338,9 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
338 338
339 if profile not in (None, const.ATTR_NOT_SPECIFIED, 339 if profile not in (None, const.ATTR_NOT_SPECIFIED,
340 self._get_profile(binding)): 340 self._get_profile(binding)):
341 binding.profile = jsonutils.dumps(profile) 341 binding.profile = profile
342 if len(binding.profile) > models.BINDING_PROFILE_LEN: 342 if (len(jsonutils.dumps(binding.profile)) >
343 models.BINDING_PROFILE_LEN):
343 msg = _("binding:profile value too large") 344 msg = _("binding:profile value too large")
344 raise exc.InvalidInput(error_message=msg) 345 raise exc.InvalidInput(error_message=msg)
345 changes = True 346 changes = True
@@ -347,7 +348,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
347 # Unbind the port if needed. 348 # Unbind the port if needed.
348 if changes: 349 if changes:
349 binding.vif_type = portbindings.VIF_TYPE_UNBOUND 350 binding.vif_type = portbindings.VIF_TYPE_UNBOUND
350 binding.vif_details = '' 351 binding.vif_details = None
352 binding.update()
351 db.clear_binding_levels(plugin_context, port_id, original_host) 353 db.clear_binding_levels(plugin_context, port_id, original_host)
352 mech_context._clear_binding_levels() 354 mech_context._clear_binding_levels()
353 port['status'] = const.PORT_STATUS_DOWN 355 port['status'] = const.PORT_STATUS_DOWN
@@ -357,13 +359,14 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
357 359
358 if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: 360 if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
359 binding.vif_type = portbindings.VIF_TYPE_UNBOUND 361 binding.vif_type = portbindings.VIF_TYPE_UNBOUND
360 binding.vif_details = '' 362 binding.vif_details = None
361 db.clear_binding_levels(plugin_context, port_id, original_host) 363 db.clear_binding_levels(plugin_context, port_id, original_host)
362 mech_context._clear_binding_levels() 364 mech_context._clear_binding_levels()
363 binding.host = '' 365 binding.host = ''
366 binding.update()
364 367
365 self._update_port_dict_binding(port, binding) 368 self._update_port_dict_binding(port, binding)
366 binding.persist_state_to_session(plugin_context.session) 369 binding.update()
367 return changes 370 return changes
368 371
369 @db_api.retry_db_errors 372 @db_api.retry_db_errors
@@ -435,12 +438,15 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
435 # transaction. 438 # transaction.
436 port = orig_context.current 439 port = orig_context.current
437 orig_binding = orig_context._binding 440 orig_binding = orig_context._binding
438 new_binding = models.PortBinding( 441 profile = orig_binding.profile or {}
442 new_binding = obj_port.PortBinding(
443 orig_context._plugin_context,
444 port_id=orig_binding.port_id,
439 host=orig_binding.host, 445 host=orig_binding.host,
440 vnic_type=orig_binding.vnic_type, 446 vnic_type=orig_binding.vnic_type,
441 profile=orig_binding.profile, 447 profile=profile,
442 vif_type=portbindings.VIF_TYPE_UNBOUND, 448 vif_type=portbindings.VIF_TYPE_UNBOUND,
443 vif_details='' 449 vif_details=None
444 ) 450 )
445 self._update_port_dict_binding(port, new_binding) 451 self._update_port_dict_binding(port, new_binding)
446 new_context = driver_context.PortContext( 452 new_context = driver_context.PortContext(
@@ -477,7 +483,13 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
477 # mechanism driver update_port_*commit() calls. 483 # mechanism driver update_port_*commit() calls.
478 try: 484 try:
479 port_db = self._get_port(plugin_context, port_id) 485 port_db = self._get_port(plugin_context, port_id)
480 cur_binding = port_db.port_binding 486 plugin_context.session.refresh(port_db)
487 # TODO(korzen) replace get_objects with port_obj.binding when
488 # Port OVO is integrated in _get_port
489 bindings = obj_port.PortBinding.get_objects(
490 plugin_context, port_id=port_db.id,
491 status=const.ACTIVE)
492 cur_binding = bindings.pop() if bindings else None
481 except exc.PortNotFound: 493 except exc.PortNotFound:
482 port_db, cur_binding = None, None 494 port_db, cur_binding = None, None
483 if not port_db or not cur_binding: 495 if not port_db or not cur_binding:
@@ -544,10 +556,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
544 cur_binding.host) 556 cur_binding.host)
545 db.set_binding_levels(plugin_context, 557 db.set_binding_levels(plugin_context,
546 bind_context._binding_levels) 558 bind_context._binding_levels)
547 # refresh context with a snapshot of updated state 559 cur_context._binding = cur_binding
548 cur_context._binding = driver_context.InstanceSnapshot(
549 cur_binding)
550 cur_context._binding_levels = bind_context._binding_levels 560 cur_context._binding_levels = bind_context._binding_levels
561 cur_binding.update()
562 plugin_context.session.refresh(port_db)
551 563
552 # Update PortContext's port dictionary to reflect the 564 # Update PortContext's port dictionary to reflect the
553 # updated binding state. 565 # updated binding state.
@@ -598,6 +610,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
598 def _get_vif_details(self, binding): 610 def _get_vif_details(self, binding):
599 if binding.vif_details: 611 if binding.vif_details:
600 try: 612 try:
613 # TODO(lujinluo): remove isinstance check once we switch to
614 # objects for all operations.
615 if isinstance(binding.vif_details, dict):
616 return binding.vif_details
601 return jsonutils.loads(binding.vif_details) 617 return jsonutils.loads(binding.vif_details)
602 except Exception: 618 except Exception:
603 LOG.error("Serialized vif_details DB value '%(value)s' " 619 LOG.error("Serialized vif_details DB value '%(value)s' "
@@ -609,6 +625,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
609 def _get_profile(self, binding): 625 def _get_profile(self, binding):
610 if binding.profile: 626 if binding.profile:
611 try: 627 try:
628 # TODO(lujinluo): remove isinstance check once we switch to
629 # objects for all operations.
630 if isinstance(binding.profile, dict):
631 return binding.profile
612 return jsonutils.loads(binding.profile) 632 return jsonutils.loads(binding.profile)
613 except Exception: 633 except Exception:
614 LOG.error("Serialized profile DB value '%(value)s' for " 634 LOG.error("Serialized profile DB value '%(value)s' for "
@@ -1292,7 +1312,12 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1292 original_port=original_port) 1312 original_port=original_port)
1293 with db_api.context_manager.writer.using(context): 1313 with db_api.context_manager.writer.using(context):
1294 port_db = self._get_port(context, id) 1314 port_db = self._get_port(context, id)
1295 binding = port_db.port_binding 1315 context.session.refresh(port_db)
1316 # TODO(korzen) replace _get_objects with port_obj.binding when
1317 # Port OVO is integrated in _get_port
1318 bindings = obj_port.PortBinding.get_objects(
1319 context, port_id=port_db.id)
1320 binding = bindings.pop() if bindings else None
1296 if not binding: 1321 if not binding:
1297 raise exc.PortNotFound(port_id=id) 1322 raise exc.PortNotFound(port_id=id)
1298 mac_address_updated = self._check_mac_update_allowed( 1323 mac_address_updated = self._check_mac_update_allowed(
@@ -1431,19 +1456,21 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1431 binding = mech_context._binding 1456 binding = mech_context._binding
1432 port = mech_context.current 1457 port = mech_context.current
1433 port_id = port['id'] 1458 port_id = port['id']
1459 clear_host = None
1434 1460
1435 if binding.vif_type != portbindings.VIF_TYPE_UNBOUND: 1461 if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
1436 binding.vif_details = '' 1462 binding.vif_details = None
1437 binding.vif_type = portbindings.VIF_TYPE_UNBOUND 1463 binding.vif_type = portbindings.VIF_TYPE_UNBOUND
1438 if binding.host: 1464 if binding.host:
1439 db.clear_binding_levels(plugin_context, port_id, binding.host) 1465 db.clear_binding_levels(plugin_context, port_id, binding.host)
1440 binding.host = '' 1466 clear_host = ''
1441 1467
1442 self._update_port_dict_binding(port, binding) 1468 self._update_port_dict_binding(port, binding)
1443 binding.host = attrs and attrs.get(portbindings.HOST_ID) 1469 new_host = attrs and attrs.get(portbindings.HOST_ID) or clear_host
1444 binding.router_id = attrs and attrs.get('device_id') 1470 binding.router_id = attrs and attrs.get('device_id')
1445 # merge into session to reflect changes 1471 if new_host:
1446 binding.persist_state_to_session(plugin_context.session) 1472 binding.host = new_host
1473 binding.update()
1447 1474
1448 @utils.transaction_guard 1475 @utils.transaction_guard
1449 @db_api.retry_if_session_inactive() 1476 @db_api.retry_if_session_inactive()
@@ -1514,7 +1541,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1514 with db_api.context_manager.writer.using(context): 1541 with db_api.context_manager.writer.using(context):
1515 try: 1542 try:
1516 port_db = self._get_port(context, id) 1543 port_db = self._get_port(context, id)
1517 binding = port_db.port_binding 1544 # TODO(korzen) replace get_objects with port_obj.binding when
1545 # Port OVO is integrated in _get_port
1546 bindings = obj_port.PortBinding.get_objects(
1547 context, port_id=port_db.id)
1548 binding = bindings.pop() if bindings else None
1518 except exc.PortNotFound: 1549 except exc.PortNotFound:
1519 LOG.debug("The port '%s' was deleted", id) 1550 LOG.debug("The port '%s' was deleted", id)
1520 return 1551 return
@@ -1758,6 +1789,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1758 return 1789 return
1759 if binding.status != status: 1790 if binding.status != status:
1760 binding.status = status 1791 binding.status = status
1792 binding.update()
1761 updated = True 1793 updated = True
1762 1794
1763 if (updated and 1795 if (updated and
diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py
index 8a16b08..2492185 100644
--- a/neutron/tests/unit/objects/test_objects.py
+++ b/neutron/tests/unit/objects/test_objects.py
@@ -62,7 +62,7 @@ object_data = {
62 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 62 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
63 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8', 63 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8',
64 'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5', 64 'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5',
65 'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578', 65 'PortBinding': '1.0-0ad9727c4e72d609d5b4f70bcd3bc727',
66 'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060', 66 'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060',
67 'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2', 67 'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2',
68 'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023', 68 'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023',
diff --git a/neutron/tests/unit/plugins/ml2/test_db.py b/neutron/tests/unit/plugins/ml2/test_db.py
index 88ba641..8dc1b87 100644
--- a/neutron/tests/unit/plugins/ml2/test_db.py
+++ b/neutron/tests/unit/plugins/ml2/test_db.py
@@ -23,7 +23,6 @@ from neutron_lib import context
23from neutron_lib.plugins.ml2 import api 23from neutron_lib.plugins.ml2 import api
24from oslo_utils import uuidutils 24from oslo_utils import uuidutils
25from sqlalchemy.orm import exc 25from sqlalchemy.orm import exc
26from sqlalchemy.orm import query
27 26
28from neutron.db import api as db_api 27from neutron.db import api as db_api
29from neutron.db import db_base_plugin_v2 28from neutron.db import db_base_plugin_v2
@@ -33,7 +32,6 @@ from neutron.db import segments_db
33from neutron.objects import network as network_obj 32from neutron.objects import network as network_obj
34from neutron.objects import ports as port_obj 33from neutron.objects import ports as port_obj
35from neutron.plugins.ml2 import db as ml2_db 34from neutron.plugins.ml2 import db as ml2_db
36from neutron.plugins.ml2 import models
37from neutron.tests.unit import testlib_api 35from neutron.tests.unit import testlib_api
38 36
39 37
@@ -64,10 +62,8 @@ class Ml2DBTestCase(testlib_api.SqlTestCase):
64 return port 62 return port
65 63
66 def _setup_neutron_portbinding(self, port_id, vif_type, host): 64 def _setup_neutron_portbinding(self, port_id, vif_type, host):
67 with db_api.context_manager.writer.using(self.ctx): 65 port_obj.PortBinding(
68 self.ctx.session.add(models.PortBinding(port_id=port_id, 66 self.ctx, port_id=port_id, vif_type=vif_type, host=host).create()
69 vif_type=vif_type,
70 host=host))
71 67
72 @staticmethod 68 @staticmethod
73 def _sort_segments(segments): 69 def _sort_segments(segments):
@@ -318,44 +314,45 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
318 314
319 def _setup_distributed_binding(self, network_id, 315 def _setup_distributed_binding(self, network_id,
320 port_id, router_id, host_id): 316 port_id, router_id, host_id):
321 with db_api.context_manager.writer.using(self.ctx): 317 binding_obj = port_obj.DistributedPortBinding(
322 record = models.DistributedPortBinding( 318 self.ctx,
323 port_id=port_id, 319 port_id=port_id,
324 host=host_id, 320 host=host_id,
325 router_id=router_id, 321 router_id=router_id,
326 vif_type=portbindings.VIF_TYPE_UNBOUND, 322 vif_type=portbindings.VIF_TYPE_UNBOUND,
327 vnic_type=portbindings.VNIC_NORMAL, 323 vnic_type=portbindings.VNIC_NORMAL,
328 status='DOWN') 324 status='DOWN')
329 self.ctx.session.add(record) 325 binding_obj.create()
330 return record 326 return binding_obj
331 327
332 def test_ensure_distributed_port_binding_deals_with_db_duplicate(self): 328 def test_ensure_distributed_port_binding_deals_with_db_duplicate(self):
333 network_id = uuidutils.generate_uuid() 329 network_id = uuidutils.generate_uuid()
334 port_id = uuidutils.generate_uuid() 330 port_id = uuidutils.generate_uuid()
335 router_id = 'foo_router_id' 331 router_id = uuidutils.generate_uuid()
336 host_id = 'foo_host_id' 332 host_id = uuidutils.generate_uuid()
337 self._setup_neutron_network(network_id, [port_id]) 333 self._setup_neutron_network(network_id, [port_id])
338 self._setup_distributed_binding(network_id, port_id, 334 dpb = self._setup_distributed_binding(network_id, port_id,
339 router_id, host_id) 335 router_id, host_id)
340 with mock.patch.object(query.Query, 'first') as query_first: 336 with mock.patch.object(port_obj.DistributedPortBinding,
341 query_first.return_value = [] 337 'get_object') as get_object:
342 with mock.patch.object(ml2_db.LOG, 'debug') as log_trace: 338 get_object.side_effect = [None, dpb]
343 binding = ml2_db.ensure_distributed_port_binding( 339 binding = ml2_db.ensure_distributed_port_binding(
344 self.ctx, port_id, host_id, router_id) 340 self.ctx, port_id, host_id, router_id)
345 self.assertTrue(query_first.called) 341 self.assertTrue(get_object.called)
346 self.assertTrue(log_trace.called)
347 self.assertEqual(port_id, binding.port_id) 342 self.assertEqual(port_id, binding.port_id)
348 343
349 def test_ensure_distributed_port_binding(self): 344 def test_ensure_distributed_port_binding(self):
350 network_id = uuidutils.generate_uuid() 345 network_id = uuidutils.generate_uuid()
351 port_id = uuidutils.generate_uuid() 346 expected_port_id = uuidutils.generate_uuid()
352 self._setup_neutron_network(network_id, [port_id]) 347 self._setup_neutron_network(network_id, [expected_port_id])
353 router = self._setup_neutron_router() 348 router = self._setup_neutron_router()
354 ml2_db.ensure_distributed_port_binding( 349 ml2_db.ensure_distributed_port_binding(
355 self.ctx, port_id, 'foo_host', router.id) 350 self.ctx, expected_port_id, 'foo_host', router.id)
356 expected = (self.ctx.session.query(models.DistributedPortBinding). 351 actual_objs = port_obj.DistributedPortBinding.get_objects(
357 filter_by(port_id=port_id).one()) 352 self.ctx, port_id=expected_port_id)
358 self.assertEqual(port_id, expected.port_id) 353 self.assertEqual(1, len(actual_objs))
354 actual_obj = actual_objs.pop()
355 self.assertEqual(expected_port_id, actual_obj.port_id)
359 356
360 def test_ensure_distributed_port_binding_multiple_bindings(self): 357 def test_ensure_distributed_port_binding_multiple_bindings(self):
361 network_id = uuidutils.generate_uuid() 358 network_id = uuidutils.generate_uuid()
@@ -366,9 +363,9 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
366 self.ctx, port_id, 'foo_host_1', router.id) 363 self.ctx, port_id, 'foo_host_1', router.id)
367 ml2_db.ensure_distributed_port_binding( 364 ml2_db.ensure_distributed_port_binding(
368 self.ctx, port_id, 'foo_host_2', router.id) 365 self.ctx, port_id, 'foo_host_2', router.id)
369 bindings = (self.ctx.session.query(models.DistributedPortBinding). 366 count_objs = port_obj.DistributedPortBinding.count(
370 filter_by(port_id=port_id).all()) 367 self.ctx, port_id=port_id)
371 self.assertEqual(2, len(bindings)) 368 self.assertEqual(2, count_objs)
372 369
373 def test_delete_distributed_port_binding_if_stale(self): 370 def test_delete_distributed_port_binding_if_stale(self):
374 network_id = uuidutils.generate_uuid() 371 network_id = uuidutils.generate_uuid()
@@ -377,21 +374,23 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
377 binding = self._setup_distributed_binding( 374 binding = self._setup_distributed_binding(
378 network_id, port_id, None, 'foo_host_id') 375 network_id, port_id, None, 'foo_host_id')
379 376
380 ml2_db.delete_distributed_port_binding_if_stale(self.ctx, 377 ml2_db.delete_distributed_port_binding_if_stale(self.ctx, binding)
381 binding) 378
382 count = (self.ctx.session.query(models.DistributedPortBinding). 379 obj_exists = port_obj.DistributedPortBinding.objects_exist(
383 filter_by(port_id=binding.port_id).count()) 380 self.ctx, port_id=binding.port_id)
384 self.assertFalse(count) 381 self.assertFalse(obj_exists)
385 382
386 def test_get_distributed_port_binding_by_host_not_found(self): 383 def test_get_distributed_port_binding_by_host_not_found(self):
384 port_id = uuidutils.generate_uuid()
385 host_id = uuidutils.generate_uuid()
387 port = ml2_db.get_distributed_port_binding_by_host( 386 port = ml2_db.get_distributed_port_binding_by_host(
388 self.ctx, 'foo_port_id', 'foo_host_id') 387 self.ctx, port_id, host_id)
389 self.assertIsNone(port) 388 self.assertIsNone(port)
390 389
391 def test_get_distributed_port_bindings_not_found(self): 390 def test_get_distributed_port_bindings_not_found(self):
392 port = ml2_db.get_distributed_port_bindings(self.ctx, 391 port = ml2_db.get_distributed_port_bindings(self.ctx,
393 'foo_port_id') 392 uuidutils.generate_uuid())
394 self.assertFalse(len(port)) 393 self.assertEqual(0, len(port))
395 394
396 def test_get_distributed_port_bindings(self): 395 def test_get_distributed_port_bindings(self):
397 network_id = uuidutils.generate_uuid() 396 network_id = uuidutils.generate_uuid()
@@ -412,8 +411,9 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
412 network_obj.Network(self.ctx, id=network_id).create() 411 network_obj.Network(self.ctx, id=network_id).create()
413 with db_api.context_manager.writer.using(self.ctx): 412 with db_api.context_manager.writer.using(self.ctx):
414 device_owner = constants.DEVICE_OWNER_DVR_INTERFACE 413 device_owner = constants.DEVICE_OWNER_DVR_INTERFACE
414 port_id = uuidutils.generate_uuid()
415 port = models_v2.Port( 415 port = models_v2.Port(
416 id='port_id', 416 id=port_id,
417 network_id=network_id, 417 network_id=network_id,
418 mac_address='00:11:22:33:44:55', 418 mac_address='00:11:22:33:44:55',
419 admin_state_up=True, 419 admin_state_up=True,
@@ -421,23 +421,20 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
421 device_id='device_id', 421 device_id='device_id',
422 device_owner=device_owner) 422 device_owner=device_owner)
423 self.ctx.session.add(port) 423 self.ctx.session.add(port)
424 binding_kwarg = { 424 binding_kwarg = {
425 'port_id': 'port_id', 425 'port_id': port_id,
426 'host': 'host', 426 'host': 'host',
427 'vif_type': portbindings.VIF_TYPE_UNBOUND, 427 'vif_type': portbindings.VIF_TYPE_UNBOUND,
428 'vnic_type': portbindings.VNIC_NORMAL, 428 'vnic_type': portbindings.VNIC_NORMAL,
429 'router_id': 'router_id', 429 'router_id': 'router_id',
430 'status': constants.PORT_STATUS_DOWN 430 'status': constants.PORT_STATUS_DOWN
431 } 431 }
432 self.ctx.session.add(models.DistributedPortBinding( 432 port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create()
433 **binding_kwarg)) 433 binding_kwarg['host'] = 'another-host'
434 binding_kwarg['host'] = 'another-host' 434 port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create()
435 self.ctx.session.add(models.DistributedPortBinding(
436 **binding_kwarg))
437 with warnings.catch_warnings(record=True) as warning_list: 435 with warnings.catch_warnings(record=True) as warning_list:
438 with db_api.context_manager.writer.using(self.ctx): 436 with db_api.context_manager.writer.using(self.ctx):
439 self.ctx.session.delete(port) 437 self.ctx.session.delete(port)
440 self.assertEqual([], warning_list) 438 self.assertEqual([], warning_list)
441 ports = ml2_db.get_distributed_port_bindings(self.ctx, 439 bindings = ml2_db.get_distributed_port_bindings(self.ctx, port_id)
442 'port_id') 440 self.assertEqual(0, len(bindings))
443 self.assertEqual(0, len(ports))
diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py
index 4456d8a..580afe8 100644
--- a/neutron/tests/unit/plugins/ml2/test_plugin.py
+++ b/neutron/tests/unit/plugins/ml2/test_plugin.py
@@ -46,6 +46,7 @@ from neutron.db import provisioning_blocks
46from neutron.db import segments_db 46from neutron.db import segments_db
47from neutron.extensions import multiprovidernet as mpnet 47from neutron.extensions import multiprovidernet as mpnet
48from neutron.objects import base as base_obj 48from neutron.objects import base as base_obj
49from neutron.objects import ports as obj_port
49from neutron.objects import router as l3_obj 50from neutron.objects import router as l3_obj
50from neutron.plugins.ml2.common import exceptions as ml2_exc 51from neutron.plugins.ml2.common import exceptions as ml2_exc
51from neutron.plugins.ml2 import db as ml2_db 52from neutron.plugins.ml2 import db as ml2_db
@@ -1629,9 +1630,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1629 # create a port and delete it so we have an expired mechanism context 1630 # create a port and delete it so we have an expired mechanism context
1630 with self.port() as port: 1631 with self.port() as port:
1631 plugin = directory.get_plugin() 1632 plugin = directory.get_plugin()
1632 binding = plugin._get_port(self.context, 1633 binding = obj_port.PortBinding.get_object(
1633 port['port']['id']).port_binding 1634 self.context, port_id=port['port']['id'], host='')
1634 binding['host'] = 'test' 1635 binding.host = 'test'
1636 binding.update()
1635 mech_context = driver_context.PortContext( 1637 mech_context = driver_context.PortContext(
1636 plugin, self.context, port['port'], 1638 plugin, self.context, port['port'],
1637 plugin.get_network(self.context, port['port']['network_id']), 1639 plugin.get_network(self.context, port['port']['network_id']),
@@ -1650,10 +1652,11 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1650 def _create_port_and_bound_context(self, port_vif_type, bound_vif_type): 1652 def _create_port_and_bound_context(self, port_vif_type, bound_vif_type):
1651 with self.port() as port: 1653 with self.port() as port:
1652 plugin = directory.get_plugin() 1654 plugin = directory.get_plugin()
1653 binding = plugin._get_port( 1655 binding = obj_port.PortBinding.get_object(
1654 self.context, port['port']['id']).port_binding 1656 self.context, port_id=port['port']['id'], host='')
1655 binding['host'] = 'fake_host' 1657 binding.host = 'fake_host'
1656 binding['vif_type'] = port_vif_type 1658 binding['vif_type'] = port_vif_type
1659 binding.update()
1657 # Generates port context to be used before the bind. 1660 # Generates port context to be used before the bind.
1658 port_context = driver_context.PortContext( 1661 port_context = driver_context.PortContext(
1659 plugin, self.context, port['port'], 1662 plugin, self.context, port['port'],
@@ -1765,10 +1768,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1765 def test_update_port_binding_host_id_none(self): 1768 def test_update_port_binding_host_id_none(self):
1766 with self.port() as port: 1769 with self.port() as port:
1767 plugin = directory.get_plugin() 1770 plugin = directory.get_plugin()
1768 binding = plugin._get_port( 1771 binding = obj_port.PortBinding.get_object(
1769 self.context, port['port']['id']).port_binding 1772 self.context, port_id=port['port']['id'], host='')
1770 with self.context.session.begin(subtransactions=True): 1773 binding.host = 'test'
1771 binding.host = 'test' 1774 binding.update()
1772 mech_context = driver_context.PortContext( 1775 mech_context = driver_context.PortContext(
1773 plugin, self.context, port['port'], 1776 plugin, self.context, port['port'],
1774 plugin.get_network(self.context, port['port']['network_id']), 1777 plugin.get_network(self.context, port['port']['network_id']),
@@ -1779,15 +1782,18 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1779 self.assertEqual('test', binding.host) 1782 self.assertEqual('test', binding.host)
1780 with self.context.session.begin(subtransactions=True): 1783 with self.context.session.begin(subtransactions=True):
1781 plugin._process_port_binding(mech_context, attrs) 1784 plugin._process_port_binding(mech_context, attrs)
1785 updated_binding = obj_port.PortBinding.get_objects(self.context,
1786 port_id=port['port']['id']).pop()
1782 self.assertTrue(update_mock.mock_calls) 1787 self.assertTrue(update_mock.mock_calls)
1783 self.assertEqual('', binding.host) 1788 self.assertEqual('', updated_binding.host)
1784 1789
1785 def test_update_port_binding_host_id_not_changed(self): 1790 def test_update_port_binding_host_id_not_changed(self):
1786 with self.port() as port: 1791 with self.port() as port:
1787 plugin = directory.get_plugin() 1792 plugin = directory.get_plugin()
1788 binding = plugin._get_port( 1793 binding = obj_port.PortBinding.get_object(
1789 self.context, port['port']['id']).port_binding 1794 self.context, port_id=port['port']['id'], host='')
1790 binding['host'] = 'test' 1795 binding.host = 'test'
1796 binding.update()
1791 mech_context = driver_context.PortContext( 1797 mech_context = driver_context.PortContext(
1792 plugin, self.context, port['port'], 1798 plugin, self.context, port['port'],
1793 plugin.get_network(self.context, port['port']['network_id']), 1799 plugin.get_network(self.context, port['port']['network_id']),
@@ -1800,30 +1806,34 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1800 self.assertEqual('test', binding.host) 1806 self.assertEqual('test', binding.host)
1801 1807
1802 def test_process_distributed_port_binding_update_router_id(self): 1808 def test_process_distributed_port_binding_update_router_id(self):
1803 host_id = 'host' 1809 with self.port() as port:
1804 binding = models.DistributedPortBinding( 1810 host_id = 'host'
1805 port_id='port_id', 1811 ctxt = context.get_admin_context()
1806 host=host_id, 1812 binding_obj = obj_port.DistributedPortBinding(
1807 router_id='old_router_id', 1813 ctxt,
1808 vif_type=portbindings.VIF_TYPE_OVS, 1814 port_id=port['port']['id'],
1809 vnic_type=portbindings.VNIC_NORMAL, 1815 host=host_id,
1810 status=constants.PORT_STATUS_DOWN) 1816 profile={},
1811 plugin = directory.get_plugin() 1817 router_id='old_router_id',
1812 mock_network = {'id': 'net_id'} 1818 vif_type=portbindings.VIF_TYPE_OVS,
1813 mock_port = {'id': 'port_id'} 1819 vnic_type=portbindings.VNIC_NORMAL,
1814 ctxt = context.get_admin_context() 1820 status=constants.PORT_STATUS_DOWN)
1815 new_router_id = 'new_router' 1821 binding_obj.create()
1816 attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id} 1822 plugin = directory.get_plugin()
1817 with mock.patch.object(plugin, '_update_port_dict_binding'): 1823 mock_network = {'id': 'net_id'}
1818 with mock.patch.object(segments_db, 'get_network_segments', 1824 mock_port = {'id': 'port_id'}
1819 return_value=[]): 1825 new_router_id = 'new_router'
1820 mech_context = driver_context.PortContext( 1826 attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id}
1821 self, ctxt, mock_port, mock_network, binding, None) 1827 with mock.patch.object(plugin, '_update_port_dict_binding'):
1822 plugin._process_distributed_port_binding(mech_context, 1828 with mock.patch.object(segments_db, 'get_network_segments',
1823 ctxt, attrs) 1829 return_value=[]):
1824 self.assertEqual(new_router_id, 1830 mech_context = driver_context.PortContext(
1825 mech_context._binding.router_id) 1831 self, ctxt, mock_port, mock_network, binding_obj, None)
1826 self.assertEqual(host_id, mech_context._binding.host) 1832 plugin._process_distributed_port_binding(mech_context,
1833 ctxt, attrs)
1834 self.assertEqual(new_router_id,
1835 mech_context._binding.router_id)
1836 self.assertEqual(host_id, mech_context._binding.host)
1827 1837
1828 def test_update_distributed_port_binding_on_concurrent_port_delete(self): 1838 def test_update_distributed_port_binding_on_concurrent_port_delete(self):
1829 plugin = directory.get_plugin() 1839 plugin = directory.get_plugin()
@@ -1854,9 +1864,20 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1854 def test__bind_port_original_port_set(self): 1864 def test__bind_port_original_port_set(self):
1855 plugin = directory.get_plugin() 1865 plugin = directory.get_plugin()
1856 plugin.mechanism_manager = mock.Mock() 1866 plugin.mechanism_manager = mock.Mock()
1857 mock_port = {'id': 'port_id'} 1867 mock_port = {'id': uuidutils.generate_uuid()}
1858 context = mock.Mock() 1868 context = mock.Mock()
1869 binding_obj = obj_port.DistributedPortBinding(
1870 mock.MagicMock(),
1871 port_id=mock_port['id'],
1872 host='vm_host',
1873 profile={},
1874 router_id='old_router_id',
1875 vif_type='',
1876 vnic_type=portbindings.VNIC_NORMAL,
1877 status=constants.PORT_STATUS_DOWN)
1878 binding_obj.create()
1859 context.network.current = {'id': 'net_id'} 1879 context.network.current = {'id': 'net_id'}
1880 context._binding = binding_obj
1860 context.original = mock_port 1881 context.original = mock_port
1861 with mock.patch.object(plugin, '_update_port_dict_binding'), \ 1882 with mock.patch.object(plugin, '_update_port_dict_binding'), \
1862 mock.patch.object(segments_db, 'get_network_segments', 1883 mock.patch.object(segments_db, 'get_network_segments',
@@ -2532,13 +2553,15 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
2532 def test_update_distributed_router_interface_port(self): 2553 def test_update_distributed_router_interface_port(self):
2533 """Test validate distributed router interface update succeeds.""" 2554 """Test validate distributed router interface update succeeds."""
2534 host_id = 'host' 2555 host_id = 'host'
2535 binding = models.DistributedPortBinding( 2556 binding_obj = obj_port.DistributedPortBinding(
2536 port_id='port_id', 2557 mock.MagicMock(),
2537 host=host_id, 2558 port_id=uuidutils.generate_uuid(),
2538 router_id='old_router_id', 2559 host=host_id,
2539 vif_type=portbindings.VIF_TYPE_OVS, 2560 router_id='old_router_id',
2540 vnic_type=portbindings.VNIC_NORMAL, 2561 vif_type=portbindings.VIF_TYPE_OVS,
2541 status=constants.PORT_STATUS_DOWN) 2562 vnic_type=portbindings.VNIC_NORMAL,
2563 status=constants.PORT_STATUS_DOWN)
2564 binding_obj.create()
2542 with mock.patch.object( 2565 with mock.patch.object(
2543 mech_test.TestMechanismDriver, 2566 mech_test.TestMechanismDriver,
2544 'update_port_postcommit', 2567 'update_port_postcommit',
@@ -2548,7 +2571,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
2548 'update_port_precommit') as port_pre,\ 2571 'update_port_precommit') as port_pre,\
2549 mock.patch.object( 2572 mock.patch.object(
2550 ml2_db, 'get_distributed_port_bindings') as dist_bindings: 2573 ml2_db, 'get_distributed_port_bindings') as dist_bindings:
2551 dist_bindings.return_value = [binding] 2574 dist_bindings.return_value = [binding_obj]
2552 port_pre.return_value = True 2575 port_pre.return_value = True
2553 with self.network() as network: 2576 with self.network() as network:
2554 with self.subnet(network=network) as subnet: 2577 with self.subnet(network=network) as subnet:
@@ -2771,9 +2794,10 @@ class TestML2Segments(Ml2PluginV2TestCase):
2771 # add writer here to make sure that the following operations are 2794 # add writer here to make sure that the following operations are
2772 # performed in the same session 2795 # performed in the same session
2773 with db_api.context_manager.writer.using(self.context): 2796 with db_api.context_manager.writer.using(self.context):
2774 binding = plugin._get_port( 2797 binding = obj_port.PortBinding.get_object(
2775 self.context, port['port']['id']).port_binding 2798 self.context, port_id=port['port']['id'], host='')
2776 binding['host'] = 'host-ovs-no_filter' 2799 binding.host = 'host-ovs-no_filter'
2800 binding.update()
2777 mech_context = driver_context.PortContext( 2801 mech_context = driver_context.PortContext(
2778 plugin, self.context, port['port'], 2802 plugin, self.context, port['port'],
2779 plugin.get_network(self.context, 2803 plugin.get_network(self.context,
diff --git a/neutron/tests/unit/plugins/ml2/test_port_binding.py b/neutron/tests/unit/plugins/ml2/test_port_binding.py
index a26ed5e..c0b734d 100644
--- a/neutron/tests/unit/plugins/ml2/test_port_binding.py
+++ b/neutron/tests/unit/plugins/ml2/test_port_binding.py
@@ -19,11 +19,10 @@ from neutron_lib import constants as const
19from neutron_lib import context 19from neutron_lib import context
20from neutron_lib.plugins import directory 20from neutron_lib.plugins import directory
21from oslo_config import cfg 21from oslo_config import cfg
22from oslo_serialization import jsonutils
23 22
24from neutron.conf.plugins.ml2.drivers import driver_type 23from neutron.conf.plugins.ml2.drivers import driver_type
24from neutron.objects import ports as obj_port
25from neutron.plugins.ml2 import driver_context 25from neutron.plugins.ml2 import driver_context
26from neutron.plugins.ml2 import models as ml2_models
27from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin 26from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
28 27
29 28
@@ -111,10 +110,8 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
111 ctx = context.get_admin_context() 110 ctx = context.get_admin_context()
112 with self.port(name='name') as port: 111 with self.port(name='name') as port:
113 # emulating concurrent binding deletion 112 # emulating concurrent binding deletion
114 with ctx.session.begin(): 113 obj_port.PortBinding.delete_objects(
115 for item in (ctx.session.query(ml2_models.PortBinding). 114 ctx, port_id=port['port']['id'])
116 filter_by(port_id=port['port']['id'])):
117 ctx.session.delete(item)
118 self.assertIsNone( 115 self.assertIsNone(
119 self.plugin.get_bound_port_context(ctx, port['port']['id'])) 116 self.plugin.get_bound_port_context(ctx, port['port']['id']))
120 117
@@ -191,13 +188,9 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
191 attrs['binding:host_id'] = 'host2' 188 attrs['binding:host_id'] = 'host2'
192 updated_port = attrs.copy() 189 updated_port = attrs.copy()
193 network = {'id': attrs['network_id']} 190 network = {'id': attrs['network_id']}
194 binding = ml2_models.PortBinding( 191 binding = obj_port.PortBinding.get_object(
195 port_id=original_port['id'], 192 ctx, port_id=original_port['id'],
196 host=original_port['binding:host_id'], 193 host=original_port['binding:host_id'])
197 vnic_type=original_port['binding:vnic_type'],
198 profile=jsonutils.dumps(original_port['binding:profile']),
199 vif_type=original_port['binding:vif_type'],
200 vif_details=original_port['binding:vif_details'])
201 levels = [] 194 levels = []
202 mech_context = driver_context.PortContext( 195 mech_context = driver_context.PortContext(
203 plugin, ctx, updated_port, network, binding, levels, 196 plugin, ctx, updated_port, network, binding, levels,