Example #1
0
    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
Example #2
0
    def __init__(self,
                 name,
                 vnfd,
                 setup_env_helper_type=None,
                 resource_helper_type=None):
        super(SampleVNFTrafficGen, 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,
                                       wait=True)

        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)

        if resource_helper_type is None:
            resource_helper_type = ClientResourceHelper

        self.resource_helper = resource_helper_type(self.setup_helper)

        self.runs_traffic = True
        self.traffic_finished = False
        self._tg_process = None
        self._traffic_process = None
Example #3
0
    def __init__(self, name, vnfd):
        super(VcmtsVNF, self).__init__(name, vnfd)
        self.name = name
        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)

        self.setup_helper = VcmtsdSetupEnvHelper(self.vnfd_helper,
                                                 self.ssh_helper,
                                                 self.scenario_helper)
Example #4
0
class SampleVNFTrafficGen(GenericTrafficGen):
    """ Class providing file-like API for generic traffic generator """

    APP_NAME = 'Sample'
    RUN_WAIT = 1

    def __init__(self, name, vnfd, task_id, setup_env_helper_type=None,
                 resource_helper_type=None):
        super(SampleVNFTrafficGen, 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, wait=True)

        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)

        if resource_helper_type is None:
            resource_helper_type = ClientResourceHelper

        self.resource_helper = resource_helper_type(self.setup_helper)

        self.runs_traffic = True
        self.traffic_finished = False
        self._tg_process = None
        self._traffic_process = None

    def _start_server(self):
        # we can't share ssh paramiko objects to force new connection
        self.ssh_helper.drop_connection()

    def instantiate(self, scenario_cfg, context_cfg):
        self.scenario_helper.scenario_cfg = scenario_cfg
        self.resource_helper.update_from_context(
            Context.get_context_from_server(self.scenario_helper.nodes[self.name]),
            self.scenario_helper.nodes[self.name]
        )

        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 _check_status(self):
        raise NotImplementedError

    def _wait_for_process(self):
        while True:
            if not self._tg_process.is_alive():
                raise RuntimeError("%s traffic generator process died." % self.APP_NAME)
            LOG.info("Waiting for %s TG Server to start.. ", self.APP_NAME)
            time.sleep(1)
            status = self._check_status()
            if status == 0:
                LOG.info("%s TG Server is up and running.", self.APP_NAME)
                return self._tg_process.exitcode

    def _traffic_runner(self, traffic_profile, mq_id):
        # always drop connections first thing in new processes
        # so we don't get paramiko errors
        self.ssh_helper.drop_connection()
        LOG.info("Starting %s client...", self.APP_NAME)
        self._mq_producer = self._setup_mq_producer(mq_id)
        self.resource_helper.run_traffic(traffic_profile, self._mq_producer)

    def run_traffic(self, traffic_profile):
        """ Generate traffic on the wire according to the given params.
        Method is non-blocking, returns immediately when traffic process
        is running. Mandatory.

        :param traffic_profile:
        :return: True/False
        """
        name = '{}-{}-{}-{}'.format(self.name, self.APP_NAME,
                                    traffic_profile.__class__.__name__,
                                    os.getpid())
        self._traffic_process = Process(
            name=name, target=self._traffic_runner,
            args=(traffic_profile, uuid.uuid1().int))
        self._traffic_process.start()
        # Wait for traffic process to start
        while self.resource_helper.client_started.value == 0:
            time.sleep(self.RUN_WAIT)
            # what if traffic process takes a few seconds to start?
            if not self._traffic_process.is_alive():
                break

    def collect_kpi(self):
        # check if the tg processes have exited
        physical_node = Context.get_physical_node_from_server(
            self.scenario_helper.nodes[self.name])

        result = {"physical_node": physical_node}
        for proc in (self._tg_process, self._traffic_process):
            check_if_process_failed(proc)

        result["collect_stats"] = self.resource_helper.collect_kpi()
        LOG.debug("%s collect KPIs %s", self.APP_NAME, result)
        return result

    def terminate(self):
        """ After this method finishes, all traffic processes should stop. Mandatory.

        :return: True/False
        """
        self.traffic_finished = True
        # we must kill client before we kill the server, or the client will raise exception
        if self._traffic_process is not None:
            # be proper and try to join before terminating
            LOG.debug("joining before terminate %s", self._traffic_process.name)
            self._traffic_process.join(constants.PROCESS_JOIN_TIMEOUT)
            self._traffic_process.terminate()
        if self._tg_process is not None:
            # be proper and try to join before terminating
            LOG.debug("joining before terminate %s", self._tg_process.name)
            self._tg_process.join(constants.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')
Example #5
0
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')
Example #6
0
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')