Tap Mirror OVS driver

Related-Bug: #2015471
Change-Id: I9a80fd7a6c3b782454047b6295e51cc549842f84
This commit is contained in:
elajkat 2023-06-15 14:26:32 +02:00
parent f92cd3530d
commit faa98ef97c
13 changed files with 827 additions and 57 deletions

View File

@ -27,6 +27,7 @@ from neutron_lib import constants
from neutron_lib import context as neutron_context
from neutron_lib import rpc as n_rpc
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_service import service
@ -72,6 +73,12 @@ class TaasPluginApi(api.TaasPluginApiMixin):
cctxt.cast(context, 'set_tap_flow_status', msg=msg, status=status,
host=host)
@log_helpers.log_method_call
def set_tap_mirror_status(self, msg, status, host):
LOG.debug("In RPC Call for set Tap Mirror status")
# TODO(lajoskatona): We can have a status field to indicate that the
# Neutron port is UP, is it useful?
class TaasAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
@ -110,6 +117,16 @@ class TaasAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
'set_status_func_name': 'set_tap_flow_status',
'fail_status': constants.PENDING_DELETE,
'succ_status': constants.INACTIVE},
'create_tap_mirror': {
'msg_name': 'tap_mirror',
'set_status_func_name': 'set_tap_mirror_status',
'fail_status': constants.ERROR,
'succ_status': constants.ACTIVE},
'delete_tap_mirror': {
'msg_name': 'tap_mirror',
'set_status_func_name': 'set_tap_mirror_status',
'fail_status': constants.PENDING_DELETE,
'succ_status': constants.INACTIVE},
'periodic_tasks': {
'msg_name': 'periodic_tasks',
}
@ -212,6 +229,39 @@ class TaasAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
tap_flow_msg,
'delete_tap_flow')
@log_helpers.log_method_call
def create_tap_mirror(self, context, tap_mirror_msg, host):
"""Handle Rpc from plugin to create a tap_mirror."""
if not self._driver_and_host_verification(
host, tap_mirror_msg['port']):
LOG.debug("RPC Call for Create Tap Mirror. Either Host value [%s]"
"(received in RPC) doesn't match the host value "
"stored in agent [%s], or incompatible driver type. "
"Ignoring the message.", host, self.conf.host)
return
return self._invoke_driver_for_plugin_api(
context,
tap_mirror_msg,
'create_tap_mirror')
@log_helpers.log_method_call
def delete_tap_mirror(self, context, tap_mirror_msg, host):
"""...."""
if not self._driver_and_host_verification(host,
tap_mirror_msg['port']):
LOG.debug("RPC Call for Delete Tap Mirror. Either Host value [%s]"
"(received in RPC) doesn't match the host value "
"stored in agent [%s], or incompatible driver type. "
"Ignoring the message.", host, self.conf.host)
return
LOG.debug("In RPC Call for Delete Tap Mirror: MSG=%s", tap_mirror_msg)
return self._invoke_driver_for_plugin_api(
context,
tap_mirror_msg,
'delete_tap_mirror')
def _taas_rpc_setup(self):
# setup RPC to msg taas plugin
self.taas_plugin_rpc = TaasPluginApi(

View File

@ -57,3 +57,11 @@ class TaasAgentRpcCallbackMixin(object):
def delete_tap_flow(self, context, tap_flow_msg, host):
"""Handle RPC cast from plugin to delete a tap flow"""
pass
def create_tap_mirror(self, context, tap_mirror_msg, host):
"""Handle RPC cast from plugin to create a Tap Mirror."""
pass
def delete_tap_mirror(self, context, tap_mirror_msg, host):
"""Handle RPC cast from plugin to delete a Tap Mirror."""
pass

View File

@ -13,10 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
from neutron.agent.common import ovs_lib
from neutron.agent.linux import utils
from neutron.conf.agent import common
from neutron.conf.plugins.ml2.drivers import ovs_conf
from neutron_lib.plugins.ml2 import ovs_constants as n_ovs_consts
from neutron_taas.services.taas.agents.extensions import taas as taas_base
@ -46,6 +48,7 @@ class OvsTaasDriver(taas_base.TaasAgentDriver):
LOG.debug("Initializing Taas OVS Driver")
self.agent_api = None
self.root_helper = common.get_root_helper(cfg.CONF)
ovs_conf.register_ovs_agent_opts(cfg.CONF)
self.datapath_type = cfg.CONF.OVS.datapath_type
self.tunnel_types = cfg.CONF.AGENT.tunnel_types
@ -577,3 +580,105 @@ class OvsTaasDriver(taas_base.TaasAgentDriver):
dl_vlan=vlan_id,
dl_dst=("01:00:00:00:00:00/"
"01:00:00:00:00:00"))
@log_helpers.log_method_call
def create_tap_mirror(self, tap_mirror_msg):
source_port = tap_mirror_msg['port']
tap_mirror = tap_mirror_msg['tap_mirror']
type = ''
patch_int_tap_id = self.int_br.get_port_ofport('patch-int-tap')
patch_tap_int_id = self.tap_br.get_port_ofport('patch-tap-int')
# Get OVS port id for tap flow port
ovs_port = self.int_br.get_vif_port_by_id(source_port['id'])
ovs_port_id = ovs_port.ofport
options = collections.OrderedDict()
options['remote_ip'] = tap_mirror['remote_ip']
if 'erspan' in tap_mirror['mirror_type']:
type = 'erspan'
options['erspan_ver'] = "1"
options['erspan_idx'] = ""
options['key'] = ""
else:
type = 'gre'
directions = tap_mirror['directions']
for direction, tunnel_id in directions.items():
options['erspan_idx'] = str(tunnel_id)
# Note(lajoskatona): this is treated as hexa, so
# spanId will be d102, and Index will be d258 in the packet.
# As OVN doesn't care about this let's have OVS driver the same
# behaviour.
options['key'] = str(tunnel_id)
port_name = 'tm_%s_%s' % (direction.lower(),
tap_mirror['id'][0:6])
attrs = [('type', type),
('options', options)]
mirror_of_port = self.tap_br.add_port(port_name, *attrs)
if direction == 'IN':
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,
priority=20,
dl_dst=source_port['mac_address'],
actions="output:%s" % str(mirror_of_port))
self.int_br.add_flow(
table=0,
priority=20,
dl_dst=source_port['mac_address'],
actions="output:%s,resubmit(,%s)" %
(str(patch_int_tap_id),
str(n_ovs_consts.PACKET_RATE_LIMIT)))
if direction == 'OUT':
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,
priority=20,
dl_src=source_port['mac_address'],
actions="output:%s" % str(mirror_of_port))
self.int_br.add_flow(
table=0,
priority=20,
in_port=ovs_port_id,
actions="output:%s,resubmit(,%s)" %
(str(patch_int_tap_id),
str(n_ovs_consts.PACKET_RATE_LIMIT)))
# Add flow(s) in br-tap
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,
priority=1,
dl_dst=source_port['mac_address'],
actions="output:in_port")
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_REM,
priority=1,
dl_dst=source_port['mac_address'],
actions="output:%s" % str(patch_tap_int_id))
@log_helpers.log_method_call
def delete_tap_mirror(self, tap_mirror_msg):
source_port = tap_mirror_msg['port']
tap_mirror = tap_mirror_msg['tap_mirror']
directions = tap_mirror['directions']
# Get OVS port id for tap flow port
ovs_port = self.int_br.get_vif_port_by_id(source_port['id'])
ovs_port_id = ovs_port.ofport
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_REM,
dl_dst=source_port['mac_address'])
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC,
dl_dst=source_port['mac_address'])
for direction, tunnel_id in directions.items():
if direction == 'IN':
self.int_br.delete_flows(table=0,
dl_dst=source_port['mac_address'])
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC,
dl_dst=source_port['mac_address'])
if direction == 'OUT':
self.int_br.delete_flows(table=0, in_port=ovs_port_id)
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC,
dl_src=source_port['mac_address'])
port_name = 'tm_%s_%s' % (direction.lower(),
tap_mirror['id'][0:6])
self.tap_br.delete_port(port_name)

