Skip to content

Commit 498eaa1

Browse files
ralonsohmtomaska
authored andcommitted
[2023.1 Only][OVN] Add the bridge name and datapath type to the port
VIF details In this 2023.1 branch only version the retrieval of the bridge_name is done by checking either Chassis or Chassis_Private table. The ovn metadata agent uses already present `has_chassis_private` attribute to detect Chassis or Chassis_Private. The mech_driver checks `agent_chassis_table` name to retrieve from either Chassis_Private or Chassis Same as in ML2/OVS, the ML2/OVN mechanism driver adds to the port VIF details dictionary the OVS bridge the port is connected to and the integration bridge datapath type. Conflicts: neutron/common/_constants.py neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py Closes-Bug: #2045889 (cherry picked from commit baaf240) Change-Id: Id299eb7faf540b41836513bfb566157c8996a355
1 parent 5033488 commit 498eaa1

File tree

12 files changed

+205
-66
lines changed

12 files changed

+205
-66
lines changed

neutron/agent/ovn/metadata/agent.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,33 @@ def _load_config(self):
309309
LOG.debug("Loaded chassis name %s (UUID: %s) and ovn bridge %s.",
310310
self.chassis, self.chassis_id, self.ovn_bridge)
311311

312+
def _update_chassis_config(self):
313+
"""Update the Chassis register information
314+
315+
This method should be called once the Metadata Agent has been
316+
registered (method ``register_metadata_agent`` has been called) and
317+
the corresponding Chasis or Chassis_Private register has been
318+
created/updated. The Chassis table is used in an event when OVN does
319+
not have the Chassis_Private.
320+
"""
321+
external_ids = {ovn_const.OVN_AGENT_OVN_BRIDGE: self.ovn_bridge}
322+
if self.has_chassis_private:
323+
self.sb_idl.db_set(
324+
'Chassis_Private', self.chassis,
325+
('external_ids', external_ids)).execute(check_error=True)
326+
else:
327+
self.sb_idl.db_set(
328+
'Chassis', self.chassis,
329+
('external_ids', external_ids)).execute(check_error=True)
330+
312331
@_sync_lock
313332
def resync(self):
314333
"""Resync the agent.
315334
316335
Reload the configuration and sync the agent again.
317336
"""
318337
self._load_config()
338+
self._update_chassis_config()
319339
self.sync()
320340

321341
def start(self):
@@ -361,6 +381,7 @@ def start(self):
361381

362382
# Register the agent with its corresponding Chassis
363383
self.register_metadata_agent()
384+
self._update_chassis_config()
364385

365386
self._proxy.wait()
366387

neutron/common/_constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,6 @@
8989

9090
# Neutron-lib defines this with a /64 but it should be /128
9191
METADATA_V6_CIDR = constants.METADATA_V6_IP + '/128'
92+
93+
# TODO(ralonsoh): move this constant to neutron_lib.plugins.ml2.ovs_constants
94+
DEFAULT_BR_INT = 'br-int'

neutron/common/ovn/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
OVN_NAME_PREFIX = 'neutron-'
7272
OVN_HA_CH_GROUP_EXTPORT_PREFIX = 'neutron-extport-'
7373

74+
OVN_DATAPATH_TYPE = 'datapath-type'
75+
7476
# TODO(froyo): Move this to neutron-lib as soon as possible, and when a new
7577
# release is created and pointed to in the requirements remove this code
7678
OVN_LB_HM_PORT_DISTRIBUTED = 'ovn-lb-hm:distributed'
@@ -83,6 +85,7 @@
8385
OVN_AGENT_NEUTRON_SB_CFG_KEY = 'neutron:ovn-neutron-agent-sb-cfg'
8486
OVN_AGENT_NEUTRON_DESC_KEY = 'neutron:description-neutron-agent'
8587
OVN_AGENT_NEUTRON_ID_KEY = 'neutron:ovn-neutron-agent-id'
88+
OVN_AGENT_OVN_BRIDGE = 'neutron:ovn-bridge'
8689
OVN_CONTROLLER_AGENT = 'OVN Controller agent'
8790
OVN_CONTROLLER_GW_AGENT = 'OVN Controller Gateway agent'
8891
OVN_METADATA_AGENT = 'OVN Metadata agent'

neutron/common/ovn/utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import tenacity
4141

4242
from neutron._i18n import _
43+
from neutron.common import _constants as n_const
4344
from neutron.common.ovn import constants
4445
from neutron.common.ovn import exceptions as ovn_exc
4546
from neutron.common import utils as common_utils
@@ -702,6 +703,32 @@ def get_ovn_cms_options(chassis):
702703
constants.OVN_CMS_OPTIONS, '').split(',')]
703704

