def test_read_status(self):
        conn = mock.Mock()
        conn.execute = mock.Mock(return_value=(0, self.EXAMPLE_OUTPUT, ''))
        conn.provision_tool = mock.Mock(return_value='path_to_tool')

        dpdk_bind_helper = DpdkBindHelper(conn)

        self.assertEquals(self.PARSED_EXAMPLE, dpdk_bind_helper.read_status())
    def test_bind(self):
        conn = mock.Mock()
        conn.execute = mock.Mock(return_value=(0, '', ''))
        conn.provision_tool = mock.Mock(
            return_value='/opt/nsb_bin/dpdk_nic_bind.py')

        dpdk_bind_helper = DpdkBindHelper(conn)
        dpdk_bind_helper.read_status = mock.Mock()

        dpdk_bind_helper.bind(['0000:00:03.0', '0000:00:04.0'], 'my_driver')

        conn.execute.assert_called_with(
            'sudo /opt/nsb_bin/dpdk_nic_bind.py --force '
            '-b my_driver 0000:00:03.0 0000:00:04.0')
        dpdk_bind_helper.read_status.assert_called_once()
Esempio n. 3
0
class DpdkVnfSetupEnvHelper(SetupEnvHelper):

    APP_NAME = 'DpdkVnf'
    FIND_NET_CMD = "find /sys/class/net -lname '*{}*' -printf '%f'"

    HW_DEFAULT_CORE = 3
    SW_DEFAULT_CORE = 2

    @staticmethod
    def _update_packet_type(ip_pipeline_cfg, traffic_options):
        match_str = 'pkt_type = ipv4'
        replace_str = 'pkt_type = {0}'.format(traffic_options['pkt_type'])
        pipeline_config_str = ip_pipeline_cfg.replace(match_str, replace_str)
        return pipeline_config_str

    @classmethod
    def _update_traffic_type(cls, ip_pipeline_cfg, traffic_options):
        traffic_type = traffic_options['traffic_type']

        if traffic_options['vnf_type'] is not cls.APP_NAME:
            match_str = 'traffic_type = 4'
            replace_str = 'traffic_type = {0}'.format(traffic_type)

        elif traffic_type == 4:
            match_str = 'pkt_type = ipv4'
            replace_str = 'pkt_type = ipv4'

        else:
            match_str = 'pkt_type = ipv4'
            replace_str = 'pkt_type = ipv6'

        pipeline_config_str = ip_pipeline_cfg.replace(match_str, replace_str)
        return pipeline_config_str

    def __init__(self, vnfd_helper, ssh_helper, scenario_helper):
        super(DpdkVnfSetupEnvHelper, self).__init__(vnfd_helper, ssh_helper, scenario_helper)
        self.all_ports = None
        self.bound_pci = None
        self.socket = None
        self.used_drivers = None
        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))

    def build_config(self):
        vnf_cfg = self.scenario_helper.vnf_cfg
        task_path = self.scenario_helper.task_path

        lb_count = vnf_cfg.get('lb_count', 3)
        lb_config = vnf_cfg.get('lb_config', 'SW')
        worker_config = vnf_cfg.get('worker_config', '1C/1T')
        worker_threads = vnf_cfg.get('worker_threads', 3)

        traffic_type = self.scenario_helper.all_options.get('traffic_type', 4)
        traffic_options = {
            'traffic_type': traffic_type,
            'pkt_type': 'ipv%s' % traffic_type,
            'vnf_type': self.VNF_TYPE,
        }

        config_tpl_cfg = find_relative_file(self.DEFAULT_CONFIG_TPL_CFG, task_path)
        config_basename = posixpath.basename(self.CFG_CONFIG)
        script_basename = posixpath.basename(self.CFG_SCRIPT)
        multiport = MultiPortConfig(self.scenario_helper.topology,
                                    config_tpl_cfg,
                                    config_basename,
                                    self.vnfd_helper,
                                    self.VNF_TYPE,
                                    lb_count,
                                    worker_threads,
                                    worker_config,
                                    lb_config,
                                    self.socket)

        multiport.generate_config()
        with open(self.CFG_CONFIG) as handle:
            new_config = handle.read()

        new_config = self._update_traffic_type(new_config, traffic_options)
        new_config = self._update_packet_type(new_config, traffic_options)

        self.ssh_helper.upload_config_file(config_basename, new_config)
        self.ssh_helper.upload_config_file(script_basename,
                                           multiport.generate_script(self.vnfd_helper))

        LOG.info("Provision and start the %s", self.APP_NAME)
        self._build_pipeline_kwargs()
        return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs)

    def _build_pipeline_kwargs(self):
        tool_path = self.ssh_helper.provision_tool(tool_file=self.APP_NAME)
        # count the number of actual ports in the list of pairs
        # remove duplicate ports
        # this is really a mapping from LINK ID to DPDK PMD ID
        # e.g. 0x110 maps LINK0 -> PMD_ID_1, LINK1 -> PMD_ID_2
        #      0x1010 maps LINK0 -> PMD_ID_1, LINK1 -> PMD_ID_3
        ports = self.vnfd_helper.port_pairs.all_ports
        port_nums = self.vnfd_helper.port_nums(ports)
        # create mask from all the dpdk port numbers
        ports_mask_hex = hex(sum(2 ** num for num in port_nums))
        self.pipeline_kwargs = {
            'cfg_file': self.CFG_CONFIG,
            'script': self.CFG_SCRIPT,
            'port_mask_hex': ports_mask_hex,
            '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.kill_vnf()
        # bind before _setup_resources so we can use dpdk_port_num
        self._detect_and_bind_drivers()
        resource = self._setup_resources()
        return resource

    def kill_vnf(self):
        # have to use exact match
        self.ssh_helper.execute("sudo pkill -x %s" % self.APP_NAME)

    def _setup_dpdk(self):
        """ 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)

    def get_collectd_options(self):
        options = self.scenario_helper.all_options.get("collectd", {})
        # override with specific node settings
        options.update(self.scenario_helper.options.get("collectd", {}))
        return options

    def _setup_resources(self):
        # what is this magic?  how do we know which socket is for which port?
        # what about quad-socket?
        if any(v[5] == "0" for v in self.bound_pci):
            self.socket = 0
        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)
        port_names = (intf["name"] for intf in ports)
        collectd_options = self.get_collectd_options()
        plugins = collectd_options.get("plugins", {})
        return ResourceProfile(self.vnfd_helper.mgmt_interface, port_names=port_names, cores=cores,
                               plugins=plugins, interval=collectd_options.get("interval"))

    def _detect_and_bind_drivers(self):
        interfaces = self.vnfd_helper.interfaces

        self.dpdk_bind_helper.read_status()
        self.dpdk_bind_helper.save_used_drivers()

        self.dpdk_bind_helper.bind(self.bound_pci, 'igb_uio')

        sorted_dpdk_pci_addresses = sorted(self.dpdk_bind_helper.dpdk_bound_pci_addresses)
        for dpdk_port_num, vpci in enumerate(sorted_dpdk_pci_addresses):
            try:
                intf = next(v for v in interfaces
                            if vpci == v['virtual-interface']['vpci'])
                # force to int
                intf['virtual-interface']['dpdk_port_num'] = int(dpdk_port_num)
            except:
                pass
        time.sleep(2)

    def get_local_iface_name_by_vpci(self, vpci):
        find_net_cmd = self.FIND_NET_CMD.format(vpci)
        exit_status, stdout, _ = self.ssh_helper.execute(find_net_cmd)
        if exit_status == 0:
            return stdout
        return None

    def tear_down(self):
        self.dpdk_bind_helper.rebind_drivers()