Browse Source

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
tags/12.0.0.0b3
Lujin 2 years ago
parent
commit
febeaf5d40

+ 14
- 2
neutron/objects/base.py View File

@@ -27,6 +27,7 @@ from oslo_versionedobjects import base as obj_base
27 27
 from oslo_versionedobjects import exception as obj_exception
28 28
 from oslo_versionedobjects import fields as obj_fields
29 29
 import six
30
+from sqlalchemy import exc as sql_exc
30 31
 
31 32
 from neutron._i18n import _
32 33
 from neutron.db import api as db_api
@@ -303,7 +304,11 @@ def _detach_db_obj(func):
303 304
             # TODO(ihrachys) consider refreshing just changed attributes
304 305
             self.obj_context.session.refresh(self.db_obj)
305 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 312
         return res
308 313
     return decorator
309 314
 
@@ -330,6 +335,8 @@ class DeclarativeObject(abc.ABCMeta):
330 335
                 if key in cls.fields or key in cls.obj_extra_fields:
331 336
                     fields_no_update_set.add(key)
332 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 341
         model = getattr(cls, 'db_model', None)
335 342
         if model:
@@ -480,7 +487,12 @@ class NeutronDbObject(NeutronObject):
480 487
         obj = cls(context)
481 488
         obj.from_db_object(db_obj)
482 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 496
         return obj
485 497
 
486 498
     def obj_load_attr(self, attrname):

+ 17
- 1
neutron/objects/ports.py View File

@@ -36,6 +36,22 @@ class PortBindingBase(base.NeutronDbObject):
36 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 55
     @classmethod
40 56
     def modify_fields_to_db(cls, fields):
41 57
         result = super(PortBindingBase, cls).modify_fields_to_db(fields)
@@ -69,7 +85,7 @@ class PortBinding(PortBindingBase):
69 85
 