704705

706+
def get_ovn_bridge_from_chassis(chassis):
707+
"""Return the OVN bridge used by the local OVN controller
708+
709+
This information is stored in the Chassis or Chassis_Private register by
710+
the OVN Metadata agent. The default value returned, if not present, is
711+
"br-int".
712+
NOTE: the default value is not reading the local ``OVS.integration_bridge``
713+
configuration knob, that could be different.
714+
"""
715+
return (chassis.external_ids.get(constants.OVN_AGENT_OVN_BRIDGE) or
716+
n_const.DEFAULT_BR_INT)
717+
718+
719+
def get_datapath_type(hostname, sb_idl):
720+
"""Return the local OVS integration bridge datapath type
721+
722+
If the datapath type is not stored in the ``Chassis`` register or
723+
the register is still not created, the default value returned is "".
724+
"""
725+
chassis = sb_idl.db_find(
726+
'Chassis', ('hostname', '=', hostname)).execute(check_error=True)
727+
return (
728+
chassis[0].get('other_config', {}).get(constants.OVN_DATAPATH_TYPE, '')
729+
if chassis else '')
730+
731+
705732
def is_gateway_chassis(chassis):
706733
"""Check if the given chassis is a gateway chassis"""
707734
return constants.CMS_OPT_CHASSIS_AS_GW in get_ovn_cms_options(chassis)

neutron/plugins/ml2/drivers/ovn/db_migration.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
14+
import copy
1415

1516
from neutron_lib.api.definitions import portbindings as pb_api
1617
from neutron_lib import context as n_context
@@ -20,6 +21,7 @@
2021
from oslo_log import log as logging
2122
from sqlalchemy.orm import exc as sqla_exc
2223

24+
from neutron.common import _constants as n_const
2325
from neutron.db.models.plugins.ml2 import geneveallocation
2426
from neutron.db.models.plugins.ml2 import vxlanallocation
2527
from neutron.objects import network as network_obj
@@ -29,10 +31,6 @@
2931

3032
LOG = logging.getLogger(__name__)
3133

32-
VIF_DETAILS_TO_REMOVE = (
33-
pb_api.VIF_DETAILS_BRIDGE_NAME,
34-
)
35-
3634

