# limitations under the License.
""" Base class implementation for generic vnf implementation """
-from __future__ import absolute_import
-
-import posixpath
-import time
+from collections import Mapping
import logging
+from multiprocessing import Queue, Value, Process
import os
+import posixpath
import re
import subprocess
-from collections import Mapping
-
-from multiprocessing import Queue, Value, Process
+import time
+import six
from six.moves import cStringIO
+from trex_stl_lib.trex_stl_client import LoggerApi
+from trex_stl_lib.trex_stl_client import STLClient
+from trex_stl_lib.trex_stl_exceptions import STLError
from yardstick.benchmark.contexts.base import Context
from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file
+from yardstick.common import exceptions as y_exceptions
+from yardstick.common import utils
from yardstick.common.process import check_if_process_failed
-from yardstick.network_services.helpers.cpu import CpuSysCores
+from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper
from yardstick.network_services.helpers.samplevnf_helper import PortPairs
from yardstick.network_services.helpers.samplevnf_helper import MultiPortConfig
-from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper
from yardstick.network_services.nfvi.resource import ResourceProfile
+from yardstick.network_services.utils import get_nsb_option
from yardstick.network_services.vnf_generic.vnf.base import GenericVNF
-from yardstick.network_services.vnf_generic.vnf.base import QueueFileWrapper
from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen
-from yardstick.network_services.utils import get_nsb_option
-
-from trex_stl_lib.trex_stl_client import STLClient
-from trex_stl_lib.trex_stl_client import LoggerApi
-from trex_stl_lib.trex_stl_exceptions import STLError
-
+from yardstick.network_services.vnf_generic.vnf.base import QueueFileWrapper
from yardstick.ssh import AutoConnectSSH
+
DPDK_VERSION = "dpdk-16.07"
LOG = logging.getLogger(__name__)
CFG_CONFIG = os.path.join(REMOTE_TMP, "sample_config")
CFG_SCRIPT = os.path.join(REMOTE_TMP, "sample_script")
- CORES = []
DEFAULT_CONFIG_TPL_CFG = "sample.cfg"
PIPELINE_COMMAND = ''
VNF_TYPE = "SAMPLE"
APP_NAME = 'DpdkVnf'
FIND_NET_CMD = "find /sys/class/net -lname '*{}*' -printf '%f'"
-
- HW_DEFAULT_CORE = 3
- SW_DEFAULT_CORE = 2
+ NR_HUGEPAGES_PATH = '/proc/sys/vm/nr_hugepages'
+ HUGEPAGES_KB = 1024 * 1024 * 16
@staticmethod
def _update_packet_type(ip_pipeline_cfg, traffic_options):
self.dpdk_bind_helper = DpdkBindHelper(ssh_helper)
def _setup_hugepages(self):
- cmd = "awk '/Hugepagesize/ { print $2$3 }' < /proc/meminfo"
- hugepages = self.ssh_helper.execute(cmd)[1].rstrip()
-
- memory_path = \
- '/sys/kernel/mm/hugepages/hugepages-%s/nr_hugepages' % hugepages
- self.ssh_helper.execute("awk -F: '{ print $1 }' < %s" % memory_path)
-
- if hugepages == "2048kB":
- pages = 8192
- else:
- pages = 16
-
- self.ssh_helper.execute("echo %s | sudo tee %s" % (pages, memory_path))
+ meminfo = utils.read_meminfo(self.ssh_helper)
+ hp_size_kb = int(meminfo['Hugepagesize'])
+ nr_hugepages = int(abs(self.HUGEPAGES_KB / hp_size_kb))
+ self.ssh_helper.execute('echo %s | sudo tee %s' %
+ (nr_hugepages, self.NR_HUGEPAGES_PATH))
+ hp = six.BytesIO()
+ self.ssh_helper.get_file_obj(self.NR_HUGEPAGES_PATH, hp)
+ nr_hugepages_set = int(hp.getvalue().decode('utf-8').splitlines()[0])
+ LOG.info('Hugepages size (kB): %s, number claimed: %s, number set: %s',
+ hp_size_kb, nr_hugepages, nr_hugepages_set)
def build_config(self):
vnf_cfg = self.scenario_helper.vnf_cfg
'tool_path': tool_path,
}
- def _get_app_cpu(self):
- if self.CORES:
- return self.CORES
-
- vnf_cfg = self.scenario_helper.vnf_cfg
- sys_obj = CpuSysCores(self.ssh_helper)
- self.sys_cpu = sys_obj.get_core_socket()
- num_core = int(vnf_cfg["worker_threads"])
- if vnf_cfg.get("lb_config", "SW") == 'HW':
- num_core += self.HW_DEFAULT_CORE
- else:
- num_core += self.SW_DEFAULT_CORE
- app_cpu = self.sys_cpu[str(self.socket)][:num_core]
- return app_cpu
-
- def _get_cpu_sibling_list(self, cores=None):
- if cores is None:
- cores = self._get_app_cpu()
- sys_cmd_template = "%s/cpu%s/topology/thread_siblings_list"
- awk_template = "awk -F: '{ print $1 }' < %s"
- sys_path = "/sys/devices/system/cpu/"
- cpu_topology = []
- try:
- for core in cores:
- sys_cmd = sys_cmd_template % (sys_path, core)
- cpu_id = self.ssh_helper.execute(awk_template % sys_cmd)[1]
- cpu_topology.extend(cpu.strip() for cpu in cpu_id.split(','))
-
- return cpu_topology
- except Exception:
- return []
-
- def _validate_cpu_cfg(self):
- return self._get_cpu_sibling_list()
-
def setup_vnf_environment(self):
self._setup_dpdk()
self.bound_pci = [v['virtual-interface']["vpci"] for v in self.vnfd_helper.interfaces]
self.ssh_helper.execute("sudo killall %s" % self.APP_NAME)
def _setup_dpdk(self):
- """ setup dpdk environment needed for vnf to run """
-
+ """Setup DPDK environment needed for VNF to run"""
self._setup_hugepages()
- self.ssh_helper.execute("sudo modprobe uio && sudo modprobe igb_uio")
-
- exit_status = self.ssh_helper.execute("lsmod | grep -i igb_uio")[0]
- if exit_status == 0:
- return
-
- dpdk = self.ssh_helper.join_bin_path(DPDK_VERSION)
- dpdk_setup = self.ssh_helper.provision_tool(tool_file="nsb_setup.sh")
- exit_status = self.ssh_helper.execute("which {} >/dev/null 2>&1".format(dpdk))[0]
- if exit_status != 0:
- self.ssh_helper.execute("bash %s dpdk >/dev/null 2>&1" % dpdk_setup)
+ self.ssh_helper.execute('sudo modprobe uio && sudo modprobe igb_uio')
+ exit_status = self.ssh_helper.execute('lsmod | grep -i igb_uio')[0]
+ if exit_status:
+ raise y_exceptions.DPDKSetupDriverError()
def get_collectd_options(self):
options = self.scenario_helper.all_options.get("collectd", {})
else:
self.socket = 1
- cores = self._validate_cpu_cfg()
# implicit ordering, presumably by DPDK port num, so pre-sort by port_num
# this won't work because we don't have DPDK port numbers yet
ports = sorted(self.vnfd_helper.interfaces, key=self.vnfd_helper.port_num)
collectd_options = self.get_collectd_options()
plugins = collectd_options.get("plugins", {})
# we must set timeout to be the same as the VNF otherwise KPIs will die before VNF
- return ResourceProfile(self.vnfd_helper.mgmt_interface, port_names=port_names, cores=cores,
+ return ResourceProfile(self.vnfd_helper.mgmt_interface, port_names=port_names,
plugins=plugins, interval=collectd_options.get("interval"),
timeout=self.scenario_helper.timeout)
if vpci == v['virtual-interface']['vpci'])
# force to int
intf['virtual-interface']['dpdk_port_num'] = int(dpdk_port_num)
- except:
+ except: # pylint: disable=bare-except
pass
time.sleep(2)
def _collect_resource_kpi(self):
result = {}
- status = self.resource.check_if_sa_running("collectd")[0]
+ status = self.resource.check_if_system_agent_running("collectd")[0]
if status == 0:
result = self.resource.amqp_collect_nfvi_kpi()
self.vnfd_helper.port_nums(self.vnfd_helper.port_pairs.downlink_ports)
self.all_ports = self.vnfd_helper.port_nums(self.vnfd_helper.port_pairs.all_ports)
+ def port_num(self, intf):
+ # by default return port num
+ return self.vnfd_helper.port_num(intf)
+
def get_stats(self, *args, **kwargs):
try:
return self.client.get_stats(*args, **kwargs)
self.client.clear_stats(ports=ports)
def start(self, ports=None, *args, **kwargs):
+ # pylint: disable=keyword-arg-before-vararg
+ # NOTE(ralonsoh): defining keyworded arguments before variable
+ # positional arguments is a bug. This function definition doesn't work
+ # in Python 2, although it works in Python 3. Reference:
+ # https://www.python.org/dev/peps/pep-3102/
if ports is None:
ports = self.all_ports
self.client.start(ports=ports, *args, **kwargs)
if not self._queue.empty():
kpi = self._queue.get()
self._result.update(kpi)
- LOG.debug("Got KPIs from _queue for {0} {1}".format(
- self.scenario_helper.name, self.RESOURCE_WORD))
+ LOG.debug('Got KPIs from _queue for %s %s',
+ self.scenario_helper.name, self.RESOURCE_WORD)
return self._result
def _connect(self, client=None):
self.pipeline_kwargs = {}
self.uplink_ports = None
self.downlink_ports = None
- # TODO(esm): make QueueFileWrapper invert-able so that we
+ # NOTE(esm): make QueueFileWrapper invert-able so that we
# never have to manage the queues
self.q_in = Queue()
self.q_out = Queue()
if not self._vnf_process.is_alive():
raise RuntimeError("%s VNF process died." % self.APP_NAME)
- # TODO(esm): move to QueueFileWrapper
+ # NOTE(esm): move to QueueFileWrapper
while self.q_out.qsize() > 0:
buf.append(self.q_out.get())
message = ''.join(buf)
self._vnf_process.terminate()
# no terminate children here because we share processes with tg
- def get_stats(self, *args, **kwargs):
- """
- Method for checking the statistics
+ def get_stats(self, *args, **kwargs): # pylint: disable=unused-argument
+ """Method for checking the statistics
+
+ This method could be overridden in children classes.
- :return:
- VNF statistics
+ :return: VNF statistics
"""
cmd = 'p {0} stats'.format(self.APP_WORD)
out = self.vnf_execute(cmd)
LOG.debug("%s collect KPIs %s", self.APP_NAME, result)
return result
+ def scale(self, flavor=""):
+ """The SampleVNF base class doesn't provide the 'scale' feature"""
+ raise y_exceptions.FunctionNotImplemented(
+ function_name='scale', class_name='SampleVNFTrafficGen')
+
class SampleVNFTrafficGen(GenericTrafficGen):
""" Class providing file-like API for generic traffic generator """
def instantiate(self, scenario_cfg, context_cfg):
self.scenario_helper.scenario_cfg = scenario_cfg
- self.resource_helper.generate_cfg()
self.resource_helper.setup()
+ # must generate_cfg after DPDK bind because we need port number
+ self.resource_helper.generate_cfg()
LOG.info("Starting %s server...", self.APP_NAME)
name = "{}-{}-{}".format(self.name, self.APP_NAME, os.getpid())
self._tg_process = Process(name=name, target=self._start_server)
self._tg_process.start()
- def wait_for_instantiate(self):
- # overridden by subclasses
- return self._wait_for_process()
-
def _check_status(self):
raise NotImplementedError
return self._traffic_process.is_alive()
- def listen_traffic(self, traffic_profile):
- """ Listen to traffic with the given parameters.
- Method is non-blocking, returns immediately when traffic process
- is running. Optional.
-
- :param traffic_profile:
- :return: True/False
- """
- pass
-
- def verify_traffic(self, traffic_profile):
- """ Verify captured traffic after it has ended. Optional.
-
- :param traffic_profile:
- :return: dict
- """
- pass
-
def collect_kpi(self):
# check if the tg processes have exited
for proc in (self._tg_process, self._traffic_process):
self._tg_process.join(PROCESS_JOIN_TIMEOUT)
self._tg_process.terminate()
# no terminate children here because we share processes with vnf
+
+ def scale(self, flavor=""):
+ """A traffic generator VFN doesn't provide the 'scale' feature"""
+ raise y_exceptions.FunctionNotImplemented(
+ function_name='scale', class_name='SampleVNFTrafficGen')