test suite reviews and discussions
 help / color / mirror / Atom feed
From: "Yao, Lei A" <lei.a.yao@intel.com>
To: "Mo, YufengX" <yufengx.mo@intel.com>, "dts@dpdk.org" <dts@dpdk.org>
Subject: Re: [dts] [PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation script
Date: Tue, 24 Dec 2019 01:56:35 +0000	[thread overview]
Message-ID: <2DBBFF226F7CF64BAFCA79B681719D9549807DA1@shsmsx102.ccr.corp.intel.com> (raw)
In-Reply-To: <20191224015628.11931-5-yufengx.mo@intel.com>



> -----Original Message-----
> From: Mo, YufengX <yufengx.mo@intel.com>
> Sent: Tuesday, December 24, 2019 9:56 AM
> To: dts@dpdk.org; Yao, Lei A <lei.a.yao@intel.com>
> Cc: Mo, YufengX <yufengx.mo@intel.com>
> Subject: [dts][PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation
> script
> 
> 
> A feature allows workload to deliver policy to the host to manage power
> controls such as p-states extends the thinking of the current scheme moving
> away from direct controls to policy controls to avoid latency & jitter penalties.
> Also provides the ability to react faster.
> 
> VM Power Manager would use a hint based mechanism by which a VM can
> communicate to a host based governor about its current processing
> requirements. By mapping VMs virtual CPUs to physical CPUs the Power
> Manager can then make decisions according to some policy as to what power
> state the physical CPUs can transition to.
> 
> VM Agent shall have the ability to send the following policy to host.
> - traffic policy
> - time policy
> 
> VM Agent shall have the ability to send the following hints to host::
> - core disable turbo
> - core enable turbo
> 
> Signed-off-by: yufengmx <yufengx.mo@intel.com>
Acked-by: Lei Yao<lei.a.yao@intel.com>
> ---
>  tests/TestSuite_vm_pw_mgmt_policy.py | 994
> +++++++++++++++++++++++++++
>  1 file changed, 994 insertions(+)
>  create mode 100644 tests/TestSuite_vm_pw_mgmt_policy.py
> 
> diff --git a/tests/TestSuite_vm_pw_mgmt_policy.py
> b/tests/TestSuite_vm_pw_mgmt_policy.py
> new file mode 100644
> index 0000000..b0f1503
> --- /dev/null
> +++ b/tests/TestSuite_vm_pw_mgmt_policy.py
> @@ -0,0 +1,994 @@
> +# BSD LICENSE
> +#
> +# Copyright(c) 2010-2019 Intel Corporation. All rights reserved.
> +# All rights reserved.
> +#
> +# Redistribution and use in source and binary forms, with or without #
> +modification, are permitted provided that the following conditions #
> +are met:
> +#
> +#   * Redistributions of source code must retain the above copyright
> +#     notice, this list of conditions and the following disclaimer.
> +#   * Redistributions in binary form must reproduce the above copyright
> +#     notice, this list of conditions and the following disclaimer in
> +#     the documentation and/or other materials provided with the
> +#     distribution.
> +#   * Neither the name of Intel Corporation nor the names of its
> +#     contributors may be used to endorse or promote products derived
> +#     from this software without specific prior written permission.
> +#
> +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS #
> +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> #
> +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> FOR #
> +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT #
> +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL, #
> +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
> NOT #
> +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> USE, #
> +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> ON ANY #
> +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT #
> +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
> THE USE #
> +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
> DAMAGE.
> +
> +"""
> +DPDK Test suite.
> +virtual power manager policy (traffic/time) test suite.
> +"""
> +import os
> +import re
> +import time
> +import textwrap
> +import random
> +import traceback
> +from itertools import product, izip
> +from datetime import datetime, timedelta from copy import deepcopy
> from
> +pprint import pformat
> +
> +from utils import create_mask as dts_create_mask from test_case import
> +TestCase from pmd_output import PmdOutput from qemu_libvirt import
> +LibvirtKvm from pktgen import TRANSMIT_CONT from exception import
> +VerifyFailure from packet import Packet
> +
> +
> +class TestVmPwMgmtPolicy(TestCase):
> +    # policy mode
> +    TIME = 'TIME'
> +    TRAFFIC = 'TRAFFIC'
> +    # temporary file directory
> +    output_path = '/tmp'
> +
> +    @property
> +    def target_dir(self):
> +        # get absolute directory of target source code
> +        target_dir = '/root' + self.dut.base_dir[1:] \
> +                     if self.dut.base_dir.startswith('~') else \
> +                     self.dut.base_dir
> +        return target_dir
> +
> +    def get_cores_mask(self, config='all', crb=None):
> +        ports_socket = self.dut.get_numa_id(self.dut.get_ports()[0])
> +        mask = dts_create_mask(
> +            self.dut.get_core_list(config, socket=ports_socket))
> +        return mask
> +
> +    def prepare_binary(self, name, host_crb=None):
> +        _host_crb = host_crb if host_crb else self.dut
> +        example_dir = "examples/" + name
> +        out = _host_crb.build_dpdk_apps('./' + example_dir)
> +        self.verify("Error" not in out, "Compilation error")
> +        self.verify("No such" not in out, "Compilation error")
> +        binary_dir = os.path.join(self.target_dir, example_dir, 'build')
> +        cmd = ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5]
> +        exec_file = self.execute_cmds(cmd, name=_host_crb.session.name)
> +        binary_file = os.path.join(binary_dir, exec_file[:-1])
> +        return binary_file
> +
> +    def add_console(self, session):
> +        self.ext_con[session.name] = [
> +            session.send_expect,
> +            session.session.get_output_all]
> +
> +    def get_console(self, name):
> +        default_con_table = {
> +            self.dut.session.name: [
> +                self.dut.send_expect,
> +                self.dut.get_session_output],
> +            self.dut.alt_session.name: [
> +                self.dut.alt_session.send_expect,
> +                self.dut.alt_session.session.get_output_all]}
> +        if name not in default_con_table:
> +            return self.ext_con.get(name) or [None, None]
> +        else:
> +            return default_con_table.get(name)
> +
> +    def execute_cmds(self, cmds, name='dut'):
> +        console, msg_pipe = self.get_console(name)
> +        if len(cmds) == 0:
> +            return
> +        if isinstance(cmds, (str, unicode)):
> +            cmds = [cmds, '# ', 5]
> +        if not isinstance(cmds[0], list):
> +            cmds = [cmds]
> +        outputs = [] if len(cmds) > 1 else ''
> +        for item in cmds:
> +            expected_items = item[1]
> +            if expected_items and isinstance(expected_items, (list, tuple)):
> +                check_output = True
> +                expected_str = expected_items[0] or '# '
> +            else:
> +                check_output = False
> +                expected_str = expected_items or '# '
> +
> +            try:
> +                if len(item) == 3:
> +                    timeout = int(item[2])
> +                    output = console(item[0], expected_str, timeout)
> +                    output = msg_pipe() if not output else output
> +                else:
> +                    output = console(item[0], expected_str)
> +                    output = msg_pipe() if not output else output
> +            except Exception as e:
> +                msg = "execute '{0}' timeout".format(item[0])
> +                raise Exception(msg)
> +            time.sleep(1)
> +            if len(cmds) > 1:
> +                outputs.append(output)
> +            else:
> +                outputs = output
> +
> +        return outputs
> +
> +    def d_con(self, cmds):
> +        return self.execute_cmds(cmds, name=self.dut.session.name)
> +
> +    def d_a_con(self, cmds):
> +        return self.execute_cmds(cmds, name=self.dut.alt_session.name)
> +
> +    def vm_con(self, cmds):
> +        return self.execute_cmds(cmds, name=self.vm_dut.session.name)
> +
> +    def vm_g_con(self, cmds):
> +        return self.execute_cmds(cmds, name=self.guest_con_name)
> +
> +    def config_stream(self, stm_names=None):
> +        dmac = self.vm_dut.get_mac_address(0)
> +        # set streams for traffic
> +        pkt_configs = {
> +            'UDP_1': {
> +                'type': 'UDP',
> +                'pkt_layers': {'ether': {'dst': dmac, }, }, },
> +        }
> +        # create packet for send
> +        streams = []
> +        for stm_name in stm_names:
> +            if stm_name not in pkt_configs.keys():
> +                continue
> +            values = pkt_configs[stm_name]
> +            pkt_type = values.get('type')
> +            pkt_layers = values.get('pkt_layers')
> +            pkt = Packet(pkt_type=pkt_type)
> +            for layer in pkt_layers.keys():
> +                pkt.config_layer(layer, pkt_layers[layer])
> +            streams.append(pkt.pktgen.pkt)
> +
> +        return streams
> +
> +    def add_stream_to_pktgen(self, txport, rxport, send_pkt, option):
> +        stream_ids = []
> +        for pkt in send_pkt:
> +            _option = deepcopy(option)
> +            _option['pcap'] = pkt
> +            stream_id = self.tester.pktgen.add_stream(txport, rxport, pkt)
> +            self.tester.pktgen.config_stream(stream_id, _option)
> +            stream_ids.append(stream_id)
> +        return stream_ids
> +
> +    def send_packets_by_pktgen(self, option):
> +        txport = option.get('tx_intf')
> +        rxport = option.get('rx_intf')
> +        rate_percent = option.get('rate_percent', float(100))
> +        send_pkt = option.get('stream') or []
> +        # clear streams before add new streams
> +        self.tester.pktgen.clear_streams()
> +        # set stream into pktgen
> +        stream_option = {
> +            'stream_config': {
> +                'txmode': {},
> +                'transmit_mode': TRANSMIT_CONT,
> +                'rate': rate_percent, }
> +        }
> +        stream_ids = self.add_stream_to_pktgen(
> +            txport, rxport, send_pkt, stream_option)
> +        # run traffic options
> +        traffic_opt = option.get('traffic_opt')
> +        # run pktgen traffic
> +        result = self.tester.pktgen.measure(stream_ids, traffic_opt)
> +
> +        return result
> +
> +    def get_rate_percent(self, pps):
> +        frame_size = 64
> +        full_pps = self.wirespeed(self.nic, frame_size, 1) * 1000000.0
> +        rate_percent = round((100 * float(pps) / float(full_pps)), 2)
> +        return rate_percent
> +
> +    def run_traffic(self, option):
> +        dut_port = self.dut_ports[self.used_port]
> +        tester_tx_port_id = self.tester.get_local_port(dut_port)
> +        tester_rx_port_id = self.tester.get_local_port(dut_port)
> +        stm_type = option.get('stm_types')
> +        pps = option.get('pps')
> +        rate = self.get_rate_percent(pps)
> +        duration = option.get('duration', None) or 15
> +        ports_topo = {
> +            'tx_intf': tester_tx_port_id,
> +            'rx_intf': tester_rx_port_id,
> +            'stream': self.config_stream(stm_type),
> +            'rate_percent': rate,
> +            'traffic_opt': {
> +                'method': 'throughput',
> +                'duration': duration,
> +            }}
> +        # begin traffic checking
> +        result = self.send_packets_by_pktgen(ports_topo)
> +
> +        return result
> +
> +    def bind_ports_to_sys(self):
> +        for port in self.dut.ports_info:
> +            netdev = port.get('port')
> +            if not netdev:
> +                continue
> +            cur_drv = netdev.get_nic_driver()
> +            netdev.bind_driver(netdev.default_driver)
> +        else:
> +            cur_drv = 'igb_uio'
> +        return cur_drv
> +
> +    def bind_ports_to_dpdk(self, driver):
> +        if not driver:
> +            return
> +        for port in self.dut.ports_info:
> +            netdev = port.get('port')
> +            if not netdev:
> +                continue
> +            cur_drv = netdev.get_nic_driver()
> +            if cur_drv == driver:
> +                continue
> +            netdev.bind_driver(driver)
> +
> +    def init_vms_params(self):
> +        self.vm = self.vcpu_map = self.vm_dut = self.guest_session = \
> +            self.is_guest_on = self.is_vm_on = self.is_vf_set = None
> +        # vm config
> +        self.vm_name = 'vm0'
> +        self.vm_max_ch = 8
> +        self.vm_log_dir = '/tmp/powermonitor'
> +        self.create_powermonitor_folder()
> +
> +    def create_powermonitor_folder(self):
> +        # create temporary folder for power monitor
> +        cmd = 'mkdir -p {0}; chmod 777 {0}'.format(self.vm_log_dir)
> +        self.d_a_con(cmd)
> +
> +    def create_vf(self, driver='default'):
> +        self.dut.generate_sriov_vfs_by_port(self.used_port, 1, driver=driver)
> +        self.is_vf_set = True
> +        sriov_vfs_port = self.dut.ports_info[self.used_port]['vfs_port']
> +        return sriov_vfs_port[0].pci
> +
> +    def destroy_vf(self):
> +        if not self.is_vf_set:
> +            return
> +        self.dut.destroy_sriov_vfs_by_port(self.used_port)
> +        self.is_vf_set = False
> +        port = self.dut.ports_info[self.used_port]['port']
> +        port.bind_driver()
> +
> +    def add_nic_device(self, pci_addr, vm_inst):
> +        vm_params = {
> +            'driver': 'pci-assign',
> +            'driver': 'vfio',
> +            'opt_host': pci_addr,
> +            'guestpci':  '0000:00:07.0'}
> +        vm_inst.set_vm_device(**vm_params)
> +
> +    def start_vm(self):
> +        # set vm initialize parameters
> +        self.init_vms_params()
> +        # start vm
> +        self.vm = LibvirtKvm(self.dut, self.vm_name, self.suite_name)
> +        # pass vf to virtual machine
> +        pci_addr = self.create_vf()
> +        self.add_nic_device(pci_addr, self.vm)
> +        # add channel
> +        ch_name = 'virtio.serial.port.poweragent.{0}'
> +        vm_path = os.path.join(self.vm_log_dir, '{0}.{1}')
> +        for cnt in range(self.vm_max_ch):
> +            channel = {
> +                'path': vm_path.format(self.vm_name, cnt),
> +                'name': ch_name.format(cnt)}
> +            self.vm.add_vm_virtio_serial_channel(**channel)
> +        # set vm default driver
> +        self.vm.def_driver = 'igb_uio'
> +        # boot up vm
> +        self.vm_dut = self.vm.start()
> +        self.is_vm_on = True
> +        self.verify(self.vm_dut, "create vm_dut fail !")
> +        self.add_console(self.vm_dut.session)
> +        # get virtual machine cpu cores
> +        self.vcpu_map = self.vm.get_vm_cpu()
> +
> +    def close_vm(self):
> +        # close vm
> +        if self.is_vm_on:
> +            if self.guest_session:
> +                self.vm_dut.close_session(self.guest_session)
> +                self.guest_session = None
> +            self.vm.stop()
> +            self.is_vm_on = False
> +            self.vm = None
> +            self.dut.virt_exit()
> +            cmd_fmt = 'virsh {0} {1} > /dev/null 2>&1'.format
> +            cmds = [
> +                [cmd_fmt('shutdown', self.vm_name), '# '],
> +                [cmd_fmt('undefine', self.vm_name), '# '], ]
> +            self.d_a_con(cmds)
> +        # destroy vf
> +        if self.is_vf_set:
> +            self.destroy_vf()
> +
> +    def init_vm_power_mgr(self):
> +        self.vm_power_mgr = self.prepare_binary('vm_power_manager')
> +
> +    def start_vm_power_mgr(self):
> +        eal_option = (
> +            '-v '
> +            '-c {core_mask} '
> +            '-n {mem_channel} ').format(**{
> +                'core_mask': self.get_cores_mask("1S/3C/1T"),
> +                'mem_channel': self.dut.get_memory_channels(), })
> +        prompt = 'vmpower>'
> +        cmd = [' '.join([self.vm_power_mgr, eal_option]), prompt, 30]
> +        self.d_con(cmd)
> +        self.is_mgr_on = True
> +
> +    def set_vm_power_mgr(self):
> +        vm_name = self.vm_name
> +        cmds = [
> +            "add_vm %s" % vm_name,
> +            "add_channels %s all" % vm_name,
> +            'set_channel_status %s all enabled' % vm_name,
> +            "show_vm %s" % vm_name]
> +        prompt = 'vmpower>'
> +        self.d_con([[cmd, prompt] for cmd in cmds])
> +
> +    def close_vm_power_mgr(self):
> +        if not self.is_mgr_on:
> +            return
> +        self.d_con(['quit', '# ', 15])
> +        self.is_mgr_on = False
> +
> +    def init_guest_mgr(self):
> +        name = 'vm_power_manager/guest_cli'
> +        self.guest_cli = self.prepare_binary(name, host_crb=self.vm_dut)
> +        self.guest_con_name = \
> +            '_'.join([self.vm_dut.NAME, name.replace('/', '-')])
> +        self.guest_session = self.vm_dut.create_session(self.guest_con_name)
> +        self.add_console(self.guest_session)
> +
> +    def start_guest_mgr(self, cmd_option):
> +        prompt = r"vmpower\(guest\)>"
> +        option = (
> +            '-v '
> +            '-c {core_mask} '
> +            '-n {memory_channel} '
> +            '--file-prefix={file_prefix} '
> +            '-- ').format(**{
> +                'core_mask': '0xff',
> +                'memory_channel': self.vm_dut.get_memory_channels(),
> +                'file_prefix': 'vmpower2',
> +            }) + cmd_option
> +        guest_cmd = ' '.join([self.guest_cli, option])
> +        self.vm_g_con([guest_cmd, prompt, 120])
> +        self.is_guest_on = True
> +
> +    def guest_send_policy(self):
> +        self.vm_g_con(['send_policy now', r"vmpower\(guest\)>", 20])
> +        # it would be a problem measuring less than 500ms after sending
> +        # policy, so wait 2 second here.
> +        time.sleep(2)
> +
> +    def guest_set_vm_turbo_status(self, vcpu, status):
> +        vcpu_index = self.vcpu_map.index(str(self.check_core))
> +        cmd = ["set_cpu_freq %d %s" % (vcpu_index, status),
> +               "vmpower\(guest\)>", 5]
> +        output = self.vm_g_con(cmd)
> +        self.verify(
> +            'ACK received for message sent to host'.lower() in output.lower(),
> +            'vm guest failed to send message host')
> +
> +    def close_guest_mgr(self):
> +        if not self.is_guest_on:
> +            return
> +        self.vm_g_con("quit")
> +        self.is_guest_on = False
> +
> +    def init_vm_testpmd(self):
> +        self.vm_testpmd = PmdOutput(self.vm_dut)
> +
> +    def start_vm_testpmd(self):
> +        eal_param = (
> +            '-v '
> +            '-m {memsize} '
> +            '--file-prefix={file-prefix}').format(**{
> +                'file-prefix': 'vmpower1',
> +                'memsize': 1024, })
> +        self.vm_testpmd.start_testpmd(
> +            "Default",
> +            param='--port-topology=loop',
> +            eal_param=eal_param)
> +        self.is_pmd_on = True
> +
> +    def set_vm_testpmd(self):
> +        cmds = [
> +            'set fwd mac',
> +            'set promisc all on',
> +            'port start all',
> +            'start']
> +        self.vm_testpmd.execute_cmd(cmds)
> +
> +    def close_vm_testpmd(self):
> +        if not self.is_pmd_on:
> +            return
> +        self.vm_testpmd.quit()
> +        self.is_pmd_on = False
> +
> +    def init_query_script(self):
> +        self.query_cur_freq = os.path.join(
> +            self.output_path, 'cpu_cur_freq.log')
> +        script_content = textwrap.dedent("""
> +            # $1: core number
> +            sleep 8
> +            echo "begin get core "$1" current frequency " > {0}
> +            while :
> +            do
> +                sleep 1
> +                cat /sys/devices/system/cpu/cpu$1/cpufreq/cpuinfo_cur_freq >>
> {0}
> +            done
> +        """).format(self.query_cur_freq)
> +        fileName = 'vm_power_core.sh'
> +        query_script = os.path.join(self.output_path, fileName)
> +        with open(query_script, 'wb') as fp:
> +            fp.write('#!/bin/bash' + os.linesep + script_content)
> +        self.dut.session.copy_file_to(query_script, self.target_dir)
> +        self.query_tool = ';'.join([
> +            'cd {}'.format(self.target_dir),
> +            'chmod 777 {}'.format(fileName),
> +            './' + fileName])
> +
> +    def start_query(self, core):
> +        cmd = self.query_tool + ' {0} > /dev/null 2>&1 &'.format(core)
> +        self.d_a_con(cmd)
> +        self.is_query_on = True
> +
> +    def stop_query(self):
> +        if not self.is_query_on:
> +            return
> +        ps_name = os.path.basename(self.query_tool)
> +        cmd = "ps aux | grep -i '%s' | grep -v grep | awk {'print $2'}" % (
> +            ps_name)
> +        out = self.d_a_con(cmd)
> +        if out != "":
> +            cmd = 'pkill -f {}'.format(ps_name)
> +            self.d_a_con(cmd)
> +        self.is_query_on = False
> +        self.dut.session.copy_file_from(self.query_cur_freq,
> + self.output_path)
> +
> +    def set_desired_time(self, time_stage):
> +        if not time_stage:
> +            return None * 2
> +        # only select one random time stage
> +        random_index = random.randint(0, len(time_stage) - 1)
> +        timestamp = time_stage[random_index]
> +        ori_sys_time = datetime.now()
> +        msg = "dut system original time is {0}".format(ori_sys_time)
> +        self.logger.debug(msg)
> +        # set system time to a desired time for policy
> +        msg = "set timestamp {0}".format(timestamp)
> +        self.logger.debug(msg)
> +        date_tool = "date"
> +        cmd = ';'.join([
> +            "{0}",
> +            "{0} -s '{1}'",
> +            "clock -w"]).format(date_tool, timestamp)
> +        self.d_a_con(cmd)
> +        cmd = "{0} '+%H:00'".format(date_tool)
> +        output = self.d_a_con(cmd)
> +        msg = "desired time fails to set" \
> +              if output.strip() != timestamp \
> +              else "desired time set successful"
> +        self.logger.info(msg)
> +        # get begin time stamp
> +        pre_time = datetime.now()
> +        # when dut/tester are on the same node, separate into two timestamp
> +        return pre_time, ori_sys_time
> +
> +    def restore_system_time(self, pre_time, ori_sys_time):
> +        if not ori_sys_time:
> +            return
> +        date_tool = "date"
> +        cur_time = datetime.now()
> +        interval = (cur_time - pre_time).seconds
> +        timestamp = ori_sys_time + timedelta(seconds=interval)
> +        FMT = '%Y-%m-%d %H:%M:%S'
> +        real_time = timestamp.strftime(FMT)
> +        cmd = ';'.join([
> +            "{0}",
> +            "{0} -s '{1}'",
> +            "clock -w",
> +            "{0}", ]).format(date_tool, real_time)
> +        self.d_a_con(cmd)
> +
> +    def preset_core_freq(self):
> +        info = self.cpu_info.get(self.check_core, {})
> +        freq = info.get('scaling_available_frequencies')[-3]
> +        cmd = ("cpupower -c all frequency-set -f {} "
> +               "> /dev/null 2>&1").format(freq)
> +        self.d_a_con(cmd)
> +
> +    def get_all_cpu_attrs(self):
> +        ''' get all cpus attribute '''
> +        key_values = [
> +            'scaling_max_freq',
> +            'scaling_available_frequencies',
> +            'scaling_min_freq']
> +        freq = '/sys/devices/system/cpu/cpu{0}/cpufreq/{1}'.format
> +        cpu_topos = self.dut.get_all_cores()
> +        cpu_info = {}
> +        for cpu_topo in cpu_topos:
> +            cpu_id = int(cpu_topo.get('thread'))
> +            cpu_info[cpu_id] = {
> +                'socket': cpu_topo.get('socket'),
> +                'core': cpu_topo.get('core')}
> +
> +        for key_value in key_values:
> +            cmds = []
> +            for cpu_id in sorted(cpu_info.keys()):
> +                cmds.append('cat {0}'.format(freq(cpu_id, key_value)))
> +            output = self.d_a_con(';'.join(cmds))
> +            freqs = [int(item) for item in output.splitlines()] \
> +                if key_value != 'scaling_available_frequencies' else \
> +                    [item for item in output.splitlines()]
> +            for index, cpu_id in enumerate(sorted(cpu_info.keys())):
> +                if key_value == 'scaling_available_frequencies':
> +                    cpu_info[cpu_id][key_value] = freqs[index]
> +                cpu_info[cpu_id][key_value] = \
> +                    [int(item) for item in sorted(freqs[index].split())] \
> +                    if key_value == 'scaling_available_frequencies' else \
> +                    freqs[index]
> +
> +        return cpu_info
> +
> +    def convert_to_values(self, output):
> +        pdata_s = "^\d+$"
> +        ret = re.match(pdata_s, output)
> +        if ret:
> +            return int(output)
> +        pdata_m = "(\d+ )+"
> +        ret = re.match(pdata_m, output)
> +        if ret:
> +            return [int(item) for item in output.split()]
> +        pdata_m = "^\w+$"
> +        ret = re.match(pdata_m, output)
> +        if ret:
> +            return output
> +        pdata_m = "(\w+ )+"
> +        ret = re.match(pdata_m, output)
> +        if ret:
> +            return [item for item in output.split()]
> +
> +    def get_sys_power_driver(self):
> +        drv_file = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver"
> +        output = self.d_a_con('cat ' + drv_file)
> +        if not output:
> +            msg = 'unknown power driver'
> +            self.verify(False, msg)
> +        drv_name = output.splitlines()[0].strip()
> +        return drv_name
> +
> +    def get_linux_cpu_attrs(self, core_num, name="cpuinfo_cur_freq"):
> +        freq_path = "/sys/devices/system/cpu/cpu{0}/cpufreq/{1}".format(
> +            core_num, name)
> +        output = self.d_a_con("cat %s" % freq_path)
> +        return self.convert_to_values(output)
> +
> +    def set_single_core_turbo(self, vcpu, status):
> +        '''
> +        status: enable_turbo | disable_turbo
> +        '''
> +        dut_core_index = self.vcpu_map[vcpu]
> +        self.guest_set_vm_turbo_status(vcpu, status)
> +        return int(dut_core_index)
> +
> +    def get_expected_turbo_freq(self, core_index, status='disable'):
> +        info = self.cpu_info.get(core_index, {})
> +        value = info.get('scaling_available_frequencies')
> +        expected_freq = value[-2] if status == 'disable' else value[-1]
> +        return expected_freq
> +
> +    def check_dut_core_turbo_enable(self, vcpu):
> +        dut_core_index = self.set_single_core_turbo(vcpu, 'enable_turbo')
> +        cur_freq = self.get_linux_cpu_attrs(dut_core_index)
> +        expected_freq = self.get_expected_turbo_freq(dut_core_index,
> 'enable')
> +        if cur_freq != expected_freq:
> +            msg = ("core <{0}> turbo status: cur frequency is <{1}> "
> +                   "not as expected frequency <{2}>").format(
> +                        dut_core_index, cur_freq, expected_freq)
> +            raise VerifyFailure(msg)
> +        self.logger.info(
> +            "core <{0}> turbo status set
> + successful".format(dut_core_index))
> +
> +    def check_dut_core_turbo_disable(self, vcpu):
> +        dut_core_index = self.set_single_core_turbo(vcpu, 'disable_turbo')
> +        cur_freq = self.get_linux_cpu_attrs(dut_core_index)
> +        expected_freq = self.get_expected_turbo_freq(dut_core_index,
> 'disable')
> +        if cur_freq != expected_freq:
> +            msg = ("core <{0}> turbo status: cur frequency is <{1}> "
> +                   "not as expected frequency <{2}>").format(
> +                        dut_core_index, cur_freq, expected_freq)
> +            raise VerifyFailure(msg)
> +        self.logger.info(
> +            "core <{0}> turbo status disable
> + successful".format(dut_core_index))
> +
> +    def get_expected_freq(self, core_index, check_item):
> +        freqs = {
> +            'max': 'scaling_max_freq',
> +            'medium': 'scaling_available_frequencies',
> +            'min': 'scaling_min_freq'}
> +        info = self.cpu_info.get(core_index, {})
> +        value = info.get(freqs.get(check_item))
> +        expected_freq = value if check_item != 'medium' else \
> +            sorted(value)[len(value) / 2]
> +        return expected_freq
> +
> +    def check_core_freq(self, content):
> +        '''
> +        check core running frequency is the expected status
> +        high workload: maximum cpu frequency
> +        media workload: medium cpu frequency
> +        low workload: minimum cpu frequency
> +        '''
> +        check_item = content.get('check')
> +        query_cur_freq = os.path.join(
> +            self.output_path, os.path.basename(self.query_cur_freq))
> +        with open(query_cur_freq, 'rb') as fp:
> +            content = fp.read()
> +        self.logger.debug(content)
> +        real_freq = int(content.splitlines()[2].strip())
> +        expected_freq = self.get_expected_freq(self.check_core, check_item)
> +        msg = (
> +            'core <{0}> freq <{1}> are not '
> +            'the expected frequency <{2}>').format(
> +                self.check_core, real_freq, expected_freq)
> +        self.verify(real_freq == expected_freq, msg)
> +        msg = 'core <{0}> are running on the expected frequency <{1}>'.format(
> +            self.check_core, expected_freq)
> +        self.logger.info(msg)
> +
> +    def run_test_pre(self, policy_name):
> +        # boot up binary processes
> +        self.start_vm_power_mgr()
> +        # set binary process command
> +        self.set_vm_power_mgr()
> +        if policy_name == self.TRAFFIC:
> +            # boot up binary processes
> +            self.start_vm_testpmd()
> +            # set binary process command
> +            self.set_vm_testpmd()
> +
> +    def run_test_post(self):
> +        # close all binary processes
> +        self.stop_query()
> +        self.close_vm_testpmd()
> +        self.close_guest_mgr()
> +        self.close_vm_power_mgr()
> +
> +    def run_guest_pre(self, content):
> +        self.preset_core_freq()
> +        # boot up binary processes
> +        self.start_guest_mgr(content.get('option', ''))
> +        # set binary process command
> +        self.guest_send_policy()
> +        # start query
> +        self.start_query(self.check_core)
> +
> +    def run_guest_post(self):
> +        # close guest
> +        self.stop_query()
> +        self.close_guest_mgr()
> +
> +    def traffic_policy(self, name, content):
> +        expected_pps = content['pps']
> +        test_pps = random.randint(expected_pps[0], expected_pps[1] - 1) \
> +            if isinstance(expected_pps, list) \
> +            else expected_pps
> +        msg = "run traffic with pps {0}".format(test_pps)
> +        self.logger.info(msg)
> +        info = {
> +            'stm_types': ['UDP_1'],
> +            'pps': expected_pps}
> +        # run traffic
> +        self.run_traffic(info)
> +
> +    def run_policy(self, name, content):
> +        """ Measure cpu frequency fluctuate with work load """
> +        except_content = None
> +        try:
> +            self.run_guest_pre(content)
> +            # run traffic
> +            if name == self.TRAFFIC:
> +                self.traffic_policy(name, content)
> +            else:
> +                # run time policy, wait 10 second to make sure system ready
> +                # and get enough query data
> +                time.sleep(15)
> +            self.stop_query()
> +            # check cpu core status
> +            self.check_core_freq(content)
> +        except Exception as e:
> +            self.logger.error(traceback.format_exc())
> +            except_content = e
> +        finally:
> +            # clear testing environment
> +            self.run_guest_post()
> +
> +        if except_content:
> +            raise VerifyFailure(except_content)
> +
> +    def get_policy_test_content(self, policy_name, vm_name, edge=False):
> +        '''
> +        -n or --vm-name
> +           sets the name of the vm to be used by the host OS.
> +        -b or --busy-hours
> +           sets the list of hours that are predicted to be busy
> +        -q or --quiet-hours
> +           sets the list of hours that are predicted to be quiet
> +        -l or --vcpu-list
> +           sets the list of vcpus to monitor
> +        -o or --policy
> +           sets the default policy type
> +              ``TIME``
> +              ``TRAFFIC``
> +
> +        The format of the hours or list parameters is a comma-separated
> +        list of integers, which can take the form of
> +           a. x    e.g. --vcpu-list=1
> +           b. x,y  e.g. --quiet-hours=3,4
> +           c. x-y  e.g. --busy-hours=9-12
> +           d. combination of above (e.g. --busy-hours=4,5-7,9)
> +        '''
> +        policy_opt = '--policy={policy}'  # four types
> +        vm_opt = '--vm-name={vm}'
> +        vcpu_opt = '--vcpu-list={vcpus}'  # full cores/one core/multiple cores
> +        time_b_opt = '--busy-hours={hours}'  # all day/one hour/mixed range
> +        time_q_opt = '--quiet-hours={hours}'  # all day/one hour/mixed range
> +        # common option used by all policy
> +        opt_fmt = [vm_opt, policy_opt, vcpu_opt]
> +        # core option
> +        max_cores = len(self.vcpu_map)
> +        max_cores_list = ",".join([str(num) for num in range(max_cores)])
> +        cores_range = [max_cores_list] if edge else ['0', max_cores_list]
> +        # guest mgr option format configuration
> +        guest_opt = {
> +            'opt_fmt': opt_fmt,
> +            'option': {
> +                'vm': [vm_name],
> +                'vcpus': cores_range,
> +            }
> +        }
> +        # testing content
> +        policy_configs = {
> +            # traffic policy option
> +            self.TRAFFIC: [
> +                # low
> +                {'sys_hours': ['08:00', '10:00'],
> +                 'pps': 97000,  # below 1800000,
> +                 'check': 'min'},
> +            ],
> +            # time policy option
> +            self.TIME: [
> +                # quiet hours
> +                {'cmd': {
> +                 'opt_fmt': [time_q_opt],
> +                 'option':{
> +                     # use 23:00 as default time to run test
> +                     'hours': ['23'] if edge else ['23', '0-23', '4,5-7,23']}},
> +                 'sys_hours': ['23:00'],
> +                 'check': 'min'},
> +                # busy hours
> +                {'cmd': {
> +                 'opt_fmt': [time_b_opt],
> +                 'option':{
> +                     'hours': ['23'] if edge else ['23', '0-23', '4,5-7,23']}},
> +                    'sys_hours': ['23:00'],
> +                    'check': 'max'},
> +            ],
> +        }
> +
> +        select_config = policy_configs.get(policy_name)
> +        # make combine testing options
> +        test_content = []
> +        for config in select_config:
> +            _common_config = deepcopy(guest_opt)
> +            if 'cmd' in config:
> +                option_cfg = config.get('cmd')
> +                _common_config['opt_fmt'] += option_cfg.get('opt_fmt', [])
> +                _common_config['option'].update(option_cfg['option'])
> +                config.pop('cmd')
> +            values = _common_config['option'].values()
> +            keys = _common_config['option'].keys()
> +            opt_fmt = _common_config['opt_fmt']
> +            for item in product(*values):
> +                _options = dict(izip(keys, item))
> +                _options['policy'] = policy_name
> +                _opt_fmt = " ".join(opt_fmt)
> +                _config = deepcopy(config)
> +                _config['option'] = _opt_fmt.format(**_options)
> +                _config['vcpus'] = _options['vcpus']
> +                test_content.append(_config)
> +
> +        return test_content
> +
> +    def verify_policy(self, policy_name):
> +        test_contents = self.get_policy_test_content(
> +            policy_name, self.vm_name, edge=not self.full_test)
> +        msg = "begin test policy <{}> ...".format(policy_name)
> +        self.logger.info(msg)
> +        except_content = None
> +        try:
> +            self.run_test_pre(policy_name)
> +            for content in test_contents:
> +                self.logger.debug(pformat(content))
> +                # set system time
> +                pre_time, ori_sys_time = \
> +                    self.set_desired_time(content.get('sys_hours'))
> +                # run policy testing
> +                self.run_policy(policy_name, content)
> +                # restore system time
> +                self.restore_system_time(pre_time, ori_sys_time)
> +        except Exception as e:
> +            self.logger.error(traceback.format_exc())
> +            except_content = e
> +        finally:
> +            self.run_test_post()
> +
> +        if except_content:
> +            raise VerifyFailure(except_content)
> +
> +        msg = "test policy <{}> successful !".format(policy_name)
> +        self.logger.info(msg)
> +
> +    def verify_turbo_command(self, status):
> +        msg = "begin test turbo <{}> ...".format(status)
> +        self.logger.info(msg)
> +        except_content = None
> +        test_content = self.get_policy_test_content(
> +            self.TIME, self.vm_name, edge=True)[0]
> +        try:
> +            self.run_test_pre('turbo')
> +            self.start_guest_mgr(test_content.get('option'))
> +            check_func = getattr(
> +                self, 'check_dut_core_turbo_{}'.format(status))
> +            check_func(0)
> +        except Exception as e:
> +            self.logger.error(traceback.format_exc())
> +            except_content = e
> +        finally:
> +            self.run_test_post()
> +
> +        if except_content:
> +            raise VerifyFailure(except_content)
> +
> +        msg = "test turbo <{}> successful !".format(status)
> +        self.logger.info(msg)
> +
> +    def verify_power_driver(self):
> +        expected_drv = 'acpi-cpufreq'
> +        power_drv = self.get_sys_power_driver()
> +        msg = "{0} should work with {1} driver on DUT".format(
> +            self.suite_name, expected_drv)
> +        self.verify(power_drv == expected_drv, msg)
> +
> +    def verify_cpupower_tool(self):
> +        name = 'cpupower'
> +        cmd = "whereis {} > /dev/null 2>&1; echo $?".format(name)
> +        output = self.d_a_con(cmd)
> +        status = True if output and output.strip() == "0" else False
> +        msg = '<{}> tool have not installed on DUT'.format(name)
> +        self.verify(status, msg)
> +
> +    def preset_test_environment(self):
> +        self.is_mgr_on = self.is_pmd_on = self.is_query_on = None
> +        self.ext_con = {}
> +        # get cpu cores information
> +        self.dut.init_core_list_uncached_linux()
> +        self.cpu_info = self.get_all_cpu_attrs()
> +        # port management
> +        self.cur_drv = self.bind_ports_to_sys()
> +        self.used_port = 0
> +        # modprobe msr module to let the application can get the CPU HW info
> +        self.d_a_con('modprobe msr')
> +        self.d_a_con('cpupower frequency-set -g userspace > /dev/null 2>&1')
> +        # boot up vm
> +        self.start_vm()
> +        # init binary/tools
> +        self.init_vm_power_mgr()
> +        self.init_vm_testpmd()
> +        self.init_guest_mgr()
> +        self.init_query_script()
> +        # set branch ratio test value
> +        self.check_core = int(self.vcpu_map[0])
> +        # used to control testing range. When run with full test, cover all
> +        # possible command line options combination, it will be long time.
> +        self.full_test = False
> +    #
> +    # Test cases.
> +    #
> +    def set_up_all(self):
> +        """
> +        Run at the start of each test suite.
> +        """
> +        self.dut_ports = self.dut.get_ports(self.nic)
> +        self.verify(len(self.dut_ports) >= 1, "Not enough ports")
> +        self.verify_cpupower_tool()
> +        self.verify_power_driver()
> +        # prepare testing environment
> +        self.preset_test_environment()
> +
> +    def tear_down_all(self):
> +        """
> +        Run after each test suite.
> +        """
> +        self.close_vm()
> +        self.bind_ports_to_dpdk(self.cur_drv)
> +
> +    def set_up(self):
> +        """
> +        Run before each test case.
> +        """
> +        pass
> +
> +    def tear_down(self):
> +        """
> +        Run after each test case.
> +        """
> +        self.vm_dut.kill_all()
> +        self.dut.kill_all()
> +
> +    def test_perf_turbo_enable(self):
> +        """
> +        verify turbo enable command
> +        """
> +        self.verify_turbo_command('enable')
> +
> +    def test_perf_turbo_disable(self):
> +        """
> +        verify turbo disable command
> +        """
> +        self.verify_turbo_command('disable')
> +
> +    def test_perf_policy_traffic(self):
> +        """
> +        Measure cpu frequency fluctuate with traffic policy
> +        """
> +        self.verify_policy(self.TRAFFIC)
> +
> +    def test_perf_policy_time(self):
> +        """
> +        Measure cpu frequency fluctuate with time policy
> +        """
> +        self.verify_policy(self.TIME)
> --
> 2.21.0


  reply	other threads:[~2019-12-24  1:56 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-24  1:56 [dts] [PATCH V4 0/4] vm_pw_mgmt_policy: upload suite test plan and script yufengmx
2019-12-24  1:56 ` [dts] [PATCH V4 1/4] conf/vm_pw_mgmt_policy: virtual machine configuration yufengmx
2019-12-24  1:55   ` Yao, Lei A
2019-12-24  1:56 ` [dts] [PATCH V4 2/4] test_plans/vm_pw_mgmt_policy: add test plan index yufengmx
2019-12-24  1:55   ` Yao, Lei A
2019-12-24  1:56 ` [dts] [PATCH V4 3/4] test_plans/vm_pw_mgmt_policy: upload test plan yufengmx
2019-12-24  1:56   ` Yao, Lei A
2019-12-24  1:56 ` [dts] [PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation script yufengmx
2019-12-24  1:56   ` Yao, Lei A [this message]
2019-12-27  6:29 ` [dts] [PATCH V4 0/4] vm_pw_mgmt_policy: upload suite test plan and script Tu, Lijuan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2DBBFF226F7CF64BAFCA79B681719D9549807DA1@shsmsx102.ccr.corp.intel.com \
    --to=lei.a.yao@intel.com \
    --cc=dts@dpdk.org \
    --cc=yufengx.mo@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).