コード例 #1
0
ファイル: QemuUtils.py プロジェクト: preym17/csit
    def __init__(self, node, qemu_id=1, smp=1, mem=512, vnf=None,
                 img=Constants.QEMU_VM_IMAGE):
        """Initialize QemuUtil class.

        :param node: Node to run QEMU on.
        :param qemu_id: QEMU identifier.
        :param smp: Number of virtual SMP units (cores).
        :param mem: Amount of memory.
        :param vnf: Network function workload.
        :param img: QEMU disk image or kernel image path.
        :type node: dict
        :type qemu_id: int
        :type smp: int
        :type mem: int
        :type vnf: str
        :type img: str
        """
        self._vhost_id = 0
        self._node = node
        self._vm_info = {
            'host': node['host'],
            'type': NodeType.VM,
            'port': 10021 + qemu_id,
            'serial': 4555 + qemu_id,
            'username': '******',
            'password': '******',
            'interfaces': {},
        }
        if node['port'] != 22:
            self._vm_info['host_port'] = node['port']
            self._vm_info['host_username'] = node['username']
            self._vm_info['host_password'] = node['password']
        # Input Options.
        self._opt = dict()
        self._opt['qemu_id'] = qemu_id
        self._opt['mem'] = int(mem)
        self._opt['smp'] = int(smp)
        self._opt['img'] = img
        self._opt['vnf'] = vnf
        # Temporary files.
        self._temp = dict()
        self._temp['pidfile'] = '/var/run/qemu_{id}.pid'.format(id=qemu_id)
        if '/var/lib/vm/' in img:
            self._opt['vm_type'] = 'nestedvm'
            self._temp['qmp'] = '/var/run/qmp_{id}.sock'.format(id=qemu_id)
            self._temp['qga'] = '/var/run/qga_{id}.sock'.format(id=qemu_id)
        elif '/opt/boot/vmlinuz' in img:
            self._opt['vm_type'] = 'kernelvm'
            self._temp['log'] = '/tmp/serial_{id}.log'.format(id=qemu_id)
            self._temp['ini'] = '/etc/vm_init_{id}.conf'.format(id=qemu_id)
        else:
            raise RuntimeError('QEMU: Unknown VM image option!')
        # Computed parameters for QEMU command line.
        self._params = OptionString(prefix='-')
        self.add_params()
コード例 #2
0
    def get_testpmd_cmdline(**kwargs):
        """Get DPDK testpmd command line arguments with testpmd command.

        :param kwargs: Key-value testpmd parameters.
        :type kwargs: dict
        :returns: Command line string.
        :rtype: OptionString
        """
        options = OptionString()
        options.add(u"testpmd")
        options.extend(DpdkUtil.get_eal_options(**kwargs))
        options.add(u"--")
        options.extend(DpdkUtil.get_testpmd_pmd_options(**kwargs))
        return options
コード例 #3
0
    def dpdk_testpmd_start(node, **kwargs):
        """Start DPDK testpmd app on VM node.

        :param node: VM Node to start testpmd on.
        :param kwargs: Key-value testpmd parameters.
        :type node: dict
        :type kwargs: dict
        """
        cmd_options = OptionString()
        cmd_options.add(u"/start-testpmd.sh")
        cmd_options.extend(DpdkUtil.get_eal_options(**kwargs))
        cmd_options.add(u"--")
        cmd_options.extend(DpdkUtil.get_testpmd_pmd_options(**kwargs))
        exec_cmd_no_error(node, cmd_options, sudo=True, disconnect=True)
コード例 #4
0
ファイル: ssh.py プロジェクト: preym17/csit
    def exec_command_sudo(self, cmd, cmd_input=None, timeout=30,
                          log_stdout_err=True):
        """Execute SSH command with sudo on a new channel on the connected Node.

        :param cmd: Command to be executed.
        :param cmd_input: Input redirected to the command.
        :param timeout: Timeout.
        :param log_stdout_err: If True, stdout and stderr are logged.
        :type cmd: str
        :type cmd_input: str
        :type timeout: int
        :type log_stdout_err: bool
        :returns: return_code, stdout, stderr
        :rtype: tuple(int, str, str)

        :Example:

        >>> from ssh import SSH
        >>> ssh = SSH()
        >>> ssh.connect(node)
        >>> # Execute command without input (sudo -S cmd)
        >>> ssh.exec_command_sudo("ifconfig eth0 down")
        >>> # Execute command with input (sudo -S cmd <<< "input")
        >>> ssh.exec_command_sudo("vpp_api_test", "dump_interface_table")
        """
        if isinstance(cmd, (list, tuple)):
            cmd = OptionString(cmd)
        if cmd_input is None:
            command = 'sudo -S {c}'.format(c=cmd)
        else:
            command = 'sudo -S {c} <<< "{i}"'.format(c=cmd, i=cmd_input)
        return self.exec_command(command, timeout,
                                 log_stdout_err=log_stdout_err)
コード例 #5
0
    def get_l3fwd_args(**kwargs):
        """Get DPDK l3fwd command line arguments.

        :param kwargs: Key-value l3fwd parameters.
        :type kwargs: dict
        :returns: Command line string.
        :rtype: OptionString
        """
        options = OptionString()
        options.extend(DpdkUtil.get_eal_options(**kwargs))
        options.add(u"--")
        options.extend(DpdkUtil.get_l3fwd_pmd_options(**kwargs))
        return options
コード例 #6
0
    def get_l3fwd_pmd_options(**kwargs):
        """Create PMD parameters options for l3fwd (without --).

        :param kwargs: List of l3fwd parameters.
        :type kwargs: dict
        :returns: PMD parameters.
        :rtype: OptionString
        """
        options = OptionString(prefix=u"--")
        # Set to use software to analyze packet type.
        options.add_if_from_dict(u"parse-ptype", u"pmd_parse_ptype", kwargs,
                                 True)
        # Set the MAC address XX:XX:XX:XX:XX:XX of the peer port N.
        options.add_equals_from_dict(u"eth-dest", u"pmd_eth_dest_0", kwargs)
        options.add_equals_from_dict(u"eth-dest", u"pmd_eth_dest_1", kwargs)
        # Determines which queues from which ports are mapped to which cores.
        options.add_equals_from_dict(u"config", u"pmd_config", kwargs)
        # Set the max packet length.
        options.add_with_value_if_from_dict(u"max-pkt-len", u"9200",
                                            u"pmd_max_pkt_len", kwargs, False)
        return options
コード例 #7
0
    def exec_command_sudo(self,
                          cmd,
                          cmd_input=None,
                          timeout=30,
                          log_stdout_err=True,
                          export=True):
        """Execute SSH command with sudo on a new channel on the connected Node.

        :param cmd: Command to be executed.
        :param cmd_input: Input redirected to the command.
        :param timeout: Timeout.
        :param log_stdout_err: If True, stdout and stderr are logged.
        :param export: If false, do not attempt JSON export.
            Needed for calls outside Robot (e.g. from reservation script).
        :type cmd: str
        :type cmd_input: str
        :type timeout: int
        :type log_stdout_err: bool
        :type export: bool
        :returns: return_code, stdout, stderr
        :rtype: tuple(int, str, str)

        :Example:

        >>> from ssh import SSH
        >>> ssh = SSH()
        >>> ssh.connect(node)
        >>> # Execute command without input (sudo -S cmd)
        >>> ssh.exec_command_sudo(u"ifconfig eth0 down")
        >>> # Execute command with input (sudo -S cmd <<< 'input')
        >>> ssh.exec_command_sudo(u"vpp_api_test", u"dump_interface_table")
        """
        if isinstance(cmd, (list, tuple)):
            cmd = OptionString(cmd)
        if cmd_input is None:
            command = f"sudo -E -S {cmd}"
        else:
            command = f"sudo -E -S {cmd} <<< \"{cmd_input}\""
        return self.exec_command(command,
                                 timeout,
                                 log_stdout_err=log_stdout_err,
                                 export=export)
コード例 #8
0
ファイル: QemuUtils.py プロジェクト: yossihan/csit
    def qemu_start(self):
        """Start QEMU and wait until VM boot.

        :returns: VM node info.
        :rtype: dict
        """
        cmd_opts = OptionString()
        cmd_opts.add(f"{Constants.QEMU_BIN_PATH}/qemu-system-{self._arch}")
        cmd_opts.extend(self._params)
        message = f"QEMU: Start failed on {self._node[u'host']}!"
        try:
            DUTSetup.check_huge_page(
                self._node, u"/dev/hugepages", int(self._opt.get(u"mem")))

            exec_cmd_no_error(
                self._node, cmd_opts, timeout=300, sudo=True, message=message
            )
            self._wait_until_vm_boot()
        except RuntimeError:
            self.qemu_kill_all()
            raise
        return self._vm_info
コード例 #9
0
ファイル: QemuUtils.py プロジェクト: preym17/csit
    def qemu_start(self):
        """Start QEMU and wait until VM boot.

        :returns: VM node info.
        :rtype: dict
        """
        cmd_opts = OptionString()
        cmd_opts.add('{bin_path}/qemu-system-{arch}'.format(
            bin_path=Constants.QEMU_BIN_PATH,
            arch=Topology.get_node_arch(self._node)))
        cmd_opts.extend(self._params)
        message = ('QEMU: Start failed on {host}!'.
                   format(host=self._node['host']))
        try:
            DUTSetup.check_huge_page(
                self._node, '/dev/hugepages', self._opt.get('mem'))

            exec_cmd_no_error(
                self._node, cmd_opts, timeout=300, sudo=True, message=message)
            self._wait_until_vm_boot()
        except RuntimeError:
            self.qemu_kill_all()
            raise
        return self._vm_info