70 86
     fields = {
71 87
         'port_id': common_types.UUIDField(),
72
-        'host': obj_fields.StringField(),
88
+        'host': obj_fields.StringField(default=''),
73 89
         'profile': common_types.DictOfMiscValuesField(),
74 90
         'vif_type': obj_fields.StringField(),
75 91
         'vif_details': common_types.DictOfMiscValuesField(nullable=True),

+ 39
- 46
neutron/plugins/ml2/db.py View File

@@ -18,8 +18,8 @@ from neutron_lib.callbacks import events
18 18
 from neutron_lib.callbacks import registry
19 19
 from neutron_lib.callbacks import resources
20 20
 from neutron_lib import constants as n_const
21
+from neutron_lib.objects import exceptions
21 22
 from neutron_lib.plugins import directory
22
-from oslo_db import exception as db_exc
23 23
 from oslo_log import log
24 24
 from oslo_utils import uuidutils
25 25
 import six
@@ -31,6 +31,7 @@ from neutron.db import api as db_api
31 31
 from neutron.db.models import securitygroup as sg_models
32 32
 from neutron.db import models_v2
33 33
 from neutron.objects import ports as port_obj
34
+from neutron.objects import utils as obj_utils
34 35
 from neutron.plugins.ml2 import models
35 36
 from neutron.services.segments import exceptions as seg_exc
36 37
 
@@ -42,11 +43,10 @@ MAX_PORTS_PER_QUERY = 500
42 43
 
43 44
 @db_api.context_manager.writer
44 45
 def add_port_binding(context, port_id):
45
-    record = models.PortBinding(
46
-        port_id=port_id,
47
-        vif_type=portbindings.VIF_TYPE_UNBOUND)
48
-    context.session.add(record)
49
-    return record
46
+    binding = port_obj.PortBinding(
47
+        context, port_id=port_id, vif_type=portbindings.VIF_TYPE_UNBOUND)
48
+    binding.create()
49
+    return binding
50 50
 
51 51
 
52 52
 @db_api.context_manager.writer
@@ -91,35 +91,32 @@ def clear_binding_levels(context, port_id, host):
91 91
 
92 92
 
93 93
 def ensure_distributed_port_binding(context, port_id, host, router_id=None):
94
-    with db_api.context_manager.reader.using(context):
95
-        record = (context.session.query(models.DistributedPortBinding).
96
-                  filter_by(port_id=port_id, host=host).first())
97
-    if record:
98
-        return record
94
+    binding_obj = port_obj.DistributedPortBinding.get_object(
95
+        context, port_id=port_id, host=host)
96
+    if binding_obj:
97
+        return binding_obj
99 98
 
100 99
     try:
101
-        with db_api.context_manager.writer.using(context):
102
-            record = models.DistributedPortBinding(
103
-                port_id=port_id,
104
-                host=host,
105
-                router_id=router_id,
106
-                vif_type=portbindings.VIF_TYPE_UNBOUND,
107
-                vnic_type=portbindings.VNIC_NORMAL,
108
-                status=n_const.PORT_STATUS_DOWN)
109
-            context.session.add(record)
110
-            return record
111
-    except db_exc.DBDuplicateEntry:
100
+        binding_obj = port_obj.DistributedPortBinding(
101
+            context,
102
+            port_id=port_id,
103
+            host=host,
104
+            router_id=router_id,
105
+            vif_type=portbindings.VIF_TYPE_UNBOUND,
106
+            vnic_type=portbindings.VNIC_NORMAL,
107
+            status=n_const.PORT_STATUS_DOWN)
108
+        binding_obj.create()
109
+        return binding_obj
110
+    except exceptions.NeutronDbObjectDuplicateEntry:
112 111
         LOG.debug("Distributed Port %s already bound", port_id)
113
-        with db_api.context_manager.reader.using(context):
114
-            return (context.session.query(models.DistributedPortBinding).
115
-                    filter_by(port_id=port_id, host=host).one())
112
+        return port_obj.DistributedPortBinding.get_object(
113
+            context, port_id=port_id, host=host)
116 114
 
117 115
 
118 116
 def delete_distributed_port_binding_if_stale(context, binding):
119 117
     if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN:
120
-        with db_api.context_manager.writer.using(context):
121
-            LOG.debug("Distributed port: Deleting binding %s", binding)
122
-            context.session.delete(binding)
118
+        LOG.debug("Distributed port: Deleting binding %s", binding)
119
+        binding.delete()
123 120
 
124 121
 
125 122
 def get_port(context, port_id):
@@ -212,29 +209,27 @@ def make_port_dict_with_security_groups(port, sec_groups):
212 209
 
213 210
 
214 211
 def get_port_binding_host(context, port_id):
215
-    try:
216
-        with db_api.context_manager.reader.using(context):
217
-            query = (context.session.query(models.PortBinding).
218
-                     filter(models.PortBinding.port_id.startswith(port_id)).
219
-                     one())
220
-    except exc.NoResultFound:
212
+    binding = port_obj.PortBinding.get_objects(
213
+        context, port_id=obj_utils.StringStarts(port_id))
214
+    if not binding:
221 215
         LOG.debug("No binding found for port %(port_id)s",
222 216
                   {'port_id': port_id})
223 217
         return
224
-    except exc.MultipleResultsFound:
218
+    if len(binding) > 1:
225 219
         LOG.error("Multiple ports have port_id starting with %s",
226 220
                   port_id)
227 221
         return
228
-    return query.host
222
+    return binding[0].host
229 223
 
230 224
 
231 225
 @db_api.context_manager.reader
232 226
 def generate_distributed_port_status(context, port_id):
233 227
     # an OR'ed value of status assigned to parent port from the
234 228
     # distributedportbinding bucket
235
-    query = context.session.query(models.DistributedPortBinding)
236 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 233
         if bind.status == n_const.PORT_STATUS_ACTIVE:
239 234
             return bind.status
240 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
 
245 240
 def get_distributed_port_binding_by_host(context, port_id, host):
246
-    with db_api.context_manager.reader.using(context):
247
-        binding = (context.session.query(models.DistributedPortBinding).
248
-            filter(models.DistributedPortBinding.port_id.startswith(port_id),
249
-                   models.DistributedPortBinding.host == host).first())
241
+    bindings = port_obj.DistributedPortBinding.get_objects(
242
+        context, port_id=obj_utils.StringStarts(port_id), host=host)
243
+    binding = bindings.pop() if bindings else None
244
+
250 245
     if not binding:
251 246
         LOG.debug("No binding for distributed port %(port_id)s with host "
252 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
 
256 251
 def get_distributed_port_bindings(context, port_id):
257
-    with db_api.context_manager.reader.using(context):
258
-        bindings = (context.session.query(models.DistributedPortBinding).
259
-                    filter(models.DistributedPortBinding.port_id.startswith(
260
-                           port_id)).all())
252
+    bindings = port_obj.DistributedPortBinding.get_objects(
253
+        context, port_id=obj_utils.StringStarts(port_id))
261 254
     if not bindings:
262 255
         LOG.debug("No bindings for distributed port %s", port_id)
263 256
     return bindings

+ 2
- 5
neutron/plugins/ml2/driver_context.py View File

@@ -17,7 +17,6 @@ from neutron_lib.api.definitions import portbindings
17 17
 from neutron_lib import constants
18 18
 from neutron_lib.plugins.ml2 import api
19 19
 from oslo_log import log
20
-from oslo_serialization import jsonutils
21 20
 import sqlalchemy
22 21
 
23 22
 from neutron.db import segments_db
@@ -124,9 +123,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
124 123
         else:
125 124
             self._network_context = NetworkContext(
126 125
                 plugin, plugin_context, network) if network else None
127
-        # NOTE(kevinbenton): InstanceSnapshot can go away once we are working
128
-        # with OVO objects instead of native SQLA objects.
129
-        self._binding = InstanceSnapshot(binding)
126
+        self._binding = binding
130 127
         self._binding_levels = [InstanceSnapshot(l)
131 128
                                 for l in (binding_levels or [])]
132 129
         self._segments_to_bind = None
@@ -295,7 +292,7 @@ class PortContext(MechanismDriverContext, api.PortContext):
295 292
         # TODO(rkukura) Verify binding allowed, segment in network
296 293
         self._new_bound_segment = segment_id
297 294
         self._binding.vif_type = vif_type
298
-        self._binding.vif_details = jsonutils.dumps(vif_details)
295
+        self._binding.vif_details = vif_details
299 296
         self._new_port_status = status
300 297
 
301 298
     def continue_binding(self, segment_id, next_segments_to_bind):

+ 1
- 1
neutron/plugins/ml2/models.py View File

@@ -125,6 +125,6 @@ class DistributedPortBinding(model_base.BASEV2):
125 125
         models_v2.Port,
126 126
         load_on_pending=True,
127 127
         backref=orm.backref("distributed_port_binding",
128
-                            lazy='subquery',
128
+                            lazy='joined',
129 129
                             cascade='delete'))
130 130
     revises_on_change = ('port', )

+ 52
- 20
neutron/plugins/ml2/plugin.py View File

@@ -82,6 +82,7 @@ from neutron.db import subnet_service_type_db_models as service_type_db
82 82
 from neutron.db import vlantransparent_db
83 83
 from neutron.extensions import providernet as provider
84 84
 from neutron.extensions import vlantransparent
85
+from neutron.objects import ports as obj_port
85 86
 from neutron.plugins.common import utils as p_utils
86 87
 from neutron.plugins.ml2.common import exceptions as ml2_exc
87 88
 from neutron.plugins.ml2 import db
@@ -314,7 +315,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
314 315
         port = mech_context.current
315 316
         port_id = port['id']
316 317
         changes = False
317
-
318 318
         host = const.ATTR_NOT_SPECIFIED
319 319
         if attrs and portbindings.HOST_ID in attrs:
320 320
             host = attrs.get(portbindings.HOST_ID) or ''
@@ -338,8 +338,9 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
338 338
 
339 339
         if profile not in (None, const.ATTR_NOT_SPECIFIED,
340 340
                            self._get_profile(binding)):
341
-            binding.profile = jsonutils.dumps(profile)
342
-            if len(binding.profile) > models.BINDING_PROFILE_LEN:
341
+            binding.profile = profile
342
+            if (len(jsonutils.dumps(binding.profile)) >
343
+                    models.BINDING_PROFILE_LEN):
343 344
                 msg = _("binding:profile value too large")
344 345
                 raise exc.InvalidInput(error_message=msg)
345 346
             changes = True
@@ -347,7 +348,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
347 348
         # Unbind the port if needed.
348 349
         if changes:
349 350
             binding.vif_type = portbindings.VIF_TYPE_UNBOUND
350
-            binding.vif_details = ''
351
+            binding.vif_details = None
352
+            binding.update()
351 353
             db.clear_binding_levels(plugin_context, port_id, original_host)
352 354
             mech_context._clear_binding_levels()
353 355
             port['status'] = const.PORT_STATUS_DOWN
@@ -357,13 +359,14 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
357 359
 
358 360
         if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
359 361
             binding.vif_type = portbindings.VIF_TYPE_UNBOUND
360
-            binding.vif_details = ''
362
+            binding.vif_details = None
361 363
             db.clear_binding_levels(plugin_context, port_id, original_host)
362 364
             mech_context._clear_binding_levels()
363 365
             binding.host = ''
366
+            binding.update()
364 367
 
365 368
         self._update_port_dict_binding(port, binding)
366
-        binding.persist_state_to_session(plugin_context.session)
369
+        binding.update()
367 370
         return changes
368 371
 
369 372
     @db_api.retry_db_errors
@@ -435,12 +438,15 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
435 438
         # transaction.
436 439
         port = orig_context.current
437 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 445
             host=orig_binding.host,
440 446
             vnic_type=orig_binding.vnic_type,
441
-            profile=orig_binding.profile,
447
+            profile=profile,
442 448
             vif_type=portbindings.VIF_TYPE_UNBOUND,
443
-            vif_details=''
449
+            vif_details=None
444 450
         )
