From 1726d94d699fbdba292822f840c5cd5afb94b578 Mon Sep 17 00:00:00 2001 From: Vincenzo Riccobene Date: Fri, 11 Dec 2015 10:09:00 +0000 Subject: Add full support to the DPDK packet generator Add packet generator base class (to support extensions of packet generators Add the DPDK packet gen python wrapper and tests JIRA: YARDSTICK-35 Change-Id: I2072801bcf077586b6abb45c5d452a346e2efe70 Signed-off-by: Vincenzo Riccobene --- .../packet_generators/base_packet_generator.py | 31 + .../packet_generators/dpdk_packet_generator.py | 336 ++++++++++ .../apexlake/tests/base_packet_generator_test.py | 29 + .../apexlake/tests/dpdk_packet_generator_test.py | 705 +++++++++++++++++++++ 4 files changed, 1101 insertions(+) create mode 100644 yardstick/vTC/apexlake/experimental_framework/packet_generators/base_packet_generator.py create mode 100644 yardstick/vTC/apexlake/experimental_framework/packet_generators/dpdk_packet_generator.py create mode 100644 yardstick/vTC/apexlake/tests/base_packet_generator_test.py create mode 100644 yardstick/vTC/apexlake/tests/dpdk_packet_generator_test.py diff --git a/yardstick/vTC/apexlake/experimental_framework/packet_generators/base_packet_generator.py b/yardstick/vTC/apexlake/experimental_framework/packet_generators/base_packet_generator.py new file mode 100644 index 000000000..4d2c6fe72 --- /dev/null +++ b/yardstick/vTC/apexlake/experimental_framework/packet_generators/base_packet_generator.py @@ -0,0 +1,31 @@ +# Copyright (c) 2015 Intel Research and Development Ireland Ltd. +# +# 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. + +import abc + + +class BasePacketGenerator: + + def __init__(self): + pass + + @abc.abstractmethod + def send_traffic(self): + """ + Starts the traffic generation. + According to the specific packet generator it requires prior + initialization + :return: None + """ + raise NotImplementedError("Subclass must implement abstract method") diff --git a/yardstick/vTC/apexlake/experimental_framework/packet_generators/dpdk_packet_generator.py b/yardstick/vTC/apexlake/experimental_framework/packet_generators/dpdk_packet_generator.py new file mode 100644 index 000000000..ae54502e9 --- /dev/null +++ b/yardstick/vTC/apexlake/experimental_framework/packet_generators/dpdk_packet_generator.py @@ -0,0 +1,336 @@ +# Copyright (c) 2015 Intel Research and Development Ireland Ltd. +# +# 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. + +import os +import base_packet_generator +import experimental_framework.common as common +from experimental_framework.constants import conf_file_sections as conf_file +from experimental_framework.constants import framework_parameters as fp + + +class DpdkPacketGenerator(base_packet_generator.BasePacketGenerator): + + def __init__(self): + base_packet_generator.BasePacketGenerator.__init__(self) + self.command = '' + self.directory = '' + self.program_name = '' + self.command_options = list() + self.dpdk_interfaces = -1 + + def send_traffic(self): + ''' + Calls the packet generator and starts to send traffic + Blocking call + ''' + current_dir = os.path.dirname(os.path.realpath(__file__)) + DpdkPacketGenerator._chdir(self.directory) + dpdk_vars = common.get_dpdk_pktgen_vars() + self._init_physical_nics(self.dpdk_interfaces, dpdk_vars) + common.run_command(self.command) + self._finalize_physical_nics(self.dpdk_interfaces, dpdk_vars) + DpdkPacketGenerator._chdir(current_dir) + + def init_dpdk_pktgen(self, + dpdk_interfaces, + lua_script='generic_test.lua', + pcap_file_0='', + pcap_file_1='', + vlan_0='', + vlan_1=''): + """ + Initializes internal parameters and configuration of the module. + Needs to be called before the send_traffic + :param dpdk_interfaces: Number of interfaces to be used (type: int) + :param lua_script: Full path of the Lua script to be used (type: str) + :param pcap_file_0: Full path of the Pcap file to be used for port 0 + (type: str) + :param pcap_file_1: Full path of the Pcap file to be used for port 1 + (type: str) + :param vlan_0: VLAN tag to be used for port 0 (type: str) + :param vlan_1: VLAN tag to be used for port 1 (type: str) + :return: + """ + # Input Validation + if pcap_file_0 and not vlan_0: + message = 'In order to use NIC_0, the parameter vlan_0 is required' + raise ValueError(message) + if dpdk_interfaces > 1 and pcap_file_1 and not vlan_1: + message = 'in order to use NIC_1, the parameter vlan_1 is required' + raise ValueError(message) + + self.dpdk_interfaces = dpdk_interfaces + vars = common.get_dpdk_pktgen_vars() + + lua_directory = common.get_base_dir() + lua_directory += fp.EXPERIMENTAL_FRAMEWORK_DIR + lua_directory += fp.DPDK_PKTGEN_DIR + + pcap_directory = common.get_base_dir() + pcap_directory += fp.EXPERIMENTAL_FRAMEWORK_DIR + pcap_directory += fp.PCAP_DIR + + DpdkPacketGenerator._init_input_validation(pcap_file_0, + pcap_file_1, + lua_script, + pcap_directory, + lua_directory, + vars) + + self.directory = vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] + self.program_name = vars[conf_file.CFSP_DPDK_PROGRAM_NAME] + + core_nics = DpdkPacketGenerator.\ + _get_core_nics(dpdk_interfaces, vars[conf_file.CFSP_DPDK_COREMASK]) + self.command_options = ['-c ' + vars[conf_file.CFSP_DPDK_COREMASK], + '-n ' + vars[conf_file. + CFSP_DPDK_MEMORY_CHANNEL], + '--proc-type auto', + '--file-prefix pg', + '-- -T', + '-P', + '-m "' + core_nics + '"', + '-f ' + lua_directory + lua_script, + '-s 0:' + pcap_directory + pcap_file_0] + + if pcap_file_1: + self.command_options.append('-s 1:' + pcap_directory + pcap_file_1) + + # Avoid to show the output of the packet generator + self.command_options.append('> /dev/null') + # Prepare the command to be invoked + self.command = self.directory + self.program_name + for opt in self.command_options: + self.command += (' ' + opt) + if pcap_file_0 and vlan_0: + DpdkPacketGenerator._change_vlan(pcap_directory, pcap_file_0, + vlan_0) + if pcap_file_1 and vlan_1: + DpdkPacketGenerator._change_vlan(pcap_directory, pcap_file_1, + vlan_1) + + @staticmethod + def _get_core_nics(dpdk_interfaces, coremask): + """ + Retruns the core_nics string to be used in the dpdk pktgen command + :param dpdk_interfaces: number of interfaces to be used in the pktgen + (type: int) + :param coremask: hexadecimal value representing the cores assigned to + the pktgen (type: str) + :return: Returns the core nics param for pktgen (type: str) + """ + if dpdk_interfaces == 1: + return DpdkPacketGenerator._cores_configuration(coremask, 1, 2, 0) + elif dpdk_interfaces == 2: + return DpdkPacketGenerator._cores_configuration(coremask, 1, 2, 2) + raise ValueError("This framework only supports two ports to generate " + "traffic") + + @staticmethod + def _change_vlan(pcap_directory, pcap_file, vlan): + common.LOG.info("Changing VLAN Tag on Packet: " + pcap_file + + ". New VLAN Tag is " + vlan) + command = pcap_directory + 'vlan_tag.sh ' + command += pcap_directory + pcap_file + ' ' + vlan + common.run_command(command) + + @staticmethod + def _init_input_validation(pcap_file_0, pcap_file_1, lua_script, + pcap_directory, lua_directory, variables): + """ + Validates the input parameters values and raises an exception if + there is any non valid param + :param pcap_file_0: file name of the pcap file for NIC 0 + (it does not includes the path) (type: str) + :param pcap_file_1: file name of the pcap file for NIC 1 + (it does not includes the path) (type: str) + :param lua_script: file name of the lua script to be used + (it does not includes the path) (type: str) + :param pcap_directory: directory where the pcap files are located + (type: str) + :param lua_directory: directory where the lua scripts are located + (type: str) + :param variables: variables for the packet gen from configuration file + (type: dict) + :return: None + """ + if not pcap_directory: + raise ValueError("pcap_directory not provided correctly") + if not pcap_file_0: + raise ValueError("pcap_file_0 not provided correctly") + if not pcap_file_1: + raise ValueError("pcap_file_1 not provided correctly") + if not lua_script: + raise ValueError("lua_script not provided correctly") + if not os.path.isfile(pcap_directory + pcap_file_0): + raise ValueError("The file " + pcap_file_0 + " does not exist") + if not os.path.isfile(pcap_directory + pcap_file_1): + raise ValueError("The file " + pcap_file_1 + " does not exist") + if not os.path.isfile(lua_directory + lua_script): + raise ValueError("The file " + lua_script + " does not exist") + for var in [conf_file.CFSP_DPDK_PKTGEN_DIRECTORY, + conf_file.CFSP_DPDK_PROGRAM_NAME, + conf_file.CFSP_DPDK_COREMASK, + conf_file.CFSP_DPDK_MEMORY_CHANNEL]: + if var not in variables.keys() or (var in variables.keys() and + variables[var] is ''): + raise ValueError("The variable " + var + " does not exist") + + @staticmethod + def _chdir(directory): + """ + Changes the current directory + :param directory: directory where to move (type: str) + :return: None + """ + os.chdir(directory) + + def _init_physical_nics(self, dpdk_interfaces, dpdk_vars): + """ + Initializes the physical interfaces + :param dpdk_interfaces: Number of interfaces to be used (type: int) + :param dpdk_vars: variables from config file related to DPDK pktgen + (type: dict) + :return: None + """ + if not dpdk_interfaces == 1 and not dpdk_interfaces == 2: + raise ValueError('The number of NICs can be 1 or 2') + # Initialize NIC 1 + # bus_address_1 = dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1] + interface_1 = dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] + common.run_command('ifconfig ' + interface_1 + ' down') + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --unbind ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1]) + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --bind=igb_uio ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1]) + if dpdk_interfaces == 2: + # Initialize NIC 2 + # bus_address_2 = dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2] + interface_2 = dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] + common.run_command('ifconfig ' + interface_2 + ' down') + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --unbind ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2]) + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --bind=igb_uio ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2]) + + def _finalize_physical_nics(self, dpdk_interfaces, dpdk_vars): + """ + Finalizes the physical interfaces + :param dpdk_interfaces: Number of interfaces to be used (type: int) + :param dpdk_vars: variables from config file related to DPDK pktgen + (type: dict) + :return: None + """ + if not dpdk_interfaces == 1 and not dpdk_interfaces == 2: + raise ValueError('No interfaces have been indicated for packet ' + 'generation usage. Please specify one or two ' + 'NICs') + # Initialize NIC 1 + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --unbind ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1]) + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --bind=ixgbe ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1]) + common.run_command('ifconfig ' + + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] + + ' up') + if dpdk_interfaces == 2: + # Initialize NIC 2 + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --unbind ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2]) + common.run_command(dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] + + 'tools/dpdk_nic_bind.py --bind=ixgbe ' + + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2]) + common.run_command('ifconfig ' + + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] + + ' up') + + @staticmethod + def _cores_configuration(coremask, pktgen_cores=1, nic_1_cores=2, + nic_2_cores=2): + """ + Calculation of the core_nics parameter which is necessary for the + packet generator to run + + :param coremask: Hexadecimal value indicating the cores to be assigned + to the whole dpdk pktgen software (included the + ones to receive and send packets from NICs) + (type: str) + :param pktgen_cores: number of cores to be assigned to main thread of + the pktgen directly + :param nic_1_cores: number of cores to be assigned to the first NIC + :param nic_2_nics: number of cores to be assigned to the second NIC + :return: returns the core_nics parameter (type: str) + """ + required_cores = pktgen_cores + nic_1_cores + nic_2_cores + bin_coremask = bin(int(coremask, 16))[2:] + index = len(bin_coremask) + cores = [] + while index >= 0: + index -= 1 + if bin_coremask[index] == '1': + core = index + cores.append(core) + if len(cores) < required_cores: + raise ValueError('The provided coremask does not provide' + ' enough cores for the DPDK packet generator') + # ret_pktgen_cores = [] + ret_nic_1_cores = [] + ret_nic_2_cores = [] + current_core = 0 + + if nic_2_cores > 0: + ret_nic_2_cores.append(cores[current_core]) + current_core += 1 + if nic_2_cores > 1: + ret_nic_2_cores.append(cores[current_core]) + current_core += 1 + + if nic_1_cores > 0: + ret_nic_1_cores.append(cores[current_core]) + current_core += 1 + if nic_1_cores > 1: + ret_nic_1_cores.append(cores[current_core]) + current_core += 1 + + # for n in range(0, pktgen_cores): + # ret_pktgen_cores.append(cores[n]) + # for n in range(0, nic_1_cores): + # ret_nic_1_cores.append(cores[pktgen_cores + n]) + # for n in range(0, nic_2_cores): + # ret_nic_2_cores.append(cores[pktgen_cores + nic_1_cores + n]) + + corenics = '' + if nic_1_cores > 0: + if nic_1_cores < 2: + corenics += str(ret_nic_1_cores[0]) + '.0' + if nic_1_cores == 2: + corenics += '[' + str(ret_nic_1_cores[0]) + corenics += ':' + str(ret_nic_1_cores[1]) + corenics += '].0' + if nic_2_cores > 0: + corenics += ',' + if nic_2_cores < 2: + corenics += str(ret_nic_2_cores[0]) + '.1' + if nic_2_cores == 2: + corenics += '[' + str(ret_nic_2_cores[0]) + corenics += ':' + str(ret_nic_2_cores[1]) + corenics += '].1' + return corenics diff --git a/yardstick/vTC/apexlake/tests/base_packet_generator_test.py b/yardstick/vTC/apexlake/tests/base_packet_generator_test.py new file mode 100644 index 000000000..b0e27d069 --- /dev/null +++ b/yardstick/vTC/apexlake/tests/base_packet_generator_test.py @@ -0,0 +1,29 @@ +# Copyright (c) 2015 Intel Research and Development Ireland Ltd. +# +# 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. + +import unittest +from experimental_framework.packet_generators import base_packet_generator + + +class BasePacketGeneratorTest(unittest.TestCase): + + def setUp(self): + self.mut = base_packet_generator.BasePacketGenerator() + pass + + def tearDown(self): + pass + + def test_send_traffic_for_failure(self): + self.assertRaises(NotImplementedError, self.mut.send_traffic) diff --git a/yardstick/vTC/apexlake/tests/dpdk_packet_generator_test.py b/yardstick/vTC/apexlake/tests/dpdk_packet_generator_test.py new file mode 100644 index 000000000..ad1cdcd2b --- /dev/null +++ b/yardstick/vTC/apexlake/tests/dpdk_packet_generator_test.py @@ -0,0 +1,705 @@ +# Copyright (c) 2015 Intel Research and Development Ireland Ltd. +# +# 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. + +import unittest +import mock +from experimental_framework.constants import conf_file_sections as conf_file + + +from experimental_framework.packet_generators \ + import dpdk_packet_generator as mut + + +def dummy_get_dpdk_pktgen_vars(): + vars = dict() + vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'pktgen_dir/' + vars[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program' + vars[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + vars[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memchannel' + return vars + + +def dummy_get_base_dir(): + return 'base_dir/' + + +def dummy_dirname(dir): + if dir == 'pktgen_dir_test': + return 'pktgen_dir' + return 'test_directory' + + +class MockChangeVlan(): + + ret_val = [False, False] + + @staticmethod + def mock_change_vlan(pcap_dir=None, pcap_file=None, vlan=None): + if not pcap_file and not vlan: + return MockChangeVlan.ret_val + + if pcap_dir == 'base_dir/experimental_framework/packet_generators/' \ + 'pcap_files/' and \ + pcap_file == 'pcap_file_1' and vlan == 'vlan0': + MockChangeVlan.ret_val[0] = True + if pcap_dir == 'base_dir/experimental_framework/packet_generators/' \ + 'pcap_files/' and \ + pcap_file == 'pcap_file_2' and vlan == 'vlan1': + MockChangeVlan.ret_val[1] = True + return False + + +class TestDpdkPacketGenConstructor(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_constructor(self): + obj = mut.DpdkPacketGenerator() + self.assertEqual(obj.command, '') + self.assertEqual(obj.directory, '') + self.assertEqual(obj.dpdk_interfaces, -1) + + +class TestDpdkPacketGenInitialization(unittest.TestCase): + + def setUp(self): + self.mut = mut.DpdkPacketGenerator() + pass + + def tearDown(self): + pass + + @mock.patch('os.path') + @mock.patch('experimental_framework.common.get_dpdk_pktgen_vars', + side_effect=dummy_get_dpdk_pktgen_vars) + @mock.patch('experimental_framework.common.get_base_dir', + side_effect=dummy_get_base_dir) + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._get_core_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_init_input_validation') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._change_vlan') + def test_init_dpdk_pktgen_for_success(self, m_change_vlan, + mock_init_input_validation, + mock_get_core_nics, + common_get_base_dir, + common_get_dpdk_vars, + mock_path): + """ + Tests the initialization of the packet generator + """ + mock_init_input_validation.return_value = None + mock_get_core_nics.return_value = "{corenics}" + mock_path.isfile.return_value = True + expected = 'pktgen_dir/program -c coremask -n memchannel ' \ + '--proc-type auto --file-prefix pg -- -T -P -m ' \ + '"{corenics}" -f base_dir/experimental_framework/' \ + 'packet_generators/dpdk_pktgen/lua_file ' \ + '-s 0:base_dir/experimental_framework/packet_generators' \ + '/pcap_files/pcap_file > /dev/null' + self.mut.init_dpdk_pktgen(dpdk_interfaces=1, lua_script='lua_file', + pcap_file_0='pcap_file', vlan_0='vlan0') + self.assertEqual(expected, self.mut.command) + m_change_vlan.assert_called_once_with('base_dir/' + 'experimental_framework/' + 'packet_generators/pcap_files/', + 'pcap_file', 'vlan0') + + @mock.patch('os.path') + @mock.patch('experimental_framework.common.get_dpdk_pktgen_vars', + side_effect=dummy_get_dpdk_pktgen_vars) + @mock.patch('experimental_framework.common.get_base_dir', + side_effect=dummy_get_base_dir) + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._get_core_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_init_input_validation') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_change_vlan', side_effect=MockChangeVlan.mock_change_vlan) + def test_init_dpdk_pktgen_2_for_success(self, m_change_vlan, + mock_init_input_validation, + mock_get_core_nics, + common_get_base_dir, + common_get_dpdk_vars, mock_path): + """ + Tests the initialization of the packet generator + :param common_get_base_dir: mock obj + :param common_get_dpdk_vars: mock obj + :param mock_path: mock obj + :return: None + """ + mock_init_input_validation.return_value = None + mock_get_core_nics.return_value = "{corenics}" + mock_path.isfile.return_value = True + expected = 'pktgen_dir/program -c coremask -n memchannel ' \ + '--proc-type auto --file-prefix pg -- -T -P -m ' \ + '"{corenics}" -f base_dir/experimental_framework/' \ + 'packet_generators/dpdk_pktgen/lua_file ' \ + '-s 0:base_dir/experimental_framework/packet_generators/' \ + 'pcap_files/pcap_file_1 ' \ + '-s 1:base_dir/experimental_framework/packet_generators/' \ + 'pcap_files/pcap_file_2 ' \ + '> /dev/null' + self.mut.init_dpdk_pktgen(dpdk_interfaces=1, lua_script='lua_file', + pcap_file_0='pcap_file_1', + pcap_file_1='pcap_file_2', vlan_0='vlan0', + vlan_1='vlan1') + self.assertEqual(expected, self.mut.command) + self.assertEqual(MockChangeVlan.mock_change_vlan(), [True, True]) + + @mock.patch('os.path') + @mock.patch('experimental_framework.common.get_dpdk_pktgen_vars', + side_effect=dummy_get_dpdk_pktgen_vars) + @mock.patch('experimental_framework.common.get_base_dir', + side_effect=dummy_get_base_dir) + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._get_core_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_init_input_validation') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._change_vlan') + def test_init_dpdk_pktgen_for_failure(self, m_change_vlan, + mock_init_input_validation, + mock_get_core_nics, + common_get_base_dir, + common_get_dpdk_vars, + mock_path): + """ + Tests the initialization of the packet generator + :param common_get_base_dir: mock obj + :param common_get_dpdk_vars: mock obj + :param mock_path: mock obj + :return: None + """ + mock_init_input_validation.return_value = None + mock_get_core_nics.return_value = "{corenics}" + self.assertRaises(ValueError, self.mut.init_dpdk_pktgen, 1, + 'lua_file', 'pcap_file') + + @mock.patch('os.path') + @mock.patch('experimental_framework.common.get_dpdk_pktgen_vars', + side_effect=dummy_get_dpdk_pktgen_vars) + @mock.patch('experimental_framework.common.get_base_dir', + side_effect=dummy_get_base_dir) + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_get_core_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_init_input_validation') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_change_vlan') + def test_init_dpdk_pktgen_for_failure_2(self, m_change_vlan, + mock_init_input_validation, + mock_get_core_nics, + common_get_base_dir, + common_get_dpdk_vars, + mock_path): + """ + Tests the initialization of the packet generator + :param common_get_base_dir: mock obj + :param common_get_dpdk_vars: mock obj + :param mock_path: mock obj + :return: None + """ + mock_init_input_validation.return_value = None + mock_get_core_nics.return_value = "{corenics}" + self.assertRaises(ValueError, self.mut.init_dpdk_pktgen, 2, + 'lua_file_1', 'pcap_file_1', 'pcap_file_2', + 'vlan_0') + + +class DpdkPacketGeneratorDummy(mut.DpdkPacketGenerator): + + def __init__(self): + self.directory = 'self_directory' + self.dpdk_interfaces = 1 + self.command = 'command' + self._count = 0 + + chdir_test = [False, False] + + @staticmethod + def _chdir(directory=None): + if not directory: + return DpdkPacketGeneratorDummy.chdir_test + if directory == 'current_directory': + DpdkPacketGeneratorDummy.chdir_test[0] = True + # self._count += 1 + if directory == 'self_directory': + DpdkPacketGeneratorDummy.chdir_test[1] = True + # self._count += 1 + return DpdkPacketGeneratorDummy.chdir_test + + +class TestDpdkPacketGenSendTraffic(unittest.TestCase): + + def setUp(self): + self.mut = DpdkPacketGeneratorDummy() + + @mock.patch('os.system') + @mock.patch('os.path') + @mock.patch('os.path.dirname', side_effect=dummy_dirname) + @mock.patch('experimental_framework.common.get_dpdk_pktgen_vars', + side_effect=dummy_get_dpdk_pktgen_vars) + @mock.patch('experimental_framework.common.get_base_dir', + side_effect=dummy_get_base_dir) + @mock.patch('experimental_framework.common.run_command') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._get_core_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_init_physical_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_finalize_physical_nics') + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator._chdir', + side_effect=DpdkPacketGeneratorDummy._chdir) + def test_send_traffic_for_success(self, mock_ch_dir, + mock_finalize_physical_nics, + mock_init_physical_nics, + mock_get_core_nics, + common_run_command, + common_get_base_dir, + common_get_dpdk_vars, + mock_dir_name, + mock_os_path, + mock_os_system): + ''' + Calls the packet generator and starts to send traffic + Blocking call + ''' + mock_get_core_nics.return_value = "{corenics}" + mock_os_path.realpath.return_value = 'pktgen_dir_test' + mock_os_path.dirname.return_value = 'current_directory' + self.mut.send_traffic() + + self.assertEqual(DpdkPacketGeneratorDummy._chdir(), [True, True]) + mock_init_physical_nics.\ + assert_called_once_with(1, {'coremask': 'coremask', + 'program_name': 'program', + 'memory_channels': 'memchannel', + 'pktgen_directory': 'pktgen_dir/'}) + mock_finalize_physical_nics.\ + assert_called_once_with(1, {'coremask': 'coremask', + 'program_name': 'program', + 'memory_channels': 'memchannel', + 'pktgen_directory': 'pktgen_dir/'}) + common_run_command.assert_called_once_with('command') + + +class MockRunCommand: + + ret_val = [False, False, False, False, False, False] + ret_val_finalization = [False, False, False, False, False, False] + + @staticmethod + def mock_run_command(command=None): + if command == 'ifconfig interface_1 down': + MockRunCommand.ret_val[0] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py --unbind 1:00.0': + MockRunCommand.ret_val[1] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py ' \ + '--bind=igb_uio 1:00.0': + MockRunCommand.ret_val[2] = True + if command == 'ifconfig interface_2 down': + MockRunCommand.ret_val[3] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py ' \ + '--unbind 1:00.1': + MockRunCommand.ret_val[4] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py ' \ + '--bind=igb_uio 1:00.1': + MockRunCommand.ret_val[5] = True + else: + return MockRunCommand.ret_val + + @staticmethod + def mock_run_command_finalization(command=None): + if command == 'dpdk_directory/tools/dpdk_nic_bind.py --unbind 1:00.0': + MockRunCommand.ret_val_finalization[0] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py ' \ + '--bind=ixgbe 1:00.0': + MockRunCommand.ret_val_finalization[1] = True + if command == 'ifconfig interface_1 up': + MockRunCommand.ret_val_finalization[2] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py --unbind 1:00.1': + MockRunCommand.ret_val_finalization[3] = True + if command == 'dpdk_directory/tools/dpdk_nic_bind.py ' \ + '--bind=ixgbe 1:00.1': + MockRunCommand.ret_val_finalization[4] = True + if command == 'ifconfig interface_2 up': + MockRunCommand.ret_val_finalization[5] = True + else: + return MockRunCommand.ret_val_finalization + + +class TestDpdkPacketGenOthers(unittest.TestCase): + + def setUp(self): + self.mut = mut.DpdkPacketGenerator() + + def tearDown(self): + pass + + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_cores_configuration') + def test__get_core_nics_for_failure(self, mock_cores_configuration): + mock_cores_configuration.return_value = None + self.assertRaises(ValueError, mut.DpdkPacketGenerator._get_core_nics, + '', '') + + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_cores_configuration') + def test__get_core_nics_one_nic_for_success(self, + mock_cores_configuration): + mock_cores_configuration.return_value = 'ret_val' + expected = 'ret_val' + output = mut.DpdkPacketGenerator._get_core_nics(1, 'coremask') + self.assertEqual(expected, output) + mock_cores_configuration.assert_called_once_with('coremask', 1, 2, 0) + + @mock.patch('experimental_framework.packet_generators.' + 'dpdk_packet_generator.DpdkPacketGenerator.' + '_cores_configuration') + def test__get_core_nics_two_nics_for_success(self, + mock_cores_configuration): + mock_cores_configuration.return_value = 'ret_val' + expected = 'ret_val' + output = mut.DpdkPacketGenerator._get_core_nics(2, 'coremask') + self.assertEqual(expected, output) + mock_cores_configuration.assert_called_once_with('coremask', 1, 2, 2) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_success(self, mock_is_file): + mock_is_file.return_value = True + + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + pcap_directory = 'pcap_directory' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertEqual(mut.DpdkPacketGenerator._init_input_validation( + pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, + variables), None) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_failure(self, mock_is_file): + mock_is_file.return_value = True + + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + pcap_directory = 'pcap_directory' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + # variables[common.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_failure_2(self, mock_is_file): + mock_is_file.return_value = True + + pcap_directory = None + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_failure_3(self, mock_is_file): + mock_is_file.return_value = True + + pcap_directory = 'directory' + pcap_file_0 = None + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_failure_4(self, mock_is_file): + mock_is_file.return_value = True + + pcap_directory = 'directory' + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = None + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile') + def test__init_input_validation_for_failure_5(self, mock_is_file): + mock_is_file.return_value = True + + pcap_directory = 'directory' + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = None + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile', side_effect=[False]) + def test__init_input_validation_for_failure_6(self, mock_is_file): + # mock_is_file.return_value = False + + pcap_directory = 'directory' + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile', side_effect=[True, False]) + def test__init_input_validation_for_failure_7(self, mock_is_file): + pcap_directory = 'directory' + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.path.isfile', side_effect=[True, True, False]) + def test__init_input_validation_for_failure_8(self, mock_is_file): + pcap_directory = 'directory' + pcap_file_0 = 'pcap_file_0' + pcap_file_1 = 'pcap_file_1' + lua_script = 'lua_script' + lua_directory = 'lua_directory' + + variables = dict() + variables[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'directory' + variables[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + variables[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + variables[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + + self.assertRaises(ValueError, + mut.DpdkPacketGenerator. + _init_input_validation, pcap_file_0, pcap_file_1, + lua_script, pcap_directory, lua_directory, variables) + + @mock.patch('os.chdir') + def test__chdir_for_success(self, mock_os_chdir): + mut.DpdkPacketGenerator._chdir('directory') + mock_os_chdir.assert_called_once_with('directory') + + @mock.patch('experimental_framework.common.run_command', + side_effect=MockRunCommand.mock_run_command) + def test__init_physical_nics_for_success(self, mock_run_command): + dpdk_interfaces = 1 + dpdk_vars = dict() + + dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] = 'dpdk_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'pktgen_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + dpdk_vars[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + dpdk_vars[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1] = '1:00.0' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2] = '1:00.1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] = 'interface_1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] = 'interface_2' + self.mut._init_physical_nics(dpdk_interfaces, dpdk_vars) + self.assertEqual(MockRunCommand.mock_run_command(), + [True, True, True, False, False, False]) + + @mock.patch('experimental_framework.common.run_command', + side_effect=MockRunCommand.mock_run_command) + def test__init_physical_nics_for_success_2(self, mock_run_command): + dpdk_interfaces = 2 + dpdk_vars = dict() + + dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] = 'dpdk_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'pktgen_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + dpdk_vars[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + dpdk_vars[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1] = '1:00.0' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2] = '1:00.1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] = 'interface_1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] = 'interface_2' + self.mut._init_physical_nics(dpdk_interfaces, dpdk_vars) + self.assertEqual(MockRunCommand.mock_run_command(), + [True, True, True, True, True, True]) + + @mock.patch('experimental_framework.common.run_command') + def test__init_physical_nics_for_failure(self, mock_run_command): + dpdk_interfaces = 3 + dpdk_vars = dict() + self.assertRaises(ValueError, self.mut._init_physical_nics, + dpdk_interfaces, dpdk_vars) + + @mock.patch('experimental_framework.common.run_command', + side_effect=MockRunCommand.mock_run_command_finalization) + def test__finalize_physical_nics_for_success(self, mock_run_command): + dpdk_interfaces = 1 + dpdk_vars = dict() + dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] = 'dpdk_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'pktgen_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + dpdk_vars[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + dpdk_vars[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1] = '1:00.0' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2] = '1:00.1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] = 'interface_1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] = 'interface_2' + self.mut._finalize_physical_nics(dpdk_interfaces, dpdk_vars) + self.assertEqual(MockRunCommand.mock_run_command_finalization(), + [True, True, True, False, False, False]) + + @mock.patch('experimental_framework.common.run_command', + side_effect=MockRunCommand.mock_run_command_finalization) + def test__finalize_physical_nics_for_success_2(self, mock_run_command): + dpdk_interfaces = 2 + dpdk_vars = dict() + dpdk_vars[conf_file.CFSP_DPDK_DPDK_DIRECTORY] = 'dpdk_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PKTGEN_DIRECTORY] = 'pktgen_directory/' + dpdk_vars[conf_file.CFSP_DPDK_PROGRAM_NAME] = 'program_name' + dpdk_vars[conf_file.CFSP_DPDK_COREMASK] = 'coremask' + dpdk_vars[conf_file.CFSP_DPDK_MEMORY_CHANNEL] = 'memory_channels' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_1] = '1:00.0' + dpdk_vars[conf_file.CFSP_DPDK_BUS_SLOT_NIC_2] = '1:00.1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_1] = 'interface_1' + dpdk_vars[conf_file.CFSP_DPDK_NAME_IF_2] = 'interface_2' + self.mut._finalize_physical_nics(dpdk_interfaces, dpdk_vars) + self.assertEqual(MockRunCommand.mock_run_command_finalization(), + [True, True, True, True, True, True]) + + def test__finalize_physical_nics_for_failure(self): + dpdk_interfaces = 0 + dpdk_vars = dict() + self.assertRaises(ValueError, self.mut._finalize_physical_nics, + dpdk_interfaces, dpdk_vars) + + def test__cores_configuration_for_success(self): + coremask = '1f' + expected = '[2:1].0,[4:3].1' + output = mut.DpdkPacketGenerator._cores_configuration(coremask, + 1, 2, 2) + self.assertEqual(expected, output) + + def test__cores_configuration_for_success_2(self): + coremask = '1f' + expected = '2.0,[4:3].1' + output = mut.DpdkPacketGenerator._cores_configuration(coremask, + 1, 1, 2) + self.assertEqual(expected, output) + + def test__cores_configuration_for_success_3(self): + coremask = '1f' + expected = '[3:2].0,4.1' + output = mut.DpdkPacketGenerator._cores_configuration(coremask, + 1, 2, 1) + self.assertEqual(expected, output) + + def test__cores_configuration_for_failure(self): + coremask = '1' + self.assertRaises(ValueError, + mut.DpdkPacketGenerator._cores_configuration, + coremask, 1, 2, 2) + + @mock.patch('experimental_framework.common.LOG') + @mock.patch('experimental_framework.common.run_command') + def test__change_vlan_for_success(self, mock_run_command, mock_log): + mut.DpdkPacketGenerator._change_vlan('/directory/', 'pcap_file', '10') + expected_param = '/directory/vlan_tag.sh /directory/pcap_file 10' + mock_run_command.assert_called_once_with(expected_param) -- cgit 1.2.3-korg