From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 34AE2A04DD; Tue, 24 Dec 2019 02:56:41 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 2B41E2BA3; Tue, 24 Dec 2019 02:56:41 +0100 (CET) Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by dpdk.org (Postfix) with ESMTP id 9D7491252 for ; Tue, 24 Dec 2019 02:56:39 +0100 (CET) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 23 Dec 2019 17:56:38 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.69,349,1571727600"; d="scan'208";a="219663714" Received: from fmsmsx108.amr.corp.intel.com ([10.18.124.206]) by orsmga003.jf.intel.com with ESMTP; 23 Dec 2019 17:56:38 -0800 Received: from fmsmsx125.amr.corp.intel.com (10.18.125.40) by FMSMSX108.amr.corp.intel.com (10.18.124.206) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 23 Dec 2019 17:56:38 -0800 Received: from shsmsx106.ccr.corp.intel.com (10.239.4.159) by FMSMSX125.amr.corp.intel.com (10.18.125.40) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 23 Dec 2019 17:56:37 -0800 Received: from shsmsx102.ccr.corp.intel.com ([169.254.2.109]) by SHSMSX106.ccr.corp.intel.com ([169.254.10.236]) with mapi id 14.03.0439.000; Tue, 24 Dec 2019 09:56:36 +0800 From: "Yao, Lei A" To: "Mo, YufengX" , "dts@dpdk.org" Thread-Topic: [dts][PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation script Thread-Index: AQHVuf0AqXYQQzQ4e0aLaRVejBZMl6fIhq+w Date: Tue, 24 Dec 2019 01:56:35 +0000 Message-ID: <2DBBFF226F7CF64BAFCA79B681719D9549807DA1@shsmsx102.ccr.corp.intel.com> References: <20191224015628.11931-1-yufengx.mo@intel.com> <20191224015628.11931-5-yufengx.mo@intel.com> In-Reply-To: <20191224015628.11931-5-yufengx.mo@intel.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiNDQ5ZDlmODMtNDA2Ni00ZWEwLTg3MjktZmVjZTIxMTZjYzcyIiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoiMnZcL1JiM2RTZGlIOUpNQVM1bEhKbkVuajJHWTV4bzZwRXpQVGtETG9zT01BMnluRko3N0VPdkNEeXoxbjVPUDQifQ== x-ctpclassification: CTP_NT dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation script X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" > -----Original Message----- > From: Mo, YufengX > Sent: Tuesday, December 24, 2019 9:56 AM > To: dts@dpdk.org; Yao, Lei A > Cc: Mo, YufengX > Subject: [dts][PATCH V4 4/4] tests/vm_pw_mgmt_policy: upload automation > script >=20 >=20 > 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 movi= ng > away from direct controls to policy controls to avoid latency & jitter pe= nalties. > Also provides the ability to react faster. >=20 > 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. >=20 > VM Agent shall have the ability to send the following policy to host. > - traffic policy > - time policy >=20 > VM Agent shall have the ability to send the following hints to host:: > - core disable turbo > - core enable turbo >=20 > Signed-off-by: yufengmx Acked-by: Lei Yao > --- > tests/TestSuite_vm_pw_mgmt_policy.py | 994 > +++++++++++++++++++++++++++ > 1 file changed, 994 insertions(+) > create mode 100644 tests/TestSuite_vm_pw_mgmt_policy.py >=20 > 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 =3D 'TIME' > + TRAFFIC =3D 'TRAFFIC' > + # temporary file directory > + output_path =3D '/tmp' > + > + @property > + def target_dir(self): > + # get absolute directory of target source code > + target_dir =3D '/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=3D'all', crb=3DNone): > + ports_socket =3D self.dut.get_numa_id(self.dut.get_ports()[0]) > + mask =3D dts_create_mask( > + self.dut.get_core_list(config, socket=3Dports_socket)) > + return mask > + > + def prepare_binary(self, name, host_crb=3DNone): > + _host_crb =3D host_crb if host_crb else self.dut > + example_dir =3D "examples/" + name > + out =3D _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 =3D os.path.join(self.target_dir, example_dir, 'build= ') > + cmd =3D ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5] > + exec_file =3D self.execute_cmds(cmd, name=3D_host_crb.session.na= me) > + binary_file =3D os.path.join(binary_dir, exec_file[:-1]) > + return binary_file > + > + def add_console(self, session): > + self.ext_con[session.name] =3D [ > + session.send_expect, > + session.session.get_output_all] > + > + def get_console(self, name): > + default_con_table =3D { > + 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=3D'dut'): > + console, msg_pipe =3D self.get_console(name) > + if len(cmds) =3D=3D 0: > + return > + if isinstance(cmds, (str, unicode)): > + cmds =3D [cmds, '# ', 5] > + if not isinstance(cmds[0], list): > + cmds =3D [cmds] > + outputs =3D [] if len(cmds) > 1 else '' > + for item in cmds: > + expected_items =3D item[1] > + if expected_items and isinstance(expected_items, (list, tupl= e)): > + check_output =3D True > + expected_str =3D expected_items[0] or '# ' > + else: > + check_output =3D False > + expected_str =3D expected_items or '# ' > + > + try: > + if len(item) =3D=3D 3: > + timeout =3D int(item[2]) > + output =3D console(item[0], expected_str, timeout) > + output =3D msg_pipe() if not output else output > + else: > + output =3D console(item[0], expected_str) > + output =3D msg_pipe() if not output else output > + except Exception as e: > + msg =3D "execute '{0}' timeout".format(item[0]) > + raise Exception(msg) > + time.sleep(1) > + if len(cmds) > 1: > + outputs.append(output) > + else: > + outputs =3D output > + > + return outputs > + > + def d_con(self, cmds): > + return self.execute_cmds(cmds, name=3Dself.dut.session.name) > + > + def d_a_con(self, cmds): > + return self.execute_cmds(cmds, name=3Dself.dut.alt_session.name) > + > + def vm_con(self, cmds): > + return self.execute_cmds(cmds, name=3Dself.vm_dut.session.name) > + > + def vm_g_con(self, cmds): > + return self.execute_cmds(cmds, name=3Dself.guest_con_name) > + > + def config_stream(self, stm_names=3DNone): > + dmac =3D self.vm_dut.get_mac_address(0) > + # set streams for traffic > + pkt_configs =3D { > + 'UDP_1': { > + 'type': 'UDP', > + 'pkt_layers': {'ether': {'dst': dmac, }, }, }, > + } > + # create packet for send > + streams =3D [] > + for stm_name in stm_names: > + if stm_name not in pkt_configs.keys(): > + continue > + values =3D pkt_configs[stm_name] > + pkt_type =3D values.get('type') > + pkt_layers =3D values.get('pkt_layers') > + pkt =3D Packet(pkt_type=3Dpkt_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 =3D [] > + for pkt in send_pkt: > + _option =3D deepcopy(option) > + _option['pcap'] =3D pkt > + stream_id =3D 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 =3D option.get('tx_intf') > + rxport =3D option.get('rx_intf') > + rate_percent =3D option.get('rate_percent', float(100)) > + send_pkt =3D option.get('stream') or [] > + # clear streams before add new streams > + self.tester.pktgen.clear_streams() > + # set stream into pktgen > + stream_option =3D { > + 'stream_config': { > + 'txmode': {}, > + 'transmit_mode': TRANSMIT_CONT, > + 'rate': rate_percent, } > + } > + stream_ids =3D self.add_stream_to_pktgen( > + txport, rxport, send_pkt, stream_option) > + # run traffic options > + traffic_opt =3D option.get('traffic_opt') > + # run pktgen traffic > + result =3D self.tester.pktgen.measure(stream_ids, traffic_opt) > + > + return result > + > + def get_rate_percent(self, pps): > + frame_size =3D 64 > + full_pps =3D self.wirespeed(self.nic, frame_size, 1) * 1000000.0 > + rate_percent =3D round((100 * float(pps) / float(full_pps)), 2) > + return rate_percent > + > + def run_traffic(self, option): > + dut_port =3D self.dut_ports[self.used_port] > + tester_tx_port_id =3D self.tester.get_local_port(dut_port) > + tester_rx_port_id =3D self.tester.get_local_port(dut_port) > + stm_type =3D option.get('stm_types') > + pps =3D option.get('pps') > + rate =3D self.get_rate_percent(pps) > + duration =3D option.get('duration', None) or 15 > + ports_topo =3D { > + '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 =3D self.send_packets_by_pktgen(ports_topo) > + > + return result > + > + def bind_ports_to_sys(self): > + for port in self.dut.ports_info: > + netdev =3D port.get('port') > + if not netdev: > + continue > + cur_drv =3D netdev.get_nic_driver() > + netdev.bind_driver(netdev.default_driver) > + else: > + cur_drv =3D 'igb_uio' > + return cur_drv > + > + def bind_ports_to_dpdk(self, driver): > + if not driver: > + return > + for port in self.dut.ports_info: > + netdev =3D port.get('port') > + if not netdev: > + continue > + cur_drv =3D netdev.get_nic_driver() > + if cur_drv =3D=3D driver: > + continue > + netdev.bind_driver(driver) > + > + def init_vms_params(self): > + self.vm =3D self.vcpu_map =3D self.vm_dut =3D self.guest_session= =3D \ > + self.is_guest_on =3D self.is_vm_on =3D self.is_vf_set =3D No= ne > + # vm config > + self.vm_name =3D 'vm0' > + self.vm_max_ch =3D 8 > + self.vm_log_dir =3D '/tmp/powermonitor' > + self.create_powermonitor_folder() > + > + def create_powermonitor_folder(self): > + # create temporary folder for power monitor > + cmd =3D 'mkdir -p {0}; chmod 777 {0}'.format(self.vm_log_dir) > + self.d_a_con(cmd) > + > + def create_vf(self, driver=3D'default'): > + self.dut.generate_sriov_vfs_by_port(self.used_port, 1, driver=3D= driver) > + self.is_vf_set =3D True > + sriov_vfs_port =3D 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 =3D False > + port =3D self.dut.ports_info[self.used_port]['port'] > + port.bind_driver() > + > + def add_nic_device(self, pci_addr, vm_inst): > + vm_params =3D { > + '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 =3D LibvirtKvm(self.dut, self.vm_name, self.suite_name) > + # pass vf to virtual machine > + pci_addr =3D self.create_vf() > + self.add_nic_device(pci_addr, self.vm) > + # add channel > + ch_name =3D 'virtio.serial.port.poweragent.{0}' > + vm_path =3D os.path.join(self.vm_log_dir, '{0}.{1}') > + for cnt in range(self.vm_max_ch): > + channel =3D { > + '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 =3D 'igb_uio' > + # boot up vm > + self.vm_dut =3D self.vm.start() > + self.is_vm_on =3D 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 =3D 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 =3D None > + self.vm.stop() > + self.is_vm_on =3D False > + self.vm =3D None > + self.dut.virt_exit() > + cmd_fmt =3D 'virsh {0} {1} > /dev/null 2>&1'.format > + cmds =3D [ > + [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 =3D self.prepare_binary('vm_power_manager') > + > + def start_vm_power_mgr(self): > + eal_option =3D ( > + '-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 =3D 'vmpower>' > + cmd =3D [' '.join([self.vm_power_mgr, eal_option]), prompt, 30] > + self.d_con(cmd) > + self.is_mgr_on =3D True > + > + def set_vm_power_mgr(self): > + vm_name =3D self.vm_name > + cmds =3D [ > + "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 =3D '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 =3D False > + > + def init_guest_mgr(self): > + name =3D 'vm_power_manager/guest_cli' > + self.guest_cli =3D self.prepare_binary(name, host_crb=3Dself.vm_= dut) > + self.guest_con_name =3D \ > + '_'.join([self.vm_dut.NAME, name.replace('/', '-')]) > + self.guest_session =3D self.vm_dut.create_session(self.guest_con= _name) > + self.add_console(self.guest_session) > + > + def start_guest_mgr(self, cmd_option): > + prompt =3D r"vmpower\(guest\)>" > + option =3D ( > + '-v ' > + '-c {core_mask} ' > + '-n {memory_channel} ' > + '--file-prefix=3D{file_prefix} ' > + '-- ').format(**{ > + 'core_mask': '0xff', > + 'memory_channel': self.vm_dut.get_memory_channels(), > + 'file_prefix': 'vmpower2', > + }) + cmd_option > + guest_cmd =3D ' '.join([self.guest_cli, option]) > + self.vm_g_con([guest_cmd, prompt, 120]) > + self.is_guest_on =3D 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 =3D self.vcpu_map.index(str(self.check_core)) > + cmd =3D ["set_cpu_freq %d %s" % (vcpu_index, status), > + "vmpower\(guest\)>", 5] > + output =3D self.vm_g_con(cmd) > + self.verify( > + 'ACK received for message sent to host'.lower() in output.lo= wer(), > + '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 =3D False > + > + def init_vm_testpmd(self): > + self.vm_testpmd =3D PmdOutput(self.vm_dut) > + > + def start_vm_testpmd(self): > + eal_param =3D ( > + '-v ' > + '-m {memsize} ' > + '--file-prefix=3D{file-prefix}').format(**{ > + 'file-prefix': 'vmpower1', > + 'memsize': 1024, }) > + self.vm_testpmd.start_testpmd( > + "Default", > + param=3D'--port-topology=3Dloop', > + eal_param=3Deal_param) > + self.is_pmd_on =3D True > + > + def set_vm_testpmd(self): > + cmds =3D [ > + '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 =3D False > + > + def init_query_script(self): > + self.query_cur_freq =3D os.path.join( > + self.output_path, 'cpu_cur_freq.log') > + script_content =3D 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_fr= eq >> > {0} > + done > + """).format(self.query_cur_freq) > + fileName =3D 'vm_power_core.sh' > + query_script =3D 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 =3D ';'.join([ > + 'cd {}'.format(self.target_dir), > + 'chmod 777 {}'.format(fileName), > + './' + fileName]) > + > + def start_query(self, core): > + cmd =3D self.query_tool + ' {0} > /dev/null 2>&1 &'.format(core) > + self.d_a_con(cmd) > + self.is_query_on =3D True > + > + def stop_query(self): > + if not self.is_query_on: > + return > + ps_name =3D os.path.basename(self.query_tool) > + cmd =3D "ps aux | grep -i '%s' | grep -v grep | awk {'print $2'}= " % ( > + ps_name) > + out =3D self.d_a_con(cmd) > + if out !=3D "": > + cmd =3D 'pkill -f {}'.format(ps_name) > + self.d_a_con(cmd) > + self.is_query_on =3D 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 =3D random.randint(0, len(time_stage) - 1) > + timestamp =3D time_stage[random_index] > + ori_sys_time =3D datetime.now() > + msg =3D "dut system original time is {0}".format(ori_sys_time) > + self.logger.debug(msg) > + # set system time to a desired time for policy > + msg =3D "set timestamp {0}".format(timestamp) > + self.logger.debug(msg) > + date_tool =3D "date" > + cmd =3D ';'.join([ > + "{0}", > + "{0} -s '{1}'", > + "clock -w"]).format(date_tool, timestamp) > + self.d_a_con(cmd) > + cmd =3D "{0} '+%H:00'".format(date_tool) > + output =3D self.d_a_con(cmd) > + msg =3D "desired time fails to set" \ > + if output.strip() !=3D timestamp \ > + else "desired time set successful" > + self.logger.info(msg) > + # get begin time stamp > + pre_time =3D datetime.now() > + # when dut/tester are on the same node, separate into two timest= amp > + return pre_time, ori_sys_time > + > + def restore_system_time(self, pre_time, ori_sys_time): > + if not ori_sys_time: > + return > + date_tool =3D "date" > + cur_time =3D datetime.now() > + interval =3D (cur_time - pre_time).seconds > + timestamp =3D ori_sys_time + timedelta(seconds=3Dinterval) > + FMT =3D '%Y-%m-%d %H:%M:%S' > + real_time =3D timestamp.strftime(FMT) > + cmd =3D ';'.join([ > + "{0}", > + "{0} -s '{1}'", > + "clock -w", > + "{0}", ]).format(date_tool, real_time) > + self.d_a_con(cmd) > + > + def preset_core_freq(self): > + info =3D self.cpu_info.get(self.check_core, {}) > + freq =3D info.get('scaling_available_frequencies')[-3] > + cmd =3D ("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 =3D [ > + 'scaling_max_freq', > + 'scaling_available_frequencies', > + 'scaling_min_freq'] > + freq =3D '/sys/devices/system/cpu/cpu{0}/cpufreq/{1}'.format > + cpu_topos =3D self.dut.get_all_cores() > + cpu_info =3D {} > + for cpu_topo in cpu_topos: > + cpu_id =3D int(cpu_topo.get('thread')) > + cpu_info[cpu_id] =3D { > + 'socket': cpu_topo.get('socket'), > + 'core': cpu_topo.get('core')} > + > + for key_value in key_values: > + cmds =3D [] > + for cpu_id in sorted(cpu_info.keys()): > + cmds.append('cat {0}'.format(freq(cpu_id, key_value))) > + output =3D self.d_a_con(';'.join(cmds)) > + freqs =3D [int(item) for item in output.splitlines()] \ > + if key_value !=3D 'scaling_available_frequencies' else \ > + [item for item in output.splitlines()] > + for index, cpu_id in enumerate(sorted(cpu_info.keys())): > + if key_value =3D=3D 'scaling_available_frequencies': > + cpu_info[cpu_id][key_value] =3D freqs[index] > + cpu_info[cpu_id][key_value] =3D \ > + [int(item) for item in sorted(freqs[index].split())]= \ > + if key_value =3D=3D 'scaling_available_frequencies' = else \ > + freqs[index] > + > + return cpu_info > + > + def convert_to_values(self, output): > + pdata_s =3D "^\d+$" > + ret =3D re.match(pdata_s, output) > + if ret: > + return int(output) > + pdata_m =3D "(\d+ )+" > + ret =3D re.match(pdata_m, output) > + if ret: > + return [int(item) for item in output.split()] > + pdata_m =3D "^\w+$" > + ret =3D re.match(pdata_m, output) > + if ret: > + return output > + pdata_m =3D "(\w+ )+" > + ret =3D re.match(pdata_m, output) > + if ret: > + return [item for item in output.split()] > + > + def get_sys_power_driver(self): > + drv_file =3D "/sys/devices/system/cpu/cpu0/cpufreq/scaling_drive= r" > + output =3D self.d_a_con('cat ' + drv_file) > + if not output: > + msg =3D 'unknown power driver' > + self.verify(False, msg) > + drv_name =3D output.splitlines()[0].strip() > + return drv_name > + > + def get_linux_cpu_attrs(self, core_num, name=3D"cpuinfo_cur_freq"): > + freq_path =3D "/sys/devices/system/cpu/cpu{0}/cpufreq/{1}".forma= t( > + core_num, name) > + output =3D 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 =3D 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=3D'disable'): > + info =3D self.cpu_info.get(core_index, {}) > + value =3D info.get('scaling_available_frequencies') > + expected_freq =3D value[-2] if status =3D=3D 'disable' else valu= e[-1] > + return expected_freq > + > + def check_dut_core_turbo_enable(self, vcpu): > + dut_core_index =3D self.set_single_core_turbo(vcpu, 'enable_turb= o') > + cur_freq =3D self.get_linux_cpu_attrs(dut_core_index) > + expected_freq =3D self.get_expected_turbo_freq(dut_core_index, > 'enable') > + if cur_freq !=3D expected_freq: > + msg =3D ("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 =3D self.set_single_core_turbo(vcpu, 'disable_tur= bo') > + cur_freq =3D self.get_linux_cpu_attrs(dut_core_index) > + expected_freq =3D self.get_expected_turbo_freq(dut_core_index, > 'disable') > + if cur_freq !=3D expected_freq: > + msg =3D ("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 =3D { > + 'max': 'scaling_max_freq', > + 'medium': 'scaling_available_frequencies', > + 'min': 'scaling_min_freq'} > + info =3D self.cpu_info.get(core_index, {}) > + value =3D info.get(freqs.get(check_item)) > + expected_freq =3D value if check_item !=3D '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 =3D content.get('check') > + query_cur_freq =3D os.path.join( > + self.output_path, os.path.basename(self.query_cur_freq)) > + with open(query_cur_freq, 'rb') as fp: > + content =3D fp.read() > + self.logger.debug(content) > + real_freq =3D int(content.splitlines()[2].strip()) > + expected_freq =3D self.get_expected_freq(self.check_core, check_= item) > + msg =3D ( > + 'core <{0}> freq <{1}> are not ' > + 'the expected frequency <{2}>').format( > + self.check_core, real_freq, expected_freq) > + self.verify(real_freq =3D=3D expected_freq, msg) > + msg =3D '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 =3D=3D 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 =3D content['pps'] > + test_pps =3D random.randint(expected_pps[0], expected_pps[1] - 1= ) \ > + if isinstance(expected_pps, list) \ > + else expected_pps > + msg =3D "run traffic with pps {0}".format(test_pps) > + self.logger.info(msg) > + info =3D { > + '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 =3D None > + try: > + self.run_guest_pre(content) > + # run traffic > + if name =3D=3D self.TRAFFIC: > + self.traffic_policy(name, content) > + else: > + # run time policy, wait 10 second to make sure system re= ady > + # 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 =3D 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=3DFalse= ): > + ''' > + -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=3D1 > + b. x,y e.g. --quiet-hours=3D3,4 > + c. x-y e.g. --busy-hours=3D9-12 > + d. combination of above (e.g. --busy-hours=3D4,5-7,9) > + ''' > + policy_opt =3D '--policy=3D{policy}' # four types > + vm_opt =3D '--vm-name=3D{vm}' > + vcpu_opt =3D '--vcpu-list=3D{vcpus}' # full cores/one core/mult= iple cores > + time_b_opt =3D '--busy-hours=3D{hours}' # all day/one hour/mixe= d range > + time_q_opt =3D '--quiet-hours=3D{hours}' # all day/one hour/mix= ed range > + # common option used by all policy > + opt_fmt =3D [vm_opt, policy_opt, vcpu_opt] > + # core option > + max_cores =3D len(self.vcpu_map) > + max_cores_list =3D ",".join([str(num) for num in range(max_cores= )]) > + cores_range =3D [max_cores_list] if edge else ['0', max_cores_li= st] > + # guest mgr option format configuration > + guest_opt =3D { > + 'opt_fmt': opt_fmt, > + 'option': { > + 'vm': [vm_name], > + 'vcpus': cores_range, > + } > + } > + # testing content > + policy_configs =3D { > + # 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 =3D policy_configs.get(policy_name) > + # make combine testing options > + test_content =3D [] > + for config in select_config: > + _common_config =3D deepcopy(guest_opt) > + if 'cmd' in config: > + option_cfg =3D config.get('cmd') > + _common_config['opt_fmt'] +=3D option_cfg.get('opt_fmt',= []) > + _common_config['option'].update(option_cfg['option']) > + config.pop('cmd') > + values =3D _common_config['option'].values() > + keys =3D _common_config['option'].keys() > + opt_fmt =3D _common_config['opt_fmt'] > + for item in product(*values): > + _options =3D dict(izip(keys, item)) > + _options['policy'] =3D policy_name > + _opt_fmt =3D " ".join(opt_fmt) > + _config =3D deepcopy(config) > + _config['option'] =3D _opt_fmt.format(**_options) > + _config['vcpus'] =3D _options['vcpus'] > + test_content.append(_config) > + > + return test_content > + > + def verify_policy(self, policy_name): > + test_contents =3D self.get_policy_test_content( > + policy_name, self.vm_name, edge=3Dnot self.full_test) > + msg =3D "begin test policy <{}> ...".format(policy_name) > + self.logger.info(msg) > + except_content =3D 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 =3D \ > + 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 =3D e > + finally: > + self.run_test_post() > + > + if except_content: > + raise VerifyFailure(except_content) > + > + msg =3D "test policy <{}> successful !".format(policy_name) > + self.logger.info(msg) > + > + def verify_turbo_command(self, status): > + msg =3D "begin test turbo <{}> ...".format(status) > + self.logger.info(msg) > + except_content =3D None > + test_content =3D self.get_policy_test_content( > + self.TIME, self.vm_name, edge=3DTrue)[0] > + try: > + self.run_test_pre('turbo') > + self.start_guest_mgr(test_content.get('option')) > + check_func =3D getattr( > + self, 'check_dut_core_turbo_{}'.format(status)) > + check_func(0) > + except Exception as e: > + self.logger.error(traceback.format_exc()) > + except_content =3D e > + finally: > + self.run_test_post() > + > + if except_content: > + raise VerifyFailure(except_content) > + > + msg =3D "test turbo <{}> successful !".format(status) > + self.logger.info(msg) > + > + def verify_power_driver(self): > + expected_drv =3D 'acpi-cpufreq' > + power_drv =3D self.get_sys_power_driver() > + msg =3D "{0} should work with {1} driver on DUT".format( > + self.suite_name, expected_drv) > + self.verify(power_drv =3D=3D expected_drv, msg) > + > + def verify_cpupower_tool(self): > + name =3D 'cpupower' > + cmd =3D "whereis {} > /dev/null 2>&1; echo $?".format(name) > + output =3D self.d_a_con(cmd) > + status =3D True if output and output.strip() =3D=3D "0" else Fal= se > + msg =3D '<{}> tool have not installed on DUT'.format(name) > + self.verify(status, msg) > + > + def preset_test_environment(self): > + self.is_mgr_on =3D self.is_pmd_on =3D self.is_query_on =3D None > + self.ext_con =3D {} > + # get cpu cores information > + self.dut.init_core_list_uncached_linux() > + self.cpu_info =3D self.get_all_cpu_attrs() > + # port management > + self.cur_drv =3D self.bind_ports_to_sys() > + self.used_port =3D 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 =3D 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 tim= e. > + self.full_test =3D False > + # > + # Test cases. > + # > + def set_up_all(self): > + """ > + Run at the start of each test suite. > + """ > + self.dut_ports =3D self.dut.get_ports(self.nic) > + self.verify(len(self.dut_ports) >=3D 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