[dts] [PATCH V1 2/3] vm_pw_mgmt_policy: upload automation script

yufengx.mo at intel.com yufengx.mo at intel.com
Wed Jun 6 07:35:33 CEST 2018


From: yufengmx <yufengx.mo at intel.com>


This automation script is is for vm power mgmt policy feature.

The DPDK Power Management feature allows users space applications to save power
by dynamically adjusting CPU frequency or entering into different C-States.

*   Adjusting the CPU frequency dynamically according to the utilization of RX
    queue.

*   Entering into different deeper C-States according to the adaptive algorithms
    to speculate brief periods of time suspending the application if no packets
    are received.

This feauture are realized through example/vm_power_manager

Signed-off-by: yufengmx <yufengx.mo at intel.com>
---
 tests/TestSuite_vm_pw_mgmt_policy.py | 884 +++++++++++++++++++++++++++++++++++
 1 file changed, 884 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..7cfd9ec
--- /dev/null
+++ b/tests/TestSuite_vm_pw_mgmt_policy.py
@@ -0,0 +1,884 @@
+# BSD LICENSE
+#
+# Copyright(c) 2010-2018 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.
+VM power manager test suite.
+"""
+import os
+import re
+import utils
+import time
+import random
+from datetime import datetime, timedelta
+
+# for deubg
+from functools import wraps
+from pprint import pformat, pprint
+import inspect
+import traceback
+
+from serializer import Serializer
+from test_case import TestCase
+from settings import HEADER_SIZE
+from qemu_libvirt import LibvirtKvm
+from exception import TimeoutException, VerifyFailure
+
+from packet import Packet, NVGRE, IPPROTO_NVGRE
+from scapy.sendrecv import sendp
+from scapy.utils import wrpcap, rdpcap, hexstr
+
+class TestVmPwMgmtPolicy(TestCase):
+
+    #---------------------------------------------------------
+    # execute ssh session command 
+    #---------------------------------------------------------
+    def d_console(self, cmds):
+        return self.execute_cmds(cmds, con_name='dut')
+
+    def d_a_console(self, cmds):
+        return self.execute_cmds(cmds, con_name='dut_alt')
+
+    def t_console(self, cmds):
+        return self.execute_cmds(cmds, con_name='tester')
+
+    def vm_console(self, cmds):
+        return self.execute_cmds(cmds, con_name='vm_dut')
+
+    def get_console(self, name):
+        if name == 'tester':
+            console = self.tester.send_expect
+            msg_pipe = self.tester.get_session_output
+        elif name == 'dut':
+            console = self.dut.send_expect
+            msg_pipe = self.dut.get_session_output
+        elif name == 'dut_alt':
+            console = self.dut.alt_session.send_expect
+            msg_pipe = self.dut.alt_session.session.get_output_all
+        elif name == 'vm_dut':
+            console = self.vm_dut.send_expect
+            msg_pipe = self.vm_dut.get_session_output
+        return console, msg_pipe
+
+    def execute_cmds(self, cmds, con_name='dut'):
+        console, msg_pipe = self.get_console(con_name)
+        if len(cmds) == 0:
+            return
+        if not isinstance(cmds[0], list):
+            cmds = [cmds]
+        if len(cmds) > 1:
+            outputs = []
+        else:
+            outputs = ''
+        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(timeout) if not output else output
+                else:
+                    output = console(item[0], expected_str)
+                    output = msg_pipe() if not output else output
+            except TimeoutException:
+                #self.check_process_status()
+                msg = "execute '{0}' timeout".format(item[0])
+                raise TimeoutException(msg)
+            finally:
+                pass
+            # when execute "port start", LSC event will cause next command 
+            # execution fail. So wait 10s here
+            time.sleep(1)
+            if len(cmds) > 1:
+                outputs.append(output)
+            else:
+                outputs = output
+            if check_output and len(expected_items) >= 2:
+                self.logger.info(output)
+                expected_output = expected_items[1]
+                check_type = True if len(expected_items) == 2 \
+                                  else expected_items[2]
+                if check_type and expected_output in output:
+                    msg = "expected '{0}' is in output".format(expected_output)
+                    self.logger.info(msg)
+                elif not check_type and expected_output not in output:
+                    msg = "unexpected '{0}' is not in output".format(
+                                                            expected_output)
+                    self.logger.info(msg)
+                else:
+                    status = "isn't in" if check_type else "is in"
+                    msg = "[{0}] {1} output".format(expected_output, status)
+                    self.logger.error(msg)
+                    raise VerifyFailure(msg)
+
+        time.sleep(2)
+        return outputs
+
+    def get_host_reponse(self):
+        output = self.dut.get_session_output(timeout=2)
+        return output
+
+    def get_guest_reponse(self):
+        output = self.vm_dut.get_session_output(timeout=2)
+        return output
+
+    #---------------------------------------------------------
+    # traffic method of ixia/scapy or others
+    #---------------------------------------------------------
+    def get_pkt_len(self, pkt_type):
+        # packet size
+        frame_size = 64
+        headers_size = sum(map(lambda x: HEADER_SIZE[x],
+                               ['eth', 'ip', pkt_type]))
+        pktlen = frame_size - headers_size
+        return pktlen
+
+    def set_stream(self, stm_names=None):
+        dmac = self.dut.get_mac_address(self.dut_ports[0])
+        #----------------------------------------------------------------------
+        # set streams for traffic
+        pkt_configs = {
+            'UDP_1': {
+            'type': 'UDP',
+            'pkt_layers': {
+                #'ether': {'src': srcmac, 'dst': nutmac},
+                'ipv4': {'src': '0.0.0.0', 'dst': '2.1.1.0'},
+                'udp': {'src': 32, 'dst': 33},
+                'ether': {'dst': dmac},
+                'raw': {'payload': ['58'] * self.get_pkt_len('udp')}}},
+            'UDP_2': {
+            'type': 'UDP',
+            'pkt_layers': {
+                #'ether': {'src': srcmac, 'dst': nutmac},
+                'ipv4': {'src': '0.0.0.0', 'dst': '1.1.1.0'},
+                'udp': {'src': 32, 'dst': 33},
+                'ether': {'dst': dmac},
+                'raw': {'payload': ['58'] * self.get_pkt_len('udp')}}}
+        }
+
+        # 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]
+            savePath = os.sep.join([self.target_source,
+                                    "pkt_{0}.pcap".format(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])
+            pkt.pktgen.write_pcap(savePath)
+            streams.append(pkt.pktgen.pkt)
+
+        return streams
+
+    def get_rate_percent(self, expect_pps):
+        frame_size = 64
+        full_pps = self.wirespeed(self.nic, frame_size, 1) * 1000000.0
+        rate_percent = round((100*float(expect_pps)/float(full_pps)), 2)
+        return rate_percent
+
+    def send_packets_by_ixia(self, **kwargs):
+        tester_port = kwargs.get('tx_intf')
+        count = kwargs.get('count', 1)
+        traffic_type = kwargs.get('traffic_type', 'normal')
+        rate_percent = kwargs.get('rate_percent', float(100))
+        expected_pps = kwargs.get('pps', 0)
+        traffic_time = kwargs.get('traffic_time', 0)
+        rate_percent = self.get_rate_percent(expected_pps)
+        msg = "expected_pps is {0}, rate percent is {1}".format(
+                                                        expected_pps,
+                                                        rate_percent)
+        self.logger.info(msg)
+        #---------------------------------------------------------------
+        self.tgen_input = []
+        tgen_input = self.tgen_input
+        send_pkts = kwargs.get('stream') or []
+        pcap = self.target_source + os.sep + 'ixia.pcap'
+        wrpcap(pcap, send_pkts)
+        #----------------------------------------------------------------
+        tgen_input.append((tester_port, 
+                           tester_port, 
+                           pcap))
+        # run latency stat statistics
+        self.rate_percent = rate_percent
+        self.pktgen_status = 'running'
+        #if traffic_type == 'burst':
+        stream_configs = kwargs.get('stream configs', None)
+        if not stream_configs:
+            raise VerifyFailure("no stream configs set")
+        traffic = self.tester.loop_traffic_generator_throughput
+        #rx_bps, rx_pps = 
+        traffic(tgen_input, rate_percent)
+        # move stop method in packet thread
+        if traffic_time:
+            time.sleep(traffic_time)
+
+        return True
+
+    def stop_ixia(self, data_types='packets'):
+        cur_data = {}
+        cur_data['ixia statistics'] = []
+        append = cur_data['ixia statistics'].append
+        # get ixia statistics
+        if self.pktgen_status != 'running':
+            return cur_data
+        try:
+            line_rate = self.tester.get_port_line_rate()
+            stop_traffic = self.tester.stop_traffic_generator_throughput_loop
+            rx_bps, rx_pps = stop_traffic(self.tgen_input)
+            output = self.tester.traffic_get_port_stats(self.tgen_input)
+            append('send packets: {0}'.format(output[0]))
+            append('line_rate: {0}'.format(line_rate[0]))
+            append('rate_percent: {0}%'.format(self.rate_percent))
+        except Exception as e:
+            msg = traceback.format_exc()
+            self.logger.error(msg)
+        finally:
+            self.pktgen_status = 'stop'
+        return cur_data
+
+    def get_time_unit(self, unit):
+        #
+        unit = unit or 'gapSeconds'
+        time_unit = {'gapNanoSeconds': 1/10e8,
+                     'gapMicroSeconds': 1/10e5,
+                     'gapMilliSeconds': 1/10e2,
+                     'gapSeconds': 1,}
+        return time_unit[unit]
+
+    def send_packets_by_scapy(self, **kwargs):
+        tester_port = kwargs.get('tx_intf')
+        send_pkts = kwargs.get('stream') or []
+        stream_configs = kwargs.get('stream configs', None)
+        rate_limit = kwargs.get('traffic_type') or 0
+        if not stream_configs:
+            raise VerifyFailure("no stream configs set")
+        count = stream_configs.get('count') or 1
+        frameConfigs = stream_configs.get('frameType') or None
+        if rate_limit:
+            interval = 1/count
+        else:
+            interval = 0.01
+        #---------------------------------------------------------------
+        self.pktgen_status = 'running'
+        self.logger.info("begin transmission ...")
+        sendp(send_pkts, iface=tester_port, count=count, 
+              inter=interval, verbose=False)
+        self.logger.info("complete transmission")
+        self.pktgen_status = 'stop'
+
+    def traffic(self, ports_topo):
+        """
+        stream transmission on specified link topology
+        """
+        time.sleep(2)
+        if self.pktgen_name == 'ixia':
+            result = self.send_packets_by_ixia(**ports_topo)
+        elif self.pktgen_name == 'scapy':
+            result = self.send_packets_by_scapy(**ports_topo)
+        else:
+            self.logger.error("set wrong pkt generator")
+            return
+
+        # end traffic
+        self.logger.info("complete transmission")
+
+        return result
+
+    def run_traffic(self, **kwargs):
+        #-----------------------------------------
+        # set traffic topology
+        # for lack ixia port, one of ixia port use normal link peer 
+        # so there set a hard code for temporarily usage
+        port = 0
+        tester_port_id = self.tester.get_local_port(self.dut_ports[port])
+        stm_type = kwargs.get('stm_types')
+        packets = kwargs.get('packets')
+        if self.pktgen_name == 'ixia':
+            expected_pps = kwargs.get('pps')
+            traffic_time = kwargs.get('traffic_time', None) or 30
+            tx_port = tester_port_id
+            ports_topo = {'tx_intf': tx_port,
+                          'rx_intf': 0,
+                          'stream': self.set_stream(stm_type),
+                          'stream configs': {
+                                'count': packets,
+                                'frameType': {
+                                    # gapNanoSeconds gapMilliSeconds gapSeconds
+                                    'gapUnit': 'gapMilliSeconds'},
+                                'stream_type': 'burst'
+                                },
+                          # send bursts of 32 packets
+                          'traffic_type': 'normal',
+                          'pps': expected_pps,
+                          # 0 means stop after one round traffic
+                          # xx value means stop after traffic_time time
+                          'traffic_time': traffic_time,}
+        else:
+            tx_port = self.tester.get_interface(tester_port_id)
+            ports_topo = {'tx_intf': tx_port,
+                          'rx_intf': 0,
+                          'stream': self.set_stream(stm_type),
+                          'stream configs': {
+                                'count': packets
+                                },
+                          'traffic_type': 'rate_limit',
+                          }
+
+        # begin traffic checking
+        result = self.traffic(ports_topo)
+
+        return result
+    
+    @property
+    def is_perf(self):
+        return self._enable_perf
+
+    def set_up_all(self):
+        """
+        Run at the start of each test suite.
+        """
+        self.skip_compile = self.dut.skip_setup
+        self.dut_ports = self.dut.get_ports(self.nic)
+        self.verify(len(self.dut_ports) >= 2,
+                    "Not enough ports for " + self.nic)
+        self.target_source = self.dut.base_dir
+        self.debug = self._enable_debug
+        # rename hard code of vm name in dpdk code
+        self.replace_vm_name()
+        # boot up vm
+        self.vm = None
+        self.vm_dut = None
+        self.set_up_vm()
+
+        #------------------------------------------------------------------
+        # initialize ixia session
+        self.pktgen_status = 'stop'
+        #------------------------------------------------------------------
+        # initialize packet generator
+        self.pktgen_name = 'ixia' if self.is_perf else 'scapy'
+
+    def set_up(self):
+        """
+        Run before each test case.
+        """
+        pass
+
+    def tear_down(self):
+        """
+        Run after each test case.
+        """
+        if self.vm_dut:
+            self.vm_dut.send_expect("quit", "# ")
+        pass
+
+    def tear_down_all(self):
+        """
+        Run after each test suite.
+        """
+        self.dut.send_expect("quit", "# ")
+        if self.vm:
+            self.vm.stop()
+        self.dut.virt_exit()
+        pass
+
+    def replace_vm_name(self):
+        '''
+        till 18.02 rc0, developer use hard code to set vm name.
+        '''
+        console = self.d_a_console
+        old_word = "ubuntu2"
+        new_word = "vm0"
+        sed_file = os.sep.join([self.target_source, 
+                   "examples/vm_power_manager/guest_cli/vm_power_cli_guest.c"])
+        cmds = [["sed -i -e 's/{0}/{1}/' {2}".format(old_word, new_word,
+                                                    sed_file), "# "]]
+        console(cmds)
+
+    def set_up_vm(self):
+        # map between host vcpu and guest vcpu
+        try:
+            self.vcpu_map = []
+            # start vm
+            self.vm_name = "vm0"
+            self.vm = LibvirtKvm(self.dut, self.vm_name, self.suite_name)
+            base_path = '/tmp/powermonitor'
+            #-----------------------
+            # create temporary folder for power monitor
+            cmd = "mkdir -p {0}".format(base_path)
+            self.dut.send_expect(cmd, "# ")
+            cmd = "chmod 777 {0}".format(base_path)
+            self.dut.send_expect(cmd, "# ")
+            #-----------------------
+            ch_name = 'virtio.serial.port.poweragent.{0}'
+            vm_path = base_path + os.sep + '{0}.{1}'
+            channels = []
+            max_ch = 4
+            for cnt in range(max_ch):
+                channel = {
+                        'path': vm_path.format(self.vm_name, cnt), 
+                        'name': ch_name.format(cnt)}
+                self.vm.add_vm_virtio_serial_channel(**channel)
+            # boot up vm
+            self.vm_dut = self.vm.start()
+            if not self.vm_dut:
+                raise VerifyFailure("create vm_dut fail !")
+
+            # ping cpus
+            cpus = self.vm.get_vm_cpu()
+            self.vcpu_map = cpus[:]
+            self.core_num = len(cpus)
+            # build guest cli
+            if not self.skip_compile:
+                out = self.vm_dut.build_dpdk_apps(
+                    "examples/vm_power_manager/guest_cli")
+                self.verify("Error" not in out, "Compilation error")
+                self.verify("No such" not in out, "Compilation error")
+
+            # compile vm power manager
+            if not self.skip_compile:
+                out = self.dut.build_dpdk_apps("./examples/vm_power_manager")
+                self.verify("Error" not in out, "Compilation error")
+                self.verify("No such" not in out, "Compilation error")
+            bin_dir = os.sep.join([self.target_source, 
+                                   "examples/vm_power_manager"])
+            cmd = "guest_cli/build/guest_vm_power_mgr -c 0xf -n 4 -- -i"
+            self.guest_cli = os.sep.join([bin_dir, cmd])
+            #----------------------------------------------------------------
+            try:
+                cmd = "build/vm_power_mgr -c 0x3 -n 4"
+                self.mgr = os.sep.join([bin_dir, cmd])
+                self.dut.send_expect(self.mgr, "vmpower>", )
+                cmds = [[self.mgr, 'vmpower>', 120],
+                        ["add_vm %s" % self.vm_name, 'vmpower>'],
+                        ["add_channels %s all" % self.vm_name, 'vmpower>'],
+                        ["show_vm %s" % self.vm_name, 'vmpower>']]
+                pprint(cmds)
+                self.d_console(cmds)
+            except Exception as e:
+                pass
+            finally:
+                pass
+        except Exception as e:
+            msg = traceback.format_exc()
+            self.logger.error(msg)
+        finally:
+            pass
+
+    def set_cpu_full_load(self, cpu_id):
+        cmd = "taskset -c {0} dd if=/dev/zero of=/dev/null".format(cpu_id)
+
+    def check_guest_cli(self):
+        process_name = 'guest_vm_power_mgr'
+        cmd = "ps aux | grep -i %s | grep -v grep | awk {'print $2'}"%(
+                                                                process_name)
+        out = self.vm_dut.send_expect(cmd, "# ", 10)
+        if out != "":
+            process_pid = out.splitlines()[0]
+            cmd = 'kill -9 {0}'.format(process_pid)
+            self.vm_dut.send_expect(cmd, ">", 10)
+
+    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 linux_get_cpu_attrs(self, core_num, name="cpuinfo_cur_freq"):
+        fmt = "/sys/devices/system/cpu/cpu{0}/cpufreq/{1}"
+        freq_path = fmt.format(core_num, name)
+        console = self.d_a_console
+        console(["ll %s"%freq_path, "# "])
+        if console(["echo $?", "# "]) != "0":
+            msg = "cpu attribute <{0}> isn't existed".format(name)
+            raise VerifyFailure(msg)
+            return
+        output = console(["cat %s"%freq_path, "# "])
+        return self.convert_to_values(output)
+
+    def linux_set_core_freq(self, core_num, attr, value):
+        fmt = "/sys/devices/system/cpu/cpu{0}/cpufreq/{1}"
+        freq_path = fmt.format(core_num, attr)
+        console = self.d_a_console
+        console(["echo {0} > {1}".format(value ,freq_path), "# "])
+        if console(["echo $?", "# "]) != "0":
+            msg = "cpu attribute <{0}> isn't existed".format(name)
+            raise VerifyFailure(msg)
+
+    def linux_preset_core_freq(self, core_num, value):
+        attr = 'scaling_setspeed'
+        self.linux_set_core_freq(core_num, attr, value)
+        cur_value = self.get_cur_freq(core_num)
+        if value != cur_value:
+            msg = "failed to set core {0} to {1}".format(core_num, value)
+            self.logger.error(msg)
+
+    def get_cur_freq(self, core_num):
+        name = 'cpuinfo_cur_freq'
+        freq = self.linux_get_cpu_attrs(core_num, name)
+        return freq
+
+    def get_preset_freq(self, available_freqs, check_reqs):
+        # set cores to one frequency not in min/max/medium frequency
+        # if available frequencys are not enough, use core's default frequency
+        available = set(available_freqs)
+        check = set(check_reqs)
+        for_use = list(available.difference(check))
+        index = random.randint(0, len(for_use)-1)
+        _freq = for_use[index]
+        return _freq
+
+    def preset_policy_freqs(self):
+        freqs = { 'max': 'scaling_max_freq',
+                  'medium': 'scaling_available_frequencies',
+                  'min': 'scaling_min_freq'}
+        freqs_for_check = {}
+        # note: till 17.11 rc4, developer only realize policy on first two core
+        # using hard code.
+        for vcpu in range(len(self.vcpu_map[:2])):
+            core_num = int(self.vcpu_map[vcpu])
+            freqs_for_check[core_num] = {}
+            sub_core_freq = freqs_for_check[core_num]
+            for name, attr in freqs.iteritems():
+                value = self.linux_get_cpu_attrs(core_num, attr)
+                if name == 'medium':
+                    available_freqs = sorted(value)
+                    index = len(value)/2
+                    sub_core_freq[name] = available_freqs[index]
+                else:
+                    sub_core_freq[name] = value
+            set_freq = self.get_preset_freq(available_freqs, 
+                                            sub_core_freq.values())
+            self.linux_preset_core_freq(core_num, set_freq)
+        return freqs_for_check
+
+    def get_vm_turbo_status(self, core_num):
+        pass
+
+    def save_system_time(self):
+        self.bak_sys_time = datetime.now()
+        msg = "system original time is {0}".format(self.bak_sys_time)
+        self.logger.info(msg)
+
+    def restore_system_time(self, interval):
+        console = self.d_a_console
+        timestamp = self.bak_sys_time + timedelta(seconds=interval)
+        FMT = '%Y-%m-%d %H:%M:%S'
+        date_tool = "date"
+        clock_cmd = "clock -w"
+        real_time = timestamp.strftime(FMT)
+        cmds = [[date_tool, '', 5],
+                ["{0} -s '{1}' &".format(date_tool, real_time), '', 20],
+                [clock_cmd, '', 5],
+                [date_tool, '', 5]]
+        console(cmds)
+
+    def set_desired_time(self, timestamp):
+        console = self.d_a_console
+        date_tool = "date"
+        clock_cmd = "clock -w"
+        cmds = [[date_tool, '', 5],
+                ["{0} -s '{1}' &".format(date_tool, timestamp), '', 20],
+                [clock_cmd, '', 5]]
+        console(cmds)
+        cmds = ["{0} '+%H:00'".format(date_tool), '', 5]
+        output = console(cmds)
+        if output.strip() != timestamp:
+            msg = "desired time fails to set"
+        else:
+            msg = "desired time set successful"
+        self.logger.info(msg)
+
+    def set_vm_turbo_status(self, vcpu, status):
+        output = self.vm_dut.send_expect(
+                    "set_cpu_freq %d %s" % (vcpu, status), 
+                    "vmpower\(guest\)>", 120)
+        vm_status = self.get_vm_turbo_status(vcpu)
+
+    def start_guest_mgr(self):
+        self.check_guest_cli()
+        guest_cmd = self.guest_cli
+        out = self.vm_dut.send_expect(guest_cmd, "vmpower\(guest\)>", 120)
+
+    def guest_console(self, cmd):
+        out = self.vm_dut.send_expect(cmd, "# ", 120)
+        self.get_guest_reponse()
+
+    def close_guest_mgr(self):
+        self.vm_dut.send_expect("quit", "# ")
+
+    def set_single_core_turbo(self, vcpu, status):
+        '''
+        status: enable_turbo | disable_turbo
+        '''
+        dut_core_num = self.vcpu_map[vcpu]
+        self.set_vm_turbo_status(vcpu, status)
+        dut_cpupower_cmd = "cpupower -c {0} frequency-info | grep Active"
+        out = self.dut.alt_session.send_expect(
+                        dut_cpupower_cmd.format(dut_core_num), "# ")
+        dut_status = out.split(': ')[1]
+        self.get_host_reponse()
+        return dut_status, dut_core_num
+
+    def check_single_core_turbo_enable(self, vcpu):
+        dut_status, dut_core_num = self.set_single_core_turbo(vcpu, 
+                                                              'enable_turbo')
+        if dut_status.lower() != 'yes': 
+              msg = "core <{0}> turbo status not correct".format(dut_core_num)
+              raise VerifyFailure(msg)
+        self.logger.info( 
+            "core <{0}> turbo status set successful".format(dut_core_num))
+        return True
+
+    def check_single_core_turbo_disable(self, vcpu):
+        dut_status, dut_core_num = self.set_single_core_turbo(vcpu, 
+                                                              'disable_turbo')
+        if dut_status.lower() != 'no':
+            msg = "core <{0}> turbo status not correct".format(dut_core_num)
+            raise VerifyFailure(msg)
+        self.logger.info( 
+            "core <{0}> turbo status clear successful".format(dut_core_num))
+        return True
+
+    def check_turbo_by_core_mask(self, option):
+        self.logger.error("set frequency by core mask is not supported now")
+        return True
+
+    def run_test_pre(self):
+        ref_freqs = self.preset_policy_freqs()
+        #------------------------------------
+        # prepare testing environment
+        #self.result_table_create(latency_header)
+        # note: till 17.11 rc4, developer only realize 'TIME' policy 
+        # using hard code
+        self.start_guest_mgr()
+        #----------------------------
+        # set work load for vm power testing
+        policy = 'now'
+        cmd = "send_policy {0}".format(policy)
+        self.logger.info("get_host_reponse before send policy")
+        self.get_host_reponse()
+        output = self.vm_dut.send_expect(cmd, "vmpower\(guest\)>")
+        self.logger.info("get_host_reponse before traffic")
+        self.get_host_reponse()
+        self.logger.info("get_guest_reponse before traffic")
+        self.get_guest_reponse()
+        return ref_freqs
+
+    def run_test_post(self):
+        pass
+
+    def check_cpus_status(self, check_item, ref_freqs):
+        erro_logs = [] 
+        msg = "check content {0}".format(check_item)
+        self.logger.info(msg)
+        for core_num, value in ref_freqs.iteritems():
+            ref_req = value[check_item]
+            
+            freq = self.get_cur_freq(core_num)
+            msg = "core {0} expected freq is {1}" + \
+                  "  current freq is {2}"
+            log = msg.format(core_num, ref_req, freq)
+            if ref_req != freq:
+                self.logger.error(log)
+                erro_logs.append(log)
+            else:
+                self.logger.info(log)
+
+        if erro_logs:
+            raise VerifyFailure("cpu freqency is wrong")
+
+    def run_policy(self, name, content):
+        """
+        Measure cpu frequency fluctuate with work load
+        """
+        try:
+            ref_freqs = self.run_test_pre()
+            #------------------
+            # run traffic
+            if name == 'TRAFFIC':
+                expected_pps = content['pps']
+                if isinstance(expected_pps, list):
+                    test_pps = random.randint(expected_pps[0],
+                                              expected_pps[1]-1)
+                else:
+                    test_pps = expected_pps
+                msg = "set pps {0}".format(test_pps)
+                self.logger.info(msg)
+                info = {}
+                # note: till 17.11 rc4, developer using NO.0 binded port to 
+                # send policy pkts by default
+                info['stm_types'] = ['UDP_1']
+                info['packets'] = 25
+                info['pps'] = expected_pps
+                # run traffic
+                self.run_traffic(**info)
+                self.logger.info("get_host_reponse after traffic")
+                self.get_host_reponse()
+                self.logger.info("get_guest_reponse after traffic")
+                self.get_guest_reponse()
+                check_flg = True
+            time.sleep(10)
+            # check cpu core status
+            check_item = content['check']
+            self.check_cpus_status(check_item, ref_freqs)
+        except Exception as e:
+            pass
+        finally:
+            pass
+
+        if self.pktgen_name == "ixia" and name == 'TRAFFIC':
+            result = self.stop_ixia()
+        #------------------------------------
+        # clear testing environment
+        self.close_guest_mgr()
+
+    def get_policy_test_content(self, name):
+        # note: till 17.11 rc4, developer only use hard code to expose   
+        # 'TIME/TRAFFIC' policy to user to run testing
+        policys = {'TRAFFIC': [
+                        {"time stage": ['08:00', '10:00'],
+                         "pps":  97000,#[96000, 1800000], 
+                         "check": "min"},
+                        {"time stage": ['08:00', '10:00'],
+                         "pps": 1900000,#[1800000, 2000000], 
+                         "check": "medium"},
+                        {"time stage": ['08:00', '10:00'],
+                         "pps": 2200000,
+                         "check": "max"},
+                        ],
+                    'TIME': [
+                        {'time stage': ['11:00', '12:00', '13:00'],
+                         "check": 'min'},
+                        {"time stage": ['03:00', '04:00', '05:00'],
+                        "check": 'max'},
+                        ],
+                    'WORKLOAD':None
+                }
+        if not self.is_perf and name == 'TRAFFIC':
+            return policys[name][:1]
+
+        return policys[name]
+
+    def check_policy(self, policy_name):
+        check_results = []
+        test_contents = self.get_policy_test_content(policy_name)
+        msg = "begin test policy <{0}>".format(policy_name)
+        self.logger.info(msg)
+        for content in test_contents:
+            #----------------------------
+            # set system time
+            time_stage = content['time stage']
+            random_index = random.randint(0, len(time_stage)-1)
+            timestamp = time_stage[random_index]
+            msg = "set timestamp {0}".format(timestamp)
+            self.logger.info(msg)
+            #------------------------------------------------
+            # set system time to a desired tiem
+            self.save_system_time()
+            self.set_desired_time(timestamp)
+            pre_time = datetime.now()
+            # run policy testing
+            try: 
+                self.run_policy(policy_name, content)
+            except Exception as e:
+                check_results.append(e)
+            finally:
+                pass
+            #------------------------------------------------
+            # restore system time
+            post_time = datetime.now()
+            interval = (post_time - pre_time).seconds
+            self.restore_system_time(interval)
+        msg = "complete test policy <{0}>".format(policy_name)
+        self.logger.warning(msg)
+
+        if check_results:
+            for e in check_results:
+                self.logger.error(e)
+            raise VerifyFailure('check_policy is failed')
+
+    def test_turbo_enable_by_core_num(self):
+        self.start_guest_mgr()
+        self.check_single_core_turbo_enable(vcpu)
+        self.close_guest_mgr()
+
+    def test_turbo_disable_by_core_num(self):
+        self.start_guest_mgr()
+        for vcpu in range(self.core_num):
+            self.check_single_core_turbo_disable(vcpu)
+        self.close_guest_mgr()
+
+    def test_policy_traffic(self):
+        """
+        Measure cpu frequency fluctuate with work load(traffic policy)
+        """
+        self.check_policy("TRAFFIC")
+
+    def test_policy_time(self):
+        """
+        Measure cpu frequency fluctuate with work load(time policy)
+        """
+        self.check_policy("TIME")
+
+    def test_perf_policy_traffic(self):
+        """
+        Measure cpu frequency fluctuate with work load(traffic policy)
+        """
+        self.check_policy("TRAFFIC")
+
+    def test_perf_policy_time(self):
+        """
+        Measure cpu frequency fluctuate with work load(time policy)
+        """
+        self.check_policy("TIME")
\ No newline at end of file
-- 
1.9.3



More information about the dts mailing list