Skip to content

Commit c4d0621

Browse files
Ihar Hrachyshkabooxter
authored andcommitted
Revert "[OVN] Prevent Trunk creation/deletion with parent port bound"
There are three reasons to revert this patch. 1. It broke RPC push API for trunks because it added port db model to event payload that is not serializeable. 2. It also broke the callback event payload interface, which requires that all entries in .states attribute belong to the same core object. To quote from neutron-lib, ``` # an iterable of states for the resource from the newest to the oldest # for example db states or api request/response # the actual object type for states will vary depending on event caller self.states = ... ``` 3. There is no good justification why ml2/ovn would not allow this operation. The rationale for the original patch was to align the behavior with ml2/ovs, but we don't such parity requirements. The 409 error that can be returned by the API endpoints is backend specific. To quote api-ref, ``` 409 The operation returns this error code for one of these reasons: A system configuration prevents the operation from succeeding. ``` AFAIU there is nothing that prevents ml2/ovn to create a trunk in this situation. This will have to be backported in all supported branches (the original patch was backported down to Wallaby). Conflicts: neutron/services/trunk/drivers/ovn/trunk_driver.py neutron/common/utils.py This reverts commit 833a6d8. Closes-Bug: #2065707 Related-Bug: #2022059 Change-Id: I067c2f7286b2684b67b4389ca085d06a93f856ce (cherry picked from commit 57af141126de3a2b8442277c6b4d09b2a085f4a6)
1 parent cd92d42 commit c4d0621

File tree

8 files changed

+19
-81
lines changed

8 files changed

+19
-81
lines changed

neutron/common/utils.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,9 @@
3737
from eventlet.green import subprocess
3838
import netaddr
3939
from neutron_lib.api.definitions import availability_zone as az_def
40-
from neutron_lib.api.definitions import portbindings
41-
from neutron_lib.api.definitions import portbindings_extended
4240
from neutron_lib import constants as n_const
4341
from neutron_lib import context as n_context
4442
from neutron_lib.db import api as db_api
45-
from neutron_lib.plugins import utils as plugin_utils
4643
from neutron_lib.services.qos import constants as qos_consts
4744
from neutron_lib.services.trunk import constants as trunk_constants
4845
from neutron_lib.utils import helpers
@@ -1091,16 +1088,3 @@ def sign_instance_id(conf, instance_id):
10911088
secret = encodeutils.to_utf8(secret)
10921089
instance_id = encodeutils.to_utf8(instance_id)
10931090
return hmac.new(secret, instance_id, hashlib.sha256).hexdigest()
1094-
1095-
1096-
# TODO(slaweq): this should be moved to neutron_lib.plugins.utils module
1097-
def is_port_bound(port, log_message=True):
1098-
active_binding = plugin_utils.get_port_binding_by_status_and_host(
1099-
port.get('port_bindings', []), n_const.ACTIVE)
1100-
if not active_binding:
1101-
if log_message:
1102-
LOG.warning('Binding for port %s was not found.', port)
1103-
return False
1104-
return active_binding[portbindings_extended.VIF_TYPE] not in (
1105-
portbindings.VIF_TYPE_UNBOUND,
1106-
portbindings.VIF_TYPE_BINDING_FAILED)

neutron/db/l3_dvr_db.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from neutron_lib.api.definitions import external_net as extnet_apidef
1818
from neutron_lib.api.definitions import l3 as l3_apidef
1919
from neutron_lib.api.definitions import portbindings
20+
from neutron_lib.api.definitions import portbindings_extended
2021
from neutron_lib.api.definitions import router_admin_state_down_before_update
2122
from neutron_lib.api import validators
2223
from neutron_lib.callbacks import events
@@ -70,6 +71,18 @@ def is_admin_state_down_necessary():
7071
return _IS_ADMIN_STATE_DOWN_NECESSARY
7172

7273

