From 305c50188851155207b41120e8c89fee4c5eeb63 Mon Sep 17 00:00:00 2001 From: treyad Date: Thu, 15 Nov 2018 08:44:33 -0800 Subject: Add vIPSEC VNF for running Crypto performance test case VNF life cycle - instantiate - collect_kpi - terminate JIRA: YARDSTICK-1484 Change-Id: I39fd24fdadbce6cee161c906fc95c16a36cb7cd8 Signed-off-by: treyad --- yardstick/network_services/helpers/cpu.py | 27 +- yardstick/network_services/vnf_generic/vnf/base.py | 2 +- .../network_services/vnf_generic/vnf/ipsec_vnf.py | 219 ++++ .../vnf_generic/vnf/vpp_helpers.py | 76 ++ .../unit/network_services/helpers/test_cpu.py | 22 + .../vnf_generic/vnf/test_ipsec_vnf.py | 1327 ++++++++++++++++++++ .../vnf_generic/vnf/test_vpp_helpers.py | 335 +++++ 7 files changed, 2002 insertions(+), 6 deletions(-) create mode 100644 yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py create mode 100644 yardstick/network_services/vnf_generic/vnf/vpp_helpers.py create mode 100644 yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py create mode 100644 yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py (limited to 'yardstick') diff --git a/yardstick/network_services/helpers/cpu.py b/yardstick/network_services/helpers/cpu.py index 8c21754ff..8cdd829a0 100644 --- a/yardstick/network_services/helpers/cpu.py +++ b/yardstick/network_services/helpers/cpu.py @@ -33,11 +33,11 @@ class CpuSysCores(object): core_lines = {} for line in lines: if line.strip(): - name, value = line.split(":", 1) - core_lines[name.strip()] = value.strip() + name, value = line.split(":", 1) + core_lines[name.strip()] = value.strip() else: - core_details.append(core_lines) - core_lines = {} + core_details.append(core_lines) + core_lines = {} return core_details @@ -51,7 +51,7 @@ class CpuSysCores(object): lines = self._open_cpuinfo() core_details = self._get_core_details(lines) for core in core_details: - for k, v in core.items(): + for k, _ in core.items(): if k == "physical id": if core["physical id"] not in self.core_map: self.core_map[core['physical id']] = [] @@ -60,6 +60,16 @@ class CpuSysCores(object): return self.core_map + def get_cpu_layout(self): + _, stdout, _ = self.connection.execute("lscpu -p") + cpuinfo = {} + cpuinfo['cpuinfo'] = list() + for line in stdout.split("\n"): + if line and line[0] != "#": + cpuinfo['cpuinfo'].append([CpuSysCores._str2int(x) for x in + line.split(",")]) + return cpuinfo + def validate_cpu_cfg(self, vnf_cfg=None): if vnf_cfg is None: vnf_cfg = { @@ -78,3 +88,10 @@ class CpuSysCores(object): return -1 return 0 + + @staticmethod + def _str2int(string): + try: + return int(string) + except ValueError: + return 0 diff --git a/yardstick/network_services/vnf_generic/vnf/base.py b/yardstick/network_services/vnf_generic/vnf/base.py index 8064ae927..8ef96b744 100644 --- a/yardstick/network_services/vnf_generic/vnf/base.py +++ b/yardstick/network_services/vnf_generic/vnf/base.py @@ -94,7 +94,7 @@ class VnfdHelper(dict): for interface in self.interfaces: virtual_intf = interface["virtual-interface"] if virtual_intf[key] == value: - return interface + return virtual_intf raise KeyError() def find_interface(self, **kwargs): diff --git a/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py b/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py new file mode 100644 index 000000000..75a8cce06 --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/ipsec_vnf.py @@ -0,0 +1,219 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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 logging +import re +import time + +from yardstick.benchmark.contexts.base import Context +from yardstick.common.process import check_if_process_failed +from yardstick.network_services import constants +from yardstick.network_services.helpers.cpu import CpuSysCores +from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF +from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \ + VppSetupEnvHelper + +LOG = logging.getLogger(__name__) + + +class VipsecApproxSetupEnvHelper(VppSetupEnvHelper): + DEFAULT_IPSEC_VNF_CFG = { + 'crypto_type': 'SW_cryptodev', + 'rxq': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + } + + def __init__(self, vnfd_helper, ssh_helper, scenario_helper): + super(VipsecApproxSetupEnvHelper, self).__init__( + vnfd_helper, ssh_helper, scenario_helper) + + def _get_crypto_type(self): + vnf_cfg = self.scenario_helper.options.get('vnf_config', + self.DEFAULT_IPSEC_VNF_CFG) + return vnf_cfg.get('crypto_type', 'SW_cryptodev') + + def _get_crypto_algorithms(self): + vpp_cfg = self.scenario_helper.all_options.get('vpp_config', {}) + return vpp_cfg.get('crypto_algorithms', 'aes-gcm') + + def _get_n_tunnels(self): + vpp_cfg = self.scenario_helper.all_options.get('vpp_config', {}) + return vpp_cfg.get('tunnels', 1) + + def _get_n_connections(self): + try: + flow_cfg = self.scenario_helper.all_options['flow'] + return flow_cfg['count'] + except KeyError: + raise KeyError("Missing flow definition in scenario section" + + " of the task definition file") + + def _get_flow_src_start_ip(self): + node_name = self.find_encrypted_data_interface()["node_name"] + try: + flow_cfg = self.scenario_helper.all_options['flow'] + src_ips = flow_cfg['src_ip'] + dst_ips = flow_cfg['dst_ip'] + except KeyError: + raise KeyError("Missing flow definition in scenario section" + + " of the task definition file") + + for src, dst in zip(src_ips, dst_ips): + flow_src_start_ip, _ = src.split('-') + flow_dst_start_ip, _ = dst.split('-') + + if node_name == "vnf__0": + return flow_src_start_ip + elif node_name == "vnf__1": + return flow_dst_start_ip + + def _get_flow_dst_start_ip(self): + node_name = self.find_encrypted_data_interface()["node_name"] + try: + flow_cfg = self.scenario_helper.all_options['flow'] + src_ips = flow_cfg['src_ip'] + dst_ips = flow_cfg['dst_ip'] + except KeyError: + raise KeyError("Missing flow definition in scenario section" + + " of the task definition file") + + for src, dst in zip(src_ips, dst_ips): + flow_src_start_ip, _ = src.split('-') + flow_dst_start_ip, _ = dst.split('-') + + if node_name == "vnf__0": + return flow_dst_start_ip + elif node_name == "vnf__1": + return flow_src_start_ip + + def build_config(self): + # TODO Implement later + pass + + def setup_vnf_environment(self): + resource = super(VipsecApproxSetupEnvHelper, + self).setup_vnf_environment() + + self.start_vpp_service() + + sys_cores = CpuSysCores(self.ssh_helper) + self._update_vnfd_helper(sys_cores.get_cpu_layout()) + + return resource + + @staticmethod + def calculate_frame_size(frame_cfg): + if not frame_cfg: + return 64 + + imix_count = {size.upper().replace('B', ''): int(weight) + for size, weight in frame_cfg.items()} + imix_sum = sum(imix_count.values()) + if imix_sum <= 0: + return 64 + packets_total = sum([int(size) * weight + for size, weight in imix_count.items()]) + return packets_total / imix_sum + + def check_status(self): + ipsec_created = False + cmd = "vppctl show int" + _, stdout, _ = self.ssh_helper.execute(cmd) + entries = re.split(r"\n+", stdout) + tmp = [re.split(r"\s\s+", entry, 5) for entry in entries] + + for item in tmp: + if isinstance(item, list): + if item[0] and item[0] != 'local0': + if "ipsec" in item[0] and not ipsec_created: + ipsec_created = True + if len(item) > 2 and item[2] == 'down': + return False + return ipsec_created + + def get_vpp_statistics(self): + # TODO Implement later + return None + + def create_ipsec_tunnels(self): + # TODO Implement later + pass + + def find_raw_data_interface(self): + try: + return self.vnfd_helper.find_virtual_interface(vld_id="uplink_0") + except KeyError: + return self.vnfd_helper.find_virtual_interface(vld_id="downlink_0") + + def find_encrypted_data_interface(self): + return self.vnfd_helper.find_virtual_interface(vld_id="ciphertext") + + +class VipsecApproxVnf(SampleVNF): + """ This class handles vIPSEC VNF model-driver definitions """ + + APP_NAME = 'vIPSEC' + APP_WORD = 'vipsec' + WAIT_TIME = 20 + + def __init__(self, name, vnfd, setup_env_helper_type=None, + resource_helper_type=None): + if setup_env_helper_type is None: + setup_env_helper_type = VipsecApproxSetupEnvHelper + super(VipsecApproxVnf, self).__init__( + name, vnfd, setup_env_helper_type, + resource_helper_type) + + def _run(self): + # we can't share ssh paramiko objects to force new connection + self.ssh_helper.drop_connection() + # kill before starting + self.setup_helper.kill_vnf() + self._build_config() + self.setup_helper.create_ipsec_tunnels() + + def wait_for_instantiate(self): + time.sleep(self.WAIT_TIME) + while True: + status = self.setup_helper.check_status() + if not self._vnf_process.is_alive() and not status: + raise RuntimeError("%s VNF process died." % self.APP_NAME) + LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME) + time.sleep(self.WAIT_TIME_FOR_SCRIPT) + status = self.setup_helper.check_status() + if status: + LOG.info("%s VNF is up and running.", self.APP_NAME) + self._vnf_up_post() + return self._vnf_process.exitcode + + def terminate(self): + self.setup_helper.kill_vnf() + self._tear_down() + self.resource_helper.stop_collect() + if self._vnf_process is not None: + # be proper and join first before we kill + LOG.debug("joining before terminate %s", self._vnf_process.name) + self._vnf_process.join(constants.PROCESS_JOIN_TIMEOUT) + self._vnf_process.terminate() + + def collect_kpi(self): + # we can't get KPIs if the VNF is down + check_if_process_failed(self._vnf_process, 0.01) + physical_node = Context.get_physical_node_from_server( + self.scenario_helper.nodes[self.name]) + result = {"physical_node": physical_node} + result["collect_stats"] = self.setup_helper.get_vpp_statistics() + LOG.debug("%s collect KPIs %s", self.APP_NAME, result) + return result diff --git a/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py b/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py new file mode 100644 index 000000000..86c42ecfd --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/vpp_helpers.py @@ -0,0 +1,76 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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 logging + +from yardstick.network_services.vnf_generic.vnf.sample_vnf import \ + DpdkVnfSetupEnvHelper + +LOG = logging.getLogger(__name__) + + +class VppSetupEnvHelper(DpdkVnfSetupEnvHelper): + APP_NAME = "vpp" + CFG_CONFIG = "/etc/vpp/startup.conf" + CFG_SCRIPT = "" + PIPELINE_COMMAND = "" + VNF_TYPE = "IPSEC" + VAT_BIN_NAME = 'vpp_api_test' + + def __init__(self, vnfd_helper, ssh_helper, scenario_helper): + super(VppSetupEnvHelper, self).__init__(vnfd_helper, ssh_helper, + scenario_helper) + + def kill_vnf(self): + ret_code, _, _ = \ + self.ssh_helper.execute( + 'service {name} stop'.format(name=self.APP_NAME)) + if int(ret_code): + raise RuntimeError( + 'Failed to stop service {name}'.format(name=self.APP_NAME)) + + def tear_down(self): + pass + + def start_vpp_service(self): + ret_code, _, _ = \ + self.ssh_helper.execute( + 'service {name} restart'.format(name=self.APP_NAME)) + if int(ret_code): + raise RuntimeError( + 'Failed to start service {name}'.format(name=self.APP_NAME)) + + def _update_vnfd_helper(self, additional_data, iface_key=None): + for k, v in additional_data.items(): + if iface_key is None: + if isinstance(v, dict) and k in self.vnfd_helper: + self.vnfd_helper[k].update(v) + else: + self.vnfd_helper[k] = v + else: + if isinstance(v, + dict) and k in self.vnfd_helper.find_virtual_interface( + ifname=iface_key): + self.vnfd_helper.find_virtual_interface(ifname=iface_key)[ + k].update(v) + else: + self.vnfd_helper.find_virtual_interface(ifname=iface_key)[ + k] = v + + def get_value_by_interface_key(self, interface, key): + try: + return self.vnfd_helper.find_virtual_interface( + ifname=interface).get(key) + except (KeyError, ValueError): + return None diff --git a/yardstick/tests/unit/network_services/helpers/test_cpu.py b/yardstick/tests/unit/network_services/helpers/test_cpu.py index 871fbf8c9..c28178d4b 100644 --- a/yardstick/tests/unit/network_services/helpers/test_cpu.py +++ b/yardstick/tests/unit/network_services/helpers/test_cpu.py @@ -119,3 +119,25 @@ class TestCpuSysCores(unittest.TestCase): vnf_cfg = {'lb_config': 'SW', 'lb_count': 1, 'worker_config': '1C/1T', 'worker_threads': 1} self.assertEqual(-1, cpu_topo.validate_cpu_cfg(vnf_cfg)) + + def test_get_cpu_layout(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock( + return_value=(1, "# CPU,Core,Socket,Node,,L1d,L1i,L2,L3\n'" + "0,0,0,0,,0,0,0,0\n" + "1,1,0,0,,1,1,1,0\n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + self.assertEqual({'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]}, + cpu_topo.get_cpu_layout()) + + def test__str2int(self): + self.assertEqual(1, CpuSysCores._str2int("1")) + + def test__str2int_error(self): + self.assertEqual(0, CpuSysCores._str2int("err")) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py new file mode 100644 index 000000000..f8126307c --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py @@ -0,0 +1,1327 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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 multiprocessing import Process + +import mock + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.common import utils +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.vnf_generic.vnf import ipsec_vnf +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf.ipsec_vnf import \ + VipsecApproxSetupEnvHelper +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import \ + mock_ssh + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +NAME = 'vnf__1' + + +@mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") +class TestVipsecApproxVnf(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + ]}} + + VNFD_ERROR = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_1", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + ]}} + + scenario_cfg = { + "nodes": { + "tg__0": "trafficgen.yardstick-5486cc2f", + "vnf__0": "vnf0.yardstick-5486cc2f", + "vnf__1": "vnf1.yardstick-5486cc2f" + }, + "options": { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + }, + "runner": { + "duration": 500, + "interval": 10, + "object": + "yardstick.benchmark.scenarios.networking.vnf_generic.NetworkServiceTestCase", + "output_config": { + "DEFAULT": { + "debug": "False", + "dispatcher": [ + "influxdb" + ] + }, + "dispatcher_file": { + "debug": "False", + "dispatcher": "influxdb", + "file_path": "/tmp/yardstick.out" + }, + "dispatcher_http": { + "debug": "False", + "dispatcher": "influxdb", + "target": "http://127.0.0.1:8000/results", + "timeout": "20" + }, + "dispatcher_influxdb": { + "db_name": "yardstick", + "debug": "False", + "dispatcher": "influxdb", + "password": "r00t", + "target": "http://192.168.100.3:8086", + "timeout": "20", + "username": "root" + }, + "nsb": { + "bin_path": "/opt/nsb_bin", + "debug": "False", + "dispatcher": "influxdb", + "trex_client_lib": "/opt/nsb_bin/trex_client/stl", + "trex_path": "/opt/nsb_bin/trex/scripts" + } + }, + "runner_id": 1105, + "type": "Duration" + }, + "task_id": "5486cc2f-d4d3-4feb-b0df-5e0bcd584c9e", + "task_path": "samples/vnf_samples/nsut/ipsec", + "tc": "tc_baremetal_rfc2544_ipv4_1flow_sw_aesgcm_4cores_64B_trex", + "topology": "vpp-tg-topology-2.yaml", + "traffic_profile": "../../traffic_profiles/ipv4_throughput_latency_vpp.yaml", + "type": "NSPerf" + } + + context_cfg = { + "networks": {}, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vpp_tpl.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:00:06.0" + }, + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.2", + "dst_mac": "90:e2:ba:7c:41:a9", + "ifname": "xe1", + "local_ip": "192.168.101.1", + "local_mac": "90:e2:ba:7c:30:e9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.1", + "dst_mac": "90:e2:ba:7c:30:e9", + "ifname": "xe0", + "local_ip": "192.168.101.2", + "local_mac": "90:e2:ba:7c:41:a9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "tg__0", + "vld_id": "downlink_0", + "vpci": "0000:00:06.0" + }, + "peer_name": "vnf__1", + "vld_id": "downlink_0", + "vpci": "0000:81:00.1" + } + }, + "ip": "10.10.10.10", + "member-vnf-index": "1", + "name": "trafficgen.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "TrafficGen", + "user": "root", + "username": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vpp_vnfd.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:00:06.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + } + }, + "ip": "10.10.10.101", + "member-vnf-index": "2", + "name": "vnf0.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "VirtualNetworkFunction", + "user": "root", + "username": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vpp_vnfd.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.101.1", + "dst_mac": "90:e2:ba:7c:30:e9", + "ifname": "xe0", + "local_ip": "192.168.101.2", + "local_mac": "90:e2:ba:7c:41:a9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.2", + "dst_mac": "90:e2:ba:7c:41:a9", + "ifname": "xe1", + "local_ip": "192.168.101.1", + "local_mac": "90:e2:ba:7c:30:e9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__1", + "vld_id": "downlink_0", + "vpci": "0000:81:00.1" + }, + "peer_name": "tg__0", + "vld_id": "downlink_0", + "vpci": "0000:00:06.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + } + }, + "ip": "10.10.10.102", + "member-vnf-index": "3", + "name": "vnf1.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "VirtualNetworkFunction", + "user": "root", + "username": "root", + "vnfd-id-ref": "vnf__1" + } + } + } + + def test___init__(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + self.assertIsNone(vipsec_vnf._vnf_process) + + @mock.patch(SSH_HELPER) + def test__run(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf._build_config = mock.MagicMock() + vipsec_vnf.setup_helper.kill_vnf = mock.MagicMock() + vipsec_vnf.setup_helper.create_ipsec_tunnels = mock.MagicMock() + vipsec_vnf.queue_wrapper = mock.MagicMock() + vipsec_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + vipsec_vnf.vnf_cfg = {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1} + vipsec_vnf.all_options = {'traffic_type': '4', + 'topology': 'nsb_test_case.yaml'} + vipsec_vnf._run() + # vipsec_vnf.setup_helper.ssh_helper.execute.assert_called_once() + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate(self, ssh, *args): + mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.resource_helper.resource = mock.MagicMock() + vipsec_vnf.setup_helper = mock.MagicMock() + vipsec_vnf.setup_helper.check_status.return_value = True + vipsec_vnf._vnf_process = mock_process + vipsec_vnf.WAIT_TIME = 0 + self.assertEqual(vipsec_vnf.wait_for_instantiate(), 432) + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate_crash(self, ssh, *args): + mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = False + mock_process.exitcode = 432 + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.resource_helper.resource = mock.MagicMock() + vipsec_vnf.setup_helper = mock.MagicMock() + vipsec_vnf.setup_helper.check_status.return_value = False + vipsec_vnf._vnf_process = mock_process + vipsec_vnf.WAIT_TIME = 0 + vipsec_vnf.WAIT_TIME_FOR_SCRIPT = 0 + + with self.assertRaises(RuntimeError) as raised: + vipsec_vnf.wait_for_instantiate() + + self.assertIn('VNF process died', str(raised.exception)) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + @mock.patch.object(ipsec_vnf.VipsecApproxSetupEnvHelper, + 'get_vpp_statistics', + return_value={'packets_in': 0, 'packets_fwd': 0, + 'packets_dropped': 0}) + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.scenario_helper.scenario_cfg = { + 'nodes': {vipsec_vnf.name: "mock"} + } + result = { + 'collect_stats': {'packets_in': 0, 'packets_fwd': 0, + 'packets_dropped': 0}, + 'physical_node': 'mock_node' + } + self.assertEqual(result, vipsec_vnf.collect_kpi()) + + @mock.patch.object(utils, 'find_relative_file') + @mock.patch( + "yardstick.network_services.vnf_generic.vnf.sample_vnf.Context") + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.deploy_helper = mock.MagicMock() + vipsec_vnf.resource_helper = mock.MagicMock() + vipsec_vnf._build_config = mock.MagicMock() + vipsec_vnf.WAIT_TIME = 0 + self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) + self.assertIsNone(vipsec_vnf.instantiate(self.scenario_cfg, + self.context_cfg)) + + @mock.patch.object(ipsec_vnf.VipsecApproxSetupEnvHelper, 'kill_vnf', + return_value='') + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf._vnf_process = mock.MagicMock() + vipsec_vnf._vnf_process.terminate = mock.Mock() + self.assertIsNone(vipsec_vnf.terminate()) + + +class TestVipsecApproxSetupEnvHelper(unittest.TestCase): + ALL_OPTIONS = { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + } + + ALL_OPTIONS_ERROR = { + "flow_error": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + } + + OPTIONS = { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + } + + CPU_LAYOUT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 9, 9, 4], + [10, 6, 0, 1, 0, 10, 10, 5], + [11, 6, 0, 1, 0, 11, 11, 5], + [12, 7, 0, 1, 0, 12, 12, 6], + [13, 7, 0, 1, 0, 13, 13, 6], + [14, 8, 0, 1, 0, 14, 14, 7], + [15, 8, 0, 1, 0, 15, 15, 7], + [16, 9, 0, 1, 0, 16, 16, 8], + [17, 9, 0, 1, 0, 17, 17, 8]]} + + VPP_INTERFACES_DUMP = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 19, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + VPP_INTERFACES_STATUS = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9000/0/0/0 \n' \ + 'VirtualFunctionEthernetff/7/0 2 up 9000/0/0/0 \n' \ + 'ipsec0 2 up 9000/0/0/0 \n' \ + 'local0 0 down 0/0/0/0 ' + + VPP_INTERFACES_STATUS_FALSE = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 down 9000/0/0/0 \n' \ + 'VirtualFunctionEthernetff/7/0 2 down 9000/0/0/0 \n' \ + 'ipsec0 2 down 9000/0/0/0 \n' \ + 'local0 0 down 0/0/0/0 ' + + def test__get_crypto_type(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('SW_cryptodev', + ipsec_approx_setup_helper._get_crypto_type()) + + def test__get_crypto_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('aes-gcm', + ipsec_approx_setup_helper._get_crypto_algorithms()) + + def test__get_n_tunnels(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(1, ipsec_approx_setup_helper._get_n_tunnels()) + + def test__get_n_connections(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(1, ipsec_approx_setup_helper._get_n_connections()) + + def test__get_n_connections_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_n_connections() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test__get_flow_src_start_ip(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('10.0.0.0', + ipsec_approx_setup_helper._get_flow_src_start_ip()) + + def test__get_flow_src_start_ip_vnf1(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('20.0.0.0', + ipsec_approx_setup_helper._get_flow_src_start_ip()) + + def test__get_flow_src_start_ip_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_flow_src_start_ip() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test__get_flow_dst_start_ip(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('20.0.0.0', + ipsec_approx_setup_helper._get_flow_dst_start_ip()) + + def test__get_flow_dst_start_ip_vnf1(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('10.0.0.0', + ipsec_approx_setup_helper._get_flow_dst_start_ip()) + + def test__get_flow_dst_start_ip_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_flow_dst_start_ip() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test_build_config(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + ipsec_approx_setup_helper.build_config() + + @mock.patch.object(utils, 'setup_hugepages') + def test_setup_vnf_environment(self, *args): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.nodes = [None, None] + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertIsInstance( + ipsec_approx_setup_helper.setup_vnf_environment(), + ResourceProfile) + + def test_calculate_frame_size(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(16984 / 48, + ipsec_approx_setup_helper.calculate_frame_size( + {'64B': 28, '570B': 16, '1518B': 4})) + + def test_calculate_frame_size_64(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(64, + ipsec_approx_setup_helper.calculate_frame_size({})) + + def test_calculate_frame_size_64_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(64, + ipsec_approx_setup_helper.calculate_frame_size( + {'64B': -28, '570B': 16, '1518B': 4})) + + def test_check_status(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, self.VPP_INTERFACES_STATUS, '' + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertTrue(ipsec_approx_setup_helper.check_status()) + + def test_check_status_false(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, self.VPP_INTERFACES_STATUS_FALSE, '' + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertFalse(ipsec_approx_setup_helper.check_status()) + + def test_get_vpp_statistics(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + ipsec_approx_setup_helper.get_vpp_statistics() + + def test_create_ipsec_tunnels(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + ipsec_approx_setup_helper.create_ipsec_tunnels() + + def test_find_raw_data_interface(self): + expected = {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.1', + 'dst_mac': '90:e2:ba:7c:30:e8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.2', + 'local_mac': '90:e2:ba:7c:41:a8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__0', + 'peer_ifname': 'xe0', + 'peer_intf': {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.2', + 'dst_mac': '90:e2:ba:7c:41:a8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.1', + 'local_mac': '90:e2:ba:7c:30:e8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'tg__0', + 'peer_ifname': 'xe0', + 'peer_name': 'vnf__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:81:00.0'}, + 'peer_name': 'tg__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:ff:06.0'} + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(expected, + ipsec_approx_setup_helper.find_raw_data_interface()) + + def test_find_raw_data_interface_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError): + ipsec_approx_setup_helper.find_raw_data_interface() + + def test_find_encrypted_data_interface(self): + expected = {'dpdk_port_num': 1, + 'driver': 'igb_uio', + 'dst_ip': '1.1.1.2', + 'dst_mac': '0a:b1:ec:fd:a2:66', + 'ifname': 'xe1', + 'local_ip': '1.1.1.1', + 'local_mac': '4e:90:85:d3:c5:13', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__0', + 'peer_ifname': 'xe1', + 'peer_intf': {'driver': 'igb_uio', + 'dst_ip': '1.1.1.1', + 'dst_mac': '4e:90:85:d3:c5:13', + 'ifname': 'xe1', + 'local_ip': '1.1.1.2', + 'local_mac': '0a:b1:ec:fd:a2:66', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__1', + 'peer_ifname': 'xe1', + 'peer_name': 'vnf__0', + 'vld_id': 'ciphertext', + 'vpci': '0000:00:07.0'}, + 'peer_name': 'vnf__1', + 'vld_id': 'ciphertext', + 'vpci': '0000:ff:07.0'} + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(expected, + ipsec_approx_setup_helper.find_encrypted_data_interface()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py new file mode 100644 index 000000000..597844c72 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py @@ -0,0 +1,335 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# 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 yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \ + VppSetupEnvHelper + + +class TestVppSetupEnvHelper(unittest.TestCase): + VNFD_0 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + VPP_INTERFACES_DUMP = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 19, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + def test_kill_vnf(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.kill_vnf() + + def test_kill_vnf_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.kill_vnf() + + self.assertIn('Failed to stop service vpp', str(raised.exception)) + + def test_tear_down(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.tear_down() + + def test_start_vpp_service(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.start_vpp_service() + + def test_start_vpp_service_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.start_vpp_service() + + self.assertIn('Failed to start service vpp', str(raised.exception)) + + def test__update_vnfd_helper(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}) + + self.assertEqual({'vpp-key': 'vpp-value'}, + vpp_setup_env_helper.vnfd_helper.get('vpp-data', {})) + + def test__update_vnfd_helper_with_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper({'driver': 'qat'}, 'xe0') + + self.assertEqual('qat', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'driver')) + + def test__update_vnfd_helper_dict_without_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'mgmt-interface': {'name': 'net'}}) + + self.assertEqual({'ip': '10.10.10.101', + 'name': 'net', + 'password': 'r00t', + 'user': 'root', + 'vdu-id': 'ipsecvnf-baremetal'}, + vpp_setup_env_helper.vnfd_helper.get('mgmt-interface', + {})) + + def test_get_value_by_interface_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}, 'xe0') + + self.assertEqual({'vpp-key': 'vpp-value'}, + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp-data')) + + def test_get_value_by_interface_key_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}, 'xe0') + + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe2', 'vpp-err')) -- cgit 1.2.3-korg