コード例 #10
0
ファイル: Iperf3.py プロジェクト: gvnn3/csit
    def iperf3_cmdline(**kwargs):
        """Get iperf_client driver command line.

        :param kwargs: List of iperf_client driver parameters.
        :type kwargs: dict
        :returns: iperf_client driver command line.
        :rtype: OptionString
        """
        cmd = OptionString()
        cmd.add(u"python3")
        dirname = f"{Constants.REMOTE_FW_DIR}/resources/tools/iperf"
        cmd.add(f"'{dirname}/iperf_client.py'")

        cmd_options = OptionString(prefix=u"--")
        # Namespace to execute iPerf3 client on.
        cmd_options.add_with_value_from_dict(u"namespace", u"namespace",
                                             kwargs)

        # Client connecting to an iPerf3 server running on host.
        cmd_options.add_with_value_from_dict(u"host", u"host", kwargs)

        # Client bind IP address.
        cmd_options.add_with_value_from_dict(u"bind", u"bind", kwargs)

        # Use UDP rather than TCP.
        cmd_options.add_if_from_dict(u"udp", u"udp", kwargs, False)

        # Set the CPU affinity, if possible.
        cmd_options.add_with_value_from_dict(u"affinity", u"affinity", kwargs)

        # Time expressed in seconds for how long to send traffic.
        cmd_options.add_with_value_from_dict(u"duration", u"duration", kwargs)

        # Send bi- (2) or uni- (1) directional traffic.
        cmd_options.add_with_value_from_dict(u"traffic_directions",
                                             u"traffic_directions", kwargs, 1)

        # Traffic warm-up time in seconds, (0=disable).
        cmd_options.add_with_value_from_dict(u"warmup_time", u"warmup_time",
                                             kwargs, 5.0)

        # L2 frame size to send (without padding and IPG).
        cmd_options.add_with_value_from_dict(u"frame_size", u"frame_size",
                                             kwargs)

        # Traffic rate expressed with units.
        cmd_options.add_with_value_from_dict(u"rate", u"rate", kwargs)

        # If enabled then don't wait for all incoming traffic.
        cmd_options.add_if_from_dict(u"async_start", u"async_call", kwargs,
                                     False)

        # Number of iPerf3 client parallel instances.
        cmd_options.add_with_value_from_dict(u"instances", u"instances",
                                             kwargs, 1)

        # Number of iPerf3 client parallel flows.
        cmd_options.add_with_value_from_dict(u"parallel", u"parallel", kwargs,
                                             8)

        return cmd.extend(cmd_options)
コード例 #11
0
    def get_cmd_options(**kwargs):
        """Create  parameters options.

        :param kwargs: Dict of cmd parameters.
        :type kwargs: dict
        :returns: Cmd parameters.
        :rtype: OptionString
        """
        cmd = OptionString()
        cmd.add(u"python3")
        dirname = f"{Constants.REMOTE_FW_DIR}/resources/tools/ab"
        cmd.add(f"{dirname}/ABFork.py")
        cmd_options = OptionString(prefix=u"-")
        # Number of requests to perform.
        cmd_options.add_with_value_from_dict(u"r", u"requests", kwargs)
        # Server port number to use.
        cmd_options.add_with_value_from_dict(u"p", u"port", kwargs)
        # Number of clients being processed at the same time.
        cmd_options.add_with_value_from_dict(u"c", u"clients", kwargs)
        # Filename to be requested from the servers.
        cmd_options.add_with_value_from_dict(u"f", u"files", kwargs)
        # Server ip address.
        cmd_options.add_with_value_from_dict(u"i", u"ip", kwargs)
        # tg ip address.
        cmd_options.add_with_value_from_dict(u"g", u"tip", kwargs)
        # Specify SSL/TLS cipher suite.
        cmd_options.add_with_value_from_dict(u"z",
                                             u"cipher",
                                             kwargs,
                                             default=0)
        # Specify SSL/TLS protocol.
        cmd_options.add_with_value_from_dict(u"t",
                                             u"protocol",
                                             kwargs,
                                             default=0)
        # Mode: RPS or CPS.
        cmd_options.add_with_value_from_dict(u"m", u"mode", kwargs)
        return cmd.extend(cmd_options)
コード例 #12
0
ファイル: QemuUtils.py プロジェクト: nidhyanandhan/csit
    def __init__(self,
                 node,
                 qemu_id=1,
                 smp=1,
                 mem=512,
                 vnf=None,
                 img=Constants.QEMU_VM_IMAGE):
        """Initialize QemuUtil class.

        :param node: Node to run QEMU on.
        :param qemu_id: QEMU identifier.
        :param smp: Number of virtual SMP units (cores).
        :param mem: Amount of memory.
        :param vnf: Network function workload.
        :param img: QEMU disk image or kernel image path.
        :type node: dict
        :type qemu_id: int
        :type smp: int
        :type mem: int
        :type vnf: str
        :type img: str
        """
        self._nic_id = 0
        self._node = node
        self._arch = Topology.get_node_arch(self._node)
        self._opt = dict()

        # Architecture specific options
        if self._arch == u"aarch64":
            dpdk_target = u"arm64-armv8a"
            self._opt[u"machine_args"] = \
                u"virt,accel=kvm,usb=off,mem-merge=off,gic-version=3"
            self._opt[u"console"] = u"ttyAMA0"
        else:
            dpdk_target = u"x86_64-native"
            self._opt[u"machine_args"] = u"pc,accel=kvm,usb=off,mem-merge=off"
            self._opt[u"console"] = u"ttyS0"
        self._testpmd_path = f"{Constants.QEMU_VM_DPDK}/" \
            f"{dpdk_target}-linux-gcc/app"
        self._vm_info = {
            u"host": node[u"host"],
            u"type": NodeType.VM,
            u"port": 10021 + qemu_id,
            u"serial": 4555 + qemu_id,
            u"username": '******',
            u"password": '******',
            u"interfaces": {},
        }
        if node[u"port"] != 22:
            self._vm_info[u"host_port"] = node[u"port"]
            self._vm_info[u"host_username"] = node[u"username"]
            self._vm_info[u"host_password"] = node[u"password"]
        # Input Options.
        self._opt[u"qemu_id"] = qemu_id
        self._opt[u"mem"] = int(mem)
        self._opt[u"smp"] = int(smp)
        self._opt[u"img"] = img
        self._opt[u"vnf"] = vnf
        # Temporary files.
        self._temp = dict()
        self._temp[u"log"] = f"/tmp/serial_{qemu_id}.log"
        self._temp[u"pidfile"] = f"/run/qemu_{qemu_id}.pid"
        if img == Constants.QEMU_VM_IMAGE:
            self._temp[u"qmp"] = f"/run/qmp_{qemu_id}.sock"
            self._temp[u"qga"] = f"/run/qga_{qemu_id}.sock"
        elif img == Constants.QEMU_VM_KERNEL:
            self._opt[u"img"], _ = exec_cmd_no_error(
                node,
                f"ls -1 {Constants.QEMU_VM_KERNEL}* | tail -1",
                message=u"Qemu Kernel VM image not found!")
            self._temp[u"ini"] = f"/etc/vm_init_{qemu_id}.conf"
            self._opt[u"initrd"], _ = exec_cmd_no_error(
                node,
                f"ls -1 {Constants.QEMU_VM_KERNEL_INITRD}* | tail -1",
                message=u"Qemu Kernel initrd image not found!")
        else:
            raise RuntimeError(f"QEMU: Unknown VM image option: {img}")
        # Computed parameters for QEMU command line.
        self._params = OptionString(prefix=u"-")
