[OVN] Add the bridge name and datapath type to the port VIF details

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.

Closes-Bug: #2045889
Change-Id: Ifda46c42b9506449a58fbaf312cc71c72d9cf2df
This commit is contained in:
Rodolfo Alonso Hernandez 2023-08-19 05:11:55 +00:00
parent 97b84180b2
commit baaf240ce3
12 changed files with 206 additions and 72 deletions

View File

@ -370,6 +370,18 @@ class MetadataAgent(object):
LOG.debug("Loaded chassis name %s (UUID: %s) and ovn bridge %s.",
self.chassis, self.chassis_id, self.ovn_bridge)
def _update_chassis_private_config(self):
"""Update the Chassis_Private register information
This method should be called once the Metadata Agent has been
registered (method ``register_metadata_agent`` has been called) and
the corresponding Chassis_Private register has been created/updated.
"""
external_ids = {ovn_const.OVN_AGENT_OVN_BRIDGE: self.ovn_bridge}
self.sb_idl.db_set(
'Chassis_Private', self.chassis,
('external_ids', external_ids)).execute(check_error=True)
@_sync_lock
def resync(self):
"""Resync the agent.
@ -377,6 +389,7 @@ class MetadataAgent(object):
Reload the configuration and sync the agent again.
"""
self._load_config()
self._update_chassis_private_config()
self.sync()
def start(self):
@ -409,6 +422,7 @@ class MetadataAgent(object):
# Register the agent with its corresponding Chassis
self.register_metadata_agent()
self._update_chassis_private_config()
self._proxy.wait()

View File

@ -92,3 +92,7 @@ METADATA_V6_CIDR = constants.METADATA_V6_IP + '/128'
# TODO(haleyb): move this constant to neutron_lib.constants
IPV4_MIN_MTU = 68
# TODO(ralonsoh): move this constant to neutron_lib.plugins.ml2.ovs_constants
DEFAULT_BR_INT = 'br-int'

View File

@ -72,6 +72,8 @@ OVN_NAME_PREFIX = 'neutron-'
OVN_HA_CH_GROUP_EXTPORT_PREFIX = 'neutron-extport-'
OVN_METADATA_PREFIX = 'ovnmeta-'
OVN_DATAPATH_TYPE = 'datapath-type'
# TODO(froyo): Move this to neutron-lib as soon as possible, and when a new
# release is created and pointed to in the requirements remove this code
OVN_LB_HM_PORT_DISTRIBUTED = 'ovn-lb-hm:distributed'
@ -84,6 +86,7 @@ OVN_AGENT_METADATA_ID_KEY = 'neutron:ovn-metadata-id'
OVN_AGENT_NEUTRON_SB_CFG_KEY = 'neutron:ovn-neutron-agent-sb-cfg'
OVN_AGENT_NEUTRON_DESC_KEY = 'neutron:description-neutron-agent'
OVN_AGENT_NEUTRON_ID_KEY = 'neutron:ovn-neutron-agent-id'
OVN_AGENT_OVN_BRIDGE = 'neutron:ovn-bridge'
OVN_CONTROLLER_AGENT = 'OVN Controller agent'
OVN_CONTROLLER_GW_AGENT = 'OVN Controller Gateway agent'
OVN_METADATA_AGENT = 'OVN Metadata agent'

View File

@ -40,6 +40,7 @@ from ovsdbapp import constants as ovsdbapp_const
import tenacity
from neutron._i18n import _
from neutron.common import _constants as n_const
from neutron.common.ovn import constants
from neutron.common.ovn import exceptions as ovn_exc
from neutron.common import utils as common_utils
@ -707,6 +708,31 @@ def get_ovn_cms_options(chassis):
constants.OVN_CMS_OPTIONS, '').split(',')]
def get_ovn_bridge_from_chassis_private(chassis_private):
"""Return the OVN bridge used by the local OVN controller
This information is stored in the Chassis_Private register by the OVN
Metadata agent. The default value returned, if not present, is "br-int".
NOTE: the default value is not reading the local ``OVS.integration_bridge``
configuration knob, that could be different.
"""
return (chassis_private.external_ids.get(constants.OVN_AGENT_OVN_BRIDGE) or
n_const.DEFAULT_BR_INT)
def get_datapath_type(hostname, sb_idl):
"""Return the local OVS integration bridge datapath type
If the datapath type is not stored in the ``Chassis`` register or
the register is still not created, the default value returned is "".
"""
chassis = sb_idl.db_find(
'Chassis', ('hostname', '=', hostname)).execute(check_error=True)
return (
chassis[0].get('other_config', {}).get(constants.OVN_DATAPATH_TYPE, '')
if chassis else '')
def is_gateway_chassis(chassis):
"""Check if the given chassis is a gateway chassis"""
return constants.CMS_OPT_CHASSIS_AS_GW in get_ovn_cms_options(chassis)