View File

@ -55,3 +55,19 @@ class TaasBaseDriver(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def delete_tap_flow_postcommit(self, context):
pass
@abc.abstractmethod
def create_tap_mirror_precommit(self, context):
pass
@abc.abstractmethod
def create_tap_mirror_postcommit(self, context):
pass
@abc.abstractmethod
def delete_tap_mirror_precommit(self, context):
pass
@abc.abstractmethod
def delete_tap_mirror_postcommit(self, context):
pass

View File

@ -65,3 +65,14 @@ class TapFlowContext(ServiceDriverContext):
@property
def tap_flow(self):
return self._tap_flow
class TapMirrorContext(ServiceDriverContext):
def __init__(self, service_plugin, plugin_context, tap_mirror):
super().__init__(service_plugin, plugin_context)
self._tap_mirror = tap_mirror
@property
def tap_mirror(self):
return self._tap_mirror

View File

@ -15,6 +15,7 @@
# under the License.
from neutron_lib import rpc as n_rpc
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
import oslo_messaging as messaging
@ -60,3 +61,15 @@ class TaasAgentApi(object):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'delete_tap_flow', tap_flow_msg=tap_flow_msg,
host=host)
@log_helpers.log_method_call
def create_tap_mirror(self, context, tap_mirror_msg, host):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'create_tap_mirror', tap_mirror_msg=tap_mirror_msg,
host=host)
@log_helpers.log_method_call
def delete_tap_mirror(self, context, tap_mirror_msg, host):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'delete_tap_mirror', tap_mirror_msg=tap_mirror_msg,
host=host)

