Skip to content

Commit cd4f345

Browse files
averdaguralonsoh
authored andcommitted
[OVN][Trunk] Add port binding info on subport when parent is bound
The host ID and VIF details are added on the subport when the trunk is created, if it's created when it's not attached to any VM this fields will remain empty and be filled on the parent port when it gets bound to a host, but there's no callback to add this info on the subport. Closes-Bug: #2018289 Closes-Bug: #2024160 Change-Id: I34bb6f178c314907bdf9f76789777f6736938b67 (cherry picked from commit 955e621)
1 parent 410ce2f commit cd4f345

File tree

3 files changed

+62
-20
lines changed

3 files changed

+62
-20
lines changed

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ def _set_sub_ports(self, parent_port, subports):
4949
context = n_context.get_admin_context()
5050
db_parent_port = port_obj.Port.get_object(context, id=parent_port)
5151
parent_port_status = db_parent_port.status
52+
parent_port_bindings = db_parent_port.bindings[0]
5253
for subport in subports:
5354
with db_api.CONTEXT_WRITER.using(context), (
5455
txn(check_error=True)) as ovn_txn:
5556
port = self._set_binding_profile(context, subport, parent_port,
56-
parent_port_status, ovn_txn)
57+
parent_port_status,
58+
parent_port_bindings, ovn_txn)
5759
db_rev.bump_revision(context, port, ovn_const.TYPE_PORTS)
5860

5961
def _unset_sub_ports(self, subports):
@@ -67,7 +69,8 @@ def _unset_sub_ports(self, subports):
6769