View File

@ -11,6 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from neutron_lib.api.definitions import portbindings as pb_api
from neutron_lib import constants
@ -21,6 +22,7 @@ from oslo_db import exception as db_exc
from oslo_log import log as logging
from sqlalchemy.orm import exc as sqla_exc
from neutron.common import _constants as n_const
from neutron.db.models.plugins.ml2 import geneveallocation
from neutron.db.models.plugins.ml2 import vxlanallocation
from neutron.objects import network as network_obj
@ -30,10 +32,6 @@ from neutron.objects import trunk as trunk_obj
LOG = logging.getLogger(__name__)
VIF_DETAILS_TO_REMOVE = (
pb_api.VIF_DETAILS_BRIDGE_NAME,
)
def migrate_neutron_database_to_ovn():
"""Change DB content from OVS to OVN mech driver.
@ -75,18 +73,22 @@ def migrate_neutron_database_to_ovn():
with db_api.CONTEXT_WRITER.using(ctx):
pb = port_obj.PortBinding.get_object(ctx, port_id=port_id,
host=host)
if not pb or not pb.vif_details:
if not pb:
continue
vif_details = pb.vif_details.copy()
for detail in VIF_DETAILS_TO_REMOVE:
try:
del vif_details[detail]
except KeyError:
pass
if vif_details == pb.vif_details:
# Update the OVS bridge name in the VIF details: now all
# port are directly connected to the integration bridge.
# Because the name of each host integration bridge is not
# know by the Neutron API at this point, the default value
# "br-int" will be used.
# The OVS datapath type is unchanged.
vif_details = copy.deepcopy(pb.vif_details) or {}
if (vif_details.get(pb_api.VIF_DETAILS_BRIDGE_NAME) ==
n_const.DEFAULT_BR_INT):
continue
vif_details[pb_api.VIF_DETAILS_BRIDGE_NAME] = (
n_const.DEFAULT_BR_INT)
pb.vif_details = vif_details
pb.update()
except (exceptions.ObjectNotFound,

View File

@ -187,7 +187,7 @@ class OVNMechanismDriver(api.MechanismDriver):
vif_types = set()
for ch in self.sb_ovn.chassis_list().execute(check_error=True):
other_config = ovn_utils.get_ovn_chassis_other_config(ch)
dp_type = other_config.get('datapath-type', '')
dp_type = other_config.get(ovn_const.OVN_DATAPATH_TYPE, '')
if dp_type == ovn_const.CHASSIS_DATAPATH_NETDEV:
vif_types.add(portbindings.VIF_TYPE_VHOST_USER)
else:
@ -990,7 +990,7 @@ class OVNMechanismDriver(api.MechanismDriver):
return
chassis = agent.chassis
other_config = ovn_utils.get_ovn_chassis_other_config(chassis)
datapath_type = other_config.get('datapath-type', '')
datapath_type = other_config.get(ovn_const.OVN_DATAPATH_TYPE, '')
iface_types = other_config.get('iface-types', '')
iface_types = iface_types.split(',') if iface_types else []
chassis_physnets = self.sb_ovn._get_chassis_physnets(chassis)
@ -1037,7 +1037,7 @@ class OVNMechanismDriver(api.MechanismDriver):
vif_type = portbindings.VIF_TYPE_VHOST_USER
port[portbindings.VIF_DETAILS].update({
portbindings.VHOST_USER_SOCKET: vhost_user_socket})
vif_details = dict(self.vif_details[vif_type])
vif_details = copy.deepcopy(self.vif_details[vif_type])
vif_details[portbindings.VHOST_USER_SOCKET] = (
vhost_user_socket)
elif (vnic_type == portbindings.VNIC_VIRTIO_FORWARDER):
@ -1046,15 +1046,22 @@ class OVNMechanismDriver(api.MechanismDriver):
vif_type = portbindings.VIF_TYPE_AGILIO_OVS
port[portbindings.VIF_DETAILS].update({
portbindings.VHOST_USER_SOCKET: vhost_user_socket})
vif_details = dict(self.vif_details[vif_type])
vif_details = copy.deepcopy(self.vif_details[vif_type])
vif_details[portbindings.VHOST_USER_SOCKET] = (
vhost_user_socket)
vif_details[portbindings.VHOST_USER_MODE] = (
portbindings.VHOST_USER_MODE_CLIENT)
else:
vif_type = portbindings.VIF_TYPE_OVS
vif_details = self.vif_details[vif_type]
vif_details = copy.deepcopy(self.vif_details[vif_type])
ovn_bridge = ovn_utils.get_ovn_bridge_from_chassis_private(
agent.chassis_private)
dp_type = ovn_utils.get_datapath_type(bind_host, self.sb_ovn)
vif_details.update({
portbindings.VIF_DETAILS_BRIDGE_NAME: ovn_bridge,
portbindings.OVS_DATAPATH_TYPE: dp_type,
})
context.set_binding(segment_to_bind[api.ID], vif_type,
vif_details)
break

View File

@ -114,6 +114,11 @@ class TestMetadataAgent(base.TestOVNFunctionalBase):
with mock.patch.object(metadata_server.UnixDomainMetadataProxy,
'wait'):
agt.start()
external_ids = agt.sb_idl.db_get(
'Chassis_Private', agt.chassis, 'external_ids').execute(
check_error=True)
self.assertEqual(external_ids[ovn_const.OVN_AGENT_OVN_BRIDGE],
self.OVN_BRIDGE)
# Metadata agent will open connections to OVS and SB databases.
# Close connections to them when the test ends,

View File

@ -43,6 +43,8 @@ OVS_VIF_DETAILS = {
portbindings.CAP_PORT_FILTER: True,
portbindings.VIF_DETAILS_CONNECTIVITY: portbindings.CONNECTIVITY_L2,
portbindings.VIF_DETAILS_BOUND_DRIVERS: {'0': 'ovn'},
portbindings.VIF_DETAILS_BRIDGE_NAME: 'br-int',
portbindings.OVS_DATAPATH_TYPE: 'system',
}
VHOSTUSER_VIF_DETAILS = {
portbindings.CAP_PORT_FILTER: False,
@ -50,34 +52,39 @@ VHOSTUSER_VIF_DETAILS = {
'vhostuser_ovs_plug': True,
portbindings.VIF_DETAILS_CONNECTIVITY: portbindings.CONNECTIVITY_L2,
portbindings.VIF_DETAILS_BOUND_DRIVERS: {'0': 'ovn'},
portbindings.VIF_DETAILS_BRIDGE_NAME: 'br-int',
portbindings.OVS_DATAPATH_TYPE: 'netdev',
}
class TestPortBinding(base.TestOVNFunctionalBase):
def setUp(self):
super(TestPortBinding, self).setUp()
def setUp(self, **kwargs):
super().setUp(**kwargs)
self.ovs_host = 'ovs-host'
self.dpdk_host = 'dpdk-host'
self.invalid_dpdk_host = 'invalid-host'
self.insecure_host = 'insecure-host'
self.smartnic_dpu_host = 'smartnic-dpu-host'
self.smartnic_dpu_serial = 'fake-smartnic-dpu-serial'
self.add_fake_chassis(self.ovs_host)
self.add_fake_chassis(
self.ovs_host,
other_config={ovn_const.OVN_DATAPATH_TYPE: 'system'})
self.add_fake_chassis(
self.dpdk_host,
other_config={'datapath-type': 'netdev',
other_config={ovn_const.OVN_DATAPATH_TYPE: 'netdev',
'iface-types': 'dummy,dummy-internal,dpdkvhostuser'})
self.add_fake_chassis(
self.invalid_dpdk_host,
other_config={'datapath-type': 'netdev',
other_config={ovn_const.OVN_DATAPATH_TYPE: 'netdev',
'iface-types': 'dummy,dummy-internal,geneve,vxlan'})
self.add_fake_chassis(
self.smartnic_dpu_host,
other_config={ovn_const.OVN_CMS_OPTIONS: '{}={}'.format(
ovn_const.CMS_OPT_CARD_SERIAL_NUMBER,
self.smartnic_dpu_serial)})
self.smartnic_dpu_serial),
ovn_const.OVN_DATAPATH_TYPE: 'system'})
self.n1 = self._make_network(self.fmt, 'n1', True)
res = self._create_subnet(self.fmt, self.n1['network']['id'],
'10.0.0.0/24')
@ -152,9 +159,13 @@ class TestPortBinding(base.TestOVNFunctionalBase):
self._verify_vif_details(port_id, self.dpdk_host, 'vhostuser',
expected_vif_details)
expected_vif_details = copy.deepcopy(VHOSTUSER_VIF_DETAILS)
expected_vif_details.pop('vhostuser_mode')
expected_vif_details.pop('vhostuser_ovs_plug')
expected_vif_details[portbindings.CAP_PORT_FILTER] = True
port_id = self._create_or_update_port(hostname=self.invalid_dpdk_host)
self._verify_vif_details(port_id, self.invalid_dpdk_host, 'ovs',
OVS_VIF_DETAILS)
expected_vif_details)
def test_port_binding_create_remote_managed_port(self):
pci_vendor_info = 'fake-pci-vendor-info'
@ -206,8 +217,12 @@ class TestPortBinding(base.TestOVNFunctionalBase):
port_id = self._create_or_update_port(port_id=port_id,
hostname=self.invalid_dpdk_host)
expected_vif_details = copy.deepcopy(VHOSTUSER_VIF_DETAILS)
expected_vif_details.pop('vhostuser_mode')
expected_vif_details.pop('vhostuser_ovs_plug')
expected_vif_details[portbindings.CAP_PORT_FILTER] = True
self._verify_vif_details(port_id, self.invalid_dpdk_host, 'ovs',
OVS_VIF_DETAILS)
expected_vif_details)
def test_port_binding_update_remote_managed_port(self):
port_id = self._create_or_update_port(

View File

@ -181,6 +181,7 @@ class FakeOvsdbSbOvnIdl(object):
self.get_extport_chassis_from_cms_options = mock.Mock(return_value=[])
self.is_col_present = mock.Mock()
self.is_col_present.return_value = False
self.db_find = mock.MagicMock()
self.db_set = mock.Mock()
self.lookup = mock.MagicMock()
self.chassis_list = mock.MagicMock()
@ -856,7 +857,8 @@ class FakeChassis(object):
def create(attrs=None, az_list=None, chassis_as_gw=False,
bridge_mappings=None, rp_bandwidths=None,
rp_inventory_defaults=None, rp_hypervisors=None,
card_serial_number=None, chassis_as_extport=False):
card_serial_number=None, chassis_as_extport=False,
datapath_type=None):
cms_opts = []
if az_list:
cms_opts.append("%s=%s" % (ovn_const.CMS_OPT_AVAILABILITY_ZONES,
@ -901,6 +903,9 @@ class FakeChassis(object):
if bridge_mappings:
other_config['ovn-bridge-mappings'] = ','.join(bridge_mappings)
if datapath_type:
other_config[ovn_const.OVN_DATAPATH_TYPE] = datapath_type
chassis_attrs = {
'encaps': [],
'external_ids': '',

View File

@ -46,6 +46,7 @@ from oslo_utils import uuidutils
from ovsdbapp.backend.ovs_idl import idlutils
from webob import exc
from neutron.common import _constants as n_const
from neutron.common import config
from neutron.common.ovn import acl as ovn_acl
from neutron.common.ovn import constants as ovn_const
@ -76,6 +77,7 @@ from neutron.tests.unit.plugins.ml2 import test_security_group
OVN_PROFILE = ovn_const.OVN_PORT_BINDING_PROFILE
CLASS_PLACEMENT_REPORT = ('neutron.services.placement_report.plugin.'
'PlacementReportPlugin')
DEFAULT_DP_TYPE = 'system' # For testing, we define "system" as default.
OvnRevNumberRow = collections.namedtuple(
'OvnRevNumberRow', ['created_at'])
@ -101,7 +103,7 @@ class MechDriverSetupBase(abc.ABC):
self.mock_vp_parents = mock.patch.object(
ovn_utils, 'get_virtual_port_parents', return_value=None).start()
def _add_chassis(self, nb_cfg, name=None):
def _add_chassis_private(self, nb_cfg, name=None):
chassis_private = mock.Mock()
chassis_private.nb_cfg = nb_cfg
chassis_private.uuid = uuid.uuid4()
@ -109,26 +111,40 @@ class MechDriverSetupBase(abc.ABC):
chassis_private.nb_cfg_timestamp = timeutils.utcnow_ts() * 1000
return chassis_private
def _add_chassis_agent(self, nb_cfg, agent_type, chassis_private=None):
chassis_private = chassis_private or self._add_chassis(nb_cfg)
def _add_chassis(self, name, hostname, external_ids=None,
other_config=None):
external_ids = external_ids or {}
other_config = other_config or {}
return mock.Mock(name=name, hostname=hostname,
external_ids=external_ids, other_config=other_config)
def _add_chassis_agent(self, nb_cfg, agent_type, chassis_private=None,
hostname=None):
chassis_private = chassis_private or self._add_chassis_private(nb_cfg)
hostname = hostname or chassis_private.name + '_host'
if hasattr(chassis_private, 'nb_cfg_timestamp') and isinstance(
chassis_private.nb_cfg_timestamp, mock.Mock):
del chassis_private.nb_cfg_timestamp
chassis_private.external_ids = {}
chassis_private.other_config = {}
chassis_private.external_ids = {
ovn_const.OVN_AGENT_OVN_BRIDGE: n_const.DEFAULT_BR_INT,
ovn_const.OVN_DATAPATH_TYPE: DEFAULT_DP_TYPE,
}
if agent_type == ovn_const.OVN_METADATA_AGENT:
chassis_private.external_ids.update({
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
ovn_const.OVN_AGENT_METADATA_ID_KEY: str(uuid.uuid4())})
chassis_private.chassis = [chassis_private]
chassis_private.chassis = [self._add_chassis(chassis_private.name,
hostname)]
return neutron_agent.AgentCache().update(agent_type, chassis_private)
def _add_agent(self, name, nb_cfg_offset=0):
def _add_agent(self, name, nb_cfg_offset=0, hostname=None):
hostname = hostname or name + '_host'
nb_cfg = 5
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + nb_cfg_offset
chassis = self._add_chassis(nb_cfg, name=name)
chassis_private = self._add_chassis_private(nb_cfg, name=name)
return self._add_chassis_agent(
nb_cfg, ovn_const.OVN_CONTROLLER_AGENT, chassis)
nb_cfg, ovn_const.OVN_CONTROLLER_AGENT,
chassis_private=chassis_private, hostname=hostname)
class TestOVNMechanismDriverBase(MechDriverSetupBase,
@ -1242,6 +1258,9 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
self._test_bind_port_failed([])
def _test_bind_port(self, fake_segments):
fake_chassis = fakes.FakeChassis.create(datapath_type=DEFAULT_DP_TYPE)
self.sb_ovn.db_find.return_value.execute.return_value = [
{'other_config': fake_chassis.other_config}]
fake_port = fakes.FakePort.create_one_port().info()
fake_host = 'host'
fake_port_context = fakes.FakePortContext(
@ -1250,12 +1269,18 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
neutron_agent.AgentCache().get_agents.assert_called_once_with(
{'host': fake_host,
'agent_type': ovn_const.OVN_CONTROLLER_TYPES})
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'],
portbindings.VIF_TYPE_OVS,
vif_details = copy.deepcopy(
self.mech_driver.vif_details[portbindings.VIF_TYPE_OVS])
vif_details[
portbindings.VIF_DETAILS_BRIDGE_NAME] = n_const.DEFAULT_BR_INT
vif_details[portbindings.OVS_DATAPATH_TYPE] = DEFAULT_DP_TYPE
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'], portbindings.VIF_TYPE_OVS, vif_details)
def _test_bind_port_sriov(self, fake_segments):
fake_chassis = fakes.FakeChassis.create(datapath_type=DEFAULT_DP_TYPE)
self.sb_ovn.db_find.return_value.execute.return_value = [
{'other_config': fake_chassis.other_config}]
fake_port = fakes.FakePort.create_one_port(
attrs={'binding:vnic_type': 'direct',
'binding:profile': {
@ -1268,12 +1293,18 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
neutron_agent.AgentCache().get_agents.assert_called_once_with(
{'host': fake_host,
'agent_type': ovn_const.OVN_CONTROLLER_TYPES})
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'],
portbindings.VIF_TYPE_OVS,
vif_details = copy.deepcopy(
self.mech_driver.vif_details[portbindings.VIF_TYPE_OVS])
vif_details[
portbindings.VIF_DETAILS_BRIDGE_NAME] = n_const.DEFAULT_BR_INT
vif_details[portbindings.OVS_DATAPATH_TYPE] = DEFAULT_DP_TYPE
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'], portbindings.VIF_TYPE_OVS, vif_details)
def _test_bind_port_virtio_forwarder(self, fake_segments):
fake_chassis = fakes.FakeChassis.create(datapath_type=DEFAULT_DP_TYPE)
self.sb_ovn.db_find.return_value.execute.return_value = [
{'other_config': fake_chassis.other_config}]
fake_port = fakes.FakePort.create_one_port(
attrs={'binding:vnic_type': 'virtio-forwarder'}).info()
fake_host = 'host'
@ -1281,11 +1312,15 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
fake_port, fake_host, fake_segments)
self.mech_driver.bind_port(fake_port_context)
vif_details = self.mech_driver.\
vif_details[portbindings.VIF_TYPE_AGILIO_OVS]
vif_details.update({"vhostuser_socket": ovn_utils.ovn_vhu_sockpath(
ovn_conf.get_ovn_vhost_sock_dir(), fake_port['id'])})
vif_details.update({"vhostuser_mode": "client"})
vif_details = copy.deepcopy(
self.mech_driver.vif_details[portbindings.VIF_TYPE_AGILIO_OVS])
vif_details.update({
'vhostuser_socket': ovn_utils.ovn_vhu_sockpath(
ovn_conf.get_ovn_vhost_sock_dir(), fake_port['id']),
'vhostuser_mode': 'client',
portbindings.VIF_DETAILS_BRIDGE_NAME: n_const.DEFAULT_BR_INT,
portbindings.OVS_DATAPATH_TYPE: DEFAULT_DP_TYPE,
})
neutron_agent.AgentCache().get_agents.assert_called_once_with(
{'host': fake_host,
@ -1312,6 +1347,9 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
attrs={'hostname': fake_smartnic_dpu},
card_serial_number=fake_serial)
fake_chassis = fakes.FakeChassis.create(datapath_type=DEFAULT_DP_TYPE)
self.sb_ovn.db_find.return_value.execute.return_value = [
{'other_config': fake_chassis.other_config}]
self.sb_ovn.get_chassis_by_card_serial_from_cms_options.\
return_value = ch_smartnic_dpu
fake_host = 'host'
@ -1321,12 +1359,18 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
neutron_agent.AgentCache().get_agents.assert_called_once_with(
{'host': fake_smartnic_dpu,
'agent_type': ovn_const.OVN_CONTROLLER_TYPES})
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'],
portbindings.VIF_TYPE_OVS,
vif_details = copy.deepcopy(
self.mech_driver.vif_details[portbindings.VIF_TYPE_OVS])
vif_details[
portbindings.VIF_DETAILS_BRIDGE_NAME] = n_const.DEFAULT_BR_INT
vif_details[portbindings.OVS_DATAPATH_TYPE] = DEFAULT_DP_TYPE
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'], portbindings.VIF_TYPE_OVS, vif_details)
def test_bind_port_vdpa(self):
fake_chassis = fakes.FakeChassis.create(datapath_type=DEFAULT_DP_TYPE)
self.sb_ovn.db_find.return_value.execute.return_value = [
{'other_config': fake_chassis.other_config}]
segment_attrs = {'network_type': 'geneve',
'physical_network': None,
'segmentation_id': 1023}
@ -1343,10 +1387,13 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
neutron_agent.AgentCache().get_agents.assert_called_once_with(
{'host': fake_host,
'agent_type': ovn_const.OVN_CONTROLLER_TYPES})
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'],
portbindings.VIF_TYPE_OVS,
vif_details = copy.deepcopy(
self.mech_driver.vif_details[portbindings.VIF_TYPE_OVS])
vif_details[
portbindings.VIF_DETAILS_BRIDGE_NAME] = n_const.DEFAULT_BR_INT
vif_details[portbindings.OVS_DATAPATH_TYPE] = DEFAULT_DP_TYPE
fake_port_context.set_binding.assert_called_once_with(
fake_segments[0]['id'], portbindings.VIF_TYPE_OVS, vif_details)
def test_bind_port_geneve(self):
segment_attrs = {'network_type': 'geneve',
@ -2374,7 +2421,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
mock_notify_dhcp.assert_called_with(fake_port['id'])
def test_agent_alive_true(self):
chassis_private = self._add_chassis(5)
chassis_private = self._add_chassis_private(5)
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
ovn_const.OVN_METADATA_AGENT):
self.mech_driver.nb_ovn.nb_global.nb_cfg = 5
@ -2386,7 +2433,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
# Agent should be reported as alive when the nb_cfg delta is 1
# even if the last update time was old enough.
nb_cfg = 5
chassis_private = self._add_chassis(nb_cfg)
chassis_private = self._add_chassis_private(nb_cfg)
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
ovn_const.OVN_METADATA_AGENT):
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 1
@ -2401,7 +2448,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
def test_agent_alive_not_timed_out(self):
nb_cfg = 3
chassis_private = self._add_chassis(nb_cfg)
chassis_private = self._add_chassis_private(nb_cfg)
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
ovn_const.OVN_METADATA_AGENT):
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
@ -2412,7 +2459,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
def test_agent_alive_timed_out(self):
nb_cfg = 3
chassis_private = self._add_chassis(nb_cfg)
chassis_private = self._add_chassis_private(nb_cfg)
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
ovn_const.OVN_METADATA_AGENT):
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
@ -2427,7 +2474,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
def test_agent_with_nb_cfg_timestamp_timeout(self):
nb_cfg = 3
chassis_private = self._add_chassis(nb_cfg)
chassis_private = self._add_chassis_private(nb_cfg)
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
updated_at = (timeutils.utcnow_ts() - cfg.CONF.agent_down_time - 1
@ -2441,7 +2488,7 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
def test_agent_with_nb_cfg_timestamp_not_timeout(self):
nb_cfg = 3
chassis_private = self._add_chassis(nb_cfg)
chassis_private = self._add_chassis_private(nb_cfg)
self.mech_driver.nb_ovn.nb_global.nb_cfg = nb_cfg + 2
updated_at = timeutils.utcnow_ts() * 1000

View File

@ -11,7 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from unittest import mock
from neutron_lib.api.definitions import portbindings as pb
@ -21,6 +21,7 @@ from neutron_lib.db import api as db_api
from neutron_lib import exceptions
from oslo_utils import uuidutils
from neutron.common import _constants as n_const
from neutron.db.models.plugins.ml2 import geneveallocation
from neutron.db.models.plugins.ml2 import vxlanallocation
from neutron.objects import ports as port_obj
@ -48,11 +49,12 @@ class TestMigrateNeutronDatabaseToOvn(
for vif_details in vif_details_list:
port = self._make_port(self.fmt, network_id)['port']
port_o = port_obj.PortBinding.get_object(
ctx, port_id=port['id'], host='')
port_o.vif_type = 'ovs'
port_o.vif_details = vif_details
port_o.update()
with db_api.CONTEXT_WRITER.using(ctx):
port_o = port_obj.PortBinding.get_object(
ctx, port_id=port['id'], host='')
port_o.vif_type = 'ovs'
port_o.vif_details = vif_details
port_o.update()
for i in range(1, 4):
port = self._make_port(self.fmt, network_id)['port']
@ -152,14 +154,10 @@ class TestMigrateNeutronDatabaseToOvn(
{"foo": "bar"},
{},
]
expected_vif_details = [
{pb.CAP_PORT_FILTER: "true",
pb.OVS_HYBRID_PLUG: "true",
pb.VIF_DETAILS_CONNECTIVITY: pb.CONNECTIVITY_L2},
{pb.CAP_PORT_FILTER: "true"},
{"foo": "bar"},
{},
]
expected_vif_details = copy.deepcopy(vif_details_list)
for vif_detail in expected_vif_details:
vif_detail[pb.VIF_DETAILS_BRIDGE_NAME] = n_const.DEFAULT_BR_INT
expected_vif_details.append({})
self._create_ml2_ovs_test_resources(vif_details_list)
db_migration.migrate_neutron_database_to_ovn()

View File

@ -0,0 +1,8 @@
---
fixes:
- |
[`bug 2045889 <https://bugs.launchpad.net/neutron/+bug/2045889>`_]
The ports bound to ML2/OVN now contain the OVS bridge name and datapath
type in the VIF details dictionary. NOTE: in the ML2/OVS to ML2/OVN
migration, the local host OVN bridge (integration bridge) per port is not
known; "br-int" will be used by default (that value is rarely changed).