View File

@ -30,6 +30,7 @@ from neutron_taas.services.taas.service_drivers import taas_agent_api
from neutron_taas.services.taas import taas_plugin
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
@ -328,3 +329,53 @@ class TaasRpcDriver(service_drivers.TaasBaseDriver):
def delete_tap_flow_postcommit(self, context):
pass
@log_helpers.log_method_call
def create_tap_mirror_precommit(self, context):
pass
@log_helpers.log_method_call
def create_tap_mirror_postcommit(self, context):
"""Send Tap Mirror creation RPC message to agent.
This RPC message includes ....
"""
# Get taas id associated with the Tap Service
tm = context.tap_mirror
port = self.service_plugin.get_port_details(context._plugin_context,
tm['port_id'])
host = port['binding:host_id']
rpc_msg = {'tap_mirror': tm,
'port': port}
self.agent_rpc.create_tap_mirror(context._plugin_context,
rpc_msg, host)
@log_helpers.log_method_call
def delete_tap_mirror_precommit(self, context):
"""Send Tap Mirror deletion RPC message to agent.
This RPC message includes .....
"""
tm = context.tap_mirror
try:
port = self.service_plugin.get_port_details(
context._plugin_context,
tm['port_id'])
host = port['binding:host_id']
except n_exc.PortNotFound:
# if not found, we just pass to None
port = None
host = None
rpc_msg = {'tap_mirror': tm,
'port': port}
self.agent_rpc.delete_tap_mirror(context._plugin_context,
rpc_msg, host)
@log_helpers.log_method_call
def delete_tap_mirror_postcommit(self, context):
pass

View File

@ -23,9 +23,12 @@ from neutron_lib.exceptions import taas as taas_exc
from neutron_taas.common import constants as taas_consts
from neutron_taas.db import tap_mirror_db
from neutron_taas.extensions import _tap_mirror as t_m_api_def
from neutron_taas.services.taas.service_drivers import (service_driver_context
as sd_context)
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
LOG = logging.getLogger(__name__)
@ -82,11 +85,22 @@ class TapMirrorPlugin(tap_mirror_db.Taas_mirror_db_Mixin):
LOG.debug("Host on which the port is created = %s", host)
else:
LOG.debug("Host could not be found, Port Binding disbaled!")
# Fail here? Is it a valid usecase to create a mirror for a
# port that is not bound?
with db_api.CONTEXT_WRITER.using(context):
self._validate_tap_tunnel_id(context, t_m['directions'])
tm = super().create_tap_mirror(context, tap_mirror)
# Precommit phase, is it necessary? tunnel id check should be in
# it....
driver_context = sd_context.TapMirrorContext(self, context, tm)
self.driver.create_tap_mirror_precommit(driver_context)
# Postcommit phase, do the backend stuff, or revert.
try:
self.driver.create_tap_mirror_postcommit(driver_context)
except Exception:
pass
return tm
def _validate_tap_tunnel_id(self, context, mirror_directions):
@ -104,9 +118,22 @@ class TapMirrorPlugin(tap_mirror_db.Taas_mirror_db_Mixin):
if tm:
with db_api.CONTEXT_WRITER.using(context):
# Precommit phase
driver_context = sd_context.TapMirrorContext(
self, context, tm)
self.driver.delete_tap_mirror_precommit(driver_context)
# check if tunnel id was really deleted
super().delete_tap_mirror(context, id)
# Postcommit phase, do the backend stuff, or revert.
try:
self.driver.delete_tap_mirror_postcommit(driver_context)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Failed to delete Tap Mirror on driver. "
"tap_mirror: %s", id)
@registry.receives(resources.PORT, [events.PRECOMMIT_DELETE])
@log_helpers.log_method_call
def handle_delete_port(self, resource, event, trigger, payload):

View File