コード例 #13
0
ファイル: QemuUtils.py プロジェクト: nidhyanandhan/csit
class QemuUtils:
    """QEMU utilities."""

    # Use one instance of class per tests.
    ROBOT_LIBRARY_SCOPE = u"TEST CASE"

    def __init__(self,
                 node,
                 qemu_id=1,
                 smp=1,
                 mem=512,
                 vnf=None,
                 img=Constants.QEMU_VM_IMAGE):
        """Initialize QemuUtil class.

        :param node: Node to run QEMU on.
        :param qemu_id: QEMU identifier.
        :param smp: Number of virtual SMP units (cores).
        :param mem: Amount of memory.
        :param vnf: Network function workload.
        :param img: QEMU disk image or kernel image path.
        :type node: dict
        :type qemu_id: int
        :type smp: int
        :type mem: int
        :type vnf: str
        :type img: str
        """
        self._nic_id = 0
        self._node = node
        self._arch = Topology.get_node_arch(self._node)
        self._opt = dict()

        # Architecture specific options
        if self._arch == u"aarch64":
            dpdk_target = u"arm64-armv8a"
            self._opt[u"machine_args"] = \
                u"virt,accel=kvm,usb=off,mem-merge=off,gic-version=3"
            self._opt[u"console"] = u"ttyAMA0"
        else:
            dpdk_target = u"x86_64-native"
            self._opt[u"machine_args"] = u"pc,accel=kvm,usb=off,mem-merge=off"
            self._opt[u"console"] = u"ttyS0"
        self._testpmd_path = f"{Constants.QEMU_VM_DPDK}/" \
            f"{dpdk_target}-linux-gcc/app"
        self._vm_info = {
            u"host": node[u"host"],
            u"type": NodeType.VM,
            u"port": 10021 + qemu_id,
            u"serial": 4555 + qemu_id,
            u"username": '******',
            u"password": '******',
            u"interfaces": {},
        }
        if node[u"port"] != 22:
            self._vm_info[u"host_port"] = node[u"port"]
            self._vm_info[u"host_username"] = node[u"username"]
            self._vm_info[u"host_password"] = node[u"password"]
        # Input Options.
        self._opt[u"qemu_id"] = qemu_id
        self._opt[u"mem"] = int(mem)
        self._opt[u"smp"] = int(smp)
        self._opt[u"img"] = img
        self._opt[u"vnf"] = vnf
        # Temporary files.
        self._temp = dict()
        self._temp[u"log"] = f"/tmp/serial_{qemu_id}.log"
        self._temp[u"pidfile"] = f"/run/qemu_{qemu_id}.pid"
        if img == Constants.QEMU_VM_IMAGE:
            self._temp[u"qmp"] = f"/run/qmp_{qemu_id}.sock"
            self._temp[u"qga"] = f"/run/qga_{qemu_id}.sock"
        elif img == Constants.QEMU_VM_KERNEL:
            self._opt[u"img"], _ = exec_cmd_no_error(
                node,
                f"ls -1 {Constants.QEMU_VM_KERNEL}* | tail -1",
                message=u"Qemu Kernel VM image not found!")
            self._temp[u"ini"] = f"/etc/vm_init_{qemu_id}.conf"
            self._opt[u"initrd"], _ = exec_cmd_no_error(
                node,
                f"ls -1 {Constants.QEMU_VM_KERNEL_INITRD}* | tail -1",
                message=u"Qemu Kernel initrd image not found!")
        else:
            raise RuntimeError(f"QEMU: Unknown VM image option: {img}")
        # Computed parameters for QEMU command line.
        self._params = OptionString(prefix=u"-")

    def add_default_params(self):
        """Set default QEMU command line parameters."""
        self._params.add(u"daemonize")
        self._params.add(u"nodefaults")
        self._params.add_with_value(
            u"name", f"vnf{self._opt.get(u'qemu_id')},debug-threads=on")
        self._params.add(u"no-user-config")
        self._params.add(u"nographic")
        self._params.add(u"enable-kvm")
        self._params.add_with_value(u"pidfile", self._temp.get(u"pidfile"))
        self._params.add_with_value(u"cpu", u"host")

        self._params.add_with_value(u"machine", self._opt.get(u"machine_args"))
        self._params.add_with_value(
            u"smp", f"{self._opt.get(u'smp')},sockets=1,"
            f"cores={self._opt.get(u'smp')},threads=1")
        self._params.add_with_value(
            u"object", f"memory-backend-file,id=mem,"
            f"size={self._opt.get(u'mem')}M,mem-path=/dev/hugepages,share=on")
        self._params.add_with_value(u"m", f"{self._opt.get(u'mem')}M")
        self._params.add_with_value(u"numa", u"node,memdev=mem")
        self._params.add_with_value(u"balloon", u"none")

    def add_net_user(self):
        """Set managment port forwarding."""
        self._params.add_with_value(
            u"netdev", f"user,id=mgmt,net=172.16.255.0/24,"
            f"hostfwd=tcp::{self._vm_info[u'port']}-:22")
        self._params.add_with_value(u"device", f"virtio-net,netdev=mgmt")

    def add_qmp_qga(self):
        """Set QMP, QGA management."""
        self._params.add_with_value(
            u"chardev", f"socket,path={self._temp.get(u'qga')},"
            f"server,nowait,id=qga0")
        self._params.add_with_value(u"device", u"isa-serial,chardev=qga0")
        self._params.add_with_value(
            u"qmp", f"unix:{self._temp.get(u'qmp')},server,nowait")

    def add_serial(self):
        """Set serial to file redirect."""
        self._params.add_with_value(
            u"chardev", f"socket,host=127.0.0.1,"
            f"port={self._vm_info[u'serial']},id=gnc0,server,nowait")
        self._params.add_with_value(u"device", u"isa-serial,chardev=gnc0")
        self._params.add_with_value(u"serial",
                                    f"file:{self._temp.get(u'log')}")

    def add_drive_cdrom(self, drive_file, index=None):
        """Set CD-ROM drive.

        :param drive_file: Path to drive image.
        :param index: Drive index.
        :type drive_file: str
        :type index: int
        """
        index = f"index={index}," if index else u""
        self._params.add_with_value(u"drive",
                                    f"file={drive_file},{index}media=cdrom")

    def add_drive(self, drive_file, drive_format):
        """Set drive with custom format.

        :param drive_file: Path to drive image.
        :param drive_format: Drive image format.
        :type drive_file: str
        :type drive_format: str
        """
        self._params.add_with_value(
            u"drive", f"file={drive_file},format={drive_format},"
            u"cache=none,if=virtio,file.locking=off")

    def add_kernelvm_params(self):
        """Set KernelVM QEMU parameters."""
        self._params.add_with_value(u"serial",
                                    f"file:{self._temp.get(u'log')}")
        self._params.add_with_value(
            u"fsdev", u"local,id=root9p,path=/,security_model=none")
        self._params.add_with_value(
            u"device", u"virtio-9p-pci,fsdev=root9p,mount_tag=virtioroot")
        self._params.add_with_value(u"kernel", f"{self._opt.get(u'img')}")
        self._params.add_with_value(u"initrd", f"{self._opt.get(u'initrd')}")
        self._params.add_with_value(
            u"append", f"'ro rootfstype=9p rootflags=trans=virtio "
            f"root=virtioroot console={self._opt.get(u'console')} "
            f"tsc=reliable hugepages=512 "
            f"init={self._temp.get(u'ini')} fastboot'")

    def add_vhost_user_if(self,
                          socket,
                          server=True,
                          jumbo_frames=False,
                          queue_size=None,
                          queues=1,
                          csum=False,
                          gso=False):
        """Add Vhost-user interface.

        :param socket: Path of the unix socket.
        :param server: If True the socket shall be a listening socket.
        :param jumbo_frames: Set True if jumbo frames are used in the test.
        :param queue_size: Vring queue size.
        :param queues: Number of queues.
        :param csum: Checksum offloading.
        :param gso: Generic segmentation offloading.
        :type socket: str
        :type server: bool
        :type jumbo_frames: bool
        :type queue_size: int
        :type queues: int
        :type csum: bool
        :type gso: bool
        """
        self._nic_id += 1
        self._params.add_with_value(
            u"chardev", f"socket,id=char{self._nic_id},"
            f"path={socket}{u',server' if server is True else u''}")
        self._params.add_with_value(
            u"netdev", f"vhost-user,id=vhost{self._nic_id},"
            f"chardev=char{self._nic_id},queues={queues}")
        mac = f"52:54:00:00:{self._opt.get(u'qemu_id'):02x}:" \
            f"{self._nic_id:02x}"
        queue_size = f"rx_queue_size={queue_size},tx_queue_size={queue_size}" \
            if queue_size else u""
        self._params.add_with_value(
            u"device", f"virtio-net-pci,netdev=vhost{self._nic_id},mac={mac},"
            f"addr={self._nic_id+5}.0,mq=on,vectors={2 * queues + 2},"
            f"csum={u'on' if csum else u'off'},gso={u'on' if gso else u'off'},"
            f"guest_tso4=off,guest_tso6=off,guest_ecn=off,"
            f"{queue_size}")

        # Add interface MAC and socket to the node dict.
        if_data = {u"mac_address": mac, u"socket": socket}
        if_name = f"vhost{self._nic_id}"
        self._vm_info[u"interfaces"][if_name] = if_data
        # Add socket to temporary file list.
        self._temp[if_name] = socket

    def add_vfio_pci_if(self, pci):
        """Add VFIO PCI interface.

        :param pci: PCI address of interface.
        :type pci: str
        """
        self._nic_id += 1
        self._params.add_with_value(
            u"device", f"vfio-pci,host={pci},addr={self._nic_id+5}.0")

    def create_kernelvm_config_vpp(self, **kwargs):
        """Create QEMU VPP config files.

        :param kwargs: Key-value pairs to replace content of VPP configuration
            file.
        :type kwargs: dict
        """
        startup = f"/etc/vpp/vm_startup_{self._opt.get(u'qemu_id')}.conf"
        running = f"/etc/vpp/vm_running_{self._opt.get(u'qemu_id')}.exec"

        self._temp[u"startup"] = startup
        self._temp[u"running"] = running
        self._opt[u"vnf_bin"] = f"/usr/bin/vpp -c {startup}"

        # Create VPP startup configuration.
        vpp_config = VppConfigGenerator()
        vpp_config.set_node(self._node)
        vpp_config.add_unix_nodaemon()
        vpp_config.add_unix_cli_listen()
        vpp_config.add_unix_exec(running)
        vpp_config.add_socksvr()
        vpp_config.add_main_heap_size(u"512M")
        vpp_config.add_main_heap_page_size(u"2M")
        vpp_config.add_statseg_size(u"512M")
        vpp_config.add_statseg_page_size(u"2M")
        vpp_config.add_statseg_per_node_counters(u"on")
        vpp_config.add_buffers_per_numa(107520)
        vpp_config.add_cpu_main_core(u"0")
        if self._opt.get(u"smp") > 1:
            vpp_config.add_cpu_corelist_workers(f"1-{self._opt.get(u'smp')-1}")
        vpp_config.add_plugin(u"disable", u"default")
        vpp_config.add_plugin(u"enable", u"ping_plugin.so")
        if "2vfpt" in self._opt.get(u'vnf'):
            vpp_config.add_plugin(u"enable", u"avf_plugin.so")
        if "vhost" in self._opt.get(u'vnf'):
            vpp_config.add_plugin(u"enable", u"dpdk_plugin.so")
            vpp_config.add_dpdk_dev(u"0000:00:06.0", u"0000:00:07.0")
            vpp_config.add_dpdk_dev_default_rxq(kwargs[u"queues"])
            vpp_config.add_dpdk_log_level(u"debug")
            if not kwargs[u"jumbo_frames"]:
                vpp_config.add_dpdk_no_multi_seg()
                vpp_config.add_dpdk_no_tx_checksum_offload()
        if "ipsec" in self._opt.get(u'vnf'):
            vpp_config.add_plugin(u"enable", u"crypto_native_plugin.so")
            vpp_config.add_plugin(u"enable", u"crypto_ipsecmb_plugin.so")
            vpp_config.add_plugin(u"enable", u"crypto_openssl_plugin.so")
        if "nat" in self._opt.get(u'vnf'):
            vpp_config.add_nat(value=u"endpoint-dependent")
            vpp_config.add_plugin(u"enable", u"nat_plugin.so")
        vpp_config.write_config(startup)

        # Create VPP running configuration.
        template = f"{Constants.RESOURCES_TPL}/vm/{self._opt.get(u'vnf')}.exec"
        exec_cmd_no_error(self._node, f"rm -f {running}", sudo=True)

        with open(template, u"rt") as src_file:
            src = Template(src_file.read())
            exec_cmd_no_error(
                self._node, f"echo '{src.safe_substitute(**kwargs)}' | "
                f"sudo tee {running}")

    def create_kernelvm_config_testpmd_io(self, **kwargs):
        """Create QEMU testpmd-io command line.

        :param kwargs: Key-value pairs to construct command line parameters.
        :type kwargs: dict
        """
        pmd_max_pkt_len = u"9200" if kwargs[u"jumbo_frames"] else u"1518"
        testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
            eal_corelist=f"0-{self._opt.get(u'smp') - 1}",
            eal_driver=False,
            eal_pci_whitelist0=u"0000:00:06.0",
            eal_pci_whitelist1=u"0000:00:07.0",
            eal_in_memory=True,
            pmd_num_mbufs=16384,
            pmd_fwd_mode=u"io",
            pmd_nb_ports=u"2",
            pmd_portmask=u"0x3",
            pmd_max_pkt_len=pmd_max_pkt_len,
            pmd_mbuf_size=u"16384",
            pmd_rxq=kwargs[u"queues"],
            pmd_txq=kwargs[u"queues"],
            pmd_tx_offloads='0x0',
            pmd_nb_cores=str(self._opt.get(u"smp") - 1))

        self._opt[u"vnf_bin"] = f"{self._testpmd_path}/{testpmd_cmd}"

    def create_kernelvm_config_testpmd_mac(self, **kwargs):
        """Create QEMU testpmd-mac command line.

        :param kwargs: Key-value pairs to construct command line parameters.
        :type kwargs: dict
        """
        pmd_max_pkt_len = u"9200" if kwargs[u"jumbo_frames"] else u"1518"
        testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
            eal_corelist=f"0-{self._opt.get(u'smp') - 1}",
            eal_driver=False,
            eal_pci_whitelist0=u"0000:00:06.0",
            eal_pci_whitelist1=u"0000:00:07.0",
            eal_in_memory=True,
            pmd_num_mbufs=16384,
            pmd_fwd_mode=u"mac",
            pmd_nb_ports=u"2",
            pmd_portmask=u"0x3",
            pmd_max_pkt_len=pmd_max_pkt_len,
            pmd_mbuf_size=u"16384",
            pmd_eth_peer_0=f"0,{kwargs[u'vif1_mac']}",
            pmd_eth_peer_1=f"1,{kwargs[u'vif2_mac']}",
            pmd_rxq=kwargs[u"queues"],
            pmd_txq=kwargs[u"queues"],
            pmd_tx_offloads=u"0x0",
            pmd_nb_cores=str(self._opt.get(u"smp") - 1))

        self._opt[u"vnf_bin"] = f"{self._testpmd_path}/{testpmd_cmd}"

    def create_kernelvm_init(self, **kwargs):
        """Create QEMU init script.

        :param kwargs: Key-value pairs to replace content of init startup file.
        :type kwargs: dict
        """
        template = f"{Constants.RESOURCES_TPL}/vm/init.sh"
        init = self._temp.get(u"ini")
        exec_cmd_no_error(self._node, f"rm -f {init}", sudo=True)

        with open(template, u"rt") as src_file:
            src = Template(src_file.read())
            exec_cmd_no_error(
                self._node, f"echo '{src.safe_substitute(**kwargs)}' | "
                f"sudo tee {init}")
            exec_cmd_no_error(self._node, f"chmod +x {init}", sudo=True)

    def configure_kernelvm_vnf(self, **kwargs):
        """Create KernelVM VNF configurations.

        :param kwargs: Key-value pairs for templating configs.
        :type kwargs: dict
        """
        if u"vpp" in self._opt.get(u"vnf"):
            self.create_kernelvm_config_vpp(**kwargs)
            self.create_kernelvm_init(vnf_bin=self._opt.get(u"vnf_bin"))
        elif u"testpmd_io" in self._opt.get(u"vnf"):
            self.create_kernelvm_config_testpmd_io(**kwargs)
            self.create_kernelvm_init(vnf_bin=self._opt.get(u"vnf_bin"))
        elif u"testpmd_mac" in self._opt.get(u"vnf"):
            self.create_kernelvm_config_testpmd_mac(**kwargs)
            self.create_kernelvm_init(vnf_bin=self._opt.get(u"vnf_bin"))
        else:
            raise RuntimeError(u"QEMU: Unsupported VNF!")

    def get_qemu_pids(self):
        """Get QEMU CPU pids.

        :returns: List of QEMU CPU pids.
        :rtype: list of str
        """
        command = f"grep -rwl 'CPU' /proc/$(sudo cat " \
            f"{self._temp.get(u'pidfile')})/task/*/comm "
        command += r"| xargs dirname | sed -e 's/\/.*\///g' | uniq"

        stdout, _ = exec_cmd_no_error(self._node, command)
        return stdout.splitlines()

    def qemu_set_affinity(self, *host_cpus):
        """Set qemu affinity by getting thread PIDs via QMP and taskset to list
        of CPU cores. Function tries to execute 3 times to avoid race condition
        in getting thread PIDs.

        :param host_cpus: List of CPU cores.
        :type host_cpus: list
        """
        for _ in range(3):
            try:
                qemu_cpus = self.get_qemu_pids()

                if len(qemu_cpus) != len(host_cpus):
                    sleep(1)
                    continue
                for qemu_cpu, host_cpu in zip(qemu_cpus, host_cpus):
                    command = f"taskset -pc {host_cpu} {qemu_cpu}"
                    message = f"QEMU: Set affinity failed " \
                        f"on {self._node[u'host']}!"
                    exec_cmd_no_error(self._node,
                                      command,
                                      sudo=True,
                                      message=message)
                break
            except (RuntimeError, ValueError):
                self.qemu_kill_all()
                raise
        else:
            self.qemu_kill_all()
            raise RuntimeError(u"Failed to set Qemu threads affinity!")

    def qemu_set_scheduler_policy(self):
        """Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU
        processes.

        :raises RuntimeError: Set scheduler policy failed.
        """
        try:
            qemu_cpus = self.get_qemu_pids()

            for qemu_cpu in qemu_cpus:
                command = f"chrt -r -p 1 {qemu_cpu}"
                message = f"QEMU: Set SCHED_RR failed on {self._node[u'host']}"
                exec_cmd_no_error(self._node,
                                  command,
                                  sudo=True,
                                  message=message)
        except (RuntimeError, ValueError):
            self.qemu_kill_all()
            raise

    def _qemu_qmp_exec(self, cmd):
        """Execute QMP command.

        QMP is JSON based protocol which allows to control QEMU instance.

        :param cmd: QMP command to execute.
        :type cmd: str
        :returns: Command output in python representation of JSON format. The
            { "return": {} } response is QMP's success response. An error
            response will contain the "error" keyword instead of "return".
        """
        # To enter command mode, the qmp_capabilities command must be issued.
        command = f"echo \"{{{{ \\\"execute\\\": " \
            f"\\\"qmp_capabilities\\\" }}}}" \
            f"{{{{ \\\"execute\\\": \\\"{cmd}\\\" }}}}\" | " \
            f"sudo -S socat - UNIX-CONNECT:{self._temp.get(u'qmp')}"
        message = f"QMP execute '{cmd}' failed on {self._node[u'host']}"

        stdout, _ = exec_cmd_no_error(self._node,
                                      command,
                                      sudo=False,
                                      message=message)

        # Skip capabilities negotiation messages.
        out_list = stdout.splitlines()
        if len(out_list) < 3:
            raise RuntimeError(f"Invalid QMP output on {self._node[u'host']}")
        return json.loads(out_list[2])

    def _qemu_qga_flush(self):
        """Flush the QGA parser state."""
        command = f"(printf \"\xFF\"; sleep 1) | sudo -S socat " \
            f"- UNIX-CONNECT:{self._temp.get(u'qga')}"
        message = f"QGA flush failed on {self._node[u'host']}"
        stdout, _ = exec_cmd_no_error(self._node,
                                      command,
                                      sudo=False,
                                      message=message)

        return json.loads(stdout.split(u"\n", 1)[0]) if stdout else dict()

    def _qemu_qga_exec(self, cmd):
        """Execute QGA command.

        QGA provide access to a system-level agent via standard QMP commands.

        :param cmd: QGA command to execute.
        :type cmd: str
        """
        command = f"(echo \"{{{{ \\\"execute\\\": " \
            f"\\\"{cmd}\\\" }}}}\"; sleep 1) | " \
            f"sudo -S socat - UNIX-CONNECT:{self._temp.get(u'qga')}"
        message = f"QGA execute '{cmd}' failed on {self._node[u'host']}"
        stdout, _ = exec_cmd_no_error(self._node,
                                      command,
                                      sudo=False,
                                      message=message)

        return json.loads(stdout.split(u"\n", 1)[0]) if stdout else dict()

    def _wait_until_vm_boot(self):
        """Wait until QEMU VM is booted."""
        try:
            getattr(self, f'_wait_{self._opt["vnf"]}')()
        except AttributeError:
            self._wait_default()

    def _wait_default(self, retries=60):
        """Wait until QEMU with VPP is booted.

        :param retries: Number of retries.
        :type retries: int
        """
        for _ in range(retries):
            command = f"tail -1 {self._temp.get(u'log')}"
            stdout = None
            try:
                stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
                sleep(1)
            except RuntimeError:
                pass
            if "vpp " in stdout and "built by" in stdout:
                break
            if u"Press enter to exit" in stdout:
                break
            if u"reboot: Power down" in stdout:
                raise RuntimeError(
                    f"QEMU: NF failed to run on {self._node[u'host']}!")
        else:
            raise RuntimeError(
                f"QEMU: Timeout, VM not booted on {self._node[u'host']}!")

    def _wait_nestedvm(self, retries=12):
        """Wait until QEMU with NestedVM is booted.

        First try to flush qga until there is output.
        Then ping QEMU guest agent each 5s until VM booted or timeout.

        :param retries: Number of retries with 5s between trials.
        :type retries: int
        """
        for _ in range(retries):
            out = None
            try:
                out = self._qemu_qga_flush()
            except ValueError:
                logger.trace(f"QGA qga flush unexpected output {out}")
            # Empty output - VM not booted yet
            if not out:
                sleep(5)
            else:
                break
        else:
            raise RuntimeError(
                f"QEMU: Timeout, VM not booted on {self._node[u'host']}!")
        for _ in range(retries):
            out = None
            try:
                out = self._qemu_qga_exec(u"guest-ping")
            except ValueError:
                logger.trace(f"QGA guest-ping unexpected output {out}")
            # Empty output - VM not booted yet.
            if not out:
                sleep(5)
            # Non-error return - VM booted.
            elif out.get(u"return") is not None:
                break
            # Skip error and wait.
            elif out.get(u"error") is not None:
                sleep(5)
            else:
                # If there is an unexpected output from QGA guest-info, try
                # again until timeout.
                logger.trace(f"QGA guest-ping unexpected output {out}")
        else:
            raise RuntimeError(
                f"QEMU: Timeout, VM not booted on {self._node[u'host']}!")

    def _update_vm_interfaces(self):
        """Update interface names in VM node dict."""
        # Send guest-network-get-interfaces command via QGA, output example:
        # {"return": [{"name": "eth0", "hardware-address": "52:54:00:00:04:01"},
        # {"name": "eth1", "hardware-address": "52:54:00:00:04:02"}]}.
        out = self._qemu_qga_exec(u"guest-network-get-interfaces")
        interfaces = out.get(u"return")
        mac_name = {}
        if not interfaces:
            raise RuntimeError(
                f"Get VM interface list failed on {self._node[u'host']}")
        # Create MAC-name dict.
        for interface in interfaces:
            if u"hardware-address" not in interface:
                continue
            mac_name[interface[u"hardware-address"]] = interface[u"name"]
        # Match interface by MAC and save interface name.
        for interface in self._vm_info[u"interfaces"].values():
            mac = interface.get(u"mac_address")
            if_name = mac_name.get(mac)
            if if_name is None:
                logger.trace(f"Interface name for MAC {mac} not found")
            else:
                interface[u"name"] = if_name

    def qemu_start(self):
        """Start QEMU and wait until VM boot.

        :returns: VM node info.
        :rtype: dict
        """
        cmd_opts = OptionString()
        cmd_opts.add(f"{Constants.QEMU_BIN_PATH}/qemu-system-{self._arch}")
        cmd_opts.extend(self._params)
        message = f"QEMU: Start failed on {self._node[u'host']}!"
        try:
            DUTSetup.check_huge_page(self._node, u"/dev/hugepages",
                                     int(self._opt.get(u"mem")))

            exec_cmd_no_error(self._node,
                              cmd_opts,
                              timeout=300,
                              sudo=True,
                              message=message)
            self._wait_until_vm_boot()
        except RuntimeError:
            self.qemu_kill_all()
            raise
        return self._vm_info

    def qemu_kill(self):
        """Kill qemu process."""
        exec_cmd(self._node,
                 f"chmod +r {self._temp.get(u'pidfile')}",
                 sudo=True)
        exec_cmd(self._node,
                 f"kill -SIGKILL $(cat {self._temp.get(u'pidfile')})",
                 sudo=True)

        for value in self._temp.values():
            exec_cmd(self._node, f"cat {value}", sudo=True)
            exec_cmd(self._node, f"rm -f {value}", sudo=True)

    def qemu_kill_all(self):
        """Kill all qemu processes on DUT node if specified."""
        exec_cmd(self._node, u"pkill -SIGKILL qemu", sudo=True)

        for value in self._temp.values():
            exec_cmd(self._node, f"cat {value}", sudo=True)
            exec_cmd(self._node, f"rm -f {value}", sudo=True)

    def qemu_version(self):
        """Return Qemu version.

        :returns: Qemu version.
        :rtype: str
        """
        command = f"{Constants.QEMU_BIN_PATH}/qemu-system-{self._arch} " \
            f"--version"
        try:
            stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
            return match(r"QEMU emulator version ([\d.]*)", stdout).group(1)
        except RuntimeError:
            self.qemu_kill_all()
            raise
