Skip to content

Commit 966fa56

Browse files
author
Ihar Hrachyshka
committed
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 This reverts commit 833a6d8. Closes-Bug: #2065707 Related-Bug: #2022059 Change-Id: I067c2f7286b2684b67b4389ca085d06a93f856ce (cherry picked from commit ac15191)
1 parent 0d8cc09 commit 966fa56

File tree

9 files changed

+18
-93
lines changed

9 files changed

+18
-93
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
@@ -1106,16 +1103,3 @@ def parse_permitted_ethertypes(permitted_ethertypes):
11061103
continue
11071104

11081105
return ret
1109-
1110-
1111-
# TODO(slaweq): this should be moved to neutron_lib.plugins.utils module
1112-
def is_port_bound(port, log_message=True):
1113-
active_binding = plugin_utils.get_port_binding_by_status_and_host(
1114-
port.get('port_bindings', []), n_const.ACTIVE)
1115-
if not active_binding:
1116-
if log_message:
1117-
LOG.warning('Binding for port %s was not found.', port)
1118-
return False
1119-
return active_binding[portbindings_extended.VIF_TYPE] not in (
1120-
portbindings.VIF_TYPE_UNBOUND,
1121-
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: 0 additions & 26 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, resource, event, trunk_plugin, payload):
165159
trunk = payload.states[0]
166160
# Check if parent port is handled by OVN.
@@ -176,18 +170,6 @@ def trunk_deleted(self, resource, event, trunk_plugin, payload):
176170
if trunk.sub_ports:
177171
self._unset_sub_ports(trunk.sub_ports)
178172

179-
def trunk_created_precommit(self, resource, event, trunk_plugin, payload):
180-
# payload.desired_state below is the trunk object
181-
parent_port = payload.desired_state.db_obj.port
182-
if self._is_port_bound(parent_port):
183-
raise trunk_exc.ParentPortInUse(port_id=parent_port.id)
184-
185-
def trunk_deleted_precommit(self, resource, event, trunk_plugin, payload):
186-
trunk = payload.states[0]
187-
parent_port = payload.states[1]
188-
if self._is_port_bound(parent_port):
189-
raise trunk_exc.TrunkInUse(trunk_id=trunk.id)
190-
191173
def subports_added(self, resource, event, trunk_plugin, payload):
192174
trunk = payload.states[0]
193175
subports = payload.metadata['subports']
@@ -226,14 +208,6 @@ def register(self, resource, event, trigger, payload=None):
226208
resource, event, trigger, payload=payload)
227209
self._handler = OVNTrunkHandler(self.plugin_driver)
228210

229-
registry.subscribe(
230-
self._handler.trunk_created_precommit,
231-
resources.TRUNK,
232-
events.PRECOMMIT_CREATE)
233-
registry.subscribe(
234-
self._handler.trunk_deleted_precommit,
235-
resources.TRUNK,
236-
events.PRECOMMIT_DELETE)
237211
registry.subscribe(
238212
self._handler.trunk_created, resources.TRUNK, events.AFTER_CREATE)
239213
registry.subscribe(

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
@@ -1511,7 +1510,7 @@ def test_is_router_distributed(self):
15111510
self.assertTrue(
15121511
self.mixin.is_router_distributed(self.ctx, router_id))
15131512

1514-
@mock.patch.object(n_utils, 'is_port_bound')
1513+
@mock.patch.object(l3_dvr_db, "is_port_bound")
15151514
def test_get_ports_under_dvr_connected_subnet(self, is_port_bound_mock):
15161515
router_dict = {'name': 'test_router', 'admin_state_up': True,
15171516
'distributed': True}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,6 @@ def test_register(self):
454454
with mock.patch.object(registry, 'subscribe') as mock_subscribe:
455455
driver.register(mock.ANY, mock.ANY, mock.Mock())
456456
calls = [
457-
mock.call.mock_subscribe(
458-
mock.ANY, resources.TRUNK, events.PRECOMMIT_CREATE),
459-
mock.call.mock_subscribe(
460-
mock.ANY, resources.TRUNK, events.PRECOMMIT_DELETE),
461457
mock.call.mock_subscribe(
462458
mock.ANY, resources.TRUNK, events.AFTER_CREATE),
463459
mock.call.mock_subscribe(

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)