@ -11,8 +11,79 @@
# 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 oslotest import base
FAKE_PORT_PARAMS = {
'mac': '52:54:00:12:35:02', 'pci_slot': 3, 'vf_index': '89',
'pf_device': 'net_enp0s3_52_54_00_12_35_02', 'src_vlans': '20'}
FAKE_TAP_SERVICE = {
'taas_id': '1234',
'port': {
'id': 'fake_1',
'mac_address': "52:54:00:12:35:02",
'binding:profile': {'pci_slot': 3},
'binding:vif_details': {'vlan': '20'}
}
}
FAKE_OF_PORT = {
'port_name': 'tap4321',
'ofport': 12,
}
FAKE_PORT_DICT = {
FAKE_OF_PORT['port_name']: 4
}
FAKE_TAP_SERVICE_OVS = {
'taas_id': 4321,
'port': {
'id': 'fake_2',
'mac_address': "fa:16:3e:33:0e:d4",
'binding:profile': {},
'binding:vif_details': {
'connectivity': 'l2',
'port_filter': True,
'ovs_hybrid_plug': False,
'datapath_type': 'system',
'bridge_name': 'br-int'
}
}
}
FAKE_TAP_FLOW = {
'taas_id': FAKE_TAP_SERVICE_OVS['taas_id'],
'port': FAKE_TAP_SERVICE['port'],
'port_mac': 'fa:16:3e:5c:67:6a',
'ts_port': FAKE_TAP_SERVICE['port'],
'source_vlans_list': ['4-6', '8-10', '15-18,20'],
'vlan_filter_list': '1-5,9,18,20,27-30,4000-4095',
'tap_flow': {
'direction': 'IN', 'vlan_filter': '20'
}
}
FAKE_TAP_MIRROR_OUT = {
'tap_mirror': {
'id': 'mirror_uuid',
'port_id': 'port_uuid',
'directions': {'OUT': '102'},
'remote_ip': '100.109.0.48',
'mirror_type': 'gre',
},
'port': {
'id': 'port_uuid',
'mac_address': 'fa:16:3e:69:0e:f3',
}
}
FAKE_TAP_MIRROR_IN = copy.deepcopy(FAKE_TAP_MIRROR_OUT)
FAKE_TAP_MIRROR_IN['tap_mirror']['directions'] = {'IN': '101'}
class TaasTestCase(base.BaseTestCase):
"""Test case base class for all unit tests."""

View File