6870
@db_base_plugin_common.convert_result_to_dict
6971
def _set_binding_profile(self, context, subport, parent_port,
70-
parent_port_status, ovn_txn):
72+
parent_port_status,
73+
parent_port_bindings, ovn_txn):
7174
LOG.debug("Setting parent %s for subport %s",
7275
parent_port, subport.port_id)
7376
db_port = port_obj.Port.get_object(context, id=subport.port_id)
@@ -79,6 +82,9 @@ def _set_binding_profile(self, context, subport, parent_port,
7982
check_rev_cmd = self.plugin_driver.nb_ovn.check_revision_number(
8083
db_port.id, db_port, ovn_const.TYPE_PORTS)
8184
ovn_txn.add(check_rev_cmd)
85+
parent_binding_host = ''
86+
if parent_port_bindings.host:
87+
parent_binding_host = parent_port_bindings.host
8288
try:
8389
# NOTE(flaviof): We expect binding's host to be set. Otherwise,
8490
# sub-port will not transition from DOWN to ACTIVE.
@@ -94,6 +100,7 @@ def _set_binding_profile(self, context, subport, parent_port,
94100
port_obj.PortBinding.update_object(
95101
context,
96102
{'profile': binding.profile,
103+
'host': parent_binding_host,
97104
'vif_type': portbindings.VIF_TYPE_OVS},
98105
port_id=subport.port_id,
99106
host=binding.host)
@@ -155,6 +162,14 @@ def _unset_binding_profile(self, context, subport, ovn_txn):
155162
LOG.debug("Done unsetting parent for subport %s", subport.port_id)
156163
return db_port
157164

165+
def trunk_updated(self, trunk):
166+
# Check if parent port is handled by OVN.
167+
if not self.plugin_driver.nb_ovn.lookup('Logical_Switch_Port',
168+
trunk.port_id, default=None):
169+
return
170+
if trunk.sub_ports:
171+
self._set_sub_ports(trunk.port_id, trunk.sub_ports)
172+
158173
def trunk_created(self, trunk):
159174
# Check if parent port is handled by OVN.
160175
if not self.plugin_driver.nb_ovn.lookup('Logical_Switch_Port',
@@ -189,6 +204,8 @@ def subports_deleted(self, trunk, subports):
189204
def trunk_event(self, resource, event, trunk_plugin, payload):
190205
if event == events.AFTER_CREATE:
191206
self.trunk_created(payload.states[0])
207+
elif event == events.AFTER_UPDATE:
208+
self.trunk_updated(payload.states[0])
192209
elif event == events.AFTER_DELETE:
193210
self.trunk_deleted(payload.states[0])
194211

@@ -215,13 +232,16 @@ def register(self, resource, event, trigger, payload=None):
215232
super(OVNTrunkDriver, self).register(
216233
resource, event, trigger, payload=payload)
217234
self._handler = OVNTrunkHandler(self.plugin_driver)
218-
for trunk_event in (events.AFTER_CREATE, events.AFTER_DELETE):
235+
for _event in (events.AFTER_CREATE, events.AFTER_UPDATE,
236+
events.AFTER_DELETE):
219237
registry.subscribe(self._handler.trunk_event,
220238
resources.TRUNK,
221-
trunk_event)
239+
_event)
240+
241+
for _event in (events.AFTER_CREATE, events.AFTER_DELETE):
222242
registry.subscribe(self._handler.subport_event,
223243
resources.SUBPORTS,
224-
trunk_event)
244+
_event)
225245

226246
@classmethod
227247
def create(cls, plugin_driver):

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

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,23 @@
1414

1515
import contextlib
1616

17-
from neutron.services.trunk import plugin as trunk_plugin
18-
from neutron.tests.functional import base
17+
from neutron_lib.api.definitions import portbindings
1918
from neutron_lib import constants as n_consts
20-
from neutron_lib.objects import registry as obj_reg
19+
from neutron_lib.db import api as db_api
2120
from neutron_lib.plugins import utils
2221
from neutron_lib.services.trunk import constants as trunk_consts
2322
from oslo_utils import uuidutils
2423

2524
from neutron.common.ovn import constants as ovn_const
25+
from neutron.objects import ports as port_obj
26+
from neutron.services.trunk import plugin as trunk_plugin
27+
from neutron.tests.functional import base
2628

2729

2830
class TestOVNTrunkDriver(base.TestOVNFunctionalBase):
2931

30-
def setUp(self):
31-
super(TestOVNTrunkDriver, self).setUp()
32+
def setUp(self, **kwargs):
33+
super().setUp(**kwargs)
3234
self.trunk_plugin = trunk_plugin.TrunkPlugin()
3335
self.trunk_plugin.add_segmentation_type(
3436
trunk_consts.SEGMENTATION_TYPE_VLAN,
@@ -39,7 +41,8 @@ def trunk(self, sub_ports=None):
3941
sub_ports = sub_ports or []
4042
with self.network() as network:
4143
with self.subnet(network=network) as subnet:
42-
with self.port(subnet=subnet) as parent_port:
44+
with self.port(subnet=subnet,
45+
device_owner='compute:nova') as parent_port:
4346
tenant_id = uuidutils.generate_uuid()
4447
trunk = {'trunk': {
4548
'port_id': parent_port['port']['id'],
@@ -64,17 +67,14 @@ def _get_ovn_trunk_info(self):
6467
if row.parent_name and row.tag:
6568
device_owner = row.external_ids[
6669
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY]
67-
revision_number = row.external_ids[
68-
ovn_const.OVN_REV_NUM_EXT_ID_KEY]
6970
ovn_trunk_info.append({'port_id': row.name,
7071
'parent_port_id': row.parent_name,
7172
'tag': row.tag,
7273
'device_owner': device_owner,
73-
'revision_number': revision_number,
7474
})
7575
return ovn_trunk_info
7676

77-
def _verify_trunk_info(self, trunk, has_items):
77+
def _verify_trunk_info(self, trunk, has_items, host=''):
7878
ovn_subports_info = self._get_ovn_trunk_info()
7979
neutron_subports_info = []
8080
for subport in trunk.get('sub_ports', []):
@@ -83,19 +83,27 @@ def _verify_trunk_info(self, trunk, has_items):
8383
'parent_port_id': [trunk['port_id']],
8484
'tag': [subport['segmentation_id']],
8585
'device_owner': trunk_consts.TRUNK_SUBPORT_OWNER,
86-
'revision_number': '2',
8786
})
88-
# Check that the subport has the binding is active.
89-
binding = obj_reg.load_class('PortBinding').get_object(
90-
self.context, port_id=subport['port_id'], host='')
91-
self.assertEqual(n_consts.PORT_STATUS_ACTIVE, binding['status'])
87+
# Check the subport binding.
88+
pb = port_obj.PortBinding.get_object(
89+
self.context, port_id=subport['port_id'], host=host)
90+
self.assertEqual(n_consts.PORT_STATUS_ACTIVE, pb.status)
91+
self.assertEqual(host, pb.host)
9292

9393
self.assertCountEqual(ovn_subports_info, neutron_subports_info)
9494
self.assertEqual(has_items, len(neutron_subports_info) != 0)
9595

9696
if trunk.get('status'):
9797
self.assertEqual(trunk_consts.TRUNK_ACTIVE_STATUS, trunk['status'])
9898

99+
def _bind_port(self, port_id, host):
100+
with db_api.CONTEXT_WRITER.using(self.context):
101+
pb = port_obj.PortBinding.get_object(self.context,
102+
port_id=port_id, host='')
103+
pb.delete()
104+
port_obj.PortBinding(self.context, port_id=port_id, host=host,
105+
vif_type=portbindings.VIF_TYPE_OVS).create()
106+
99107
def test_trunk_create(self):
100108
with self.trunk() as trunk:
101109
self._verify_trunk_info(trunk, has_items=False)
@@ -113,10 +121,22 @@ def test_subport_add(self):
113121
new_trunk = self.trunk_plugin.get_trunk(self.context,
114122
trunk['id'])
115123
self._verify_trunk_info(new_trunk, has_items=True)
124+
# Bind parent port. That will trigger the binding of the
125+
# trunk subports too, using the same host ID.
126+
self._bind_port(trunk['port_id'], 'host1')
127+
self.mech_driver.set_port_status_up(trunk['port_id'])
128+
self._verify_trunk_info(new_trunk, has_items=True,
129+
host='host1')
116130

117131
def test_subport_delete(self):
118132
with self.subport() as subport:
119133
with self.trunk([subport]) as trunk:
134+
# Bind parent port.
135+
self._bind_port(trunk['port_id'], 'host1')
136+
self.mech_driver.set_port_status_up(trunk['port_id'])
137+
self._verify_trunk_info(trunk, has_items=True,
138+
host='host1')
139+
120140
self.trunk_plugin.remove_subports(self.context, trunk['id'],
121141
{'sub_ports': [subport]})
122142
new_trunk = self.trunk_plugin.get_trunk(self.context,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def test_create_trunk(self):
122122
mock.call(mock.ANY,
123123
{'profile': {'parent_name': trunk.port_id,
124124
'tag': s_port.segmentation_id},
125+
'host': mock.ANY,
125126
'vif_type': portbindings.VIF_TYPE_OVS},
126127
host=mock.ANY,
127128
port_id=s_port.port_id)
@@ -152,6 +153,7 @@ def test_create_trunk_port_db_exception(self):
152153
self.mock_update_pb.assert_called_once_with(
153154
mock.ANY, {'profile': {'parent_name': self.sub_port_1.trunk_id,
154155
'tag': self.sub_port_1.segmentation_id},
156+
'host': 'foo.com',
155157
'vif_type': portbindings.VIF_TYPE_OVS},
156158
host='foo.com', port_id=self.sub_port_1.port_id)
157159
self.mock_port_update.assert_not_called()

0 commit comments

Comments
 (0)