add configurable per port bridges
This patch add a new configuration option to use per port bridge when hybrid_plug is false. This can be used with OVN to reduce packet loss during a live migration. OVN can only install openflow rules when a port both has external_ids set and an ofport-id assigned. Since the ofport-id is only assigned when a netdev matching the port name exists connected to the dataplane, OVN cannot install the flows until libvirt create the tap on the destination host during a live migration. On loaded systems this can result in multiple seconds of packet loss. To address this we introduce per port bridges which are connencted to the integration brige by a patch port pair. Since the patch port will exist on the dataplane during pre live migration OVN can install the flows on the integration bridge before we begin the migration reducing or avoiding packet loss. Change-Id: I0d55ccbef5b585330b5512e67e442b80304a2e73 Depends-On: https://review.opendev.org/c/openstack/nova/+/797428 Closes-Bug: #1933517
This commit is contained in:
parent
b75af3ae13
commit
b837c1a74f
@ -112,6 +112,12 @@
|
||||
parent: os-vif-tempest-base
|
||||
description: |
|
||||
os-vif ovn job (tests hybrid-plug=false)
|
||||
vars:
|
||||
devstack_local_conf:
|
||||
post-config:
|
||||
$NOVA_CONF:
|
||||
os_vif_ovs:
|
||||
per_port_bridge: true
|
||||
|
||||
- job:
|
||||
name: os-vif-linuxbridge
|
||||
|
25
releasenotes/notes/per-port-bridge-c6a50179a0256de3.yaml
Normal file
25
releasenotes/notes/per-port-bridge-c6a50179a0256de3.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
The os-vif OVS plugin now supports using per-port OVS bridges when hybrid plug
|
||||
is not used. This is disabled by default and can be enabled by defining
|
||||
``[os_vif_ovs]/per_port_bridge=True`` in the compute service nova.conf.
|
||||
This capability should only be enabled if you are deploying with ml2/ovn
|
||||
and experience packet loss during live migrations. This is not supported
|
||||
on windows or when using ironic smartnic ports. This option was introduced
|
||||
to address bug: #1933517. When using OVN as a network backend OVN
|
||||
requires the OVS interface to both have an ofport-id and the neutron port
|
||||
uuid defined in the external_ids field. When the port is plugged if
|
||||
``[os_vif_ovs]/per_port_bridge`` is not enabled then the OVS port will not
|
||||
be assigned an openflow port id until the tap device is created on the host.
|
||||
On loaded system with many flows and ports it can take a few second for OVN
|
||||
to detect the creation of the tap device and install the correct flows.
|
||||
During that interval packets can be dropped.
|
||||
When ``[os_vif_ovs]/per_port_bridge`` is enabled, os-vif will add the VM tap
|
||||
device to a new bridge that is connected to the integration bridge via a
|
||||
patch port. This enables OVN to install the openflow rules on the
|
||||
integration bridge before the tap is created reducing the possibility for
|
||||
packet loss during a live migration. By default per port bridges are disabled
|
||||
and this feature is considered experimental, however it will likely be enabled
|
||||
by default in the future after we gain experience with how this bridge topology
|
||||
scales in larger deployments.
|
@ -19,10 +19,14 @@
|
||||
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from os_vif import exception as osv_exception
|
||||
from os_vif.internal.ip.api import ip as ip_lib
|
||||
from os_vif import objects
|
||||
from os_vif import plugin
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
from vif_plug_ovs import constants
|
||||
from vif_plug_ovs import exception
|
||||
@ -30,6 +34,8 @@ from vif_plug_ovs import linux_net
|
||||
from vif_plug_ovs.ovsdb import api as ovsdb_api
|
||||
from vif_plug_ovs.ovsdb import ovsdb_lib
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OvsPlugin(plugin.PluginBase):
|
||||
"""An OVS plugin that can setup VIFs in many ways
|
||||
@ -90,7 +96,13 @@ class OvsPlugin(plugin.PluginBase):
|
||||
cfg.BoolOpt('isolate_vif', default=False,
|
||||
help='Controls if VIF should be isolated when plugged '
|
||||
'to the ovs bridge. This should only be set to True '
|
||||
'when using the neutron ovs ml2 agent.')
|
||||
'when using the neutron ovs ml2 agent.'),
|
||||
cfg.BoolOpt('per_port_bridge', default=False,
|
||||
help='Controls if VIF should be plugged into a per-port '
|
||||
'bridge. This is experimental and controls the plugging '
|
||||
'behavior when not using hybrid-plug.'
|
||||
'This is only used on linux and should be set to false '
|
||||
'in all other cases such as ironic smartnic ports.')
|
||||
)
|
||||
|
||||
def __init__(self, config):
|
||||
@ -98,8 +110,8 @@ class OvsPlugin(plugin.PluginBase):
|
||||
self.ovsdb = ovsdb_lib.BaseOVS(self.config)
|
||||
|
||||
@staticmethod
|
||||
def gen_port_name(prefix, id):
|
||||
return ("%s%s" % (prefix, id))[:OvsPlugin.NIC_NAME_LEN]
|
||||
def gen_port_name(prefix, vif_id, max_length=NIC_NAME_LEN):
|
||||
return ("%s%s" % (prefix, vif_id))[:max_length]
|
||||
|
||||
@staticmethod
|
||||
def get_veth_pair_names(vif):
|
||||
@ -163,12 +175,13 @@ class OvsPlugin(plugin.PluginBase):
|
||||
# can be enabled automatically in the future.
|
||||
if self.config.isolate_vif:
|
||||
kwargs['tag'] = constants.DEAD_VLAN
|
||||
bridge = kwargs.pop('bridge', vif.network.bridge)
|
||||
self.ovsdb.create_ovs_vif_port(
|
||||
vif.network.bridge,
|
||||
bridge,
|
||||
vif_name,
|
||||
vif.port_profile.interface_id,
|
||||
vif.address, instance_info.uuid,
|
||||
mtu,
|
||||
mtu=mtu,
|
||||
**kwargs)
|
||||
|
||||
def _update_vif_port(self, vif, vif_name):
|
||||
@ -232,6 +245,37 @@ class OvsPlugin(plugin.PluginBase):
|
||||
self._get_vif_datapath_type(vif))
|
||||
self._create_vif_port(vif, vif.id, instance_info)
|
||||
|
||||
def _plug_port_bridge(self, vif, instance_info):
|
||||
"""Create a per-VIF OVS bridge and patch pair."""
|
||||
|
||||
# NOTE(sean-k-mooney): the port name prefix should not be
|
||||
# changed to avoid losing ports on upgrade.
|
||||
port_bridge_name = self.gen_port_name('pb', vif.id)
|
||||
port_bridge_patch = self.gen_port_name('pbp', vif.id, max_length=64)
|
||||
int_bridge_name = vif.network.bridge
|
||||
int_bridge_patch = self.gen_port_name('ibp', vif.id, max_length=64)
|
||||
|
||||
self.ovsdb.ensure_ovs_bridge(
|
||||
int_bridge_name, self._get_vif_datapath_type(vif))
|
||||
self.ovsdb.ensure_ovs_bridge(
|
||||
port_bridge_name, self._get_vif_datapath_type(vif))
|
||||
self._create_vif_port(
|
||||
vif, vif.vif_name, instance_info, bridge=port_bridge_name,
|
||||
set_ids=False
|
||||
)
|
||||
tag = constants.DEAD_VLAN if self.config.isolate_vif else None
|
||||
iface_id = vif.id
|
||||
mac = vif.address
|
||||
instance_id = instance_info.uuid
|
||||
LOG.debug(
|
||||
'creating patch port pair \n'
|
||||
f'{port_bridge_name}:({port_bridge_patch}) -> '
|
||||
f'{int_bridge_name}:({int_bridge_patch})'
|
||||
)
|
||||
self.ovsdb.create_patch_port_pair(
|
||||
port_bridge_name, port_bridge_patch, int_bridge_name,
|
||||
int_bridge_patch, iface_id, mac, instance_id, tag=tag)
|
||||
|
||||
def _plug_vif_generic(self, vif, instance_info):
|
||||
"""Create a per-VIF OVS port."""
|
||||
self.ovsdb.ensure_ovs_bridge(vif.network.bridge,
|
||||
@ -289,20 +333,30 @@ class OvsPlugin(plugin.PluginBase):
|
||||
raise exception.WrongPortProfile(
|
||||
profile=vif.port_profile.__class__.__name__)
|
||||
|
||||
if isinstance(vif, objects.vif.VIFOpenVSwitch):
|
||||
if sys.platform != constants.PLATFORM_WIN32:
|
||||
if sys.platform == constants.PLATFORM_WIN32:
|
||||
if type(vif) not in (
|
||||
objects.vif.VIFOpenVSwitch, objects.vif.VIFBridge
|
||||
):
|
||||
raise osv_exception.PlugException(
|
||||
vif=vif, err="This vif type is not supported on Windows")
|
||||
|
||||
self._plug_vif_windows(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFOpenVSwitch):
|
||||
if self.config.per_port_bridge:
|
||||
self._plug_port_bridge(vif, instance_info)
|
||||
else:
|
||||
self._plug_vif_generic(vif, instance_info)
|
||||
else:
|
||||
self._plug_vif_windows(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFBridge):
|
||||
if sys.platform != constants.PLATFORM_WIN32:
|
||||
self._plug_bridge(vif, instance_info)
|
||||
else:
|
||||
self._plug_vif_windows(vif, instance_info)
|
||||
self._plug_bridge(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFVHostUser):
|
||||
self._plug_vhostuser(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFHostDevice):
|
||||
self._plug_vf(vif, instance_info)
|
||||
else:
|
||||
# This should never be raised.
|
||||
raise osv_exception.PlugException(
|
||||
vif=vif,
|
||||
err="This vif type is not supported by this plugin")
|
||||
|
||||
def _unplug_vhostuser(self, vif, instance_info):
|
||||
self.ovsdb.delete_ovs_vif_port(vif.network.bridge,
|
||||
@ -328,6 +382,18 @@ class OvsPlugin(plugin.PluginBase):
|
||||
self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.id,
|
||||
delete_netdev=False)
|
||||
|
||||
def _unplug_port_bridge(self, vif, instance_info):
|
||||
"""Create a per-VIF OVS bridge and patch pair."""
|
||||
# NOTE(sean-k-mooney): the port name prefix should not be
|
||||
# changed to avoid loosing ports on upgrade.
|
||||
port_bridge_name = self.gen_port_name('pb', vif.id)
|
||||
port_bridge_patch = self.gen_port_name('pbp', vif.id, max_length=64)
|
||||
int_bridge_patch = self.gen_port_name('ibp', vif.id, max_length=64)
|
||||
self.ovsdb.delete_ovs_vif_port(vif.network.bridge, int_bridge_patch)
|
||||
self.ovsdb.delete_ovs_vif_port(port_bridge_name, port_bridge_patch)
|
||||
self.ovsdb.delete_ovs_vif_port(port_bridge_name, vif.vif_name)
|
||||
self.ovsdb.delete_ovs_bridge(port_bridge_name)
|
||||
|
||||
def _unplug_vif_generic(self, vif, instance_info):
|
||||
"""Remove port from OVS."""
|
||||
# NOTE(sean-k-mooney): even with the partial revert of change
|
||||
@ -363,18 +429,26 @@ class OvsPlugin(plugin.PluginBase):
|
||||
objects.vif.VIFPortProfileOpenVSwitch):
|
||||
raise exception.WrongPortProfile(
|
||||
profile=vif.port_profile.__class__.__name__)
|
||||
|
||||
if isinstance(vif, objects.vif.VIFOpenVSwitch):
|
||||
if sys.platform != constants.PLATFORM_WIN32:
|
||||
if sys.platform == constants.PLATFORM_WIN32:
|
||||
if type(vif) not in (
|
||||
objects.vif.VIFOpenVSwitch, objects.vif.VIFBridge
|
||||
):
|
||||
raise osv_exception.UnplugException(
|
||||
vif=vif, err="This vif type is not supported on windows.")
|
||||
self._unplug_vif_windows(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFOpenVSwitch):
|
||||
if self.config.per_port_bridge:
|
||||
self._unplug_port_bridge(vif, instance_info)
|
||||
else:
|
||||
self._unplug_vif_generic(vif, instance_info)
|
||||
else:
|
||||
self._unplug_vif_windows(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFBridge):
|
||||
if sys.platform != constants.PLATFORM_WIN32:
|
||||
self._unplug_bridge(vif, instance_info)
|
||||
else:
|
||||
self._unplug_vif_windows(vif, instance_info)
|
||||
self._unplug_bridge(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFVHostUser):
|
||||
self._unplug_vhostuser(vif, instance_info)
|
||||
elif isinstance(vif, objects.vif.VIFHostDevice):
|
||||
self._unplug_vf(vif)
|
||||
else:
|
||||
# this should never be raised.
|
||||
raise osv_exception.UnplugException(
|
||||
vif=vif,
|
||||
err="This vif type is not supported by this plugin")
|
||||
|
@ -71,10 +71,75 @@ class BaseOVS(object):
|
||||
return self.ovsdb.add_br(bridge, may_exist=True,
|
||||
datapath_type=datapath_type).execute()
|
||||
|
||||
def create_ovs_vif_port(self, bridge, dev, iface_id, mac, instance_id,
|
||||
mtu=None, interface_type=None,
|
||||
vhost_server_path=None, tag=None,
|
||||
pf_pci=None, vf_num=None):
|
||||
def delete_ovs_bridge(self, bridge):
|
||||
"""Delete ovs brdige by name
|
||||
|
||||
:param bridge: bridge name as a string
|
||||
|
||||
.. note:: Do Not call with br-int !!!
|
||||
"""
|
||||
# TODO(sean-k-mooney): when we fix bug: #1914886
|
||||
# add a guard against deleting the integration bridge
|
||||
# after adding a config option to store its name.
|
||||
return self.ovsdb.del_br(bridge).execute()
|
||||
|
||||
def create_patch_port_pair(
|
||||
self, port_bridge, port_bridge_port, int_bridge, int_bridge_port,
|
||||
iface_id, mac, instance_id, tag=None
|
||||
):
|
||||
"""Create a patch port pair between any two bridges.
|
||||
|
||||
:param port_bridge: the source bridge name for the patch port pair.
|
||||
:param port_bridge_port: the name of the patch port on the
|
||||
source bridge.
|
||||
:param int_bridge: the target bridge name, typically br-int.
|
||||
:param int_bridge_port: the name of the patch port on the
|
||||
target bridge.
|
||||
:param iface_id: neutron port ID.
|
||||
:param mac: port MAC.
|
||||
:param instance_id: instance uuid.
|
||||
:param mtu: port MTU.
|
||||
:param tag: OVS interface tag used for vlan isolation.
|
||||
"""
|
||||
|
||||
# NOTE(sean-k-mooney): we use a transaction here for 2 reasons:
|
||||
# 1.) if using the vsctl client its faster
|
||||
# 2.) in all cases we either want to fully create the patch port
|
||||
# pair or not create it atomicly. By using a transaction we know
|
||||
# that we will never be in a mixed state where it was partly created.
|
||||
with self.ovsdb.transaction() as txn:
|
||||
# create integration bridge patch peer
|
||||
external_ids = {
|
||||
'iface-id': iface_id, 'iface-status': 'active',
|
||||
'attached-mac': mac, 'vm-uuid': instance_id
|
||||
}
|
||||
col_values = [
|
||||
('external_ids', external_ids),
|
||||
('type', 'patch'),
|
||||
('options', {'peer': port_bridge_port})
|
||||
]
|
||||
|
||||
txn.add(self.ovsdb.add_port(int_bridge, int_bridge_port))
|
||||
if tag:
|
||||
txn.add(
|
||||
self.ovsdb.db_set('Port', int_bridge_port, ('tag', tag)))
|
||||
txn.add(
|
||||
self.ovsdb.db_set('Interface', int_bridge_port, *col_values))
|
||||
|
||||
# create port bidge patch peer
|
||||
col_values = [
|
||||
('type', 'patch'),
|
||||
('options', {'peer': int_bridge_port})
|
||||
]
|
||||
txn.add(self.ovsdb.add_port(port_bridge, port_bridge_port))
|
||||
txn.add(
|
||||
self.ovsdb.db_set('Interface', port_bridge_port, *col_values))
|
||||
|
||||
def create_ovs_vif_port(
|
||||
self, bridge, dev, iface_id, mac, instance_id,
|
||||
mtu=None, interface_type=None, vhost_server_path=None,
|
||||
tag=None, pf_pci=None, vf_num=None, set_ids=True
|
||||
):
|
||||
"""Create OVS port
|
||||
|
||||
:param bridge: bridge name to create the port on.
|
||||
@ -88,6 +153,7 @@ class BaseOVS(object):
|
||||
:param tag: OVS interface tag.
|
||||
:param pf_pci: PCI address of PF for dpdk representor port.
|
||||
:param vf_num: VF number of PF for dpdk representor port.
|
||||
:param set_ids: set external ids on port (bool).
|
||||
|
||||
.. note:: create DPDK representor port by setting all three values:
|
||||
`interface_type`, `pf_pci` and `vf_num`. if interface type is
|
||||
@ -98,7 +164,7 @@ class BaseOVS(object):
|
||||
'iface-status': 'active',
|
||||
'attached-mac': mac,
|
||||
'vm-uuid': instance_id}
|
||||
col_values = [('external_ids', external_ids)]
|
||||
col_values = [('external_ids', external_ids)] if set_ids else []
|
||||
if interface_type:
|
||||
col_values.append(('type', interface_type))
|
||||
if vhost_server_path:
|
||||
@ -114,7 +180,8 @@ class BaseOVS(object):
|
||||
txn.add(self.ovsdb.add_port(bridge, dev))
|
||||
if tag:
|
||||
txn.add(self.ovsdb.db_set('Port', dev, ('tag', tag)))
|
||||
txn.add(self.ovsdb.db_set('Interface', dev, *col_values))
|
||||
if col_values:
|
||||
txn.add(self.ovsdb.db_set('Interface', dev, *col_values))
|
||||
self.update_device_mtu(dev, mtu, interface_type=interface_type)
|
||||
|
||||
def update_ovs_vif_port(self, dev, mtu=None, interface_type=None):
|
||||
|
@ -152,3 +152,40 @@ class TestOVSDBLib(testscenarios.WithScenarios,
|
||||
self.ovs.ensure_ovs_bridge(bridge_name, constants.OVS_DATAPATH_SYSTEM)
|
||||
self.assertTrue(self._check_bridge(bridge_name))
|
||||
self.addCleanup(self._del_bridge, bridge_name)
|
||||
|
||||
def test_create_patch_port_pair(self):
|
||||
port_bridge = 'fake-pb'
|
||||
port_bridge_port = 'fake-pbp'
|
||||
int_bridge = 'pb-int'
|
||||
int_bridge_port = 'fake-ibp'
|
||||
iface_id = 'iface_id'
|
||||
mac = 'ca:fe:ca:fe:ca:fe'
|
||||
instance_id = uuidutils.generate_uuid()
|
||||
|
||||
# deleting a bridge deletes all ports on bridges so we register the
|
||||
# bridge cleanup first so if we fail anywhere it runs.
|
||||
self.addCleanup(self._del_bridge, port_bridge)
|
||||
self.addCleanup(self._del_bridge, int_bridge)
|
||||
self.ovs.ensure_ovs_bridge(port_bridge, constants.OVS_DATAPATH_SYSTEM)
|
||||
self.ovs.ensure_ovs_bridge(int_bridge, constants.OVS_DATAPATH_SYSTEM)
|
||||
self.ovs.create_patch_port_pair(
|
||||
port_bridge, port_bridge_port, int_bridge, int_bridge_port,
|
||||
iface_id, mac, instance_id, tag=2000)
|
||||
self.assertTrue(self._check_bridge(port_bridge))
|
||||
self.assertTrue(self._check_bridge(int_bridge))
|
||||
|
||||
expected_external_ids = {'iface-status': 'active',
|
||||
'iface-id': iface_id,
|
||||
'attached-mac': mac,
|
||||
'vm-uuid': instance_id}
|
||||
self._check_parameter(
|
||||
'Interface', int_bridge_port, 'external_ids',
|
||||
expected_external_ids)
|
||||
self._check_parameter('Interface', int_bridge_port, 'type', 'patch')
|
||||
port_opts = {'peer': port_bridge_port}
|
||||
self._check_parameter(
|
||||
'Interface', int_bridge_port, 'options', port_opts)
|
||||
self._check_parameter('Port', int_bridge_port, 'tag', 2000)
|
||||
port_opts = {'peer': int_bridge_port}
|
||||
self._check_parameter(
|
||||
'Interface', port_bridge_port, 'options', port_opts)
|
||||
|
@ -66,6 +66,7 @@ class PluginTest(testtools.TestCase):
|
||||
interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa',
|
||||
datapath_type='system')
|
||||
|
||||
# This is used for ironic with vif_type=smartnic
|
||||
self.profile_ovs_smart_nic = objects.vif.VIFPortProfileOpenVSwitch(
|
||||
interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa',
|
||||
create_port=True)
|
||||
@ -89,6 +90,7 @@ class PluginTest(testtools.TestCase):
|
||||
vif_name='tap-xxx-yyy-zzz',
|
||||
port_profile=self.profile_ovs)
|
||||
|
||||
# This is used for ironic with vif_type=smartnic
|
||||
self.vif_ovs_smart_nic = objects.vif.VIFOpenVSwitch(
|
||||
id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
|
||||
address='ca:fe:de:ad:be:ef',
|
||||
@ -153,7 +155,7 @@ class PluginTest(testtools.TestCase):
|
||||
self.vif_ovs.network.bridge, mock.sentinel.vif_name,
|
||||
self.vif_ovs.port_profile.interface_id,
|
||||
self.vif_ovs.address, self.instance.uuid,
|
||||
plugin.config.network_device_mtu,
|
||||
mtu=plugin.config.network_device_mtu,
|
||||
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_ovs_vif_port')
|
||||
@ -167,7 +169,7 @@ class PluginTest(testtools.TestCase):
|
||||
self.vif_ovs.network.bridge, mock.sentinel.vif_name,
|
||||
self.vif_ovs.port_profile.interface_id,
|
||||
self.vif_ovs.address, self.instance.uuid,
|
||||
self.network_ovs_mtu.mtu,
|
||||
mtu=self.network_ovs_mtu.mtu,
|
||||
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_ovs_vif_port')
|
||||
@ -181,18 +183,28 @@ class PluginTest(testtools.TestCase):
|
||||
self.vif_ovs.network.bridge, mock.sentinel.vif_name,
|
||||
self.vif_ovs.port_profile.interface_id,
|
||||
self.vif_ovs.address, self.instance.uuid,
|
||||
plugin.config.network_device_mtu,
|
||||
mtu=plugin.config.network_device_mtu,
|
||||
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE,
|
||||
tag=constants.DEAD_VLAN)
|
||||
|
||||
@mock.patch.object(ovs, 'sys')
|
||||
@mock.patch.object(ovs.OvsPlugin, '_plug_vif_generic')
|
||||
def test_plug_ovs(self, plug_vif_generic, mock_sys):
|
||||
def test_plug_ovs_port_bridge_false(self, plug_vif_generic, mock_sys):
|
||||
mock_sys.platform = 'linux'
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin.plug(self.vif_ovs, self.instance)
|
||||
plug_vif_generic.assert_called_once_with(self.vif_ovs,
|
||||
self.instance)
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
||||
plugin.plug(self.vif_ovs, self.instance)
|
||||
plug_vif_generic.assert_called_once_with(
|
||||
self.vif_ovs, self.instance)
|
||||
|
||||
@mock.patch.object(ovs, 'sys')
|
||||
@mock.patch.object(ovs.OvsPlugin, '_plug_port_bridge')
|
||||
def test_plug_ovs_port_bridge_true(self, plug_vif, mock_sys):
|
||||
mock_sys.platform = 'linux'
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', True):
|
||||
plugin.plug(self.vif_ovs, self.instance)
|
||||
plug_vif.assert_called_once_with(self.vif_ovs, self.instance)
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
|
||||
@mock.patch.object(ovs.OvsPlugin, "_create_vif_port")
|
||||
@ -298,11 +310,21 @@ class PluginTest(testtools.TestCase):
|
||||
|
||||
@mock.patch.object(ovs, 'sys')
|
||||
@mock.patch.object(ovs.OvsPlugin, '_unplug_vif_generic')
|
||||
def test_unplug_ovs(self, unplug, mock_sys):
|
||||
def test_unplug_ovs_port_bridge_false(self, unplug, mock_sys):
|
||||
mock_sys.platform = 'linux'
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin.unplug(self.vif_ovs, self.instance)
|
||||
unplug.assert_called_once_with(self.vif_ovs, self.instance)
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
||||
plugin.unplug(self.vif_ovs, self.instance)
|
||||
unplug.assert_called_once_with(self.vif_ovs, self.instance)
|
||||
|
||||
@mock.patch.object(ovs, 'sys')
|
||||
@mock.patch.object(ovs.OvsPlugin, '_unplug_port_bridge')
|
||||
def test_unplug_ovs_port_bridge_true(self, unplug, mock_sys):
|
||||
mock_sys.platform = 'linux'
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', True):
|
||||
plugin.unplug(self.vif_ovs, self.instance)
|
||||
unplug.assert_called_once_with(self.vif_ovs, self.instance)
|
||||
|
||||
@mock.patch.object(ovs.OvsPlugin, '_unplug_vif_generic')
|
||||
def test_unplug_vif_generic(self, delete_port):
|
||||
@ -371,7 +393,7 @@ class PluginTest(testtools.TestCase):
|
||||
'e65867e0-9340-4a7f-a256-09af6eb7a3aa',
|
||||
'ca:fe:de:ad:be:ef',
|
||||
'f0000000-0000-0000-0000-000000000001',
|
||||
1500, interface_type='dpdkvhostuserclient',
|
||||
mtu=1500, interface_type='dpdkvhostuserclient',
|
||||
vhost_server_path='/var/run/openvswitch/vhub679325f-ca'
|
||||
)],
|
||||
'ensure_ovs_bridge': [mock.call('br0', dp_type)]
|
||||
@ -472,17 +494,19 @@ class PluginTest(testtools.TestCase):
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
|
||||
@mock.patch.object(ovs.OvsPlugin, "_create_vif_port")
|
||||
def test_plug_vif_ovs_smart_nic(self, create_port, ensure_bridge):
|
||||
def test_plug_vif_ovs_ironic_smart_nic(self, create_port, ensure_bridge):
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin.plug(self.vif_ovs_smart_nic, self.instance)
|
||||
ensure_bridge.assert_called_once()
|
||||
create_port.assert_called_once()
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
||||
plugin.plug(self.vif_ovs_smart_nic, self.instance)
|
||||
ensure_bridge.assert_called_once()
|
||||
create_port.assert_called_once()
|
||||
|
||||
@mock.patch.object(ovs.OvsPlugin, '_unplug_vif_generic')
|
||||
def test_unplug_vif_ovs_smart_nic(self, delete_port):
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin.unplug(self.vif_ovs_smart_nic, self.instance)
|
||||
delete_port.assert_called_once()
|
||||
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
||||
plugin.unplug(self.vif_ovs_smart_nic, self.instance)
|
||||
delete_port.assert_called_once()
|
||||
|
||||
@mock.patch.object(linux_net, 'get_dpdk_representor_port_name')
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
|
||||
@ -544,3 +568,33 @@ class PluginTest(testtools.TestCase):
|
||||
get_dpdk_representor_port_name.assert_has_calls(
|
||||
calls['get_dpdk_representor_port_name'])
|
||||
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_patch_port_pair')
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
|
||||
@mock.patch.object(ovs.OvsPlugin, "_create_vif_port")
|
||||
def test_plug_port_bridge(
|
||||
self, create_port, ensure_bridge, create_patch_port_pair):
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin._plug_port_bridge(self.vif_ovs, self.instance)
|
||||
calls = [
|
||||
mock.call('br0', 'netdev'),
|
||||
mock.call('pbb679325f-ca8', 'netdev')
|
||||
]
|
||||
ensure_bridge.assert_has_calls(calls)
|
||||
create_port.assert_called_once()
|
||||
create_patch_port_pair.assert_called_once()
|
||||
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
|
||||
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
||||
def test_unplug_port_bridge(
|
||||
self, delete_ovs_bridge, delete_ovs_vif_port):
|
||||
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
||||
plugin._unplug_port_bridge(self.vif_ovs, self.instance)
|
||||
calls = [
|
||||
mock.call('br0', 'ibpb679325f-ca89-4ee0-a8be-6db1409b69ea'),
|
||||
mock.call(
|
||||
'pbb679325f-ca8', 'pbpb679325f-ca89-4ee0-a8be-6db1409b69ea'),
|
||||
mock.call('pbb679325f-ca8', 'tap-xxx-yyy-zzz')
|
||||
]
|
||||
delete_ovs_vif_port.assert_has_calls(calls)
|
||||
delete_ovs_bridge.assert_called_once_with('pbb679325f-ca8')
|
||||
|
Loading…
x
Reference in New Issue
Block a user