@ -0,0 +1,381 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from neutron_lib.plugins.ml2 import ovs_constants as n_ovs_consts
import neutron_taas.services.taas.drivers.linux.ovs_constants \
as taas_ovs_consts
from neutron_taas.services.taas.drivers.linux import ovs_taas
from neutron_taas.tests import base
class FakeVifPort(object):
def __init__(self, port_name, ofport, vif_id, vif_mac, switch):
self.port_name = port_name
self.ofport = ofport
self.vif_id = vif_id
self.vif_mac = vif_mac
self.switch = switch
class FakeBridge(object):
def __init__(self, br_name):
self.br_name = br_name
def add_patch_port(self, local_name, remote_name):
pass
def get_port_ofport(self, port_name):
return base.FAKE_OF_PORT
def add_flow(self, **kwargs):
pass
def delete_flows(self, **kwargs):
pass
def get_vif_port_by_id(self, port_id):
port = base.FAKE_TAP_SERVICE_OVS['port']
return FakeVifPort(
port_name=base.FAKE_OF_PORT['port_name'],
ofport=base.FAKE_OF_PORT['ofport'],
vif_id=port_id,
vif_mac=port['mac_address'],
switch=port['binding:vif_details']['bridge_name']
)
def get_port_tag_dict(self):
return base.FAKE_PORT_DICT
class TestOvsDriverTaas(base.TaasTestCase):
def setUp(self):
super(TestOvsDriverTaas, self).setUp()
def _create_tun_flood_flow(self):
return ''
def _init_taas_driver(self, mock_ovs_ext_api, mock_tap_ext):
obj = ovs_taas.OvsTaasDriver()
obj.agent_api = mock_ovs_ext_api
obj.tunnel_types = 'vxlan'
obj._create_tunnel_flood_flow_action = self._create_tun_flood_flow
mock_br_int = mock_ovs_ext_api.request_int_br.return_value
mock_br_int.add_flow = mock.Mock()
mock_br_int.delete_flows = mock.Mock()
mock_br_int.add_patch_port = mock.Mock()
mock_br_tun = mock_ovs_ext_api.request_tun_br.return_value
mock_br_tun.add_flow = mock.Mock()
mock_br_tun.delete_flows = mock.Mock()
mock_br_tun.add_patch_port = mock.Mock()
mock_tap_bridge = mock_tap_ext.return_value
mock_tap_bridge.create.return_value = None
mock_tap_bridge.add_flow = mock.Mock()
mock_tap_bridge.delete_flows = mock.Mock()
obj.initialize()
return obj, mock_tap_bridge, mock_br_int, mock_br_tun
def _vlidate_bridge_initialization(self, mock_ovs_ext_api,
mock_tap_bridge, mock_br_int,
mock_br_tun):
mock_ovs_ext_api.request_int_br.assert_called_once()
mock_ovs_ext_api.request_tun_br.assert_called_once()
mock_tap_bridge.create.assert_called_once()
mock_br_int.add_patch_port.assert_called_once()
mock_br_tun.add_patch_port.assert_called_once()
mock_tap_bridge.add_patch_port.assert_has_calls([
mock.call('patch-tap-int', 'patch-int-tap'),
mock.call('patch-tap-tun', 'patch-tun-tap')
])
mock_tap_bridge.add_flow.assert_has_calls([
mock.call(table=0, priority=1, in_port=mock.ANY,
actions='resubmit(,1)'),
mock.call(table=0, priority=1, in_port=mock.ANY,
actions='resubmit(,2)'),
mock.call(table=0, priority=0, actions='drop'),
mock.call(table=1, priority=0, actions=mock.ANY),
mock.call(table=2, priority=0, actions='drop'),
])
mock_br_tun.add_flow.assert_has_calls([
mock.call(table=0, priority=1, in_port=mock.ANY,
actions='resubmit(,30)'),
mock.call(table=30, priority=0, actions='resubmit(,31)'),
mock.call(table=35, priority=2, reg0=0, actions='resubmit(,36)'),
mock.call(table=35, priority=1, reg0=1, actions='resubmit(,36)'),
mock.call(table=35, priority=1, reg0=2, actions='resubmit(,37)'),
mock.call(table=36, priority=0, actions='drop'),
mock.call(table=37, priority=0, actions='drop'),
mock.call(table=38, priority=2, reg0=0, actions=mock.ANY),
mock.call(table=38, priority=1, reg0=1, actions=mock.ANY),
mock.call(table=39, priority=1, actions=mock.ANY)
])
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_create_tap_service(self, mock_tap_ext, mock_api):
tap_service = base.FAKE_TAP_SERVICE_OVS
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.add_flow.reset_mock()
mock_br_tun.add_flow.reset_mock()
with mock.patch('neutron.agent.linux.utils.execute'):
obj.create_tap_service(tap_service)
mock_tap_bridge.add_flow.assert_has_calls([
mock.call(table=1, priority=1, dl_vlan=mock.ANY,
actions='output:in_port'),
mock.call(table=2, priority=1, dl_vlan=mock.ANY, actions=mock.ANY)
])
mock_br_int.add_flow.assert_called_once_with(
table=0, priority=25, in_port=mock.ANY, dl_vlan=mock.ANY,
actions=mock.ANY
)
mock_br_tun.add_flow.assert_has_calls([
mock.call(table=n_ovs_consts.GRE_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=n_ovs_consts.VXLAN_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=n_ovs_consts.GENEVE_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_DST_CHECK, priority=1,
tun_id=mock.ANY, actions='resubmit(,38)')
])
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_delete_tap_service(self, mock_tap_ext, mock_api):
tap_service = base.FAKE_TAP_SERVICE_OVS
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.delete_flows.reset_mock()
mock_br_int.delete_flows.reset_mock()
mock_br_tun.delete_flows.reset_mock()
obj.delete_tap_service(tap_service)
mock_tap_bridge.delete_flows.assert_has_calls([
mock.call(table=1, dl_vlan=mock.ANY),
mock.call(table=2, dl_vlan=mock.ANY),
])
mock_br_int.delete_flows.assert_called_once_with(
table=0, in_port=mock.ANY, dl_vlan=mock.ANY,
)
mock_br_tun.delete_flows.assert_has_calls([
mock.call(table=n_ovs_consts.GRE_TUN_TO_LV, tun_id=mock.ANY),
mock.call(table=n_ovs_consts.VXLAN_TUN_TO_LV, tun_id=mock.ANY),
mock.call(table=n_ovs_consts.GENEVE_TUN_TO_LV, tun_id=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_DST_CHECK, tun_id=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_SRC_CHECK, tun_id=mock.ANY)
])
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_create_tap_flow(self, mock_tap_ext, mock_api):
tap_flow = base.FAKE_TAP_FLOW
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.add_flow.reset_mock()
mock_br_tun.add_flow.reset_mock()
obj.create_tap_flow(tap_flow)
mock_tap_bridge.add_flow.assert_not_called()
mock_br_tun.add_flow.assert_has_calls([
mock.call(table=n_ovs_consts.GRE_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=n_ovs_consts.VXLAN_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=n_ovs_consts.GENEVE_TUN_TO_LV, priority=1,
tun_id=mock.ANY, actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_SRC_CHECK, priority=1,
tun_id=mock.ANY, actions=mock.ANY)
])
mock_br_int.add_flow.assert_called_once_with(
table=0, priority=20, dl_dst=tap_flow['port_mac'],
actions=mock.ANY
)
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_delete_tap_flow(self, mock_tap_ext, mock_api):
tap_flow = base.FAKE_TAP_FLOW
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.delete_flows.reset_mock()
mock_br_tun.delete_flows.reset_mock()
obj.delete_tap_flow(tap_flow)
mock_tap_bridge.delete_flows.assert_not_called()
mock_br_tun.delete_flows.assert_not_called()
mock_br_int.delete_flows.assert_called_once_with(
table=0, dl_dst=tap_flow['port_mac']
)
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_create_tap_mirror_out_direction(self, mock_tap_ext, mock_api):
tap_mirror = base.FAKE_TAP_MIRROR_OUT
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.add_flow.reset_mock()
obj.create_tap_mirror(tap_mirror)
mock_tap_bridge.add_flow.assert_has_calls([
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC, priority=20,
dl_src=tap_mirror['port']['mac_address'],
actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC, priority=1,
dl_dst=tap_mirror['port']['mac_address'],
actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_RECV_REM, priority=1,
dl_dst=tap_mirror['port']['mac_address'],
actions=mock.ANY),
])
mock_br_int.add_flow.assert_called_once_with(
table=0, priority=20, in_port=mock.ANY, actions=mock.ANY
)
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_create_tap_mirror_in_direction(self, mock_tap_ext, mock_api):
tap_mirror = base.FAKE_TAP_MIRROR_IN
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.add_flow.reset_mock()
obj.create_tap_mirror(tap_mirror)
mock_tap_bridge.add_flow.assert_has_calls([
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC, priority=20,
dl_dst=tap_mirror['port']['mac_address'],
actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC, priority=1,
dl_dst=tap_mirror['port']['mac_address'],
actions=mock.ANY),
mock.call(table=taas_ovs_consts.TAAS_RECV_REM, priority=1,
dl_dst=tap_mirror['port']['mac_address'],
actions=mock.ANY),
])
mock_br_int.add_flow.assert_called_once_with(
table=0, priority=20,
dl_dst=base.FAKE_TAP_MIRROR_IN['port']['mac_address'],
actions=mock.ANY
)
@mock.patch('neutron.plugins.ml2.drivers.openvswitch.agent.'
'ovs_agent_extension_api.OVSAgentExtensionAPI')
@mock.patch('neutron_taas.services.taas.drivers.linux.ovs_taas.'
'OVSBridge_tap_extension')
def test_delete_tap_mirror(self, mock_tap_ext, mock_api):
tap_mirror = base.FAKE_TAP_MIRROR_OUT
mock_ovs_ext_api = mock_api.return_value
mock_ovs_ext_api.request_int_br.return_value = FakeBridge('br_int')
mock_ovs_ext_api.request_tun_br.return_value = FakeBridge('br_tun')
obj, mock_tap_bridge, mock_br_int, mock_br_tun = \
self._init_taas_driver(mock_ovs_ext_api, mock_tap_ext)
self._vlidate_bridge_initialization(mock_ovs_ext_api, mock_tap_bridge,
mock_br_int, mock_br_tun)
mock_tap_bridge.reset_mock()
mock_br_int.delete_flows.reset_mock()
obj.delete_tap_mirror(tap_mirror)
mock_tap_bridge.delete_flows.assert_has_calls([
mock.call(table=taas_ovs_consts.TAAS_RECV_REM,
dl_dst=tap_mirror['port']['mac_address']),
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC,
dl_dst=tap_mirror['port']['mac_address']),
mock.call(table=taas_ovs_consts.TAAS_RECV_LOC,
dl_src=tap_mirror['port']['mac_address']),
])
mock_br_int.delete_flows.assert_called_once()