74+
# TODO(slaweq): this should be moved to neutron_lib.plugins.utils module
75+
def is_port_bound(port):
76+
active_binding = plugin_utils.get_port_binding_by_status_and_host(
77+
port.get("port_bindings", []), const.ACTIVE)
78+
if not active_binding:
79+
LOG.warning("Binding for port %s was not found.", port)
80+
return False
81+
return active_binding[portbindings_extended.VIF_TYPE] not in [
82+
portbindings.VIF_TYPE_UNBOUND,
83+
portbindings.VIF_TYPE_BINDING_FAILED]
84+
85+
7386
@registry.has_registry_receivers
7487
class DVRResourceOperationHandler(object):
7588
"""Contains callbacks for DVR operations.
@@ -1422,7 +1435,7 @@ def is_router_distributed(self, context, router_id):
14221435

14231436
def get_ports_under_dvr_connected_subnet(self, context, subnet_id):
14241437
ports = dvr_mac_db.get_ports_query_by_subnet_and_ip(context, subnet_id)
1425-
ports = [p for p in ports if n_utils.is_port_bound(p)]
1438+
ports = [p for p in ports if is_port_bound(p)]
14261439
# TODO(slaweq): if there would be way to pass to neutron-lib only
14271440
# list of extensions which actually should be processed, than setting
14281441
# process_extensions=True below could avoid that second loop and

neutron/services/trunk/drivers/ovn/trunk_driver.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
from oslo_log import log
2323

2424
from neutron.common.ovn import constants as ovn_const
25-
from neutron.common import utils as n_utils
2625
from neutron.db import db_base_plugin_common
2726
from neutron.db import ovn_revision_numbers_db as db_rev
2827
from neutron.objects import ports as port_obj
2928
from neutron.services.trunk.drivers import base as trunk_base
30-
from neutron.services.trunk import exceptions as trunk_exc
3129

3230

3331
SUPPORTED_INTERFACES = (
@@ -157,10 +155,6 @@ def _unset_binding_profile(self, context, subport, ovn_txn):
157155
LOG.debug("Done unsetting parent for subport %s", subport.port_id)
158156
return db_port
159157

160-
@staticmethod
161-
def _is_port_bound(port):
162-
return n_utils.is_port_bound(port, log_message=False)
163-
164158
def trunk_created(self, trunk):
165159
# Check if parent port is handled by OVN.
166160
if not self.plugin_driver.nb_ovn.lookup('Logical_Switch_Port',
@@ -197,16 +191,6 @@ def trunk_event(self, resource, event, trunk_plugin, payload):
197191
self.trunk_created(payload.states[0])
198192
elif event == events.AFTER_DELETE:
199193
self.trunk_deleted(payload.states[0])
200-
elif event == events.PRECOMMIT_CREATE:
201-
trunk = payload.desired_state
202-
parent_port = trunk.db_obj.port
203-
if self._is_port_bound(parent_port):
204-
raise trunk_exc.ParentPortInUse(port_id=parent_port.id)
205-
elif event == events.PRECOMMIT_DELETE:
206-
trunk = payload.states[0]
207-
parent_port = payload.states[1]
208-
if self._is_port_bound(parent_port):
209-
raise trunk_exc.TrunkInUse(trunk_id=trunk.id)
210194

211195
def subport_event(self, resource, event, trunk_plugin, payload):
212196
if event == events.AFTER_CREATE:
@@ -231,8 +215,7 @@ def register(self, resource, event, trigger, payload=None):
231215
super(OVNTrunkDriver, self).register(
232216
resource, event, trigger, payload=payload)
233217
self._handler = OVNTrunkHandler(self.plugin_driver)
234-
for _event in (events.AFTER_CREATE, events.AFTER_DELETE,
235-
events.PRECOMMIT_CREATE, events.PRECOMMIT_DELETE):
218+
for _event in (events.AFTER_CREATE, events.AFTER_DELETE):
236219
registry.subscribe(self._handler.trunk_event,
237220
resources.TRUNK,
238221
_event)

neutron/services/trunk/plugin.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ def delete_trunk(self, context, trunk_id):
294294
trunk = self._get_trunk(context, trunk_id)
295295
rules.trunk_can_be_managed(context, trunk)
296296
trunk_port_validator = rules.TrunkPortValidator(trunk.port_id)
297-
parent_port = trunk.db_obj.port
298297
if trunk_port_validator.can_be_trunked_or_untrunked(context):
299298
# NOTE(status_police): when a trunk is deleted, the logical
300299
# object disappears from the datastore, therefore there is no
@@ -308,7 +307,7 @@ def delete_trunk(self, context, trunk_id):
308307
'deleting trunk port %s: %s', trunk_id,
309308
str(e))
310309
payload = events.DBEventPayload(context, resource_id=trunk_id,
311-
states=(trunk, parent_port))
310+
states=(trunk,))
312311
registry.publish(resources.TRUNK, events.PRECOMMIT_DELETE,
313312
self, payload=payload)
314313
else:
@@ -318,7 +317,7 @@ def delete_trunk(self, context, trunk_id):
318317
registry.publish(resources.TRUNK, events.AFTER_DELETE, self,
319318
payload=events.DBEventPayload(
320319
context, resource_id=trunk_id,
321-
states=(trunk, parent_port)))
320+
states=(trunk,)))
322321

323322
@db_base_plugin_common.convert_result_to_dict
324323
def add_subports(self, context, trunk_id, subports):

neutron/tests/functional/services/trunk/drivers/ovn/test_trunk_driver.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,13 @@
1414

1515
import contextlib
1616

17-
from neutron_lib.api.definitions import portbindings
18-
from neutron_lib.callbacks import exceptions as n_exc
1917
from neutron_lib import constants as n_consts
2018
from neutron_lib.objects import registry as obj_reg
2119
from neutron_lib.plugins import utils
2220
from neutron_lib.services.trunk import constants as trunk_consts
2321
from oslo_utils import uuidutils
2422

2523
from neutron.common.ovn import constants as ovn_const
26-
from neutron.objects import ports as port_obj
2724
from neutron.services.trunk import plugin as trunk_plugin
2825
from neutron.tests.functional import base
2926

@@ -108,25 +105,6 @@ def test_trunk_create_with_subports(self):
108105
with self.trunk([subport]) as trunk:
109106
self._verify_trunk_info(trunk, has_items=True)
110107

111-
def test_trunk_create_parent_port_bound(self):
112-
with self.network() as network:
113-
with self.subnet(network=network) as subnet:
114-
with self.port(subnet=subnet) as parent_port:
115-
pb = port_obj.PortBinding.get_objects(
116-
self.context, port_id=parent_port['port']['id'])
117-
port_obj.PortBinding.update_object(
118-
self.context, {'vif_type': portbindings.VIF_TYPE_OVS},
119-
port_id=pb[0].port_id, host=pb[0].host)
120-
tenant_id = uuidutils.generate_uuid()
121-
trunk = {'trunk': {
122-
'port_id': parent_port['port']['id'],
123-
'tenant_id': tenant_id, 'project_id': tenant_id,
124-
'admin_state_up': True,
125-
'name': 'trunk', 'sub_ports': []}}
126-
self.assertRaises(n_exc.CallbackFailure,
127-
self.trunk_plugin.create_trunk,
128-
self.context, trunk)
129-
130108
def test_subport_add(self):
131109
with self.subport() as subport:
132110
with self.trunk() as trunk:
@@ -149,14 +127,3 @@ def test_trunk_delete(self):
149127
with self.trunk() as trunk:
150128
self.trunk_plugin.delete_trunk(self.context, trunk['id'])
151129
self._verify_trunk_info({}, has_items=False)
152-
153-
def test_trunk_delete_parent_port_bound(self):
154-
with self.trunk() as trunk:
155-
bp = port_obj.PortBinding.get_objects(
156-
self.context, port_id=trunk['port_id'])
157-
port_obj.PortBinding.update_object(
158-
self.context, {'vif_type': portbindings.VIF_TYPE_OVS},
159-
port_id=bp[0].port_id, host=bp[0].host)
160-
self.assertRaises(n_exc.CallbackFailure,
161-
self.trunk_plugin.delete_trunk,
162-
self.context, trunk['id'])

neutron/tests/unit/db/test_l3_dvr_db.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from neutron_lib.plugins import utils as plugin_utils
3131
from oslo_utils import uuidutils
3232

33-
from neutron.common import utils as n_utils
3433
from neutron.db import agents_db
3534
from neutron.db import l3_dvr_db
3635
from neutron.db import l3_dvrscheduler_db
@@ -1522,7 +1521,7 @@ def test_is_router_distributed(self):
15221521
self.assertTrue(
15231522
self.mixin.is_router_distributed(self.ctx, router_id))
15241523

1525-
@mock.patch.object(n_utils, 'is_port_bound')
1524+
@mock.patch.object(l3_dvr_db, "is_port_bound")
15261525
def test_get_ports_under_dvr_connected_subnet(self, is_port_bound_mock):
15271526
router_dict = {'name': 'test_router', 'admin_state_up': True,
15281527
'distributed': True}

neutron/tests/unit/services/trunk/test_plugin.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ def _test_trunk_delete_notify(self, event):
162162
resources.TRUNK, event, self.trunk_plugin, payload=mock.ANY)
163163
payload = callback.mock_calls[0][2]['payload']
164164
self.assertEqual(self.context, payload.context)
165-
self.assertEqual(trunk_obj, payload.states[0])
166-
self.assertEqual(parent_port['port']['id'], payload.states[1].id)
165+
self.assertEqual(trunk_obj, payload.latest_state)
167166
self.assertEqual(trunk['id'], payload.resource_id)
168167

169168
def test_delete_trunk_notify_after_delete(self):

releasenotes/notes/ovn-trunk-check-parent-port-eeca2eceaca9d158.yaml

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)