From 7acc6654ecdffafb22e6ef9ad64606e0c902a722 Mon Sep 17 00:00:00 2001 From: elajkat Date: Thu, 15 Jun 2023 14:26:32 +0200 Subject: [PATCH] Tap Mirror OVS driver Related-Bug: #2015471 Change-Id: I9a80fd7a6c3b782454047b6295e51cc549842f84 --- .../services/taas/agents/common/taas_agent.py | 51 +++ .../services/taas/agents/taas_agent_api.py | 8 + .../services/taas/drivers/linux/ovs_taas.py | 105 +++++ .../services/taas/service_drivers/__init__.py | 16 + .../service_drivers/service_driver_context.py | 11 + .../taas/service_drivers/taas_agent_api.py | 13 + .../services/taas/service_drivers/taas_rpc.py | 51 +++ .../services/taas/tap_mirror_plugin.py | 31 +- neutron_taas/tests/base.py | 71 ++++ .../services/drivers/test_linux_ovs_taas.py | 381 ++++++++++++++++++ .../drivers/test_linux_sriov_nic_driver.py | 91 ++--- .../services/taas/test_tap_mirror_plugin.py | 51 ++- ...rror-with-ovs-driver-7f09654e1e960a0d.yaml | 4 + 13 files changed, 826 insertions(+), 58 deletions(-) create mode 100644 neutron_taas/tests/unit/services/drivers/test_linux_ovs_taas.py create mode 100644 releasenotes/notes/tap-mirror-with-ovs-driver-7f09654e1e960a0d.yaml diff --git a/neutron_taas/services/taas/agents/common/taas_agent.py b/neutron_taas/services/taas/agents/common/taas_agent.py index 0a4f719d..983fce62 100644 --- a/neutron_taas/services/taas/agents/common/taas_agent.py +++ b/neutron_taas/services/taas/agents/common/taas_agent.py @@ -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', } @@ -213,6 +230,40 @@ 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 + LOG.debug("In RPC Call for Creae Tap Mirror: MSG=%s", tap_mirror_msg) + + 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( diff --git a/neutron_taas/services/taas/agents/taas_agent_api.py b/neutron_taas/services/taas/agents/taas_agent_api.py index c940be0c..a12a5394 100644 --- a/neutron_taas/services/taas/agents/taas_agent_api.py +++ b/neutron_taas/services/taas/agents/taas_agent_api.py @@ -57,3 +57,11 @@ class TaasAgentRpcCallbackMixin: 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 diff --git a/neutron_taas/services/taas/drivers/linux/ovs_taas.py b/neutron_taas/services/taas/drivers/linux/ovs_taas.py index 35ba74cc..865e1985 100644 --- a/neutron_taas/services/taas/drivers/linux/ovs_taas.py +++ b/neutron_taas/services/taas/drivers/linux/ovs_taas.py @@ -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 @@ -576,3 +579,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' + # For ERSPAN type 2 (version I) as that is supported + # only from taas + options['erspan_ver'] = "1" + 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) diff --git a/neutron_taas/services/taas/service_drivers/__init__.py b/neutron_taas/services/taas/service_drivers/__init__.py index 8a791932..b26bb770 100644 --- a/neutron_taas/services/taas/service_drivers/__init__.py +++ b/neutron_taas/services/taas/service_drivers/__init__.py @@ -55,3 +55,19 @@ class TaasBaseDriver(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 diff --git a/neutron_taas/services/taas/service_drivers/service_driver_context.py b/neutron_taas/services/taas/service_drivers/service_driver_context.py index 846ad0c6..46a9ff4f 100644 --- a/neutron_taas/services/taas/service_drivers/service_driver_context.py +++ b/neutron_taas/services/taas/service_drivers/service_driver_context.py @@ -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 diff --git a/neutron_taas/services/taas/service_drivers/taas_agent_api.py b/neutron_taas/services/taas/service_drivers/taas_agent_api.py index a4f02ba1..0989c43d 100644 --- a/neutron_taas/services/taas/service_drivers/taas_agent_api.py +++ b/neutron_taas/services/taas/service_drivers/taas_agent_api.py @@ -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: 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) diff --git a/neutron_taas/services/taas/service_drivers/taas_rpc.py b/neutron_taas/services/taas/service_drivers/taas_rpc.py index 43e306b5..d92aa03c 100644 --- a/neutron_taas/services/taas/service_drivers/taas_rpc.py +++ b/neutron_taas/services/taas/service_drivers/taas_rpc.py @@ -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 diff --git a/neutron_taas/services/taas/tap_mirror_plugin.py b/neutron_taas/services/taas/tap_mirror_plugin.py index 71ed9507..436aa5fb 100644 --- a/neutron_taas/services/taas/tap_mirror_plugin.py +++ b/neutron_taas/services/taas/tap_mirror_plugin.py @@ -23,10 +23,12 @@ from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import taas as taas_exc from oslo_log import helpers as log_helpers from oslo_log import log as logging +from oslo_utils import excutils from neutron_taas.common import constants as taas_consts from neutron_taas.db import tap_mirror_db - +from neutron_taas.services.taas.service_drivers import (service_driver_context + as sd_context) LOG = logging.getLogger(__name__) @@ -80,12 +82,23 @@ class TapMirrorPlugin(tap_mirror_db.Taas_mirror_db_mixin): if host is not None: LOG.debug("Host on which the port is created = %s", host) else: - LOG.debug("Host could not be found, Port Binding disabled!") + 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 fail. + try: + self.driver.create_tap_mirror_postcommit(driver_context) + except Exception: + pass return tm def _validate_tap_tunnel_id(self, context, mirror_directions): @@ -100,10 +113,24 @@ class TapMirrorPlugin(tap_mirror_db.Taas_mirror_db_mixin): def delete_tap_mirror(self, context, id): with db_api.CONTEXT_WRITER.using(context): tm = self.get_tap_mirror(context, id) + if tm: + # 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 fail. + 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): diff --git a/neutron_taas/tests/base.py b/neutron_taas/tests/base.py index bae95bca..81905033 100644 --- a/neutron_taas/tests/base.py +++ b/neutron_taas/tests/base.py @@ -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.""" diff --git a/neutron_taas/tests/unit/services/drivers/test_linux_ovs_taas.py b/neutron_taas/tests/unit/services/drivers/test_linux_ovs_taas.py new file mode 100644 index 00000000..ec28b0af --- /dev/null +++ b/neutron_taas/tests/unit/services/drivers/test_linux_ovs_taas.py @@ -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() diff --git a/neutron_taas/tests/unit/services/drivers/test_linux_sriov_nic_driver.py b/neutron_taas/tests/unit/services/drivers/test_linux_sriov_nic_driver.py index d58b5bbb..7865ce8b 100644 --- a/neutron_taas/tests/unit/services/drivers/test_linux_sriov_nic_driver.py +++ b/neutron_taas/tests/unit/services/drivers/test_linux_sriov_nic_driver.py @@ -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] diff --git a/neutron_taas/tests/unit/services/taas/test_tap_mirror_plugin.py b/neutron_taas/tests/unit/services/taas/test_tap_mirror_plugin.py index 388fe190..20a4572e 100644 --- a/neutron_taas/tests/unit/services/taas/test_tap_mirror_plugin.py +++ b/neutron_taas/tests/unit/services/taas/test_tap_mirror_plugin.py @@ -61,7 +61,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, } @@ -70,8 +71,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']) @@ -88,6 +99,40 @@ 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: + with mock.patch.object(self._plugin, 'get_tap_mirrors', + return_value=[tm1]): + with testtools.ExpectedException( + taas_exc.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( + taas_exc.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( + taas_exc.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']) diff --git a/releasenotes/notes/tap-mirror-with-ovs-driver-7f09654e1e960a0d.yaml b/releasenotes/notes/tap-mirror-with-ovs-driver-7f09654e1e960a0d.yaml new file mode 100644 index 00000000..8a030833 --- /dev/null +++ b/releasenotes/notes/tap-mirror-with-ovs-driver-7f09654e1e960a0d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add possibility to create (CRUD) ``tap_mirrors`` with ``OVS`` backend.