3735
def migrate_neutron_database_to_ovn():
3836
"""Change DB content from OVS to OVN mech driver.
@@ -74,18 +72,22 @@ def migrate_neutron_database_to_ovn():
7472
with db_api.CONTEXT_WRITER.using(ctx):
7573
pb = port_obj.PortBinding.get_object(ctx, port_id=port_id,
7674
host=host)
77-
if not pb or not pb.vif_details:
75+
if not pb:
7876
continue
7977

80-
vif_details = pb.vif_details.copy()
81-
for detail in VIF_DETAILS_TO_REMOVE:
82-
try:
83-
del vif_details[detail]
84-
except KeyError:
85-
pass
86-
if vif_details == pb.vif_details:
78+
# Update the OVS bridge name in the VIF details: now all
79+
# port are directly connected to the integration bridge.
80+
# Because the name of each host integration bridge is not
81+
# know by the Neutron API at this point, the default value
82+
# "br-int" will be used.
83+
# The OVS datapath type is unchanged.
84+
vif_details = copy.deepcopy(pb.vif_details) or {}
85+
if (vif_details.get(pb_api.VIF_DETAILS_BRIDGE_NAME) ==
86+
n_const.DEFAULT_BR_INT):
8787
continue
8888

89+
vif_details[pb_api.VIF_DETAILS_BRIDGE_NAME] = (
90+
n_const.DEFAULT_BR_INT)
8991
pb.vif_details = vif_details
9092
pb.update()
9193
except (exceptions.ObjectNotFound,

neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def get_supported_vif_types(self):
190190
vif_types = set()
191191
for ch in self.sb_ovn.chassis_list().execute(check_error=True):
192192
other_config = ovn_utils.get_ovn_chassis_other_config(ch)
193-
dp_type = other_config.get('datapath-type', '')
193+
dp_type = other_config.get(ovn_const.OVN_DATAPATH_TYPE, '')
194194
if dp_type == ovn_const.CHASSIS_DATAPATH_NETDEV:
195195
vif_types.add(portbindings.VIF_TYPE_VHOST_USER)
196196
else:
@@ -988,7 +988,7 @@ def bind_port(self, context):
988988
return
989989
chassis = agent.chassis
990990
other_config = ovn_utils.get_ovn_chassis_other_config(chassis)
991-
datapath_type = other_config.get('datapath-type', '')
991+
datapath_type = other_config.get(ovn_const.OVN_DATAPATH_TYPE, '')
992992
iface_types = other_config.get('iface-types', '')
993993
iface_types = iface_types.split(',') if iface_types else []
994994
chassis_physnets = self.sb_ovn._get_chassis_physnets(chassis)
@@ -1035,13 +1035,25 @@ def bind_port(self, context):
10351035
vif_type = portbindings.VIF_TYPE_VHOST_USER
10361036
port[portbindings.VIF_DETAILS].update({
10371037
portbindings.VHOST_USER_SOCKET: vhost_user_socket})
1038-
vif_details = dict(self.vif_details[vif_type])
1038+
vif_details = copy.deepcopy(self.vif_details[vif_type])
10391039
vif_details[portbindings.VHOST_USER_SOCKET] = (
10401040
vhost_user_socket)
10411041
else:
10421042
vif_type = portbindings.VIF_TYPE_OVS
1043-
vif_details = self.vif_details[vif_type]
1043+
vif_details = copy.deepcopy(self.vif_details[vif_type])
10441044

1045+
if self.agent_chassis_table == 'Chassis_Private':
1046+
chassis_to_retrieve = agent.chassis_private
1047+
else:
1048+
chassis_to_retrieve = agent.chassis
1049+
ovn_bridge = ovn_utils.get_ovn_bridge_from_chassis(
1050+
chassis_to_retrieve)
1051+
1052+
dp_type = ovn_utils.get_datapath_type(bind_host, self.sb_ovn)
1053+
vif_details.update({
1054+
portbindings.VIF_DETAILS_BRIDGE_NAME: ovn_bridge,
1055+
portbindings.OVS_DATAPATH_TYPE: dp_type,
1056+
})
10451057
context.set_binding(segment_to_bind[api.ID], vif_type,
10461058
vif_details)
10471059
break

neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ def _start_metadata_agent(self):
110110
with mock.patch.object(metadata_server.UnixDomainMetadataProxy,
111111
'wait'):
112112
agt.start()
113+
external_ids = agt.sb_idl.db_get(
114+
'Chassis_Private', agt.chassis, 'external_ids').execute(
115+
check_error=True)
116+
self.assertEqual(external_ids[ovn_const.OVN_AGENT_OVN_BRIDGE],
117+
self.OVN_BRIDGE)
113118

114119
# Metadata agent will open connections to OVS and SB databases.
115120
# Close connections to them when the test ends,

neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,41 +43,48 @@
4343
portbindings.CAP_PORT_FILTER: True,
4444
portbindings.VIF_DETAILS_CONNECTIVITY: portbindings.CONNECTIVITY_L2,
4545
portbindings.VIF_DETAILS_BOUND_DRIVERS: {'0': 'ovn'},
46+
portbindings.VIF_DETAILS_BRIDGE_NAME: 'br-int',
47+
portbindings.OVS_DATAPATH_TYPE: 'system',
4648
}
4749
VHOSTUSER_VIF_DETAILS = {
4850
portbindings.CAP_PORT_FILTER: False,
4951
'vhostuser_mode': VHU_MODE,
5052
'vhostuser_ovs_plug': True,
5153
portbindings.VIF_DETAILS_CONNECTIVITY: portbindings.CONNECTIVITY_L2,
5254
portbindings.VIF_DETAILS_BOUND_DRIVERS: {'0': 'ovn'},
55+
portbindings.VIF_DETAILS_BRIDGE_NAME: 'br-int',
56+
portbindings.OVS_DATAPATH_TYPE: 'netdev',
5357
}
5458

5559

5660
class TestPortBinding(base.TestOVNFunctionalBase):
5761

58-
def setUp(self):
59-
super(TestPortBinding, self).setUp()
62+
def setUp(self, **kwargs):
63+
super().setUp(**kwargs)
6064
self.ovs_host = 'ovs-host'
6165
self.dpdk_host = 'dpdk-host'
6266
self.invalid_dpdk_host = 'invalid-host'
6367
self.insecure_host = 'insecure-host'
6468
self.smartnic_dpu_host = 'smartnic-dpu-host'
6569
self.smartnic_dpu_serial = 'fake-smartnic-dpu-serial'
66-
self.add_fake_chassis(self.ovs_host)
70+
self.add_fake_chassis(
71+
self.ovs_host,
72+
other_config={ovn_const.OVN_DATAPATH_TYPE: 'system'})
6773
self.add_fake_chassis(
6874
self.dpdk_host,
69-
other_config={'datapath-type': 'netdev',
75+
other_config={ovn_const.OVN_DATAPATH_TYPE: 'netdev',
7076
'iface-types': 'dummy,dummy-internal,dpdkvhostuser'})
7177

7278
self.add_fake_chassis(
7379
self.invalid_dpdk_host,
74-
other_config={'datapath-type': 'netdev',
80+
other_config={ovn_const.OVN_DATAPATH_TYPE: 'netdev',
7581
'iface-types': 'dummy,dummy-internal,geneve,vxlan'})
7682
self.add_fake_chassis(
7783
self.smartnic_dpu_host,
7884
other_config={ovn_const.OVN_CMS_OPTIONS: '{}={}'.format(
7985
ovn_const.CMS_OPT_CARD_SERIAL_NUMBER,
80-
self.smartnic_dpu_serial)})
86+
self.smartnic_dpu_serial),
87+
ovn_const.OVN_DATAPATH_TYPE: 'system'})
8188
self.n1 = self._make_network(self.fmt, 'n1', True)
8289
res = self._create_subnet(self.fmt, self.n1['network']['id'],
8390
'10.0.0.0/24')
@@ -151,9 +158,13 @@ def test_port_binding_create_port(self):
151158
self._verify_vif_details(port_id, self.dpdk_host, 'vhostuser',
152159
expected_vif_details)
153160

161+
expected_vif_details = copy.deepcopy(VHOSTUSER_VIF_DETAILS)
162+
expected_vif_details.pop('vhostuser_mode')
163+
expected_vif_details.pop('vhostuser_ovs_plug')
164+
expected_vif_details[portbindings.CAP_PORT_FILTER] = True
154165
port_id = self._create_or_update_port(hostname=self.invalid_dpdk_host)
155166
self._verify_vif_details(port_id, self.invalid_dpdk_host, 'ovs',
156-
OVS_VIF_DETAILS)
167+
expected_vif_details)
157168

158169
def test_port_binding_create_remote_managed_port(self):
159170
pci_vendor_info = 'fake-pci-vendor-info'
@@ -205,8 +216,12 @@ def test_port_binding_update_port(self):
205216

206217
port_id = self._create_or_update_port(port_id=port_id,
207218
hostname=self.invalid_dpdk_host)
219+
expected_vif_details = copy.deepcopy(VHOSTUSER_VIF_DETAILS)
220+
expected_vif_details.pop('vhostuser_mode')
221+
expected_vif_details.pop('vhostuser_ovs_plug')
222+
expected_vif_details[portbindings.CAP_PORT_FILTER] = True
208223
self._verify_vif_details(port_id, self.invalid_dpdk_host, 'ovs',
209-
OVS_VIF_DETAILS)
224+
expected_vif_details)
210225

211226
def test_port_binding_update_remote_managed_port(self):
212227
port_id = self._create_or_update_port(

neutron/tests/unit/fake_resources.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def __init__(self, **kwargs):
184184
self.get_extport_chassis_from_cms_options = mock.Mock(return_value=[])
185185
self.is_col_present = mock.Mock()
186186
self.is_col_present.return_value = False
187+
self.db_find = mock.MagicMock()
187188
self.db_set = mock.Mock()
188189
self.lookup = mock.MagicMock()
189190
self.chassis_list = mock.MagicMock()
@@ -858,7 +859,8 @@ class FakeChassis(object):
858859
def create(attrs=None, az_list=None, chassis_as_gw=False,
859860
bridge_mappings=None, rp_bandwidths=None,
860861
rp_inventory_defaults=None, rp_hypervisors=None,
861-
card_serial_number=None, chassis_as_extport=False):
862+
card_serial_number=None, chassis_as_extport=False,
863+
datapath_type=None):
862864
cms_opts = []
863865
if az_list:
864866
cms_opts.append("%s=%s" % (ovn_const.CMS_OPT_AVAILABILITY_ZONES,
@@ -903,6 +905,9 @@ def create(attrs=None, az_list=None, chassis_as_gw=False,
903905
if bridge_mappings:
904906
other_config['ovn-bridge-mappings'] = ','.join(bridge_mappings)
905907

908+
if datapath_type:
909+
other_config[ovn_const.OVN_DATAPATH_TYPE] = datapath_type
910+
906911
chassis_attrs = {
907912
'encaps': [],
908913
'external_ids': '',

0 commit comments

Comments
 (0)