Browse Source

ML2 mechanism driver access to binding details

The following properties are added to the PortContext object passed to
ML2 mechanism drivers for port operations:

* bound_driver - name of current bound driver
* original_bound_driver - name previously bound driver in an update
* original_bound_segment - network segment used in previous binding

Some issues with the existing ML2 port binding unit tests were also
fixed.

The remainder of the fix for bug 1276395, making these binding details
available to mechanism drivers when a port is deleted, will be
addressed as part of the fix for bug 1276391.

Partial-Bug: #1276395
Change-Id: I9ecff4a4e044920ed2dde709c89aeb9bc773220d
tags/2014.1.rc1
Bob Kukura 5 years ago
parent
commit
d1472deed5

+ 26
- 1
neutron/plugins/ml2/driver_api.py View File

@@ -218,7 +218,7 @@ class PortContext(object):
218 218
 
219 219
     @abstractproperty
220 220
     def original(self):
221
-        """Return the original state of the port
221
+        """Return the original state of the port.
222 222
 
223 223
         Return the original state of the port, prior to a call to
224 224
         update_port. Method is only valid within calls to
@@ -236,6 +236,31 @@ class PortContext(object):
236 236
         """Return the currently bound segment dictionary."""
237 237
         pass
238 238
 
239
+    @abstractproperty
240
+    def original_bound_segment(self):
241
+        """Return the original bound segment dictionary.
242
+
243
+        Return the original bound segment dictionary, prior to a call
244
+        to update_port.  Method is only valid within calls to
245
+        update_port_precommit and update_port_postcommit.
246
+        """
247
+        pass
248
+
249
+    @abstractproperty
250
+    def bound_driver(self):
251
+        """Return the currently bound mechanism driver name."""
252
+        pass
253
+
254
+    @abstractproperty
255
+    def original_bound_driver(self):
256
+        """Return the original bound mechanism driver name.
257
+
258
+        Return the original bound mechanism driver name, prior to a
259
+        call to update_port.  Method is only valid within calls to
260
+        update_port_precommit and update_port_postcommit.
261
+        """
262
+        pass
263
+
239 264
     @abstractmethod
240 265
     def host_agents(self, agent_type):