View File

@ -21,21 +21,6 @@ from neutron_taas.services.taas.drivers.linux import sriov_nic_exceptions \
from neutron_taas.services.taas.drivers.linux import sriov_nic_taas
from neutron_taas.tests import base
FAKE_PORT_PARAMS = {
'mac': '52:54:00:12:35:02', 'pci_slot': 3, 'vf_index': '89',
'pf_device': 'net_enp0s3_52_54_00_12_35_02', 'src_vlans': '20'}
FAKE_TAP_SERVICE = {'port': {
'id': 'fake_1', 'mac_address': "52:54:00:12:35:02",
'binding:profile': {'pci_slot': 3},
'binding:vif_details': {'vlan': '20'}}}
FAKE_TAP_FLOW = {'port': FAKE_TAP_SERVICE['port'],
'ts_port': FAKE_TAP_SERVICE['port'],
'source_vlans_list': ['4-6', '8-10', '15-18,20'],
'vlan_filter_list': '1-5,9,18,20,27-30,4000-4095',
'tap_flow': {'direction': 'IN', 'vlan_filter': '20'}}
class TestSriovNicTaas(base.TaasTestCase):
def setUp(self):
@ -43,9 +28,9 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_service(self, mock_sriov_utils):
tap_service = FAKE_TAP_SERVICE
tap_service = base.FAKE_TAP_SERVICE
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
return_value = FAKE_PORT_PARAMS
return_value = base.FAKE_PORT_PARAMS
obj = sriov_nic_taas.SriovNicTaasDriver()
obj.initialize()
obj.create_tap_service(tap_service)
@ -55,12 +40,12 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_service_no_pf_device_and_vf_index(
self, mock_sriov_utils):
tap_service = FAKE_TAP_SERVICE
temp_fake_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
tap_service = base.FAKE_TAP_SERVICE
temp_fake_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
temp_fake_port_params['pf_device'] = None
temp_fake_port_params['vf_index'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
return_value = FAKE_PORT_PARAMS
return_value = base.FAKE_PORT_PARAMS
obj = sriov_nic_taas.SriovNicTaasDriver()
obj.initialize()
self.assertIsNone(obj.create_tap_service(tap_service))
@ -69,9 +54,9 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_delete_tap_service(self, mock_sriov_utils):
tap_service = FAKE_TAP_SERVICE
tap_service = base.FAKE_TAP_SERVICE
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
return_value = FAKE_PORT_PARAMS
return_value = base.FAKE_PORT_PARAMS
obj = sriov_nic_taas.SriovNicTaasDriver()
obj.initialize()
obj.create_tap_service(tap_service)
@ -81,12 +66,12 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_delete_tap_service_no_pf_device_and_vf_index(
self, mock_sriov_utils):
tap_service = FAKE_TAP_SERVICE
temp_fake_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
tap_service = base.FAKE_TAP_SERVICE
temp_fake_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
temp_fake_port_params['pf_device'] = None
temp_fake_port_params['vf_index'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
return_value = FAKE_PORT_PARAMS
return_value = base.FAKE_PORT_PARAMS
obj = sriov_nic_taas.SriovNicTaasDriver()
obj.initialize()
self.assertIsNone(obj.create_tap_service(tap_service))
@ -95,11 +80,11 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_flow(self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'vlan_filter_list': '1-5,9,18,20,27-30,4000-4095',
'tap_flow': {'direction': 'IN', 'vlan_filter': '20'}}
src_port_params = ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]
obj = sriov_nic_taas.SriovNicTaasDriver()
@ -112,10 +97,10 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_flow_no_vlan_filter_on_source_and_probe(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'tap_flow': {'direction': 'IN', 'vlan_filter': '20'}}
src_port_params = ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params['vlan_filter'] = None
src_port_params['src_vlans'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
@ -130,10 +115,10 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_flow_no_source_pci_slot(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'tap_flow': {'direction': 'IN', 'vlan_filter': 20}}
src_port_params = ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
src_port_params['pci_slot'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]
@ -145,11 +130,11 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_flow_no_ts_pci_slot(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'tap_flow': {'direction': 'IN', 'vlan_filter': 20}}
src_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params['pci_slot'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]
@ -161,11 +146,11 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_create_tap_flow_different_pf_devices(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'tap_flow': {'direction': 'IN', 'vlan_filter': 20}}
src_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params['pf_device'] = 'net_enp0s3_52_54_00_12_35_02'
src_port_params['pf_device'] = 'net_enp0s8_52_54_00_12_35_01'
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
@ -176,12 +161,12 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_delete_tap_flow(self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'source_vlans_list': ['4-6', '8-10', '15-18,20'],
'vlan_filter_list': ['1-5,9,18,20,27-30,4000-4095'],
'tap_flow': {'direction': 'IN', 'vlan_filter': '20'}}
src_port_params = ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]
obj = sriov_nic_taas.SriovNicTaasDriver()
@ -193,12 +178,12 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_delete_tap_flow_no_source_pci_slot(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'source_vlans_list': [4, 5, 9],
'tap_flow': {'direction': 'IN', 'vlan_filter': 20}}
src_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
src_port_params['pci_slot'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]
@ -210,11 +195,11 @@ class TestSriovNicTaas(base.TaasTestCase):
@mock.patch.object(sriov_nic_taas, 'sriov_utils')
def test_delete_tap_flow_no_ts_pci_slot(
self, mock_sriov_utils):
tap_flow = {'port': FAKE_TAP_SERVICE['port'],
'tap_service_port': FAKE_TAP_SERVICE['port'],
tap_flow = {'port': base.FAKE_TAP_SERVICE['port'],
'tap_service_port': base.FAKE_TAP_SERVICE['port'],
'tap_flow': {'direction': 'IN', 'vlan_filter': 20}}
src_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(FAKE_PORT_PARAMS)
src_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params = copy.deepcopy(base.FAKE_PORT_PARAMS)
ts_port_params['pci_slot'] = None
mock_sriov_utils.SriovNicUtils().get_sriov_port_params.\
side_effect = [src_port_params, ts_port_params]

View File

@ -53,7 +53,7 @@ class TestTapMirrorPlugin(testlib_api.SqlTestCase):
}
self._tap_mirror = {
'project_id': self._project_id,
',tenant_id': self._tenant_id,
'tenant_id': self._tenant_id,
'name': 'MyMirror',
'description': 'This is my Tap Mirror',
'port_id': self._port_id,
@ -63,7 +63,8 @@ class TestTapMirrorPlugin(testlib_api.SqlTestCase):
}
@contextlib.contextmanager
def tap_mirror(self):
def tap_mirror(self, **kwargs):
self._tap_mirror.update(kwargs)
req = {
'tap_mirror': self._tap_mirror,
}
@ -72,8 +73,18 @@ class TestTapMirrorPlugin(testlib_api.SqlTestCase):
mirror = self._plugin.create_tap_mirror(self._context, req)
self._tap_mirror['id'] = mock.ANY
# TODO(lajoskatona): Add more checks for the pre/post phases
# (check the next patches)
self.driver.assert_has_calls([
mock.call.create_tap_mirror_precommit(mock.ANY),
mock.call.create_tap_mirror_postcommit(mock.ANY),
])
pre_call_args = self.driver.create_tap_mirror_precommit.call_args[0][0]
self.assertEqual(self._context, pre_call_args._plugin_context)
self.assertEqual(self._tap_mirror, pre_call_args.tap_mirror)
post_call_args = self.driver.create_tap_mirror_postcommit.call_args
post_call_args = post_call_args[0][0]
self.assertEqual(self._context, post_call_args._plugin_context)
self.assertEqual(self._tap_mirror, post_call_args.tap_mirror)
yield self._plugin.get_tap_mirror(self._context,
mirror['id'])
@ -90,6 +101,43 @@ class TestTapMirrorPlugin(testlib_api.SqlTestCase):
pass
self.assertEqual([], self.driver.mock_calls)
def test_create_duplicate_tunnel_id(self):
with self.tap_mirror() as tm1:
# TODO(lajoskatona): change this to an import to neutron-lib when
# https://review.opendev.org/c/openstack/neutron-lib/+/895603 is
# released.
with mock.patch.object(self._plugin, 'get_tap_mirrors',
return_value=[tm1]):
with testtools.ExpectedException(
tm_db.TapMirrorTunnelConflict), \
self.tap_mirror(directions={"IN": 101}):
pass
def test_create_different_tunnel_id(self):
with self.tap_mirror() as tm1:
with mock.patch.object(self._plugin, 'get_tap_mirrors',
return_value=[tm1]):
with self.tap_mirror(directions={"IN": 102}):
pass
def test_same_tunnel_id_different_direction(self):
with self.tap_mirror() as tm1:
with mock.patch.object(self._plugin, 'get_tap_mirrors',
return_value=[tm1]):
with testtools.ExpectedException(
tm_db.TapMirrorTunnelConflict), \
self.tap_mirror(directions={"OUT": 101}):
pass
def test_two_direction_tunnel_id(self):
with self.tap_mirror(directions={'IN': 101, 'OUT': 102}) as tm1:
with mock.patch.object(self._plugin, 'get_tap_mirrors',
return_value=[tm1]):
with testtools.ExpectedException(
tm_db.TapMirrorTunnelConflict), \
self.tap_mirror(directions={"OUT": 101}):
pass
def test_delete_tap_mrror(self):
with self.tap_mirror() as tm:
self._plugin.delete_tap_mirror(self._context, tm['id'])

View File

@ -0,0 +1,4 @@
---
features:
- |
Add possibility to create (CRUD) ``tap_mirrors`` with ``OVS`` backend.