コード例 #14
0
    def perf_stat(node, cpu_list=None, duration=1):
        """Get perf stat read for duration.

        :param node: Node in the topology.
        :param cpu_list: CPU List as a string separated by comma.
        :param duration: Measure time in seconds.
        :type node: dict
        :type cpu_list: str
        :type duration: int
        """
        if cpu_list:
            cpu_list = list(dict.fromkeys(cpu_list.split(u",")))
            cpu_list = ",".join(str(cpu) for cpu in cpu_list)

        cmd_opts = OptionString(prefix=u"--")
        cmd_opts.add(u"no-aggr")
        cmd_opts.add_with_value_if(u"cpu", cpu_list, cpu_list)
        cmd_opts.add_if(u"all-cpus", not (cpu_list))
        cmd_opts.add_with_value_if(u"event",
                                   f"'{{{Constants.PERF_STAT_EVENTS}}}'",
                                   Constants.PERF_STAT_EVENTS)
        cmd_opts.add_with_value(u"interval-print", 1000)
        cmd_opts.add_with_value(u"field-separator", u"';'")

        cmd_base = OptionString()
        cmd_base.add(f"perf stat")
        cmd_base.extend(cmd_opts)
        cmd_base.add(u"--")
        cmd_base.add_with_value(u"sleep", int(duration))

        exec_cmd(node, cmd_base, sudo=True)