445 451
         self._update_port_dict_binding(port, new_binding)
446 452
         new_context = driver_context.PortContext(
@@ -477,7 +483,13 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
477 483
             # mechanism driver update_port_*commit() calls.
478 484
             try:
479 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 493
             except exc.PortNotFound:
482 494
                 port_db, cur_binding = None, None
483 495
             if not port_db or not cur_binding:
@@ -544,10 +556,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
544 556
                                         cur_binding.host)
545 557
                 db.set_binding_levels(plugin_context,
546 558
                                       bind_context._binding_levels)
547
-                # refresh context with a snapshot of updated state
548
-                cur_context._binding = driver_context.InstanceSnapshot(
549
-                    cur_binding)
559
+                cur_context._binding = cur_binding
550 560
                 cur_context._binding_levels = bind_context._binding_levels
561
+                cur_binding.update()
562
+                plugin_context.session.refresh(port_db)
551 563
 
552 564
                 # Update PortContext's port dictionary to reflect the
553 565
                 # updated binding state.
@@ -598,6 +610,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
598 610
     def _get_vif_details(self, binding):
599 611
         if binding.vif_details:
600 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 617
                 return jsonutils.loads(binding.vif_details)
602 618
             except Exception:
603 619
                 LOG.error("Serialized vif_details DB value '%(value)s' "
@@ -609,6 +625,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
609 625
     def _get_profile(self, binding):
610 626
         if binding.profile:
611 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 632
                 return jsonutils.loads(binding.profile)
613 633
             except Exception:
614 634
                 LOG.error("Serialized profile DB value '%(value)s' for "
@@ -1292,7 +1312,12 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1292 1312
                         original_port=original_port)
1293 1313
         with db_api.context_manager.writer.using(context):
1294 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 1321
             if not binding:
1297 1322
                 raise exc.PortNotFound(port_id=id)
1298 1323
             mac_address_updated = self._check_mac_update_allowed(
@@ -1431,19 +1456,21 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1431 1456
         binding = mech_context._binding
1432 1457
         port = mech_context.current
1433 1458
         port_id = port['id']
1459
+        clear_host = None
1434 1460
 
1435 1461
         if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
1436
-            binding.vif_details = ''
1462
+            binding.vif_details = None
1437 1463
             binding.vif_type = portbindings.VIF_TYPE_UNBOUND
1438 1464
             if binding.host:
1439 1465
                 db.clear_binding_levels(plugin_context, port_id, binding.host)
1440
-            binding.host = ''
1466
+            clear_host = ''
1441 1467
 
1442 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 1470
         binding.router_id = attrs and attrs.get('device_id')
1445
-        # merge into session to reflect changes
1446
-        binding.persist_state_to_session(plugin_context.session)
1471
+        if new_host:
1472
+            binding.host = new_host
1473
+        binding.update()
1447 1474
 
1448 1475
     @utils.transaction_guard
1449 1476
     @db_api.retry_if_session_inactive()
@@ -1514,7 +1541,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1514 1541
         with db_api.context_manager.writer.using(context):
1515 1542
             try:
1516 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 1549
             except exc.PortNotFound:
1519 1550
                 LOG.debug("The port '%s' was deleted", id)
1520 1551
                 return
@@ -1758,6 +1789,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
1758 1789
                     return
1759 1790
                 if binding.status != status:
1760 1791
                     binding.status = status
1792
+                    binding.update()
1761 1793
                     updated = True
1762 1794
 
1763 1795
         if (updated and

+ 1
- 1
neutron/tests/unit/objects/test_objects.py View File

@@ -62,7 +62,7 @@ object_data = {
62 62
     'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',
63 63
     'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8',
64 64
     'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5',
65
-    'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578',
65
+    'PortBinding': '1.0-0ad9727c4e72d609d5b4f70bcd3bc727',
66 66
     'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060',
67 67
     'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2',
68 68
     'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023',

+ 58
- 61
neutron/tests/unit/plugins/ml2/test_db.py View File

@@ -23,7 +23,6 @@ from neutron_lib import context
23 23
 from neutron_lib.plugins.ml2 import api
24 24
 from oslo_utils import uuidutils
25 25
 from sqlalchemy.orm import exc
26
-from sqlalchemy.orm import query
27 26
 
28 27
 from neutron.db import api as db_api
29 28
 from neutron.db import db_base_plugin_v2
@@ -33,7 +32,6 @@ from neutron.db import segments_db
33 32
 from neutron.objects import network as network_obj
34 33
 from neutron.objects import ports as port_obj
35 34
 from neutron.plugins.ml2 import db as ml2_db
36
-from neutron.plugins.ml2 import models
37 35
 from neutron.tests.unit import testlib_api
38 36
 
39 37
 
@@ -64,10 +62,8 @@ class Ml2DBTestCase(testlib_api.SqlTestCase):
64 62
         return port
65 63
 
66 64
     def _setup_neutron_portbinding(self, port_id, vif_type, host):
67
-        with db_api.context_manager.writer.using(self.ctx):
68
-            self.ctx.session.add(models.PortBinding(port_id=port_id,
69
-                                                    vif_type=vif_type,
70
-                                                    host=host))
65
+        port_obj.PortBinding(
66
+            self.ctx, port_id=port_id, vif_type=vif_type, host=host).create()
71 67
 
72 68
     @staticmethod
73 69
     def _sort_segments(segments):
@@ -318,44 +314,45 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
318 314
 
319 315
     def _setup_distributed_binding(self, network_id,
320 316
                                    port_id, router_id, host_id):
321
-        with db_api.context_manager.writer.using(self.ctx):
322
-            record = models.DistributedPortBinding(
323
-                port_id=port_id,
324
-                host=host_id,
325
-                router_id=router_id,
326
-                vif_type=portbindings.VIF_TYPE_UNBOUND,
327
-                vnic_type=portbindings.VNIC_NORMAL,
328
-                status='DOWN')
329
-            self.ctx.session.add(record)
330
-            return record
317
+        binding_obj = port_obj.DistributedPortBinding(
318
+            self.ctx,
319
+            port_id=port_id,
320
+            host=host_id,
321
+            router_id=router_id,
322
+            vif_type=portbindings.VIF_TYPE_UNBOUND,
323
+            vnic_type=portbindings.VNIC_NORMAL,
324
+            status='DOWN')
325
+        binding_obj.create()
326
+        return binding_obj
331 327
 
332 328
     def test_ensure_distributed_port_binding_deals_with_db_duplicate(self):
333 329
         network_id = uuidutils.generate_uuid()
334 330
         port_id = uuidutils.generate_uuid()
335
-        router_id = 'foo_router_id'
336
-        host_id = 'foo_host_id'
331
+        router_id = uuidutils.generate_uuid()
332
+        host_id = uuidutils.generate_uuid()
337 333
         self._setup_neutron_network(network_id, [port_id])
338
-        self._setup_distributed_binding(network_id, port_id,
339
-                                        router_id, host_id)
340
-        with mock.patch.object(query.Query, 'first') as query_first:
341
-            query_first.return_value = []
342
-            with mock.patch.object(ml2_db.LOG, 'debug') as log_trace:
343
-                binding = ml2_db.ensure_distributed_port_binding(
344
-                    self.ctx, port_id, host_id, router_id)
345
-        self.assertTrue(query_first.called)
346
-        self.assertTrue(log_trace.called)
334
+        dpb = self._setup_distributed_binding(network_id, port_id,
335
+                                              router_id, host_id)
336
+        with mock.patch.object(port_obj.DistributedPortBinding,
337
+                'get_object') as get_object:
338
+            get_object.side_effect = [None, dpb]
339
+            binding = ml2_db.ensure_distributed_port_binding(
340
+                self.ctx, port_id, host_id, router_id)
341
+        self.assertTrue(get_object.called)
347 342
         self.assertEqual(port_id, binding.port_id)
348 343
 
349 344
     def test_ensure_distributed_port_binding(self):
350 345
         network_id = uuidutils.generate_uuid()
351
-        port_id = uuidutils.generate_uuid()
352
-        self._setup_neutron_network(network_id, [port_id])
346
+        expected_port_id = uuidutils.generate_uuid()
347
+        self._setup_neutron_network(network_id, [expected_port_id])
353 348
         router = self._setup_neutron_router()
354 349
         ml2_db.ensure_distributed_port_binding(
355
-            self.ctx, port_id, 'foo_host', router.id)
356
-        expected = (self.ctx.session.query(models.DistributedPortBinding).
357
-                    filter_by(port_id=port_id).one())
358
-        self.assertEqual(port_id, expected.port_id)
350
+            self.ctx, expected_port_id, 'foo_host', router.id)
351
+        actual_objs = port_obj.DistributedPortBinding.get_objects(
352
+            self.ctx, 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 357
     def test_ensure_distributed_port_binding_multiple_bindings(self):
361 358
         network_id = uuidutils.generate_uuid()
@@ -366,9 +363,9 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
366 363
             self.ctx, port_id, 'foo_host_1', router.id)
367 364
         ml2_db.ensure_distributed_port_binding(
368 365
             self.ctx, port_id, 'foo_host_2', router.id)
369
-        bindings = (self.ctx.session.query(models.DistributedPortBinding).
370
-                    filter_by(port_id=port_id).all())
371
-        self.assertEqual(2, len(bindings))
366
+        count_objs = port_obj.DistributedPortBinding.count(
367
+            self.ctx, port_id=port_id)
368
+        self.assertEqual(2, count_objs)
372 369
 
373 370
     def test_delete_distributed_port_binding_if_stale(self):
374 371
         network_id = uuidutils.generate_uuid()
@@ -377,21 +374,23 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
377 374
         binding = self._setup_distributed_binding(
378 375
             network_id, port_id, None, 'foo_host_id')
379 376
 
380
-        ml2_db.delete_distributed_port_binding_if_stale(self.ctx,
381
-                                                        binding)
382
-        count = (self.ctx.session.query(models.DistributedPortBinding).
383
-            filter_by(port_id=binding.port_id).count())
384
-        self.assertFalse(count)
377
+        ml2_db.delete_distributed_port_binding_if_stale(self.ctx, binding)
378
+
379
+        obj_exists = port_obj.DistributedPortBinding.objects_exist(
380
+            self.ctx, port_id=binding.port_id)
381
+        self.assertFalse(obj_exists)
385 382
 
386 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 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 388
         self.assertIsNone(port)
390 389
 
391 390
     def test_get_distributed_port_bindings_not_found(self):
392 391
         port = ml2_db.get_distributed_port_bindings(self.ctx,
393
-                                                    'foo_port_id')
394
-        self.assertFalse(len(port))
392
+                                                    uuidutils.generate_uuid())
393
+        self.assertEqual(0, len(port))
395 394
 
396 395
     def test_get_distributed_port_bindings(self):
397 396
         network_id = uuidutils.generate_uuid()
@@ -412,8 +411,9 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
412 411
         network_obj.Network(self.ctx, id=network_id).create()
413 412
         with db_api.context_manager.writer.using(self.ctx):
414 413
             device_owner = constants.DEVICE_OWNER_DVR_INTERFACE
414
+            port_id = uuidutils.generate_uuid()
415 415
             port = models_v2.Port(
416
-                id='port_id',
416
+                id=port_id,
417 417
                 network_id=network_id,
418 418
                 mac_address='00:11:22:33:44:55',
419 419
                 admin_state_up=True,
@@ -421,23 +421,20 @@ class Ml2DvrDBTestCase(testlib_api.SqlTestCase):
421 421
                 device_id='device_id',
422 422
                 device_owner=device_owner)
423 423
             self.ctx.session.add(port)
424
-            binding_kwarg = {
425
-                'port_id': 'port_id',
426
-                'host': 'host',
427
-                'vif_type': portbindings.VIF_TYPE_UNBOUND,
428
-                'vnic_type': portbindings.VNIC_NORMAL,
429
-                'router_id': 'router_id',
430
-                'status': constants.PORT_STATUS_DOWN
431
-            }
432
-            self.ctx.session.add(models.DistributedPortBinding(
433
-                **binding_kwarg))
434
-            binding_kwarg['host'] = 'another-host'
435
-            self.ctx.session.add(models.DistributedPortBinding(
436
-                **binding_kwarg))
424
+        binding_kwarg = {
425
+            'port_id': port_id,
426
+            'host': 'host',
427
+            'vif_type': portbindings.VIF_TYPE_UNBOUND,
428
+            'vnic_type': portbindings.VNIC_NORMAL,
429
+            'router_id': 'router_id',
430
+            'status': constants.PORT_STATUS_DOWN
431
+        }
432
+        port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create()
433
+        binding_kwarg['host'] = 'another-host'
434
+        port_obj.DistributedPortBinding(self.ctx, **binding_kwarg).create()
437 435
         with warnings.catch_warnings(record=True) as warning_list:
438 436
             with db_api.context_manager.writer.using(self.ctx):
439 437
                 self.ctx.session.delete(port)
440 438
             self.assertEqual([], warning_list)
441
-        ports = ml2_db.get_distributed_port_bindings(self.ctx,
442
-                                                     'port_id')
443
-        self.assertEqual(0, len(ports))
439
+        bindings = ml2_db.get_distributed_port_bindings(self.ctx, port_id)
440
+        self.assertEqual(0, len(bindings))

+ 74
- 50
neutron/tests/unit/plugins/ml2/test_plugin.py View File

@@ -46,6 +46,7 @@ from neutron.db import provisioning_blocks
46 46
 from neutron.db import segments_db
47 47
 from neutron.extensions import multiprovidernet as mpnet
48 48
 from neutron.objects import base as base_obj
49
+from neutron.objects import ports as obj_port
49 50
 from neutron.objects import router as l3_obj
50 51
 from neutron.plugins.ml2.common import exceptions as ml2_exc
51 52
 from neutron.plugins.ml2 import db as ml2_db
@@ -1629,9 +1630,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1629 1630
         # create a port and delete it so we have an expired mechanism context
1630 1631
         with self.port() as port:
1631 1632
             plugin = directory.get_plugin()
1632
-            binding = plugin._get_port(self.context,
1633
-                                       port['port']['id']).port_binding
1634
-            binding['host'] = 'test'
1633
+            binding = obj_port.PortBinding.get_object(
1634
+                self.context, port_id=port['port']['id'], host='')
1635
+            binding.host = 'test'
1636
+            binding.update()
1635 1637
             mech_context = driver_context.PortContext(
1636 1638
                 plugin, self.context, port['port'],
1637 1639
                 plugin.get_network(self.context, port['port']['network_id']),
@@ -1650,10 +1652,11 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1650 1652
     def _create_port_and_bound_context(self, port_vif_type, bound_vif_type):
1651 1653
         with self.port() as port:
1652 1654
             plugin = directory.get_plugin()
1653
-            binding = plugin._get_port(
1654
-                self.context, port['port']['id']).port_binding
1655
-            binding['host'] = 'fake_host'
1655
+            binding = obj_port.PortBinding.get_object(
1656
+                self.context, port_id=port['port']['id'], host='')
1657
+            binding.host = 'fake_host'
1656 1658
             binding['vif_type'] = port_vif_type
1659
+            binding.update()
1657 1660
             # Generates port context to be used before the bind.
1658 1661
             port_context = driver_context.PortContext(
1659 1662
                 plugin, self.context, port['port'],
@@ -1765,10 +1768,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1765 1768
     def test_update_port_binding_host_id_none(self):
1766 1769
         with self.port() as port:
1767 1770
             plugin = directory.get_plugin()
1768
-            binding = plugin._get_port(
1769
-                self.context, port['port']['id']).port_binding
1770
-            with self.context.session.begin(subtransactions=True):
1771
-                binding.host = 'test'
1771
+            binding = obj_port.PortBinding.get_object(
1772
+                self.context, port_id=port['port']['id'], host='')
1773
+            binding.host = 'test'
1774
+            binding.update()
1772 1775
             mech_context = driver_context.PortContext(
1773 1776
                 plugin, self.context, port['port'],
1774 1777
                 plugin.get_network(self.context, port['port']['network_id']),
@@ -1779,15 +1782,18 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1779 1782
             self.assertEqual('test', binding.host)
1780 1783
             with self.context.session.begin(subtransactions=True):
1781 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 1787
             self.assertTrue(update_mock.mock_calls)
1783
-            self.assertEqual('', binding.host)
1788
+            self.assertEqual('', updated_binding.host)
1784 1789
 
1785 1790
     def test_update_port_binding_host_id_not_changed(self):
1786 1791
         with self.port() as port:
1787 1792
             plugin = directory.get_plugin()
1788
-            binding = plugin._get_port(
1789
-                self.context, port['port']['id']).port_binding
1790
-            binding['host'] = 'test'
1793
+            binding = obj_port.PortBinding.get_object(
1794
+                self.context, port_id=port['port']['id'], host='')
1795
+            binding.host = 'test'
1796
+            binding.update()
1791 1797
             mech_context = driver_context.PortContext(
1792 1798
                 plugin, self.context, port['port'],
1793 1799
                 plugin.get_network(self.context, port['port']['network_id']),
@@ -1800,30 +1806,34 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1800 1806
             self.assertEqual('test', binding.host)
1801 1807
 
1802 1808
     def test_process_distributed_port_binding_update_router_id(self):
1803
-        host_id = 'host'
1804
-        binding = models.DistributedPortBinding(
1805
-                            port_id='port_id',
1806
-                            host=host_id,
1807
-                            router_id='old_router_id',
1808
-                            vif_type=portbindings.VIF_TYPE_OVS,
1809
-                            vnic_type=portbindings.VNIC_NORMAL,
1810
-                            status=constants.PORT_STATUS_DOWN)
1811
-        plugin = directory.get_plugin()
1812
-        mock_network = {'id': 'net_id'}
1813
-        mock_port = {'id': 'port_id'}
1814
-        ctxt = context.get_admin_context()
1815
-        new_router_id = 'new_router'
1816
-        attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id}
1817
-        with mock.patch.object(plugin, '_update_port_dict_binding'):
1818
-            with mock.patch.object(segments_db, 'get_network_segments',
1819
-                                   return_value=[]):
1820
-                mech_context = driver_context.PortContext(
1821
-                    self, ctxt, mock_port, mock_network, binding, None)
1822
-                plugin._process_distributed_port_binding(mech_context,
1823
-                                                         ctxt, attrs)
1824
-                self.assertEqual(new_router_id,
1825
-                                 mech_context._binding.router_id)
1826
-                self.assertEqual(host_id, mech_context._binding.host)
1809
+        with self.port() as port:
1810
+            host_id = 'host'
1811
+            ctxt = context.get_admin_context()
1812
+            binding_obj = obj_port.DistributedPortBinding(
1813
+                ctxt,
1814
+                port_id=port['port']['id'],
1815
+                host=host_id,
1816
+                profile={},
1817
+                router_id='old_router_id',
1818
+                vif_type=portbindings.VIF_TYPE_OVS,
1819
+                vnic_type=portbindings.VNIC_NORMAL,
1820
+                status=constants.PORT_STATUS_DOWN)
1821
+            binding_obj.create()
1822
+            plugin = directory.get_plugin()
1823
+            mock_network = {'id': 'net_id'}
1824
+            mock_port = {'id': 'port_id'}
1825
+            new_router_id = 'new_router'
1826
+            attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id}
1827
+            with mock.patch.object(plugin, '_update_port_dict_binding'):
1828
+                with mock.patch.object(segments_db, 'get_network_segments',
1829
+                                       return_value=[]):
1830
+                    mech_context = driver_context.PortContext(
1831
+                        self, ctxt, mock_port, mock_network, binding_obj, None)
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 1838
     def test_update_distributed_port_binding_on_concurrent_port_delete(self):
1829 1839
         plugin = directory.get_plugin()
@@ -1854,9 +1864,20 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
1854 1864
     def test__bind_port_original_port_set(self):
1855 1865
         plugin = directory.get_plugin()
1856 1866
         plugin.mechanism_manager = mock.Mock()
1857
-        mock_port = {'id': 'port_id'}
1867
+        mock_port = {'id': uuidutils.generate_uuid()}
1858 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 1879
         context.network.current = {'id': 'net_id'}
1880
+        context._binding = binding_obj
1860 1881
         context.original = mock_port
1861 1882
         with mock.patch.object(plugin, '_update_port_dict_binding'), \
1862 1883
             mock.patch.object(segments_db, 'get_network_segments',
@@ -2532,13 +2553,15 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
2532 2553
     def test_update_distributed_router_interface_port(self):
2533 2554
         """Test validate distributed router interface update succeeds."""
2534 2555
         host_id = 'host'
2535
-        binding = models.DistributedPortBinding(
2536
-                            port_id='port_id',
2537
-                            host=host_id,
2538
-                            router_id='old_router_id',
2539
-                            vif_type=portbindings.VIF_TYPE_OVS,
2540
-                            vnic_type=portbindings.VNIC_NORMAL,
2541
-                            status=constants.PORT_STATUS_DOWN)
2556
+        binding_obj = obj_port.DistributedPortBinding(
2557
+            mock.MagicMock(),
2558
+            port_id=uuidutils.generate_uuid(),
2559
+            host=host_id,
2560
+            router_id='old_router_id',
2561
+            vif_type=portbindings.VIF_TYPE_OVS,
2562
+            vnic_type=portbindings.VNIC_NORMAL,
2563
+            status=constants.PORT_STATUS_DOWN)
2564
+        binding_obj.create()
2542 2565
         with mock.patch.object(
2543 2566
             mech_test.TestMechanismDriver,
2544 2567
             'update_port_postcommit',
@@ -2548,7 +2571,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
2548 2571
                     'update_port_precommit') as port_pre,\
2549 2572
                 mock.patch.object(
2550 2573
                     ml2_db, 'get_distributed_port_bindings') as dist_bindings:
2551
-                dist_bindings.return_value = [binding]
2574
+                dist_bindings.return_value = [binding_obj]
2552 2575
                 port_pre.return_value = True
2553 2576
                 with self.network() as network:
2554 2577
                     with self.subnet(network=network) as subnet:
@@ -2771,9 +2794,10 @@ class TestML2Segments(Ml2PluginV2TestCase):
2771 2794
             # add writer here to make sure that the following operations are
2772 2795
             # performed in the same session
2773 2796
             with db_api.context_manager.writer.using(self.context):
2774
-                binding = plugin._get_port(
2775
-                    self.context, port['port']['id']).port_binding
2776
-                binding['host'] = 'host-ovs-no_filter'
2797
+                binding = obj_port.PortBinding.get_object(
2798
+                    self.context, port_id=port['port']['id'], host='')
2799
+                binding.host = 'host-ovs-no_filter'
2800
+                binding.update()
2777 2801
                 mech_context = driver_context.PortContext(
2778 2802
                     plugin, self.context, port['port'],
2779 2803
                     plugin.get_network(self.context,

+ 6
- 13
neutron/tests/unit/plugins/ml2/test_port_binding.py View File

@@ -19,11 +19,10 @@ from neutron_lib import constants as const
19 19
 from neutron_lib import context
20 20
 from neutron_lib.plugins import directory
21 21
 from oslo_config import cfg
22
-from oslo_serialization import jsonutils
23 22
 
24 23
 from neutron.conf.plugins.ml2.drivers import driver_type
24
+from neutron.objects import ports as obj_port
25 25
 from neutron.plugins.ml2 import driver_context
26
-from neutron.plugins.ml2 import models as ml2_models
27 26
 from 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 110
         ctx = context.get_admin_context()
112 111
         with self.port(name='name') as port:
113 112
             # emulating concurrent binding deletion
114
-            with ctx.session.begin():
115
-                for item in (ctx.session.query(ml2_models.PortBinding).
116
-                             filter_by(port_id=port['port']['id'])):
117
-                    ctx.session.delete(item)
113
+            obj_port.PortBinding.delete_objects(
114
+                ctx, port_id=port['port']['id'])
118 115
             self.assertIsNone(
119 116
                 self.plugin.get_bound_port_context(ctx, port['port']['id']))
120 117
 
@@ -191,13 +188,9 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
191 188
             attrs['binding:host_id'] = 'host2'
192 189
             updated_port = attrs.copy()
193 190
             network = {'id': attrs['network_id']}
194
-            binding = ml2_models.PortBinding(
195
-                port_id=original_port['id'],
196
-                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'])
191
+            binding = obj_port.PortBinding.get_object(
192
+                ctx, port_id=original_port['id'],
193
+                host=original_port['binding:host_id'])
201 194
             levels = []
202 195
             mech_context = driver_context.PortContext(
203 196
                 plugin, ctx, updated_port, network, binding, levels,

Loading…
Cancel
Save