def instantiate(self, scenario_cfg, context_cfg): vnf_cfg = scenario_cfg['vnf_options']['vpe']['cfg'] mgmt_interface = self.vnfd["mgmt-interface"] self.connection = ssh.SSH.from_node(mgmt_interface) self.tc_file_name = '{0}.yaml'.format(scenario_cfg['tc']) self.setup_vnf_environment(self.connection) cores = self._get_cpu_sibling_list() self.resource = ResourceProfile(self.vnfd, cores) self.connection.execute("pkill vPE_vnf") dpdk_nic_bind = \ provision_tool(self.connection, os.path.join(self.bin_path, "dpdk_nic_bind.py")) interfaces = self.vnfd["vdu"][0]['external-interface'] self.socket = \ next((0 for v in interfaces if v['virtual-interface']["vpci"][5] == "0"), 1) bound_pci = [v['virtual-interface']["vpci"] for v in interfaces] for vpci in bound_pci: self.connection.execute("%s --force -b igb_uio %s" % (dpdk_nic_bind, vpci)) queue_wrapper = \ QueueFileWrapper(self.q_in, self.q_out, "pipeline>") self._vnf_process = multiprocessing.Process(target=self._run_vpe, args=( queue_wrapper, vnf_cfg, )) self._vnf_process.start() buf = [] time.sleep(WAIT_TIME) # Give some time for config to load while True: message = '' while self.q_out.qsize() > 0: buf.append(self.q_out.get()) message = ''.join(buf) if "pipeline>" in message: LOG.info("VPE VNF is up and running.") queue_wrapper.clear() self._resource_collect_start() return self._vnf_process.exitcode if "PANIC" in message: raise RuntimeError("Error starting vPE VNF.") LOG.info("Waiting for VNF to start.. ") time.sleep(3) if not self._vnf_process.is_alive(): raise RuntimeError("vPE VNF process died.")
def test_clear(self): queue_file_wrapper = \ QueueFileWrapper(self.q_in, self.q_out, self.prompt) queue_file_wrapper.bufsize = 5 queue_file_wrapper.write("pipeline>") queue_file_wrapper.close() self.assertIsNone(queue_file_wrapper.clear()) self.assertIsNotNone(queue_file_wrapper.q_out.empty())
class SampleVNF(GenericVNF): """ Class providing file-like API for generic VNF implementation """ VNF_PROMPT = "pipeline>" WAIT_TIME = 1 WAIT_TIME_FOR_SCRIPT = 10 APP_NAME = "SampleVNF" # we run the VNF interactively, so the ssh command will timeout after this long def __init__(self, name, vnfd, task_id, setup_env_helper_type=None, resource_helper_type=None): super(SampleVNF, self).__init__(name, vnfd, task_id) self.bin_path = get_nsb_option('bin_path', '') self.scenario_helper = ScenarioHelper(self.name) self.ssh_helper = VnfSshHelper(self.vnfd_helper.mgmt_interface, self.bin_path) if setup_env_helper_type is None: setup_env_helper_type = SetupEnvHelper self.setup_helper = setup_env_helper_type(self.vnfd_helper, self.ssh_helper, self.scenario_helper) self.deploy_helper = SampleVNFDeployHelper(vnfd, self.ssh_helper) if resource_helper_type is None: resource_helper_type = ResourceHelper self.resource_helper = resource_helper_type(self.setup_helper) self.context_cfg = None self.pipeline_kwargs = {} self.uplink_ports = None self.downlink_ports = None # NOTE(esm): make QueueFileWrapper invert-able so that we # never have to manage the queues self.q_in = Queue() self.q_out = Queue() self.queue_wrapper = None self.run_kwargs = {} self.used_drivers = {} self.vnf_port_pairs = None self._vnf_process = None def _start_vnf(self): self.queue_wrapper = QueueFileWrapper(self.q_in, self.q_out, self.VNF_PROMPT) name = "{}-{}-{}".format(self.name, self.APP_NAME, os.getpid()) self._vnf_process = Process(name=name, target=self._run) self._vnf_process.start() def _vnf_up_post(self): pass def instantiate(self, scenario_cfg, context_cfg): self._update_collectd_options(scenario_cfg, context_cfg) self.scenario_helper.scenario_cfg = scenario_cfg self.context_cfg = context_cfg self.resource_helper.update_from_context( Context.get_context_from_server(self.scenario_helper.nodes[self.name]), self.scenario_helper.nodes[self.name] ) # vnf deploy is unsupported, use ansible playbooks if self.scenario_helper.options.get("vnf_deploy", False): self.deploy_helper.deploy_vnfs(self.APP_NAME) self.resource_helper.setup() self._start_vnf() def _update_collectd_options(self, scenario_cfg, context_cfg): """Update collectd configuration options This function retrieves all collectd options contained in the test case definition builds a single dictionary combining them. The following fragment represents a test case with the collectd options and priorities (1 highest, 3 lowest): --- schema: yardstick:task:0.1 scenarios: - type: NSPerf nodes: tg__0: trafficgen_1.yardstick vnf__0: vnf.yardstick options: collectd: <options> # COLLECTD priority 3 vnf__0: collectd: plugins: load <options> # COLLECTD priority 2 context: type: Node name: yardstick nfvi_type: baremetal file: /etc/yardstick/nodes/pod_ixia.yaml # COLLECTD priority 1 """ scenario_options = scenario_cfg.get('options', {}) generic_options = scenario_options.get('collectd', {}) scenario_node_options = scenario_options.get(self.name, {})\ .get('collectd', {}) context_node_options = context_cfg.get('nodes', {})\ .get(self.name, {}).get('collectd', {}) options = generic_options self._update_options(options, scenario_node_options) self._update_options(options, context_node_options) self.setup_helper.collectd_options = options def _update_options(self, options, additional_options): """Update collectd options and plugins dictionary""" for k, v in additional_options.items(): if isinstance(v, dict) and k in options: options[k].update(v) else: options[k] = v def wait_for_instantiate(self): buf = [] time.sleep(self.WAIT_TIME) # Give some time for config to load while True: if not self._vnf_process.is_alive(): raise RuntimeError("%s VNF process died." % self.APP_NAME) # NOTE(esm): move to QueueFileWrapper while self.q_out.qsize() > 0: buf.append(self.q_out.get()) message = ''.join(buf) if self.VNF_PROMPT in message: LOG.info("%s VNF is up and running.", self.APP_NAME) self._vnf_up_post() self.queue_wrapper.clear() return self._vnf_process.exitcode if "PANIC" in message: raise RuntimeError("Error starting %s VNF." % self.APP_NAME) LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME) time.sleep(self.WAIT_TIME_FOR_SCRIPT) # Send ENTER to display a new prompt in case the prompt text was corrupted # by other VNF output self.q_in.put('\r\n') def start_collect(self): self.resource_helper.start_collect() def stop_collect(self): self.resource_helper.stop_collect() def _build_run_kwargs(self): self.run_kwargs = { 'stdin': self.queue_wrapper, 'stdout': self.queue_wrapper, 'keep_stdin_open': True, 'pty': True, 'timeout': self.scenario_helper.timeout, } def _build_config(self): return self.setup_helper.build_config() def _run(self): # we can't share ssh paramiko objects to force new connection self.ssh_helper.drop_connection() cmd = self._build_config() # kill before starting self.setup_helper.kill_vnf() LOG.debug(cmd) self._build_run_kwargs() self.ssh_helper.run(cmd, **self.run_kwargs) def vnf_execute(self, cmd, wait_time=2): """ send cmd to vnf process """ LOG.info("%s command: %s", self.APP_NAME, cmd) self.q_in.put("{}\r\n".format(cmd)) time.sleep(wait_time) output = [] while self.q_out.qsize() > 0: output.append(self.q_out.get()) return "".join(output) def _tear_down(self): pass def terminate(self): self.vnf_execute("quit") 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() # no terminate children here because we share processes with tg 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 """ cmd = 'p {0} stats'.format(self.APP_WORD) out = self.vnf_execute(cmd) return out def collect_kpi(self): # we can't get KPIs if the VNF is down check_if_process_failed(self._vnf_process, 0.01) stats = self.get_stats() m = re.search(self.COLLECT_KPI, stats, re.MULTILINE) physical_node = Context.get_physical_node_from_server( self.scenario_helper.nodes[self.name]) result = {"physical_node": physical_node} if m: result.update({k: int(m.group(v)) for k, v in self.COLLECT_MAP.items()}) result["collect_stats"] = self.resource_helper.collect_kpi() else: result.update({"packets_in": 0, "packets_fwd": 0, "packets_dropped": 0}) 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 SampleVNF(GenericVNF): """ Class providing file-like API for generic VNF implementation """ VNF_PROMPT = "pipeline>" WAIT_TIME = 1 WAIT_TIME_FOR_SCRIPT = 10 APP_NAME = "SampleVNF" # we run the VNF interactively, so the ssh command will timeout after this long def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None): super(SampleVNF, self).__init__(name, vnfd) self.bin_path = get_nsb_option('bin_path', '') self.scenario_helper = ScenarioHelper(self.name) self.ssh_helper = VnfSshHelper(self.vnfd_helper.mgmt_interface, self.bin_path) if setup_env_helper_type is None: setup_env_helper_type = SetupEnvHelper self.setup_helper = setup_env_helper_type(self.vnfd_helper, self.ssh_helper, self.scenario_helper) self.deploy_helper = SampleVNFDeployHelper(vnfd, self.ssh_helper) if resource_helper_type is None: resource_helper_type = ResourceHelper self.resource_helper = resource_helper_type(self.setup_helper) self.context_cfg = None self.nfvi_context = None self.pipeline_kwargs = {} self.uplink_ports = None self.downlink_ports = None # NOTE(esm): make QueueFileWrapper invert-able so that we # never have to manage the queues self.q_in = Queue() self.q_out = Queue() self.queue_wrapper = None self.run_kwargs = {} self.used_drivers = {} self.vnf_port_pairs = None self._vnf_process = None def _build_ports(self): self._port_pairs = PortPairs(self.vnfd_helper.interfaces) self.networks = self._port_pairs.networks self.uplink_ports = self.vnfd_helper.port_nums(self._port_pairs.uplink_ports) self.downlink_ports = self.vnfd_helper.port_nums(self._port_pairs.downlink_ports) self.my_ports = self.vnfd_helper.port_nums(self._port_pairs.all_ports) def _get_route_data(self, route_index, route_type): route_iter = iter(self.vnfd_helper.vdu0.get('nd_route_tbl', [])) for _ in range(route_index): next(route_iter, '') return next(route_iter, {}).get(route_type, '') def _get_port0localip6(self): return_value = self._get_route_data(0, 'network') LOG.info("_get_port0localip6 : %s", return_value) return return_value def _get_port1localip6(self): return_value = self._get_route_data(1, 'network') LOG.info("_get_port1localip6 : %s", return_value) return return_value def _get_port0prefixlen6(self): return_value = self._get_route_data(0, 'netmask') LOG.info("_get_port0prefixlen6 : %s", return_value) return return_value def _get_port1prefixlen6(self): return_value = self._get_route_data(1, 'netmask') LOG.info("_get_port1prefixlen6 : %s", return_value) return return_value def _get_port0gateway6(self): return_value = self._get_route_data(0, 'network') LOG.info("_get_port0gateway6 : %s", return_value) return return_value def _get_port1gateway6(self): return_value = self._get_route_data(1, 'network') LOG.info("_get_port1gateway6 : %s", return_value) return return_value def _start_vnf(self): self.queue_wrapper = QueueFileWrapper(self.q_in, self.q_out, self.VNF_PROMPT) name = "{}-{}-{}".format(self.name, self.APP_NAME, os.getpid()) self._vnf_process = Process(name=name, target=self._run) self._vnf_process.start() def _vnf_up_post(self): pass def instantiate(self, scenario_cfg, context_cfg): self.scenario_helper.scenario_cfg = scenario_cfg self.context_cfg = context_cfg self.nfvi_context = Context.get_context_from_server(self.scenario_helper.nodes[self.name]) # self.nfvi_context = None # vnf deploy is unsupported, use ansible playbooks if self.scenario_helper.options.get("vnf_deploy", False): self.deploy_helper.deploy_vnfs(self.APP_NAME) self.resource_helper.setup() self._start_vnf() def wait_for_instantiate(self): buf = [] time.sleep(self.WAIT_TIME) # Give some time for config to load while True: if not self._vnf_process.is_alive(): raise RuntimeError("%s VNF process died." % self.APP_NAME) # NOTE(esm): move to QueueFileWrapper while self.q_out.qsize() > 0: buf.append(self.q_out.get()) message = ''.join(buf) if self.VNF_PROMPT in message: LOG.info("%s VNF is up and running.", self.APP_NAME) self._vnf_up_post() self.queue_wrapper.clear() self.resource_helper.start_collect() return self._vnf_process.exitcode if "PANIC" in message: raise RuntimeError("Error starting %s VNF." % self.APP_NAME) LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME) time.sleep(self.WAIT_TIME_FOR_SCRIPT) # Send ENTER to display a new prompt in case the prompt text was corrupted # by other VNF output self.q_in.put('\r\n') def _build_run_kwargs(self): self.run_kwargs = { 'stdin': self.queue_wrapper, 'stdout': self.queue_wrapper, 'keep_stdin_open': True, 'pty': True, 'timeout': self.scenario_helper.timeout, } def _build_config(self): return self.setup_helper.build_config() def _run(self): # we can't share ssh paramiko objects to force new connection self.ssh_helper.drop_connection() cmd = self._build_config() # kill before starting self.setup_helper.kill_vnf() LOG.debug(cmd) self._build_run_kwargs() self.ssh_helper.run(cmd, **self.run_kwargs) def vnf_execute(self, cmd, wait_time=2): """ send cmd to vnf process """ LOG.info("%s command: %s", self.APP_NAME, cmd) self.q_in.put("{}\r\n".format(cmd)) time.sleep(wait_time) output = [] while self.q_out.qsize() > 0: output.append(self.q_out.get()) return "".join(output) def _tear_down(self): pass def terminate(self): self.vnf_execute("quit") 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() # no terminate children here because we share processes with tg 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 """ cmd = 'p {0} stats'.format(self.APP_WORD) out = self.vnf_execute(cmd) return out def collect_kpi(self): # we can't get KPIs if the VNF is down check_if_process_failed(self._vnf_process) stats = self.get_stats() m = re.search(self.COLLECT_KPI, stats, re.MULTILINE) if m: result = {k: int(m.group(v)) for k, v in self.COLLECT_MAP.items()} result["collect_stats"] = self.resource_helper.collect_kpi() else: result = { "packets_in": 0, "packets_fwd": 0, "packets_dropped": 0, } 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 SampleVNF(GenericVNF): """ Class providing file-like API for generic VNF implementation """ VNF_PROMPT = "pipeline>" WAIT_TIME = 1 def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None): super(SampleVNF, self).__init__(name, vnfd) self.bin_path = get_nsb_option('bin_path', '') self.scenario_helper = ScenarioHelper(self.name) self.ssh_helper = VnfSshHelper(self.vnfd_helper.mgmt_interface, self.bin_path) if setup_env_helper_type is None: setup_env_helper_type = SetupEnvHelper self.setup_helper = setup_env_helper_type(self.vnfd_helper, self.ssh_helper, self.scenario_helper) self.deploy_helper = SampleVNFDeployHelper(vnfd, self.ssh_helper) if resource_helper_type is None: resource_helper_type = ResourceHelper self.resource_helper = resource_helper_type(self.setup_helper) self.context_cfg = None self.nfvi_context = None self.pipeline_kwargs = {} self.uplink_ports = None self.downlink_ports = None # TODO(esm): make QueueFileWrapper invert-able so that we # never have to manage the queues self.q_in = Queue() self.q_out = Queue() self.queue_wrapper = None self.run_kwargs = {} self.used_drivers = {} self.vnf_port_pairs = None self._vnf_process = None def _build_ports(self): self._port_pairs = PortPairs(self.vnfd_helper.interfaces) self.networks = self._port_pairs.networks self.uplink_ports = self.vnfd_helper.port_nums(self._port_pairs.uplink_ports) self.downlink_ports = self.vnfd_helper.port_nums(self._port_pairs.downlink_ports) self.my_ports = self.vnfd_helper.port_nums(self._port_pairs.all_ports) def _get_route_data(self, route_index, route_type): route_iter = iter(self.vnfd_helper.vdu0.get('nd_route_tbl', [])) for _ in range(route_index): next(route_iter, '') return next(route_iter, {}).get(route_type, '') def _get_port0localip6(self): return_value = self._get_route_data(0, 'network') LOG.info("_get_port0localip6 : %s", return_value) return return_value def _get_port1localip6(self): return_value = self._get_route_data(1, 'network') LOG.info("_get_port1localip6 : %s", return_value) return return_value def _get_port0prefixlen6(self): return_value = self._get_route_data(0, 'netmask') LOG.info("_get_port0prefixlen6 : %s", return_value) return return_value def _get_port1prefixlen6(self): return_value = self._get_route_data(1, 'netmask') LOG.info("_get_port1prefixlen6 : %s", return_value) return return_value def _get_port0gateway6(self): return_value = self._get_route_data(0, 'network') LOG.info("_get_port0gateway6 : %s", return_value) return return_value def _get_port1gateway6(self): return_value = self._get_route_data(1, 'network') LOG.info("_get_port1gateway6 : %s", return_value) return return_value def _start_vnf(self): self.queue_wrapper = QueueFileWrapper(self.q_in, self.q_out, self.VNF_PROMPT) self._vnf_process = Process(target=self._run) self._vnf_process.start() def _vnf_up_post(self): pass def instantiate(self, scenario_cfg, context_cfg): self.scenario_helper.scenario_cfg = scenario_cfg self.context_cfg = context_cfg self.nfvi_context = Context.get_context_from_server(self.scenario_helper.nodes[self.name]) # self.nfvi_context = None self.deploy_helper.deploy_vnfs(self.APP_NAME) self.resource_helper.setup() self._start_vnf() def wait_for_instantiate(self): buf = [] time.sleep(self.WAIT_TIME) # Give some time for config to load while True: if not self._vnf_process.is_alive(): raise RuntimeError("%s VNF process died." % self.APP_NAME) # TODO(esm): move to QueueFileWrapper while self.q_out.qsize() > 0: buf.append(self.q_out.get()) message = ''.join(buf) if self.VNF_PROMPT in message: LOG.info("%s VNF is up and running.", self.APP_NAME) self._vnf_up_post() self.queue_wrapper.clear() self.resource_helper.start_collect() return self._vnf_process.exitcode if "PANIC" in message: raise RuntimeError("Error starting %s VNF." % self.APP_NAME) LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME) time.sleep(1) # Send ENTER to display a new prompt in case the prompt text was corrupted # by other VNF output self.q_in.put('\r\n') def _build_run_kwargs(self): self.run_kwargs = { 'stdin': self.queue_wrapper, 'stdout': self.queue_wrapper, 'keep_stdin_open': True, 'pty': True, } def _build_config(self): return self.setup_helper.build_config() def _run(self): # we can't share ssh paramiko objects to force new connection self.ssh_helper.drop_connection() cmd = self._build_config() # kill before starting self.setup_helper.kill_vnf() LOG.debug(cmd) self._build_run_kwargs() self.ssh_helper.run(cmd, **self.run_kwargs) def vnf_execute(self, cmd, wait_time=2): """ send cmd to vnf process """ LOG.info("%s command: %s", self.APP_NAME, cmd) self.q_in.put("{}\r\n".format(cmd)) time.sleep(wait_time) output = [] while self.q_out.qsize() > 0: output.append(self.q_out.get()) return "".join(output) def _tear_down(self): pass def terminate(self): self.vnf_execute("quit") if self._vnf_process: self._vnf_process.terminate() self.setup_helper.kill_vnf() self._tear_down() self.resource_helper.stop_collect() def get_stats(self, *args, **kwargs): """ Method for checking the statistics :return: VNF statistics """ cmd = 'p {0} stats'.format(self.APP_WORD) out = self.vnf_execute(cmd) return out def collect_kpi(self): stats = self.get_stats() m = re.search(self.COLLECT_KPI, stats, re.MULTILINE) if m: result = {k: int(m.group(v)) for k, v in self.COLLECT_MAP.items()} result["collect_stats"] = self.resource_helper.collect_kpi() else: result = { "packets_in": 0, "packets_fwd": 0, "packets_dropped": 0, } LOG.debug("%s collect KPIs %s", self.APP_NAME, result) return result