コード例 #15
0
ファイル: ssh.py プロジェクト: practice20h/csit
    def exec_command(self, cmd, timeout=10, log_stdout_err=True):
        """Execute SSH command on a new channel on the connected Node.

        :param cmd: Command to run on the Node.
        :param timeout: Maximal time in seconds to wait until the command is
            done. If set to None then wait forever.
        :param log_stdout_err: If True, stdout and stderr are logged. stdout
            and stderr are logged also if the return code is not zero
            independently of the value of log_stdout_err.
        :type cmd: str or OptionString
        :type timeout: int
        :type log_stdout_err: bool
        :returns: return_code, stdout, stderr
        :rtype: tuple(int, str, str)
        :raises SSHTimeout: If command is not finished in timeout time.
        """
        if isinstance(cmd, (list, tuple)):
            cmd = OptionString(cmd)
        cmd = str(cmd)
        stdout = StringIO.StringIO()
        stderr = StringIO.StringIO()
        try:
            chan = self._ssh.get_transport().open_session(timeout=5)
            peer = self._ssh.get_transport().getpeername()
        except (AttributeError, SSHException):
            self._reconnect()
            chan = self._ssh.get_transport().open_session(timeout=5)
            peer = self._ssh.get_transport().getpeername()
        chan.settimeout(timeout)

        logger.trace(
            'exec_command on {peer} with timeout {timeout}: {cmd}'.format(
                peer=peer, timeout=timeout, cmd=cmd))

        start = time()
        chan.exec_command(cmd)
        while not chan.exit_status_ready() and timeout is not None:
            if chan.recv_ready():
                stdout.write(chan.recv(self.__MAX_RECV_BUF))

            if chan.recv_stderr_ready():
                stderr.write(chan.recv_stderr(self.__MAX_RECV_BUF))

            if time() - start > timeout:
                raise SSHTimeout(
                    'Timeout exception during execution of command: {cmd}\n'
                    'Current contents of stdout buffer: {stdout}\n'
                    'Current contents of stderr buffer: {stderr}\n'.format(
                        cmd=cmd,
                        stdout=stdout.getvalue(),
                        stderr=stderr.getvalue()))

            sleep(0.1)
        return_code = chan.recv_exit_status()

        while chan.recv_ready():
            stdout.write(chan.recv(self.__MAX_RECV_BUF))

        while chan.recv_stderr_ready():
            stderr.write(chan.recv_stderr(self.__MAX_RECV_BUF))

        end = time()
        logger.trace('exec_command on {peer} took {total} seconds'.format(
            peer=peer, total=end - start))

        logger.trace('return RC {rc}'.format(rc=return_code))
        if log_stdout_err or int(return_code):
            logger.trace(
                'return STDOUT {stdout}'.format(stdout=stdout.getvalue()))
            logger.trace(
                'return STDERR {stderr}'.format(stderr=stderr.getvalue()))
        return return_code, stdout.getvalue(), stderr.getvalue()
コード例 #16
0
    def get_pmd_options(**kwargs):
        """Create PMD parameters options (without --).

        :param kwargs: List of testpmd parameters.
        :type kwargs: dict
        :returns: PMD parameters.
        :rtype: OptionString
        """
        options = OptionString(prefix='--')
        # Set the forwarding mode: io, mac, mac_retry, mac_swap, flowgen,
        # rxonly, txonly, csum, icmpecho, ieee1588
        options.add_equals_from_dict('forward-mode', 'pmd_fwd_mode', kwargs,
                                     'io')
        # Set the number of packets per burst to N.
        options.add_equals('burst', 64)
        # Set the number of descriptors in the TX rings to N.
        options.add_equals_from_dict('txd', 'pmd_txd', kwargs, 1024)
        # Set the number of descriptors in the RX rings to N.
        options.add_equals_from_dict('rxd', 'pmd_rxd', kwargs, 1024)
        # Set the number of queues in the TX to N.
        options.add_equals_from_dict('txq', 'pmd_txq', kwargs, 1)
        # Set the number of queues in the RX to N.
        options.add_equals_from_dict('rxq', 'pmd_rxq', kwargs, 1)
        # Set the hexadecimal bitmask of offloads.
        options.add_equals_if_from_dict('txqflags', '0xf00', 'pmd_tx_offloads',
                                        kwargs, True)
        # Set the number of mbufs to be allocated in the mbuf pools.
        options.add_equals_from_dict('total-num-mbufs', 'pmd_num_mbufs',
                                     kwargs)
        # Disable hardware VLAN.
        options.add_if_from_dict('disable-hw-vlan', 'pmd_disable_hw_vlan',
                                 kwargs, True)
        # Set the MAC address XX:XX:XX:XX:XX:XX of the peer port N
        options.add_equals_from_dict('eth-peer', 'pmd_eth_peer_0', kwargs)
        options.add_equals_from_dict('eth-peer', 'pmd_eth_peer_1', kwargs)
        # Set the max packet length.
        options.add_equals_from_dict('max-pkt-len', 'pmd_max_pkt_len', kwargs)
        # Set the number of forwarding cores based on coremask.
        options.add_equals_from_dict('nb-cores', 'pmd_nb_cores', kwargs)
        return options
コード例 #17
0
ファイル: Iperf3.py プロジェクト: gvnn3/csit
    def iperf3_cmdline(**kwargs):
        """Get iPerf3 server command line.

        :param kwargs: List of iPerf3 server parameters.
        :type kwargs: dict
        :returns: iPerf3 server command line.
        :rtype: OptionString
        """
        cmd = OptionString()
        if kwargs['namespace']:
            cmd.add(f"ip netns exec {kwargs['namespace']}")
        cmd.add(f"iperf3")

        cmd_options = OptionString(prefix=u"--")
        # Run iPerf in server mode. (This will only allow one iperf connection
        # at a time)
        cmd_options.add(u"server")

        # Run the server in background as a daemon.
        cmd_options.add_if_from_dict(u"daemon", u"daemon", kwargs, True)

        # Write a file with the process ID, most useful when running as a
        # daemon.
        cmd_options.add_with_value_from_dict(u"pidfile", u"pidfile", kwargs,
                                             f"/tmp/iperf3_server.pid")

        # Send output to a log file.
        cmd_options.add_with_value_from_dict(u"logfile", u"logfile", kwargs,
                                             f"/tmp/iperf3.log")

        # The server port for the server to listen on and the client to
        # connect to. This should be the same in both client and server.
        # Default is 5201.
        cmd_options.add_with_value_from_dict(u"port", u"port", kwargs, 5201)

        # Set the CPU affinity, if possible (Linux and FreeBSD only).
        cmd_options.add_with_value_from_dict(u"affinity", u"affinity", kwargs)

        # Output in JSON format.
        cmd_options.add_if_from_dict(u"json", u"json", kwargs, True)

        # Give more detailed output.
        cmd_options.add_if_from_dict(u"verbose", u"verbose", kwargs, True)

        return cmd.extend(cmd_options)
コード例 #18
0
    def get_eal_options(**kwargs):
        """Create EAL parameters options (including -v).

        :param kwargs: Dict of testpmd parameters.
        :type kwargs: dict
        :returns: EAL parameters.
        :rtype: OptionString
        """
        options = OptionString(prefix=u"-")
        options.add(u"v")
        # Set the hexadecimal bitmask of the cores to run on.
        options.add_with_value_from_dict(u"l", u"eal_corelist", kwargs)
        # Add a PCI device in white list.
        options.add_with_value_from_dict(u"w", u"eal_pci_whitelist0", kwargs)
        options.add_with_value_from_dict(u"w", u"eal_pci_whitelist1", kwargs)
        # Set master core.
        options.add_with_value(u"-master-lcore", u"0")
        # Load an external driver. Multiple -d options are allowed.
        options.add_with_value_if_from_dict(u"d",
                                            u"/usr/lib/librte_pmd_virtio.so",
                                            u"eal_driver", kwargs, True)
        options.add_if_from_dict(u"-in-memory", u"eal_in_memory", kwargs,
                                 False)
        return options