241 266
         """Get agents of the specified type on port's host.

+ 21
- 0
neutron/plugins/ml2/driver_context.py View File

@@ -78,6 +78,12 @@ class PortContext(MechanismDriverContext, api.PortContext):
78 78
                                                network)
79 79
         self._binding = db.ensure_port_binding(plugin_context.session,
80 80
                                                port['id'])
81
+        if original_port:
82
+            self._original_bound_segment_id = self._binding.segment
83
+            self._original_bound_driver = self._binding.driver
84
+        else:
85
+            self._original_bound_segment_id = None
86
+            self._original_bound_driver = None
81 87
 
82 88
     @property
83 89
     def current(self):
@@ -99,6 +105,21 @@ class PortContext(MechanismDriverContext, api.PortContext):
99 105
                 if segment[api.ID] == id:
100 106
                     return segment
101 107
 
108
+    @property
109
+    def original_bound_segment(self):
110
+        if self._original_bound_segment_id:
111
+            for segment in self._network_context.network_segments:
112
+                if segment[api.ID] == self._original_bound_segment_id:
113
+                    return segment
114
+
115
+    @property
116
+    def bound_driver(self):
117
+        return self._binding.driver
118
+
119
+    @property
120
+    def original_bound_driver(self):
121
+        return self._original_bound_driver
122
+
102 123
     def host_agents(self, agent_type):
103 124
         return self._plugin.get_agents(self._plugin_context,
104 125
                                        filters={'agent_type': [agent_type],

+ 12
- 0
neutron/tests/unit/ml2/_test_mech_agent.py View File

@@ -67,6 +67,18 @@ class FakePortContext(api.PortContext):
67 67
                 if segment[api.ID] == self._bound_segment_id:
68 68
                     return segment
69 69
 
70
+    @property
71
+    def original_bound_segment(self):
72
+        return None
73
+
74
+    @property
75
+    def bound_driver(self):
76
+        return None
77
+
78
+    @property
79
+    def original_bound_driver(self):
80
+        return None
81
+
70 82
     def host_agents(self, agent_type):
71 83
         if agent_type == self._agent_type:
72 84
             return self._agents

+ 9
- 1
neutron/tests/unit/ml2/drivers/mechanism_logger.py View File

@@ -83,11 +83,19 @@ class LoggerMechanismDriver(api.MechanismDriver):
83 83
     def _log_port_call(self, method_name, context):
84 84
         network_context = context.network
85 85
         LOG.info(_("%(method)s called with port settings %(current)s "
86
-                   "(original settings %(original)s) on network %(network)s"),
86
+                   "(original settings %(original)s) "
87
+                   "bound to segment %(segment)s "
88
+                   "(original segment %(original_segment)s) "
89
+                   "using driver %(driver)s "
90
+                   "(original driver %(original_driver)s) "
91
+                   "on network %(network)s"),
87 92
                  {'method': method_name,
88 93
                   'current': context.current,
89 94
                   'original': context.original,
90 95
                   'segment': context.bound_segment,
96
+                  'original_segment': context.original_bound_segment,
97
+                  'driver': context.bound_driver,
98
+                  'original_driver': context.original_bound_driver,
91 99
                   'network': network_context.current})
92 100
 
93 101
     def create_port_precommit(self, context):

+ 42
- 4
neutron/tests/unit/ml2/drivers/mechanism_test.py View File

@@ -84,11 +84,34 @@ class TestMechanismDriver(api.MechanismDriver):
84 84
         assert(isinstance(context, api.PortContext))
85 85
         assert(isinstance(context.current, dict))
86 86
         assert(context.current['id'] is not None)
87
+
88
+        vif_type = context.current.get(portbindings.VIF_TYPE)
89
+        assert(vif_type is not None)
90
+        if vif_type in (portbindings.VIF_TYPE_UNBOUND,
91
+                        portbindings.VIF_TYPE_BINDING_FAILED):
92
+            assert(context.bound_segment is None)
93
+            assert(context.bound_driver is None)
94
+        else:
95
+            assert(isinstance(context.bound_segment, dict))
96
+            assert(context.bound_driver == 'test')
97
+
87 98
         if original_expected:
88 99
             assert(isinstance(context.original, dict))
89 100
             assert(context.current['id'] == context.original['id'])
101
+            vif_type = context.original.get(portbindings.VIF_TYPE)
102
+            assert(vif_type is not None)
103
+            if vif_type in (portbindings.VIF_TYPE_UNBOUND,
104
+                            portbindings.VIF_TYPE_BINDING_FAILED):
105
+                assert(context.original_bound_segment is None)
106
+                assert(context.original_bound_driver is None)
107
+            else:
108
+                assert(isinstance(context.original_bound_segment, dict))
109
+                assert(context.original_bound_driver == 'test')
90 110
         else:
91
-            assert(not context.original)
111
+            assert(context.original is None)
112
+            assert(context.original_bound_segment is None)
113
+            assert(context.original_bound_driver is None)
114
+
92 115
         network_context = context.network
93 116
         assert(isinstance(network_context, api.NetworkContext))
94 117
         self._check_network_context(network_context, False)
@@ -112,7 +135,12 @@ class TestMechanismDriver(api.MechanismDriver):
112 135
         self._check_port_context(context, False)
113 136
 
114 137
     def bind_port(self, context):
115
-        self._check_port_context(context, False)
138
+        # REVISIT(rkukura): Currently, bind_port() is called as part
139
+        # of either a create or update transaction. The fix for bug
140
+        # 1276391 will change it to be called outside any transaction,
141
+        # so the context.original* will no longer be available.
142
+        self._check_port_context(context, context.original is not None)
143
+
116 144
         host = context.current.get(portbindings.HOST_ID, None)
117 145
         segment = context.network.network_segments[0][api.ID]
118 146
         if host == "host-ovs-no_filter":
@@ -123,8 +151,18 @@ class TestMechanismDriver(api.MechanismDriver):
123 151
                                 {portbindings.CAP_PORT_FILTER: True})
124 152
 
125 153
     def validate_port_binding(self, context):
126
-        self._check_port_context(context, False)
154
+        # REVISIT(rkukura): Currently, validate_port_binding() is
155
+        # called as part of either a create or update transaction. The
156
+        # fix for bug 1276391 will change it to be called outside any
157
+        # transaction (or eliminate it altogether), so the
158
+        # context.original* will no longer be available.
159
+        self._check_port_context(context, context.original is not None)
127 160
         return True
128 161
 
129 162
     def unbind_port(self, context):
130
-        self._check_port_context(context, False)
163
+        # REVISIT(rkukura): Currently, unbind_port() is called as part
164
+        # of either an update or delete transaction. The fix for bug
165
+        # 1276391 will change it to be called outside any transaction
166
+        # (or eliminate it altogether), so the context.original* will
167
+        # no longer be available.
168
+        self._check_port_context(context, context.original is not None)

+ 7
- 7
neutron/tests/unit/ml2/test_port_binding.py View File

@@ -108,18 +108,18 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
108 108
                     self.assertFalse(notify_mock.called)
109 109
 
110 110
     def test_update_with_new_host_binding_notifies_agent(self):
111
-        self._test_update_port_binding('host-ovs-no-filter',
112
-                                       'host-bridge-no-filter')
111
+        self._test_update_port_binding('host-ovs-no_filter',
112
+                                       'host-bridge-filter')
113 113
 
114 114
     def test_update_with_same_host_binding_does_not_notify(self):
115
-        self._test_update_port_binding('host-ovs-no-filter',
116
-                                       'host-ovs-no-filter')
115
+        self._test_update_port_binding('host-ovs-no_filter',
116
+                                       'host-ovs-no_filter')
117 117
 
118 118
     def test_update_without_binding_does_not_notify(self):
119
-        self._test_update_port_binding('host-ovs-no-filter')
119
+        self._test_update_port_binding('host-ovs-no_filter')
120 120
 
121 121
     def testt_update_from_empty_to_host_binding_notifies_agent(self):
122
-        self._test_update_port_binding('', 'host-ovs-no-filter')
122
+        self._test_update_port_binding('', 'host-ovs-no_filter')
123 123
 
124 124
     def test_update_from_host_to_empty_binding_notifies_agent(self):
125
-        self._test_update_port_binding('host-ovs-no-filter', '')
125
+        self._test_update_port_binding('host-ovs-no_filter', '')

Loading…
Cancel
Save