コード例 #19
0
    def exec_command(self, cmd, timeout=10, log_stdout_err=True, export=True):
        """Execute SSH command on a new channel on the connected Node.

        :param cmd: Command to run on the Node.
        :param timeout: Maximal time in seconds to wait until the command is
            done. If set to None then wait forever.
        :param log_stdout_err: If True, stdout and stderr are logged. stdout
            and stderr are logged also if the return code is not zero
            independently of the value of log_stdout_err.
        :param export: If false, do not attempt JSON export.
            Needed for calls outside Robot (e.g. from reservation script).
        :type cmd: str or OptionString
        :type timeout: int
        :type log_stdout_err: bool
        :type export: bool
        :returns: return_code, stdout, stderr
        :rtype: tuple(int, str, str)
        :raises SSHTimeout: If command is not finished in timeout time.
        """
        if isinstance(cmd, (list, tuple)):
            cmd = OptionString(cmd)
        cmd = str(cmd)
        stdout = u""
        stderr = u""
        try:
            chan = self._ssh.get_transport().open_session(timeout=5)
            peer = self._ssh.get_transport().getpeername()
        except (AttributeError, SSHException):
            self._reconnect()
            chan = self._ssh.get_transport().open_session(timeout=5)
            peer = self._ssh.get_transport().getpeername()
        chan.settimeout(timeout)

        logger.trace(f"exec_command on {peer} with timeout {timeout}: {cmd}")

        if export:
            export_ssh_command(self._node[u"host"], self._node[u"port"], cmd)
        start = monotonic()
        chan.exec_command(cmd)
        while not chan.exit_status_ready() and timeout is not None:
            if chan.recv_ready():
                s_out = chan.recv(self.__MAX_RECV_BUF)
                stdout += s_out.decode(encoding=u'utf-8', errors=u'ignore') \
                    if isinstance(s_out, bytes) else s_out

            if chan.recv_stderr_ready():
                s_err = chan.recv_stderr(self.__MAX_RECV_BUF)
                stderr += s_err.decode(encoding=u'utf-8', errors=u'ignore') \
                    if isinstance(s_err, bytes) else s_err

            duration = monotonic() - start
            if duration > timeout:
                if export:
                    export_ssh_timeout(
                        host=self._node[u"host"],
                        port=self._node[u"port"],
                        stdout=stdout,
                        stderr=stderr,
                        duration=duration,
                    )
                raise SSHTimeout(
                    f"Timeout exception during execution of command: {cmd}\n"
                    f"Current contents of stdout buffer: "
                    f"{stdout}\n"
                    f"Current contents of stderr buffer: "
                    f"{stderr}\n")

            sleep(0.1)
        return_code = chan.recv_exit_status()

        while chan.recv_ready():
            s_out = chan.recv(self.__MAX_RECV_BUF)
            stdout += s_out.decode(encoding=u'utf-8', errors=u'ignore') \
                if isinstance(s_out, bytes) else s_out

        while chan.recv_stderr_ready():
            s_err = chan.recv_stderr(self.__MAX_RECV_BUF)
            stderr += s_err.decode(encoding=u'utf-8', errors=u'ignore') \
                if isinstance(s_err, bytes) else s_err

        duration = monotonic() - start
        logger.trace(f"exec_command on {peer} took {duration} seconds")

        logger.trace(f"return RC {return_code}")
        if log_stdout_err or int(return_code):
            logger.trace(f"return STDOUT {stdout}")
            logger.trace(f"return STDERR {stderr}")
        if export:
            export_ssh_result(
                host=self._node[u"host"],
                port=self._node[u"port"],
                code=return_code,
                stdout=stdout,
                stderr=stderr,
                duration=duration,
            )
        return return_code, stdout, stderr
コード例 #20
0
ファイル: QemuUtils.py プロジェクト: preym17/csit
class QemuUtils(object):
    """QEMU utilities."""

    # Use one instance of class per tests.
    ROBOT_LIBRARY_SCOPE = 'TEST CASE'

    def __init__(self, node, qemu_id=1, smp=1, mem=512, vnf=None,
                 img=Constants.QEMU_VM_IMAGE):
        """Initialize QemuUtil class.

        :param node: Node to run QEMU on.
        :param qemu_id: QEMU identifier.
        :param smp: Number of virtual SMP units (cores).
        :param mem: Amount of memory.
        :param vnf: Network function workload.
        :param img: QEMU disk image or kernel image path.
        :type node: dict
        :type qemu_id: int
        :type smp: int
        :type mem: int
        :type vnf: str
        :type img: str
        """
        self._vhost_id = 0
        self._node = node
        self._vm_info = {
            'host': node['host'],
            'type': NodeType.VM,
            'port': 10021 + qemu_id,
            'serial': 4555 + qemu_id,
            'username': '******',
            'password': '******',
            'interfaces': {},
        }
        if node['port'] != 22:
            self._vm_info['host_port'] = node['port']
            self._vm_info['host_username'] = node['username']
            self._vm_info['host_password'] = node['password']
        # Input Options.
        self._opt = dict()
        self._opt['qemu_id'] = qemu_id
        self._opt['mem'] = int(mem)
        self._opt['smp'] = int(smp)
        self._opt['img'] = img
        self._opt['vnf'] = vnf
        # Temporary files.
        self._temp = dict()
        self._temp['pidfile'] = '/var/run/qemu_{id}.pid'.format(id=qemu_id)
        if '/var/lib/vm/' in img:
            self._opt['vm_type'] = 'nestedvm'
            self._temp['qmp'] = '/var/run/qmp_{id}.sock'.format(id=qemu_id)
            self._temp['qga'] = '/var/run/qga_{id}.sock'.format(id=qemu_id)
        elif '/opt/boot/vmlinuz' in img:
            self._opt['vm_type'] = 'kernelvm'
            self._temp['log'] = '/tmp/serial_{id}.log'.format(id=qemu_id)
            self._temp['ini'] = '/etc/vm_init_{id}.conf'.format(id=qemu_id)
        else:
            raise RuntimeError('QEMU: Unknown VM image option!')
        # Computed parameters for QEMU command line.
        self._params = OptionString(prefix='-')
        self.add_params()

    def add_params(self):
        """Set QEMU command line parameters."""
        self.add_default_params()
        if self._opt.get('vm_type', '') == 'nestedvm':
            self.add_nestedvm_params()
        elif self._opt.get('vm_type', '') == 'kernelvm':
            self.add_kernelvm_params()
        else:
            raise RuntimeError('QEMU: Unsupported VM type!')

    def add_default_params(self):
        """Set default QEMU command line parameters."""
        self._params.add('daemonize')
        self._params.add('nodefaults')
        self._params.add_with_value('name', 'vnf{qemu},debug-threads=on'.format(
            qemu=self._opt.get('qemu_id')))
        self._params.add('no-user-config')
        self._params.add_with_value('monitor', 'none')
        self._params.add_with_value('display', 'none')
        self._params.add_with_value('vga', 'none')
        self._params.add('enable-kvm')
        self._params.add_with_value('pidfile', self._temp.get('pidfile'))
        self._params.add_with_value('cpu', 'host')
        self._params.add_with_value(
            'machine', 'pc,accel=kvm,usb=off,mem-merge=off')
        self._params.add_with_value(
            'smp', '{smp},sockets=1,cores={smp},threads=1'.format(
                smp=self._opt.get('smp')))
        self._params.add_with_value(
            'object', 'memory-backend-file,id=mem,size={mem}M,'
            'mem-path=/dev/hugepages,share=on'.format(mem=self._opt.get('mem')))
        self._params.add_with_value(
            'm', '{mem}M'.format(mem=self._opt.get('mem')))
        self._params.add_with_value('numa', 'node,memdev=mem')
        self._params.add_with_value('balloon', 'none')

    def add_nestedvm_params(self):
        """Set NestedVM QEMU parameters."""
        self._params.add_with_value(
            'net', 'nic,macaddr=52:54:00:00:{qemu:02x}:ff'.format(
                qemu=self._opt.get('qemu_id')))
        self._params.add_with_value(
            'net', 'user,hostfwd=tcp::{info[port]}-:22'.format(
                info=self._vm_info))
        # TODO: Remove try except after fully migrated to Bionic or
        # qemu_set_node is removed.
        try:
            locking = ',file.locking=off'\
                if self.qemu_version(version='2.10') else ''
        except AttributeError:
            locking = ''
        self._params.add_with_value(
            'drive', 'file={img},format=raw,cache=none,if=virtio{locking}'.
            format(img=self._opt.get('img'), locking=locking))
        self._params.add_with_value(
            'qmp', 'unix:{qmp},server,nowait'.format(qmp=self._temp.get('qmp')))
        self._params.add_with_value(
            'chardev', 'socket,host=127.0.0.1,port={info[serial]},'
            'id=gnc0,server,nowait'.format(info=self._vm_info))
        self._params.add_with_value('device', 'isa-serial,chardev=gnc0')
        self._params.add_with_value(
            'chardev', 'socket,path={qga},server,nowait,id=qga0'.format(
                qga=self._temp.get('qga')))
        self._params.add_with_value('device', 'isa-serial,chardev=qga0')

    def add_kernelvm_params(self):
        """Set KernelVM QEMU parameters."""
        self._params.add_with_value(
            'chardev', 'file,id=char0,path={log}'.format(
                log=self._temp.get('log')))
        self._params.add_with_value('device', 'isa-serial,chardev=char0')
        self._params.add_with_value(
            'fsdev', 'local,id=root9p,path=/,security_model=none')
        self._params.add_with_value(
            'device', 'virtio-9p-pci,fsdev=root9p,mount_tag=/dev/root')
        self._params.add_with_value(
            'kernel', '$(readlink -m {img}* | tail -1)'.format(
                img=self._opt.get('img')))
        self._params.add_with_value(
            'append', '"ro rootfstype=9p rootflags=trans=virtio console=ttyS0'
            ' tsc=reliable hugepages=256 init={init}"'.format(
                init=self._temp.get('ini')))

    def create_kernelvm_config_vpp(self, **kwargs):
        """Create QEMU VPP config files.

        :param kwargs: Key-value pairs to replace content of VPP configuration
            file.
        :type kwargs: dict
        """
        startup = ('/etc/vpp/vm_startup_{id}.conf'.
                   format(id=self._opt.get('qemu_id')))
        running = ('/etc/vpp/vm_running_{id}.exec'.
                   format(id=self._opt.get('qemu_id')))

        self._temp['startup'] = startup
        self._temp['running'] = running
        self._opt['vnf_bin'] = ('/usr/bin/vpp -c {startup}'.
                                format(startup=startup))

        # Create VPP startup configuration.
        vpp_config = VppConfigGenerator()
        vpp_config.set_node(self._node)
        vpp_config.add_unix_nodaemon()
        vpp_config.add_unix_cli_listen()
        vpp_config.add_unix_exec(running)
        vpp_config.add_cpu_main_core('0')
        vpp_config.add_cpu_corelist_workers('1-{smp}'.
                                            format(smp=self._opt.get('smp')-1))
        vpp_config.add_dpdk_dev('0000:00:06.0', '0000:00:07.0')
        vpp_config.add_dpdk_dev_default_rxq(kwargs['queues'])
        vpp_config.add_dpdk_log_level('debug')
        if not kwargs['jumbo_frames']:
            vpp_config.add_dpdk_no_multi_seg()
            vpp_config.add_dpdk_no_tx_checksum_offload()
        vpp_config.add_plugin('disable', 'default')
        vpp_config.add_plugin('enable', 'dpdk_plugin.so')
        vpp_config.write_config(startup)

        # Create VPP running configuration.
        template = '{res}/{tpl}.exec'.format(res=Constants.RESOURCES_TPL_VM,
                                             tpl=self._opt.get('vnf'))
        exec_cmd_no_error(self._node, 'rm -f {running}'.format(running=running),
                          sudo=True)

        with open(template, 'r') as src_file:
            src = Template(src_file.read())
            exec_cmd_no_error(
                self._node, "echo '{out}' | sudo tee {running}".format(
                    out=src.safe_substitute(**kwargs), running=running))

    def create_kernelvm_config_testpmd_io(self, **kwargs):
        """Create QEMU testpmd-io command line.

        :param kwargs: Key-value pairs to construct command line parameters.
        :type kwargs: dict
        """
        testpmd_path = ('{path}/{arch}-native-linuxapp-gcc/app'.
                        format(path=Constants.QEMU_VM_DPDK,
                               arch=Topology.get_node_arch(self._node)))
        testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
            eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
            eal_driver=False,
            eal_in_memory=True,
            pmd_num_mbufs=16384,
            pmd_rxq=kwargs['queues'],
            pmd_txq=kwargs['queues'],
            pmd_tx_offloads=False,
            pmd_disable_hw_vlan=False,
            pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
            pmd_nb_cores=str(self._opt.get('smp') - 1))

        self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
                                format(testpmd_path=testpmd_path,
                                       testpmd_cmd=testpmd_cmd))

    def create_kernelvm_config_testpmd_mac(self, **kwargs):
        """Create QEMU testpmd-mac command line.

        :param kwargs: Key-value pairs to construct command line parameters.
        :type kwargs: dict
        """
        testpmd_path = ('{path}/{arch}-native-linuxapp-gcc/app'.
                        format(path=Constants.QEMU_VM_DPDK,
                               arch=Topology.get_node_arch(self._node)))
        testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
            eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
            eal_driver=False,
            eal_in_memory=True,
            pmd_num_mbufs=16384,
            pmd_fwd_mode='mac',
            pmd_eth_peer_0='0,{mac}'.format(mac=kwargs['vif1_mac']),
            pmd_eth_peer_1='1,{mac}'.format(mac=kwargs['vif2_mac']),
            pmd_rxq=kwargs['queues'],
            pmd_txq=kwargs['queues'],
            pmd_tx_offloads=False,
            pmd_disable_hw_vlan=False,
            pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
            pmd_nb_cores=str(self._opt.get('smp') - 1))

        self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
                                format(testpmd_path=testpmd_path,
                                       testpmd_cmd=testpmd_cmd))

    def create_kernelvm_init(self, **kwargs):
        """Create QEMU init script.

        :param kwargs: Key-value pairs to replace content of init startup file.
        :type kwargs: dict
        """
        template = '{res}/init.sh'.format(res=Constants.RESOURCES_TPL_VM)
        init = self._temp.get('ini')
        exec_cmd_no_error(
            self._node, 'rm -f {init}'.format(init=init), sudo=True)

        with open(template, 'r') as src_file:
            src = Template(src_file.read())
            exec_cmd_no_error(
                self._node, "echo '{out}' | sudo tee {init}".format(
                    out=src.safe_substitute(**kwargs), init=init))
            exec_cmd_no_error(
                self._node, "chmod +x {init}".format(init=init), sudo=True)

    def configure_kernelvm_vnf(self, **kwargs):
        """Create KernelVM VNF configurations.

        :param kwargs: Key-value pairs for templating configs.
        :type kwargs: dict
        """
        if 'vpp' in self._opt.get('vnf'):
            self.create_kernelvm_config_vpp(**kwargs)
        elif 'testpmd_io' in self._opt.get('vnf'):
            self.create_kernelvm_config_testpmd_io(**kwargs)
        elif 'testpmd_mac' in self._opt.get('vnf'):
            self.create_kernelvm_config_testpmd_mac(**kwargs)
        else:
            raise RuntimeError('QEMU: Unsupported VNF!')
        self.create_kernelvm_init(vnf_bin=self._opt['vnf_bin'])

    def get_qemu_pids(self):
        """Get QEMU CPU pids.

        :returns: List of QEMU CPU pids.
        :rtype: list of str
        """
        command = ("grep -rwl 'CPU' /proc/$(sudo cat {pidfile})/task/*/comm ".
                   format(pidfile=self._temp.get('pidfile')))
        command += (r"| xargs dirname | sed -e 's/\/.*\///g' | uniq")

        stdout, _ = exec_cmd_no_error(self._node, command)
        return stdout.splitlines()

    def qemu_set_affinity(self, *host_cpus):
        """Set qemu affinity by getting thread PIDs via QMP and taskset to list
        of CPU cores. Function tries to execute 3 times to avoid race condition
        in getting thread PIDs.

        :param host_cpus: List of CPU cores.
        :type host_cpus: list
        """
        for _ in range(3):
            try:
                qemu_cpus = self.get_qemu_pids()

                if len(qemu_cpus) != len(host_cpus):
                    sleep(1)
                    continue
                for qemu_cpu, host_cpu in zip(qemu_cpus, host_cpus):
                    command = ('taskset -pc {host_cpu} {thread}'.
                               format(host_cpu=host_cpu, thread=qemu_cpu))
                    message = ('QEMU: Set affinity failed on {host}!'.
                               format(host=self._node['host']))
                    exec_cmd_no_error(self._node, command, sudo=True,
                                      message=message)
                break
            except (RuntimeError, ValueError):
                self.qemu_kill_all()
                raise
        else:
            self.qemu_kill_all()
            raise RuntimeError('Failed to set Qemu threads affinity!')

    def qemu_set_scheduler_policy(self):
        """Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU
        processes.

        :raises RuntimeError: Set scheduler policy failed.
        """
        try:
            qemu_cpus = self.get_qemu_pids()

            for qemu_cpu in qemu_cpus:
                command = ('chrt -r -p 1 {thread}'.
                           format(thread=qemu_cpu))
                message = ('QEMU: Set SCHED_RR failed on {host}'.
                           format(host=self._node['host']))
                exec_cmd_no_error(self._node, command, sudo=True,
                                  message=message)
        except (RuntimeError, ValueError):
            self.qemu_kill_all()
            raise

    def qemu_add_vhost_user_if(self, socket, server=True, jumbo_frames=False,
                               queue_size=None, queues=1):
        """Add Vhost-user interface.

        :param socket: Path of the unix socket.
        :param server: If True the socket shall be a listening socket.
        :param jumbo_frames: Set True if jumbo frames are used in the test.
        :param queue_size: Vring queue size.
        :param queues: Number of queues.
        :type socket: str
        :type server: bool
        :type jumbo_frames: bool
        :type queue_size: int
        :type queues: int
        """
        self._vhost_id += 1
        self._params.add_with_value(
            'chardev', 'socket,id=char{vhost},path={socket}{server}'.format(
                vhost=self._vhost_id, socket=socket,
                server=',server' if server is True else ''))
        self._params.add_with_value(
            'netdev', 'vhost-user,id=vhost{vhost},chardev=char{vhost},'
            'queues={queues}'.format(vhost=self._vhost_id, queues=queues))
        mac = ('52:54:00:00:{qemu:02x}:{vhost:02x}'.
               format(qemu=self._opt.get('qemu_id'), vhost=self._vhost_id))
        queue_size = ('rx_queue_size={queue_size},tx_queue_size={queue_size}'.
                      format(queue_size=queue_size)) if queue_size else ''
        mbuf = 'on,host_mtu=9200'
        self._params.add_with_value(
            'device', 'virtio-net-pci,netdev=vhost{vhost},mac={mac},bus=pci.0,'
            'addr={addr}.0,mq=on,vectors={vectors},csum=off,gso=off,'
            'guest_tso4=off,guest_tso6=off,guest_ecn=off,mrg_rxbuf={mbuf},'
            '{queue_size}'.format(
                addr=self._vhost_id+5, vhost=self._vhost_id, mac=mac,
                mbuf=mbuf if jumbo_frames else 'off', queue_size=queue_size,
                vectors=(2 * queues + 2)))

        # Add interface MAC and socket to the node dict.
        if_data = {'mac_address': mac, 'socket': socket}
        if_name = 'vhost{vhost}'.format(vhost=self._vhost_id)
        self._vm_info['interfaces'][if_name] = if_data
        # Add socket to temporary file list.
        self._temp[if_name] = socket

    def _qemu_qmp_exec(self, cmd):
        """Execute QMP command.

        QMP is JSON based protocol which allows to control QEMU instance.

        :param cmd: QMP command to execute.
        :type cmd: str
        :returns: Command output in python representation of JSON format. The
            { "return": {} } response is QMP's success response. An error
            response will contain the "error" keyword instead of "return".
        """
        # To enter command mode, the qmp_capabilities command must be issued.
        command = ('echo "{{ \\"execute\\": \\"qmp_capabilities\\" }}'
                   '{{ \\"execute\\": \\"{cmd}\\" }}" | '
                   'sudo -S socat - UNIX-CONNECT:{qmp}'.
                   format(cmd=cmd, qmp=self._temp.get('qmp')))
        message = ('QMP execute "{cmd}" failed on {host}'.
                   format(cmd=cmd, host=self._node['host']))
        stdout, _ = exec_cmd_no_error(
            self._node, command, sudo=False, message=message)

        # Skip capabilities negotiation messages.
        out_list = stdout.splitlines()
        if len(out_list) < 3:
            raise RuntimeError(
                'Invalid QMP output on {host}'.format(host=self._node['host']))
        return json.loads(out_list[2])

    def _qemu_qga_flush(self):
        """Flush the QGA parser state."""
        command = ('(printf "\xFF"; sleep 1) | '
                   'sudo -S socat - UNIX-CONNECT:{qga}'.
                   format(qga=self._temp.get('qga')))
        message = ('QGA flush failed on {host}'.format(host=self._node['host']))
        stdout, _ = exec_cmd_no_error(
            self._node, command, sudo=False, message=message)

        return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()

    def _qemu_qga_exec(self, cmd):
        """Execute QGA command.

        QGA provide access to a system-level agent via standard QMP commands.

        :param cmd: QGA command to execute.
        :type cmd: str
        """
        command = ('(echo "{{ \\"execute\\": \\"{cmd}\\" }}"; sleep 1) | '
                   'sudo -S socat - UNIX-CONNECT:{qga}'.
                   format(cmd=cmd, qga=self._temp.get('qga')))
        message = ('QGA execute "{cmd}" failed on {host}'.
                   format(cmd=cmd, host=self._node['host']))
        stdout, _ = exec_cmd_no_error(
            self._node, command, sudo=False, message=message)

        return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()

    def _wait_until_vm_boot(self):
        """Wait until QEMU with NestedVM is booted."""
        if self._opt.get('vm_type') == 'nestedvm':
            self._wait_until_nestedvm_boot()
            self._update_vm_interfaces()
        elif self._opt.get('vm_type') == 'kernelvm':
            self._wait_until_kernelvm_boot()
        else:
            raise RuntimeError('QEMU: Unsupported VM type!')

    def _wait_until_nestedvm_boot(self, retries=12):
        """Wait until QEMU with NestedVM is booted.

        First try to flush qga until there is output.
        Then ping QEMU guest agent each 5s until VM booted or timeout.

        :param retries: Number of retries with 5s between trials.
        :type retries: int
        """
        for _ in range(retries):
            out = None
            try:
                out = self._qemu_qga_flush()
            except ValueError:
                logger.trace('QGA qga flush unexpected output {out}'.
                             format(out=out))
            # Empty output - VM not booted yet
            if not out:
                sleep(5)
            else:
                break
        else:
            raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
                               format(host=self._node['host']))
        for _ in range(retries):
            out = None
            try:
                out = self._qemu_qga_exec('guest-ping')
            except ValueError:
                logger.trace('QGA guest-ping unexpected output {out}'.
                             format(out=out))
            # Empty output - VM not booted yet.
            if not out:
                sleep(5)
            # Non-error return - VM booted.
            elif out.get('return') is not None:
                break
            # Skip error and wait.
            elif out.get('error') is not None:
                sleep(5)
            else:
                # If there is an unexpected output from QGA guest-info, try
                # again until timeout.
                logger.trace('QGA guest-ping unexpected output {out}'.
                             format(out=out))
        else:
            raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
                               format(host=self._node['host']))

    def _wait_until_kernelvm_boot(self, retries=60):
        """Wait until QEMU KernelVM is booted.

        :param retries: Number of retries.
        :type retries: int
        """
        vpp_ver = VPPUtil.vpp_show_version(self._node)

        for _ in range(retries):
            command = ('tail -1 {log}'.format(log=self._temp.get('log')))
            stdout = None
            try:
                stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
                sleep(1)
            except RuntimeError:
                pass
            if vpp_ver in stdout or 'Press enter to exit' in stdout:
                break
            if 'reboot: Power down' in stdout:
                raise RuntimeError('QEMU: NF failed to run on {host}!'.
                                   format(host=self._node['host']))
        else:
            raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
                               format(host=self._node['host']))

    def _update_vm_interfaces(self):
        """Update interface names in VM node dict."""
        # Send guest-network-get-interfaces command via QGA, output example:
        # {"return": [{"name": "eth0", "hardware-address": "52:54:00:00:04:01"},
        # {"name": "eth1", "hardware-address": "52:54:00:00:04:02"}]}.
        out = self._qemu_qga_exec('guest-network-get-interfaces')
        interfaces = out.get('return')
        mac_name = {}
        if not interfaces:
            raise RuntimeError('Get VM interface list failed on {host}'.
                               format(host=self._node['host']))
        # Create MAC-name dict.
        for interface in interfaces:
            if 'hardware-address' not in interface:
                continue
            mac_name[interface['hardware-address']] = interface['name']
        # Match interface by MAC and save interface name.
        for interface in self._vm_info['interfaces'].values():
            mac = interface.get('mac_address')
            if_name = mac_name.get(mac)
            if if_name is None:
                logger.trace(
                    'Interface name for MAC {mac} not found'.format(mac=mac))
            else:
                interface['name'] = if_name

    def qemu_start(self):
        """Start QEMU and wait until VM boot.

        :returns: VM node info.
        :rtype: dict
        """
        cmd_opts = OptionString()
        cmd_opts.add('{bin_path}/qemu-system-{arch}'.format(
            bin_path=Constants.QEMU_BIN_PATH,
            arch=Topology.get_node_arch(self._node)))
        cmd_opts.extend(self._params)
        message = ('QEMU: Start failed on {host}!'.
                   format(host=self._node['host']))
        try:
            DUTSetup.check_huge_page(
                self._node, '/dev/hugepages', self._opt.get('mem'))

            exec_cmd_no_error(
                self._node, cmd_opts, timeout=300, sudo=True, message=message)
            self._wait_until_vm_boot()
        except RuntimeError:
            self.qemu_kill_all()
            raise
        return self._vm_info

    def qemu_kill(self):
        """Kill qemu process."""
        exec_cmd(self._node, 'chmod +r {pidfile}'.
                 format(pidfile=self._temp.get('pidfile')), sudo=True)
        exec_cmd(self._node, 'kill -SIGKILL $(cat {pidfile})'.
                 format(pidfile=self._temp.get('pidfile')), sudo=True)

        for value in self._temp.values():
            exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
            exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)

    def qemu_kill_all(self):
        """Kill all qemu processes on DUT node if specified."""
        exec_cmd(self._node, 'pkill -SIGKILL qemu', sudo=True)

        for value in self._temp.values():
            exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
            exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)

    def qemu_version(self, version=None):
        """Return Qemu version or compare if version is higher than parameter.

        :param version: Version to compare.
        :type version: str
        :returns: Qemu version or Boolean if version is higher than parameter.
        :rtype: str or bool
        """
        command = ('{bin_path}/qemu-system-{arch} --version'.format(
            bin_path=Constants.QEMU_BIN_PATH,
            arch=Topology.get_node_arch(self._node)))
        try:
            stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
            ver = match(r'QEMU emulator version ([\d.]*)', stdout).group(1)
            return StrictVersion(ver) > StrictVersion(version) \
                if version else ver
        except RuntimeError:
            self.qemu_kill_all()
            raise
コード例 #21
0
    def get_testpmd_pmd_options(**kwargs):
        """Create PMD parameters options for testpmd (without --).

        :param kwargs: List of testpmd parameters.
        :type kwargs: dict
        :returns: PMD parameters.
        :rtype: OptionString
        """
        options = OptionString(prefix=u"--")
        # Set the forwarding mode: io, mac, mac_retry, mac_swap, flowgen,
        # rxonly, txonly, csum, icmpecho, ieee1588
        options.add_equals_from_dict(u"forward-mode", u"pmd_fwd_mode", kwargs,
                                     u"io")
        # Set the number of packets per burst to N.
        options.add_equals(u"burst", 64)
        # Set the number of descriptors in the TX rings to N.
        options.add_equals_from_dict(u"txd", u"pmd_txd", kwargs, 1024)
        # Set the number of descriptors in the RX rings to N.
        options.add_equals_from_dict(u"rxd", u"pmd_rxd", kwargs, 1024)
        # Set the number of queues in the TX to N.
        options.add_equals_from_dict(u"txq", u"pmd_txq", kwargs, 1)
        # Set the number of queues in the RX to N.
        options.add_equals_from_dict(u"rxq", u"pmd_rxq", kwargs, 1)
        # Set the hexadecimal bitmask of offloads.
        options.add_equals_from_dict(u"tx-offloads", u"pmd_tx_offloads",
                                     kwargs, u"0x0")
        # Enables numa aware allocation of mbufs.
        options.add_if_from_dict(u"numa", u"pmd_numa", kwargs, True)
        # Run by default.
        options.add_if_from_dict(u"auto-start", u"pmd_auto_start", kwargs,
                                 True)
        # Set the number of mbufs to be allocated in the mbuf pools.
        options.add_equals_from_dict(u"total-num-mbufs", u"pmd_num_mbufs",
                                     kwargs)
        # Set the number of forwarding ports.
        options.add_equals_from_dict(u"nb-ports", u"pmd_nb_ports", kwargs)
        # Set the hexadecimal bitmask of the ports used by the packet
        # forwarding test.
        options.add_equals_from_dict(u"portmask", u"pmd_portmask", kwargs)
        # Disable link status check.
        options.add_if_from_dict(u"disable-link-check",
                                 u"pmd_disable_link_check", kwargs, True)
        # Set the MAC address XX:XX:XX:XX:XX:XX of the peer port N
        options.add_equals_from_dict(u"eth-peer", u"pmd_eth_peer_0", kwargs)
        options.add_equals_from_dict(u"eth-peer", u"pmd_eth_peer_1", kwargs)
        # Set the max packet length.
        options.add_equals_from_dict(u"max-pkt-len", u"pmd_max_pkt_len",
                                     kwargs)
        # Set the max packet length.
        options.add_equals_from_dict(u"mbuf-size", u"pmd_mbuf_size", kwargs)
        # Set the number of forwarding cores based on coremask.
        options.add_equals_from_dict(u"nb-cores", u"pmd_nb_cores", kwargs)
        return options
コード例 #22
0
    def get_eal_options(**kwargs):
        """Create EAL parameters options (including -v).

        :param kwargs: Dict of testpmd parameters.
        :type kwargs: dict
        :returns: EAL parameters.
        :rtype: OptionString
        """
        options = OptionString(prefix='-')
        options.add('v')
        # Set the hexadecimal bitmask of the cores to run on.
        options.add_with_value_from_dict('l', 'eal_corelist', kwargs)
        # Set master core.
        options.add_with_value('-master-lcore', '0')
        # Load an external driver. Multiple -d options are allowed.
        options.add_with_value_if_from_dict('d',
                                            '/usr/lib/librte_pmd_virtio.so',
                                            'eal_driver', kwargs, True)
        options.add_if_from_dict('-in-memory', 'eal_in_memory', kwargs, False)
        return options