test suite reviews and discussions
 help / color / mirror / Atom feed
* [dts] [PATCH V1][pmd bonding]: common methods used by bonding suites
@ 2019-02-28  5:50 yufengmx
  2019-02-28  5:50 ` [dts] [PATCH V1]pmd_bonding: " yufengmx
  0 siblings, 1 reply; 5+ messages in thread
From: yufengmx @ 2019-02-28  5:50 UTC (permalink / raw)
  To: dts; +Cc: yufengmx

a module that common methods are ran by pmd_bonded_8023ad/pmd_stacked_bonded suites 

yufengmx (1):
  pmd_bonding: common methods used by bonding suites

 tests/bonding.py | 831 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 831 insertions(+)
 create mode 100644 tests/bonding.py

-- 
1.9.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [dts] [PATCH V1]pmd_bonding: common methods used by bonding suites
  2019-02-28  5:50 [dts] [PATCH V1][pmd bonding]: common methods used by bonding suites yufengmx
@ 2019-02-28  5:50 ` yufengmx
  2019-02-28  6:03   ` Mo, YufengX
  2019-02-28 11:00   ` Tu, Lijuan
  0 siblings, 2 replies; 5+ messages in thread
From: yufengmx @ 2019-02-28  5:50 UTC (permalink / raw)
  To: dts; +Cc: yufengmx


It is a module that common methods are ran by pmd_bonded_8023ad/pmd_stacked_bonded
suites, including:
    boot up testpmd
    sending packets
    query testpmd information of ports
    set testpmd port status
    commond dpdk bonding commands

Signed-off-by: yufengmx <yufengx.mo@intel.com>
---
 tests/bonding.py | 831 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 831 insertions(+)
 create mode 100644 tests/bonding.py

diff --git a/tests/bonding.py b/tests/bonding.py
new file mode 100644
index 0000000..fb2a69e
--- /dev/null
+++ b/tests/bonding.py
@@ -0,0 +1,831 @@
+# 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.
+
+import os
+import time
+import re
+import struct
+import socket
+from socket import htonl
+
+from packet import Packet
+from scapy.sendrecv import sendp
+from scapy.utils import wrpcap
+
+import utils
+from exception import TimeoutException, VerifyFailure
+from pmd_output import PmdOutput
+from settings import HEADER_SIZE
+
+# define bonding mode
+MODE_ROUND_ROBIN = 0
+MODE_ACTIVE_BACKUP = 1
+MODE_XOR_BALANCE = 2
+MODE_BROADCAST = 3
+MODE_LACP = 4
+MODE_TLB_BALANCE = 5
+MODE_ALB_BALANCE = 6
+
+# define packet size
+FRAME_SIZE_64 = 64
+FRAME_SIZE_128 = 128
+FRAME_SIZE_256 = 256
+FRAME_SIZE_512 = 512
+FRAME_SIZE_1024 = 1024
+FRAME_SIZE_1518 = 1518
+
+class PmdBonding(object):
+    ''' common methods for testpmd bonding '''
+
+    def __init__(self, **kwargs):
+        # set parent instance
+        self.parent = kwargs.get('parent')
+        # set target source code directory
+        self.target_source = self.parent.dut.base_dir
+        # set logger
+        self.logger = self.parent.logger
+        self.verify = self.parent.verify
+        # select packet generator
+        self.pktgen_name = 'ixia' if self.is_perf else 'scapy'
+        # traffic default config
+        self.default_pkt_size = kwargs.get('pkt_size') or FRAME_SIZE_64
+        self.default_src_mac = kwargs.get('src_mac')
+        self.default_src_ip = kwargs.get('src_ip')
+        self.default_src_port = kwargs.get('src_port')
+        self.default_dst_ip = kwargs.get('dst_ip')
+        self.default_dst_port = kwargs.get('dst_port')
+        self.default_pkt_name = kwargs.get('pkt_name')
+        # testpmd
+        self.testpmd = PmdOutput(self.parent.dut)
+        self.testpmd_status = 'close'
+    #
+    # On tester platform, packet transmission
+    #
+    def mac_str_to_int(self, mac_str):
+        """ convert the MAC type from the string into the int. """
+        mac_hex = '0x'
+        for mac_part in mac_str.split(':'):
+            mac_hex += mac_part
+        return int(mac_hex, 16)
+
+    def mac_int_to_str(self, mac_int):
+        """ Translate the MAC type from the string into the int. """
+        temp = hex(mac_int)[2:]
+        b = []
+        [b.append(temp[n:n+2]) for n in range(len(temp)) if n % 2 == 0]
+        new_mac = ":".join(b)
+        return new_mac
+
+    def ip_str_to_int(self, ip_str):
+        """
+        convert the IP type from the string into the int.
+        """
+        ip_int = socket.ntohl(struct.unpack(
+                                "I", socket.inet_aton(str(ip_str)))[0])
+        return ip_int
+
+    def ip_int_to_str(self, ip_int):
+        """
+        convert the IP type from the int into the string.
+        """
+        ip_str = socket.inet_ntoa(struct.pack('I', socket.htonl(ip_int)))
+        return ip_str
+
+    def increase_ip(self, ip, step=1):
+        ''' ip: string format '''
+        _ip_int = self.ip_str_to_int(ip)
+        new_ip = self.ip_int_to_str(_ip_int + step)
+        return new_ip
+
+    def increase_mac(self, mac, step=1):
+        ''' mac: string format '''
+        _mac_int = self.mac_str_to_int(mac)
+        new_mac = self.mac_int_to_str(_mac_int+step)
+        return new_mac
+
+    def increase_port(self, port, step=1):
+        ''' port: int format '''
+        new_port = port + step
+        return new_port
+
+    def increase_mac_ip_port(self, step=1):
+        # get source port setting
+        mac, ip, port = (self.default_src_mac,
+                         self.default_src_ip,
+                         self.default_src_port)
+        return (self.increase_mac(mac, step),
+                self.increase_ip(ip, step),
+                self.increase_port(port, step))
+
+    def get_pkt_len(self, pkt_type):
+        ''' get packet payload size '''
+        frame_size = self.default_pkt_size
+        headers_size = sum(map(lambda x: HEADER_SIZE[x],
+                               ['eth', 'ip', pkt_type]))
+        pktlen = frame_size - headers_size
+        return pktlen
+
+    def set_stream_to_slave_port(self, dut_port_id):
+        '''
+        use framework/packet.py module to create one stream, send stream to
+        slave port
+        '''
+        # get dst port mac address
+        pkt_name = self.default_pkt_name
+        destport = self.default_dst_port
+        destip = self.default_dst_ip
+        dst_mac = self.get_port_info(dut_port_id, 'mac')
+        # packet size
+        pktlen = self.get_pkt_len(pkt_name)
+        # set stream configuration
+        srcmac, srcip, srcport = self.increase_mac_ip_port(0)
+        pkt_config = {
+        'type': pkt_name.upper(),
+        'pkt_layers': {
+            # Ether(dst=nutmac, src=srcmac)
+            'ether': {'src': srcmac, 'dst': dst_mac},
+            # IP(dst=destip, src=srcip, len=%s)
+            'ipv4': {'src': srcip, 'dst': destip},
+            # pkt_name(sport=srcport, dport=destport)
+            pkt_name: {'src': srcport, 'dst': destport},
+            # Raw(load='\x50'*%s)
+            'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}}
+        # create packet
+        streams = []
+        # keep a copy of pcap for debug
+        savePath = os.sep.join([self.target_source,
+                                "pkt_{0}.pcap".format(pkt_name)])
+        pkt_type = pkt_config.get('type')
+        pkt_layers = pkt_config.get('pkt_layers')
+        pkt = Packet(pkt_type=pkt_type.upper())
+        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 set_stream_to_bond_port(self, bond_port, slaves):
+        '''
+        : use framework/packet.py module to create multiple streams
+          send streams from bond port to slaves
+        :param bond_port:
+            bonded device port id
+        :param slaves: 
+            slaves port id
+        '''
+        pkt_configs = []
+        # get dst port mac address
+        pkt_name = self.default_pkt_name
+        destport = self.default_dst_port
+        destip = self.default_dst_ip
+        dst_mac = self.get_port_info(bond_port, 'mac')
+        # packet size
+        pktlen = self.get_pkt_len(pkt_type)
+        # set stream configuration
+        for packet_id in range(len(slaves['active'])):
+            srcmac, srcip, srcport = self.increase_mac_ip_port(packet_id)
+            pkt_configs.append({
+            'type': pkt_name.upper(),
+            'pkt_layers': {
+                # Ether(dst=nutmac, src=srcmac)
+                'ether': {'src': srcmac, 'dst': dst_mac},
+                # IP(dst=destip, src=srcip, len=%s)
+                'ipv4': {'src': srcip, 'dst': destip},
+                # pkt_name(sport=srcport, dport=destport)
+                pkt_name: {'src': srcport, 'dst': destport},
+                # Raw(load='\x50'*%s)
+                'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}})
+        # create packet
+        streams = []
+        for values in pkt_configs:
+            # keep a copy of pcap for debug
+            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.upper())
+            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 send_packets_by_scapy(self, **kwargs):
+        tx_iface = kwargs.get('port topo')[0]
+        # set interface ready to send packet
+        cmd = "ifconfig {0} up".format(tx_iface)
+        self.parent.tester.send_expect(cmd, '# ', 30)
+        send_pkts = kwargs.get('stream')
+        # stream config
+        stream_configs = kwargs.get('traffic configs')
+        count = stream_configs.get('count')
+        interval = stream_configs.get('interval', 0.01)
+        # run traffic
+        sendp(send_pkts, iface=tx_iface, inter=interval, verbose=False,
+              count=count)
+
+    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')
+        traffic_time = kwargs.get('traffic_time', 0)
+        rate_percent = kwargs.get('rate_percent', float(100)) 
+        #---------------------------------------------------------------
+        send_pkts = []
+        self.tgen_input = []
+        tgen_input = self.tgen_input
+        # generate packet contain multi stream
+        for pkt in self.packet_types.values():
+            send_pkts.append(pkt.pktgen.pkt)
+        ixia_pkt = os.sep.join([self.target_source, 'bonding_ixia.pcap'])
+        wrpcap(ixia_pkt, send_pkts)
+        #----------------------------------------------------------------
+        # set packet for send
+        # pause frame basic configuration
+        pause_time = 65535
+        pause_rate = 0.50
+        # run ixia testing 
+        frame_size = self.default_pkt_size
+        # calculate number of packets
+        expect_pps = self.parent.wirespeed(self.parent.nic, frame_size, 1) * \
+                                                                    1000000.0
+        # get line rate
+        linerate = expect_pps * (frame_size + 20) * 8
+        # calculate default sleep time for one pause frame
+        sleep = (1 / linerate) * pause_time * 512
+        # calculate packets dropped in sleep time
+        self.n_pkts = int((sleep / (1 / expect_pps)) * (1 / pause_rate))
+        #----------------------------------------------------------------
+        tester_port = self.parent.tester.get_local_port(self.parent.dut_ports[0])
+        tgen_input.append((tester_port, tester_port,ixia_pkt))
+        # run latency stat statistics
+        self.parent.tester.loop_traffic_generator_throughput(tgen_input,
+                                                      self.rate_percent)
+
+    def stop_ixia(self, data_types='packets'):
+        tester_inst = self.parent.tester
+        # get ixia statistics
+        line_rate = tester_inst.get_port_line_rate()
+        rx_bps, rx_pps = \
+            tester_inst.stop_traffic_generator_throughput_loop(self.tgen_input)
+        output = tester_inst.traffic_get_port_stats(self.tgen_input)
+        self.cur_data['ixia statistics'] = []
+        append = self.cur_data['ixia statistics'].append
+        append('send packets: {0}'.format(output[0]))
+        append('line_rate: {0}'.format(line_rate[0]))
+        append('rate_percent: {0}%'.format(self.rate_percent))
+
+    def get_pktgen(self, name):
+        pkt_gens = {
+            'ixia':     self.send_packets_by_ixia,
+            'scapy':    self.send_packets_by_scapy,}
+        pkt_generator = pkt_gens.get(name)
+        
+        return pkt_generator
+    
+    def send_packet(self, traffic_config):
+        """
+        stream transmission on specified link topology
+        """
+        time.sleep(2)
+        # start traffic
+        self.logger.info("begin transmission ...")
+        pktgen = self.get_pktgen(self.pktgen_name)
+        result = pktgen(**traffic_config)
+        # end traffic
+        self.logger.info("complete transmission")
+
+        return result
+    #
+    # On dut, dpdk testpmd common methods
+    #
+    def check_process_status(self, process_name='testpmd'):
+        cmd = "ps aux | grep -i %s | grep -v grep | awk {'print $2'}"%(
+                                                                process_name)
+        out = self.parent.dut.alt_session.send_expect(cmd, "# ", 10)
+        status = True if out != "" else False
+        return status
+
+    def check_process_exist(self, process_name='testpmd'):
+        status = self.check_process_status(process_name)
+        if not status:
+            msg = "{0} process exceptional quit".format(process_name)
+            out = self.parent.dut.session.session.get_output_all()
+            self.logger.info(out)
+            raise VerifyFailure(msg)
+
+    def d_console(self, cmds):
+        ''' wrap up testpmd command interactive console '''
+        if len(cmds) == 0:
+            return
+        # check if cmds is string
+        if isinstance(cmds, str):
+            timeout = 10
+            cmds = [[cmds, '', timeout]]
+        # check if cmds is only one command
+        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 'testpmd> '
+            else:
+                check_output = False
+                expected_str = expected_items or 'testpmd> '
+            timeout = int(item[2]) if len(item) == 3 else 5
+            #----------------------------------------------------------------
+            # run command on session
+            try:
+                console = self.testpmd.execute_cmd
+                msg_pipe = self.testpmd.get_output
+                output = console(item[0], expected_str, timeout)
+                output = msg_pipe(timeout) if not output else output
+            except TimeoutException:
+                try:
+                    # check if testpmd quit
+                    self.check_process_exist()
+                except Exception as e:
+                    self.testpmd_status = 'close'
+                msg = "execute '{0}' timeout".format(item[0])
+                output = out = self.parent.dut.session.session.get_output_all()
+                self.logger.error(output)
+                raise Exception(msg)
+            
+            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:
+                    fmt = "unexpected '{0}' is not in output"
+                    msg = fmt.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 preset_testpmd(self, core_mask, options='', eal_param=''):
+        try:
+            self.testpmd.start_testpmd( core_mask,
+                                        param=' '.join(options),
+                                        eal_param=eal_param)
+        except TimeoutException:
+            # check if testpmd quit
+            try:
+                self.check_process_exist()
+            except Exception as e:
+                self.testpmd_status = 'close'
+            msg = "execute '{0}' timeout".format(item[0])
+            self.logger.error(msg_pipe(timeout))
+            raise TimeoutException(msg)
+        # wait lsc event udpate done
+        time.sleep(10)
+        # check if testpmd has bootep up
+        if self.check_process_status():
+            self.logger.info("testpmd boot up successful")
+        else:
+            raise VerifyFailure("testpmd boot up failed")
+        self.d_console(self.preset_testpmd_cmds)
+        self.preset_testpmd_cmds = []
+        time.sleep(1)
+
+    def start_testpmd(self, eal_option=''):
+        if self.testpmd_status == 'running':
+            return
+        # boot up testpmd
+        hw_mask = 'all'
+        options = ''
+        self.preset_testpmd_cmds = ['port stop all', '', 15]
+        self.preset_testpmd(hw_mask, options, eal_param=eal_option)
+        self.testpmd_status = 'running'
+
+    def stop_testpmd(self):
+        time.sleep(1)
+        testpmd_cmds =[['port stop all', '', 15],
+                       ['show port stats all', ''],
+                       ['stop', ''],]
+        output = self.d_console(testpmd_cmds)
+        time.sleep(1)
+        return output
+
+    def close_testpmd(self):
+        if self.testpmd_status == 'close':
+            return None
+        output = self.stop_testpmd()
+        time.sleep(1)
+        self.testpmd.quit()
+        time.sleep(10)
+        if self.check_process_status():
+            raise VerifyFailure("testpmd close failed")
+        else:
+            self.logger.info("close testpmd successful")
+        self.testpmd_status = 'close'
+        return output
+
+    def start_ports(self, port='all'):
+        """
+        Start a port which the testpmd can see.
+        """
+        timeout = 12 if port=='all' else 5
+        cmds =[
+           ["port start %s" % str(port), " ", timeout],
+           # to avoid lsc event message interfere normal status
+           [" ", '', timeout]]
+        self.d_console(cmds)
+
+    def get_stats(self, portid, flow=['rx', 'tx']):
+        """
+        get one port statistics of testpmd
+        """
+        _portid = int(portid) if isinstance(portid, (str, unicode)) else portid
+        info = self.testpmd.get_pmd_stats(_portid)
+        _kwd = ["-packets", "-errors", "-bytes"]
+        stats = {}
+        if isinstance(flow, list):
+            for item in flow:
+                for item2 in _kwd:
+                    name = item.upper() + item2
+                    stats[name] = int(info[name])
+        elif isinstance(flow, (str, unicode)):
+            for item in _kwd:
+                name = flow.upper() + item
+                stats[name] = int(info[name])
+        else:
+            msg = 'unknown data type'
+            raise Exception(msg)
+
+        return stats
+
+    def get_all_stats(self, ports):
+        """
+        Get a group of ports statistics, which testpmd can display.
+        """
+        stats = {}
+        attrs = ['tx', 'rx']
+        for port_id in ports:
+            stats[port_id] = self.get_stats(port_id, attrs)
+
+        return stats
+
+    def set_tester_port_status(self, port_name, status):
+        """
+        Do some operations to the network interface port,
+        such as "up" or "down".
+        """
+        eth = self.parent.tester.get_interface(port_name)
+        self.parent.tester.admin_ports_linux(eth, status)
+        time.sleep(5)
+
+    def set_dut_peer_port_by_id(self, port_id, status):
+        # stop peer port on tester
+        intf = self.parent.tester.get_local_port(self.parent.dut_ports[port_id])
+        self.set_tester_port_status(intf, status)
+        time.sleep(5)
+        cur_status = self.get_port_info(port_id, 'link_status')
+        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
+        if cur_status != status:
+            self.logger.warning("expected status is [{0}]".format(status))
+
+    def set_dut_port_status(self, port_id, status):
+        opt = 'link-up' if status == 'up' else 'link-down'
+        # stop slave link by force 
+        cmd = "set {0} port {1}".format(opt, port_id)
+        self.d_console(cmd)
+        time.sleep(5)
+        cur_status = self.get_port_info(port_id, 'link_status')
+        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
+        if cur_status != status:
+            self.logger.warning("expected status is [{0}]".format(status))
+    # 
+    # testpmd bonding commands
+    #
+    def get_value_from_str(self, key_str, regx_str, string):
+        """
+        Get some values from the given string by the regular expression.
+        """
+        if isinstance(key_str, (unicode, str)):
+            pattern = r"(?<=%s)%s" % (key_str, regx_str)
+            s = re.compile(pattern)
+            res = s.search(string)
+            if type(res).__name__ == 'NoneType':
+                msg = "{0} hasn't match anything".format(key_str)
+                self.logger.warning(msg)
+                return ' '
+            else:
+                return res.group(0)
+        elif isinstance(key_str, (list, tuple)):
+            for key in key_str:
+                pattern = r"(?<=%s)%s" % (key, regx_str)
+                s = re.compile(pattern)
+                res = s.search(string)
+                if type(res).__name__ == 'NoneType':
+                    continue
+                else:
+                    return res.group(0)
+            else:
+                self.logger.warning("all key_str hasn't match anything")
+                return ' '
+
+    def _get_detail_from_port_info(self, port_id, args):
+        """
+        Get the detail info from the output of pmd cmd 
+            'show port info <port num>'.
+        """
+        key_str, regx_str = args
+        out = self.d_console("show port info %d" % port_id)
+        find_value = self.get_value_from_str(key_str, regx_str, out)
+        return find_value
+
+    def get_detail_from_port_info(self, port_id, args):
+        if isinstance(args[0], (list, tuple)):
+            return [self._get_detail_from_port_info(port_id, sub_args)
+                        for sub_args in args]
+        else:
+            return self._get_detail_from_port_info(port_id, args)
+
+    def get_port_info(self, port_id, info_type):
+        '''
+        Get the specified port information by its output message format
+        '''
+        info_set = {
+        'mac':              ["MAC address: ", "([0-9A-F]{2}:){5}[0-9A-F]{2}"],
+        'connect_socket':   ["Connect to socket: ", "\d+"],
+        'memory_socket':    ["memory allocation on the socket: ", "\d+"],
+        'link_status':      ["Link status: ", "\S+"],
+        'link_speed':       ["Link speed: ", "\d+"],
+        'link_duplex':      ["Link duplex: ", "\S+"],
+        'promiscuous_mode': ["Promiscuous mode: ", "\S+"],
+        'allmulticast_mode':["Allmulticast mode: ", "\S+"],
+        'vlan_offload':     [
+                        ["strip ", "\S+"],
+                        ['filter', "\S+"],
+                        ['qinq\(extend\) ', "\S+"]],
+        'queue_config':     [
+                        ["Max possible RX queues: ", "\d+"],
+                        ['Max possible number of RXDs per queue: ', "\d+"],
+                        ['Min possible number of RXDs per queue: ', "\d+"],
+                        ["Max possible TX queues: ", "\d+"],
+                        ['Max possible number of TXDs per queue: ', "\d+"],
+                        ['Min possible number of TXDs per queue: ', "\d+"],]
+        }
+
+        if info_type in info_set.keys():
+            return self.get_detail_from_port_info(port_id, info_set[info_type])
+        else:
+            msg = os.linesep.join([
+                "support query items including::",
+                os.linesep.join(info_set.keys())])
+            self.logger.warning(msg)
+            return None
+    # 
+    # On dut, dpdk testpmd common bonding methods
+    #
+    def get_bonding_config(self, config_content, args):
+        """
+        Get bonding info by command "show bonding config".
+        """
+        key_str, regx_str = args
+        find_value = self.get_value_from_str(key_str, regx_str, config_content)
+        return find_value
+
+    def get_info_from_bond_config(self, config_content, args):
+        """
+        Get active slaves of the bonding device which you choose.
+        """
+        search_args = args if isinstance(args[0], (list, tuple)) else [args]
+        for search_args in search_args:
+            try:
+                info = self.get_bonding_config(config_content, search_args)
+                break
+            except Exception as e:
+                self.logger.info(e)
+        else:
+            info = None
+
+        return info
+
+    def get_bonding_info(self, bond_port, info_types):
+        ''' Get the specified port information by its output message format '''
+        info_set = {
+            'mode':          ["Bonding mode: ", "\d*"],
+            'agg_mode':      ["IEEE802.3AD Aggregator Mode: ", "\S*"],
+            'balance_policy':["Balance Xmit Policy: ", "\S+"],
+            'slaves':        [["Slaves \(\d\): \[", "\d*( \d*)*"],
+                              ["Slaves: \[", "\d*( \d*)*"]],
+            'active_slaves': [["Active Slaves \(\d\): \[", "\d*( \d*)*"],
+                              ["Acitve Slaves: \[", "\d*( \d*)*"]],
+            'primary':       ["Primary: \[", "\d*"]}
+        # get all config information
+        config_content = self.d_console("show bonding config %d" % bond_port)
+        if isinstance(info_types, (list or tuple)):
+            query_values = []
+            for info_type in info_types:
+                if info_type in info_set.keys():
+                    find_value = self.get_info_from_bond_config(
+                                            config_content, info_set[info_type])
+                    if info_type in ['active_slaves', 'slaves']:
+                        find_value = [value for value in find_value.split(' ')
+                                        if value]
+                else:
+                    find_value = None
+                query_values.append(find_value)
+            return query_values
+        else:
+            info_type = info_types
+            if info_type in info_set.keys():
+                find_value = self.get_info_from_bond_config(
+                                            config_content, info_set[info_type])
+                if info_type in ['active_slaves', 'slaves']:
+                    find_value = [value for value in find_value.split(' ') 
+                                        if value]
+                return find_value
+            else:
+                return None
+
+    def get_active_slaves(self, bond_port):
+        primary_port = int(self.get_bonding_info(bond_port, 'primary'))
+        active_slaves = self.get_bonding_info(bond_port, 'active_slaves')
+
+        return int(primary_port), [int(slave) for slave in active_slaves]
+
+    def create_bonded_device(self, mode=0, socket=0, verify_detail=False):
+        """
+        Create a bonding device with the parameters you specified.
+        """
+        cmd = "create bonded device %d %d" % (mode, socket)
+        out = self.d_console(cmd)
+        err_fmt = "Create bonded device on mode [%d] socket [%d] failed"
+        self.verify("Created new bonded device" in out, err_fmt% (mode, socket))
+        fmts = [
+             "Created new bonded device net_bond_testpmd_[\d] on \(port ",
+             "Created new bonded device net_bonding_testpmd_[\d] on \(port ",
+             "Created new bonded device eth_bond_testpmd_[\d] on \(port "]
+        bond_port = self.get_value_from_str(fmts, "\d+", out)
+        bond_port = int(bond_port)
+
+        if verify_detail:
+            out = self.d_console("show bonding config %d" % bond_port)
+            self.verify("Bonding mode: %d" % mode in out,
+                        "Bonding mode display error when create bonded device")
+            self.verify("Slaves: []" in out,
+                        "Slaves display error when create bonded device")
+            self.verify("Active Slaves: []" in out,
+                        "Active Slaves display error when create bonded device")
+            self.verify("Primary: []" not in out,
+                        "Primary display error when create bonded device")
+            out = self.d_console("show port info %d" % bond_port)
+            self.verify("Connect to socket: %d" % socket in out,
+                        "Bonding port connect socket error")
+            self.verify("Link status: down" in out,
+                        "Bonding port default link status error")
+            self.verify("Link speed: 0 Mbps" in out,
+                        "Bonding port default link speed error")
+
+        return bond_port
+
+    def add_slave(self, bond_port, invert_verify=False, expected_str='',
+                  *slave_ports):
+        """
+        Add ports into the bonding device as slaves.
+        """
+        if len(slave_ports) <= 0:
+            utils.RED("No port exist when add slave to bonded device")
+        for slave_id in slave_ports:
+            cmd = "add bonding slave %d %d" % (slave_id, bond_port)
+            out = self.d_console(cmd)
+            if expected_str:
+                self.verify(expected_str in out,
+                            "message <{0}> is missing".format(expected_str))
+            slaves = self.get_bonding_info(bond_port, 'slaves')
+            if not invert_verify:
+                self.verify(str(slave_id) in slaves,
+                            "Add port as bonding slave failed")
+            else:
+                err = "Add port as bonding slave successfully,should fail"
+                self.verify(str(slave_id) not in slaves, err)
+
+    def remove_slaves(self, bond_port, invert_verify=False, *slave_port):
+        """
+        Remove the specified slave port from the bonding device.
+        """
+        if len(slave_port) <= 0:
+            msg = "No port exist when remove slave from bonded device"
+            self.logger.error(msg)
+        for slave_id in slave_port:
+            cmd = "remove bonding slave %d %d" % (int(slave_id), bond_port)
+            self.d_console(cmd)
+            slaves = self.get_bonding_info(bond_port, 'slaves')
+            if not invert_verify:
+                self.verify(str(slave_id) not in slaves,
+                            "Remove slave to fail from bonding device")
+            else:
+                err = (
+                    "Remove slave successfully from bonding device, "
+                    "should be failed")
+                self.verify(str(slave_id) in slaves, err)
+
+    def remove_all_slaves(self, bond_port):
+        """
+        Remove all slaves of specified bound device.
+        """
+        all_slaves = self.get_bonding_info(bond_port, 'slaves')
+        if not all_slaves:
+            return
+        all_slaves = all_slaves.split()
+        if len(all_slaves) == 0:
+            return
+        self.remove_slaves(bond_port, False, *all_slaves)
+
+    def set_primary_slave(self, bond_port, slave_port, invert_verify=False):
+        """
+        Set the primary slave for the bonding device.
+        """
+        cmd = "set bonding primary %d %d" % (slave_port, bond_port)
+        self.d_console(cmd)
+        out = self.get_bonding_info(bond_port, 'primary')
+        if not invert_verify:
+            self.verify(str(slave_port) in out,
+                        "Set bonding primary port failed")
+        else:
+            err = "Set bonding primary port successfully, should not success"
+            self.verify(str(slave_port) not in out, err)
+
+    def set_bonding_mode(self, bond_port, mode):
+        """
+        Set the bonding mode for port_id.
+        """
+        cmd = "set bonding mode %d %d" % (mode, bond_port)
+        self.d_console(cmd)
+        mode_value = self.get_bonding_info(bond_port, 'mode')
+        self.verify(str(mode) in mode_value, "Set bonding mode failed")
+
+    def set_bonding_mac(self, bond_port, mac):
+        """
+        Set the MAC for the bonding device.
+        """
+        cmd = "set bonding mac_addr %s %s" % (bond_port, mac)
+        self.d_console(cmd)
+        new_mac = self.get_port_mac(bond_port)
+        self.verify(new_mac == mac, "Set bonding mac failed")
+
+    def get_port_mac(self, bond_port, query_type):
+        bond_port_mac = self.get_port_info(bond_port, query_type)
+        return bond_port_mac
+
+    def set_bonding_balance_policy(self, bond_port, policy):
+        """
+        Set the balance transmit policy for the bonding device.
+        """
+        cmd = "set bonding balance_xmit_policy %d %s" % (bond_port, policy)
+        self.d_console(cmd)
+        new_policy = self.get_bonding_info(bond_port, 'balance_policy')
+        policy = "BALANCE_XMIT_POLICY_LAYER" + policy.lstrip('l')
+        self.verify(new_policy == policy, "Set bonding balance policy failed")
+
+    @property
+    def is_perf(self):
+        return self.parent._enable_perf
-- 
1.9.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [dts] [PATCH V1]pmd_bonding: common methods used by bonding suites
  2019-02-28  5:50 ` [dts] [PATCH V1]pmd_bonding: " yufengmx
@ 2019-02-28  6:03   ` Mo, YufengX
  2019-02-28  8:40     ` Chen, Zhaoyan
  2019-02-28 11:00   ` Tu, Lijuan
  1 sibling, 1 reply; 5+ messages in thread
From: Mo, YufengX @ 2019-02-28  6:03 UTC (permalink / raw)
  To: dts; +Cc: Mo, YufengX


Tested-by: Mo, YufengX <yufengx.mo@intel.com>

> -----Original Message-----
> From: Mo, YufengX
> Sent: Thursday, February 28, 2019 1:50 PM
> To: dts@dpdk.org
> Cc: Mo, YufengX <yufengx.mo@intel.com>
> Subject: [dts][PATCH V1]pmd_bonding: common methods used by bonding suites
> 
> 
> It is a module that common methods are ran by pmd_bonded_8023ad/pmd_stacked_bonded
> suites, including:
>     boot up testpmd
>     sending packets
>     query testpmd information of ports
>     set testpmd port status
>     commond dpdk bonding commands
> 
> Signed-off-by: yufengmx <yufengx.mo@intel.com>
> ---
>  tests/bonding.py | 831 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 831 insertions(+)
>  create mode 100644 tests/bonding.py
> 
> diff --git a/tests/bonding.py b/tests/bonding.py
> new file mode 100644
> index 0000000..fb2a69e
> --- /dev/null
> +++ b/tests/bonding.py
> @@ -0,0 +1,831 @@
> +# 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.
> +
> +import os
> +import time
> +import re
> +import struct
> +import socket
> +from socket import htonl
> +
> +from packet import Packet
> +from scapy.sendrecv import sendp
> +from scapy.utils import wrpcap
> +
> +import utils
> +from exception import TimeoutException, VerifyFailure
> +from pmd_output import PmdOutput
> +from settings import HEADER_SIZE
> +
> +# define bonding mode
> +MODE_ROUND_ROBIN = 0
> +MODE_ACTIVE_BACKUP = 1
> +MODE_XOR_BALANCE = 2
> +MODE_BROADCAST = 3
> +MODE_LACP = 4
> +MODE_TLB_BALANCE = 5
> +MODE_ALB_BALANCE = 6
> +
> +# define packet size
> +FRAME_SIZE_64 = 64
> +FRAME_SIZE_128 = 128
> +FRAME_SIZE_256 = 256
> +FRAME_SIZE_512 = 512
> +FRAME_SIZE_1024 = 1024
> +FRAME_SIZE_1518 = 1518
> +
> +class PmdBonding(object):
> +    ''' common methods for testpmd bonding '''
> +
> +    def __init__(self, **kwargs):
> +        # set parent instance
> +        self.parent = kwargs.get('parent')
> +        # set target source code directory
> +        self.target_source = self.parent.dut.base_dir
> +        # set logger
> +        self.logger = self.parent.logger
> +        self.verify = self.parent.verify
> +        # select packet generator
> +        self.pktgen_name = 'ixia' if self.is_perf else 'scapy'
> +        # traffic default config
> +        self.default_pkt_size = kwargs.get('pkt_size') or FRAME_SIZE_64
> +        self.default_src_mac = kwargs.get('src_mac')
> +        self.default_src_ip = kwargs.get('src_ip')
> +        self.default_src_port = kwargs.get('src_port')
> +        self.default_dst_ip = kwargs.get('dst_ip')
> +        self.default_dst_port = kwargs.get('dst_port')
> +        self.default_pkt_name = kwargs.get('pkt_name')
> +        # testpmd
> +        self.testpmd = PmdOutput(self.parent.dut)
> +        self.testpmd_status = 'close'
> +    #
> +    # On tester platform, packet transmission
> +    #
> +    def mac_str_to_int(self, mac_str):
> +        """ convert the MAC type from the string into the int. """
> +        mac_hex = '0x'
> +        for mac_part in mac_str.split(':'):
> +            mac_hex += mac_part
> +        return int(mac_hex, 16)
> +
> +    def mac_int_to_str(self, mac_int):
> +        """ Translate the MAC type from the string into the int. """
> +        temp = hex(mac_int)[2:]
> +        b = []
> +        [b.append(temp[n:n+2]) for n in range(len(temp)) if n % 2 == 0]
> +        new_mac = ":".join(b)
> +        return new_mac
> +
> +    def ip_str_to_int(self, ip_str):
> +        """
> +        convert the IP type from the string into the int.
> +        """
> +        ip_int = socket.ntohl(struct.unpack(
> +                                "I", socket.inet_aton(str(ip_str)))[0])
> +        return ip_int
> +
> +    def ip_int_to_str(self, ip_int):
> +        """
> +        convert the IP type from the int into the string.
> +        """
> +        ip_str = socket.inet_ntoa(struct.pack('I', socket.htonl(ip_int)))
> +        return ip_str
> +
> +    def increase_ip(self, ip, step=1):
> +        ''' ip: string format '''
> +        _ip_int = self.ip_str_to_int(ip)
> +        new_ip = self.ip_int_to_str(_ip_int + step)
> +        return new_ip
> +
> +    def increase_mac(self, mac, step=1):
> +        ''' mac: string format '''
> +        _mac_int = self.mac_str_to_int(mac)
> +        new_mac = self.mac_int_to_str(_mac_int+step)
> +        return new_mac
> +
> +    def increase_port(self, port, step=1):
> +        ''' port: int format '''
> +        new_port = port + step
> +        return new_port
> +
> +    def increase_mac_ip_port(self, step=1):
> +        # get source port setting
> +        mac, ip, port = (self.default_src_mac,
> +                         self.default_src_ip,
> +                         self.default_src_port)
> +        return (self.increase_mac(mac, step),
> +                self.increase_ip(ip, step),
> +                self.increase_port(port, step))
> +
> +    def get_pkt_len(self, pkt_type):
> +        ''' get packet payload size '''
> +        frame_size = self.default_pkt_size
> +        headers_size = sum(map(lambda x: HEADER_SIZE[x],
> +                               ['eth', 'ip', pkt_type]))
> +        pktlen = frame_size - headers_size
> +        return pktlen
> +
> +    def set_stream_to_slave_port(self, dut_port_id):
> +        '''
> +        use framework/packet.py module to create one stream, send stream to
> +        slave port
> +        '''
> +        # get dst port mac address
> +        pkt_name = self.default_pkt_name
> +        destport = self.default_dst_port
> +        destip = self.default_dst_ip
> +        dst_mac = self.get_port_info(dut_port_id, 'mac')
> +        # packet size
> +        pktlen = self.get_pkt_len(pkt_name)
> +        # set stream configuration
> +        srcmac, srcip, srcport = self.increase_mac_ip_port(0)
> +        pkt_config = {
> +        'type': pkt_name.upper(),
> +        'pkt_layers': {
> +            # Ether(dst=nutmac, src=srcmac)
> +            'ether': {'src': srcmac, 'dst': dst_mac},
> +            # IP(dst=destip, src=srcip, len=%s)
> +            'ipv4': {'src': srcip, 'dst': destip},
> +            # pkt_name(sport=srcport, dport=destport)
> +            pkt_name: {'src': srcport, 'dst': destport},
> +            # Raw(load='\x50'*%s)
> +            'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}}
> +        # create packet
> +        streams = []
> +        # keep a copy of pcap for debug
> +        savePath = os.sep.join([self.target_source,
> +                                "pkt_{0}.pcap".format(pkt_name)])
> +        pkt_type = pkt_config.get('type')
> +        pkt_layers = pkt_config.get('pkt_layers')
> +        pkt = Packet(pkt_type=pkt_type.upper())
> +        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 set_stream_to_bond_port(self, bond_port, slaves):
> +        '''
> +        : use framework/packet.py module to create multiple streams
> +          send streams from bond port to slaves
> +        :param bond_port:
> +            bonded device port id
> +        :param slaves:
> +            slaves port id
> +        '''
> +        pkt_configs = []
> +        # get dst port mac address
> +        pkt_name = self.default_pkt_name
> +        destport = self.default_dst_port
> +        destip = self.default_dst_ip
> +        dst_mac = self.get_port_info(bond_port, 'mac')
> +        # packet size
> +        pktlen = self.get_pkt_len(pkt_type)
> +        # set stream configuration
> +        for packet_id in range(len(slaves['active'])):
> +            srcmac, srcip, srcport = self.increase_mac_ip_port(packet_id)
> +            pkt_configs.append({
> +            'type': pkt_name.upper(),
> +            'pkt_layers': {
> +                # Ether(dst=nutmac, src=srcmac)
> +                'ether': {'src': srcmac, 'dst': dst_mac},
> +                # IP(dst=destip, src=srcip, len=%s)
> +                'ipv4': {'src': srcip, 'dst': destip},
> +                # pkt_name(sport=srcport, dport=destport)
> +                pkt_name: {'src': srcport, 'dst': destport},
> +                # Raw(load='\x50'*%s)
> +                'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}})
> +        # create packet
> +        streams = []
> +        for values in pkt_configs:
> +            # keep a copy of pcap for debug
> +            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.upper())
> +            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 send_packets_by_scapy(self, **kwargs):
> +        tx_iface = kwargs.get('port topo')[0]
> +        # set interface ready to send packet
> +        cmd = "ifconfig {0} up".format(tx_iface)
> +        self.parent.tester.send_expect(cmd, '# ', 30)
> +        send_pkts = kwargs.get('stream')
> +        # stream config
> +        stream_configs = kwargs.get('traffic configs')
> +        count = stream_configs.get('count')
> +        interval = stream_configs.get('interval', 0.01)
> +        # run traffic
> +        sendp(send_pkts, iface=tx_iface, inter=interval, verbose=False,
> +              count=count)
> +
> +    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')
> +        traffic_time = kwargs.get('traffic_time', 0)
> +        rate_percent = kwargs.get('rate_percent', float(100))
> +        #---------------------------------------------------------------
> +        send_pkts = []
> +        self.tgen_input = []
> +        tgen_input = self.tgen_input
> +        # generate packet contain multi stream
> +        for pkt in self.packet_types.values():
> +            send_pkts.append(pkt.pktgen.pkt)
> +        ixia_pkt = os.sep.join([self.target_source, 'bonding_ixia.pcap'])
> +        wrpcap(ixia_pkt, send_pkts)
> +        #----------------------------------------------------------------
> +        # set packet for send
> +        # pause frame basic configuration
> +        pause_time = 65535
> +        pause_rate = 0.50
> +        # run ixia testing
> +        frame_size = self.default_pkt_size
> +        # calculate number of packets
> +        expect_pps = self.parent.wirespeed(self.parent.nic, frame_size, 1) * \
> +                                                                    1000000.0
> +        # get line rate
> +        linerate = expect_pps * (frame_size + 20) * 8
> +        # calculate default sleep time for one pause frame
> +        sleep = (1 / linerate) * pause_time * 512
> +        # calculate packets dropped in sleep time
> +        self.n_pkts = int((sleep / (1 / expect_pps)) * (1 / pause_rate))
> +        #----------------------------------------------------------------
> +        tester_port = self.parent.tester.get_local_port(self.parent.dut_ports[0])
> +        tgen_input.append((tester_port, tester_port,ixia_pkt))
> +        # run latency stat statistics
> +        self.parent.tester.loop_traffic_generator_throughput(tgen_input,
> +                                                      self.rate_percent)
> +
> +    def stop_ixia(self, data_types='packets'):
> +        tester_inst = self.parent.tester
> +        # get ixia statistics
> +        line_rate = tester_inst.get_port_line_rate()
> +        rx_bps, rx_pps = \
> +            tester_inst.stop_traffic_generator_throughput_loop(self.tgen_input)
> +        output = tester_inst.traffic_get_port_stats(self.tgen_input)
> +        self.cur_data['ixia statistics'] = []
> +        append = self.cur_data['ixia statistics'].append
> +        append('send packets: {0}'.format(output[0]))
> +        append('line_rate: {0}'.format(line_rate[0]))
> +        append('rate_percent: {0}%'.format(self.rate_percent))
> +
> +    def get_pktgen(self, name):
> +        pkt_gens = {
> +            'ixia':     self.send_packets_by_ixia,
> +            'scapy':    self.send_packets_by_scapy,}
> +        pkt_generator = pkt_gens.get(name)
> +
> +        return pkt_generator
> +
> +    def send_packet(self, traffic_config):
> +        """
> +        stream transmission on specified link topology
> +        """
> +        time.sleep(2)
> +        # start traffic
> +        self.logger.info("begin transmission ...")
> +        pktgen = self.get_pktgen(self.pktgen_name)
> +        result = pktgen(**traffic_config)
> +        # end traffic
> +        self.logger.info("complete transmission")
> +
> +        return result
> +    #
> +    # On dut, dpdk testpmd common methods
> +    #
> +    def check_process_status(self, process_name='testpmd'):
> +        cmd = "ps aux | grep -i %s | grep -v grep | awk {'print $2'}"%(
> +                                                                process_name)
> +        out = self.parent.dut.alt_session.send_expect(cmd, "# ", 10)
> +        status = True if out != "" else False
> +        return status
> +
> +    def check_process_exist(self, process_name='testpmd'):
> +        status = self.check_process_status(process_name)
> +        if not status:
> +            msg = "{0} process exceptional quit".format(process_name)
> +            out = self.parent.dut.session.session.get_output_all()
> +            self.logger.info(out)
> +            raise VerifyFailure(msg)
> +
> +    def d_console(self, cmds):
> +        ''' wrap up testpmd command interactive console '''
> +        if len(cmds) == 0:
> +            return
> +        # check if cmds is string
> +        if isinstance(cmds, str):
> +            timeout = 10
> +            cmds = [[cmds, '', timeout]]
> +        # check if cmds is only one command
> +        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 'testpmd> '
> +            else:
> +                check_output = False
> +                expected_str = expected_items or 'testpmd> '
> +            timeout = int(item[2]) if len(item) == 3 else 5
> +            #----------------------------------------------------------------
> +            # run command on session
> +            try:
> +                console = self.testpmd.execute_cmd
> +                msg_pipe = self.testpmd.get_output
> +                output = console(item[0], expected_str, timeout)
> +                output = msg_pipe(timeout) if not output else output
> +            except TimeoutException:
> +                try:
> +                    # check if testpmd quit
> +                    self.check_process_exist()
> +                except Exception as e:
> +                    self.testpmd_status = 'close'
> +                msg = "execute '{0}' timeout".format(item[0])
> +                output = out = self.parent.dut.session.session.get_output_all()
> +                self.logger.error(output)
> +                raise Exception(msg)
> +
> +            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:
> +                    fmt = "unexpected '{0}' is not in output"
> +                    msg = fmt.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 preset_testpmd(self, core_mask, options='', eal_param=''):
> +        try:
> +            self.testpmd.start_testpmd( core_mask,
> +                                        param=' '.join(options),
> +                                        eal_param=eal_param)
> +        except TimeoutException:
> +            # check if testpmd quit
> +            try:
> +                self.check_process_exist()
> +            except Exception as e:
> +                self.testpmd_status = 'close'
> +            msg = "execute '{0}' timeout".format(item[0])
> +            self.logger.error(msg_pipe(timeout))
> +            raise TimeoutException(msg)
> +        # wait lsc event udpate done
> +        time.sleep(10)
> +        # check if testpmd has bootep up
> +        if self.check_process_status():
> +            self.logger.info("testpmd boot up successful")
> +        else:
> +            raise VerifyFailure("testpmd boot up failed")
> +        self.d_console(self.preset_testpmd_cmds)
> +        self.preset_testpmd_cmds = []
> +        time.sleep(1)
> +
> +    def start_testpmd(self, eal_option=''):
> +        if self.testpmd_status == 'running':
> +            return
> +        # boot up testpmd
> +        hw_mask = 'all'
> +        options = ''
> +        self.preset_testpmd_cmds = ['port stop all', '', 15]
> +        self.preset_testpmd(hw_mask, options, eal_param=eal_option)
> +        self.testpmd_status = 'running'
> +
> +    def stop_testpmd(self):
> +        time.sleep(1)
> +        testpmd_cmds =[['port stop all', '', 15],
> +                       ['show port stats all', ''],
> +                       ['stop', ''],]
> +        output = self.d_console(testpmd_cmds)
> +        time.sleep(1)
> +        return output
> +
> +    def close_testpmd(self):
> +        if self.testpmd_status == 'close':
> +            return None
> +        output = self.stop_testpmd()
> +        time.sleep(1)
> +        self.testpmd.quit()
> +        time.sleep(10)
> +        if self.check_process_status():
> +            raise VerifyFailure("testpmd close failed")
> +        else:
> +            self.logger.info("close testpmd successful")
> +        self.testpmd_status = 'close'
> +        return output
> +
> +    def start_ports(self, port='all'):
> +        """
> +        Start a port which the testpmd can see.
> +        """
> +        timeout = 12 if port=='all' else 5
> +        cmds =[
> +           ["port start %s" % str(port), " ", timeout],
> +           # to avoid lsc event message interfere normal status
> +           [" ", '', timeout]]
> +        self.d_console(cmds)
> +
> +    def get_stats(self, portid, flow=['rx', 'tx']):
> +        """
> +        get one port statistics of testpmd
> +        """
> +        _portid = int(portid) if isinstance(portid, (str, unicode)) else portid
> +        info = self.testpmd.get_pmd_stats(_portid)
> +        _kwd = ["-packets", "-errors", "-bytes"]
> +        stats = {}
> +        if isinstance(flow, list):
> +            for item in flow:
> +                for item2 in _kwd:
> +                    name = item.upper() + item2
> +                    stats[name] = int(info[name])
> +        elif isinstance(flow, (str, unicode)):
> +            for item in _kwd:
> +                name = flow.upper() + item
> +                stats[name] = int(info[name])
> +        else:
> +            msg = 'unknown data type'
> +            raise Exception(msg)
> +
> +        return stats
> +
> +    def get_all_stats(self, ports):
> +        """
> +        Get a group of ports statistics, which testpmd can display.
> +        """
> +        stats = {}
> +        attrs = ['tx', 'rx']
> +        for port_id in ports:
> +            stats[port_id] = self.get_stats(port_id, attrs)
> +
> +        return stats
> +
> +    def set_tester_port_status(self, port_name, status):
> +        """
> +        Do some operations to the network interface port,
> +        such as "up" or "down".
> +        """
> +        eth = self.parent.tester.get_interface(port_name)
> +        self.parent.tester.admin_ports_linux(eth, status)
> +        time.sleep(5)
> +
> +    def set_dut_peer_port_by_id(self, port_id, status):
> +        # stop peer port on tester
> +        intf = self.parent.tester.get_local_port(self.parent.dut_ports[port_id])
> +        self.set_tester_port_status(intf, status)
> +        time.sleep(5)
> +        cur_status = self.get_port_info(port_id, 'link_status')
> +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> +        if cur_status != status:
> +            self.logger.warning("expected status is [{0}]".format(status))
> +
> +    def set_dut_port_status(self, port_id, status):
> +        opt = 'link-up' if status == 'up' else 'link-down'
> +        # stop slave link by force
> +        cmd = "set {0} port {1}".format(opt, port_id)
> +        self.d_console(cmd)
> +        time.sleep(5)
> +        cur_status = self.get_port_info(port_id, 'link_status')
> +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> +        if cur_status != status:
> +            self.logger.warning("expected status is [{0}]".format(status))
> +    #
> +    # testpmd bonding commands
> +    #
> +    def get_value_from_str(self, key_str, regx_str, string):
> +        """
> +        Get some values from the given string by the regular expression.
> +        """
> +        if isinstance(key_str, (unicode, str)):
> +            pattern = r"(?<=%s)%s" % (key_str, regx_str)
> +            s = re.compile(pattern)
> +            res = s.search(string)
> +            if type(res).__name__ == 'NoneType':
> +                msg = "{0} hasn't match anything".format(key_str)
> +                self.logger.warning(msg)
> +                return ' '
> +            else:
> +                return res.group(0)
> +        elif isinstance(key_str, (list, tuple)):
> +            for key in key_str:
> +                pattern = r"(?<=%s)%s" % (key, regx_str)
> +                s = re.compile(pattern)
> +                res = s.search(string)
> +                if type(res).__name__ == 'NoneType':
> +                    continue
> +                else:
> +                    return res.group(0)
> +            else:
> +                self.logger.warning("all key_str hasn't match anything")
> +                return ' '
> +
> +    def _get_detail_from_port_info(self, port_id, args):
> +        """
> +        Get the detail info from the output of pmd cmd
> +            'show port info <port num>'.
> +        """
> +        key_str, regx_str = args
> +        out = self.d_console("show port info %d" % port_id)
> +        find_value = self.get_value_from_str(key_str, regx_str, out)
> +        return find_value
> +
> +    def get_detail_from_port_info(self, port_id, args):
> +        if isinstance(args[0], (list, tuple)):
> +            return [self._get_detail_from_port_info(port_id, sub_args)
> +                        for sub_args in args]
> +        else:
> +            return self._get_detail_from_port_info(port_id, args)
> +
> +    def get_port_info(self, port_id, info_type):
> +        '''
> +        Get the specified port information by its output message format
> +        '''
> +        info_set = {
> +        'mac':              ["MAC address: ", "([0-9A-F]{2}:){5}[0-9A-F]{2}"],
> +        'connect_socket':   ["Connect to socket: ", "\d+"],
> +        'memory_socket':    ["memory allocation on the socket: ", "\d+"],
> +        'link_status':      ["Link status: ", "\S+"],
> +        'link_speed':       ["Link speed: ", "\d+"],
> +        'link_duplex':      ["Link duplex: ", "\S+"],
> +        'promiscuous_mode': ["Promiscuous mode: ", "\S+"],
> +        'allmulticast_mode':["Allmulticast mode: ", "\S+"],
> +        'vlan_offload':     [
> +                        ["strip ", "\S+"],
> +                        ['filter', "\S+"],
> +                        ['qinq\(extend\) ', "\S+"]],
> +        'queue_config':     [
> +                        ["Max possible RX queues: ", "\d+"],
> +                        ['Max possible number of RXDs per queue: ', "\d+"],
> +                        ['Min possible number of RXDs per queue: ', "\d+"],
> +                        ["Max possible TX queues: ", "\d+"],
> +                        ['Max possible number of TXDs per queue: ', "\d+"],
> +                        ['Min possible number of TXDs per queue: ', "\d+"],]
> +        }
> +
> +        if info_type in info_set.keys():
> +            return self.get_detail_from_port_info(port_id, info_set[info_type])
> +        else:
> +            msg = os.linesep.join([
> +                "support query items including::",
> +                os.linesep.join(info_set.keys())])
> +            self.logger.warning(msg)
> +            return None
> +    #
> +    # On dut, dpdk testpmd common bonding methods
> +    #
> +    def get_bonding_config(self, config_content, args):
> +        """
> +        Get bonding info by command "show bonding config".
> +        """
> +        key_str, regx_str = args
> +        find_value = self.get_value_from_str(key_str, regx_str, config_content)
> +        return find_value
> +
> +    def get_info_from_bond_config(self, config_content, args):
> +        """
> +        Get active slaves of the bonding device which you choose.
> +        """
> +        search_args = args if isinstance(args[0], (list, tuple)) else [args]
> +        for search_args in search_args:
> +            try:
> +                info = self.get_bonding_config(config_content, search_args)
> +                break
> +            except Exception as e:
> +                self.logger.info(e)
> +        else:
> +            info = None
> +
> +        return info
> +
> +    def get_bonding_info(self, bond_port, info_types):
> +        ''' Get the specified port information by its output message format '''
> +        info_set = {
> +            'mode':          ["Bonding mode: ", "\d*"],
> +            'agg_mode':      ["IEEE802.3AD Aggregator Mode: ", "\S*"],
> +            'balance_policy':["Balance Xmit Policy: ", "\S+"],
> +            'slaves':        [["Slaves \(\d\): \[", "\d*( \d*)*"],
> +                              ["Slaves: \[", "\d*( \d*)*"]],
> +            'active_slaves': [["Active Slaves \(\d\): \[", "\d*( \d*)*"],
> +                              ["Acitve Slaves: \[", "\d*( \d*)*"]],
> +            'primary':       ["Primary: \[", "\d*"]}
> +        # get all config information
> +        config_content = self.d_console("show bonding config %d" % bond_port)
> +        if isinstance(info_types, (list or tuple)):
> +            query_values = []
> +            for info_type in info_types:
> +                if info_type in info_set.keys():
> +                    find_value = self.get_info_from_bond_config(
> +                                            config_content, info_set[info_type])
> +                    if info_type in ['active_slaves', 'slaves']:
> +                        find_value = [value for value in find_value.split(' ')
> +                                        if value]
> +                else:
> +                    find_value = None
> +                query_values.append(find_value)
> +            return query_values
> +        else:
> +            info_type = info_types
> +            if info_type in info_set.keys():
> +                find_value = self.get_info_from_bond_config(
> +                                            config_content, info_set[info_type])
> +                if info_type in ['active_slaves', 'slaves']:
> +                    find_value = [value for value in find_value.split(' ')
> +                                        if value]
> +                return find_value
> +            else:
> +                return None
> +
> +    def get_active_slaves(self, bond_port):
> +        primary_port = int(self.get_bonding_info(bond_port, 'primary'))
> +        active_slaves = self.get_bonding_info(bond_port, 'active_slaves')
> +
> +        return int(primary_port), [int(slave) for slave in active_slaves]
> +
> +    def create_bonded_device(self, mode=0, socket=0, verify_detail=False):
> +        """
> +        Create a bonding device with the parameters you specified.
> +        """
> +        cmd = "create bonded device %d %d" % (mode, socket)
> +        out = self.d_console(cmd)
> +        err_fmt = "Create bonded device on mode [%d] socket [%d] failed"
> +        self.verify("Created new bonded device" in out, err_fmt% (mode, socket))
> +        fmts = [
> +             "Created new bonded device net_bond_testpmd_[\d] on \(port ",
> +             "Created new bonded device net_bonding_testpmd_[\d] on \(port ",
> +             "Created new bonded device eth_bond_testpmd_[\d] on \(port "]
> +        bond_port = self.get_value_from_str(fmts, "\d+", out)
> +        bond_port = int(bond_port)
> +
> +        if verify_detail:
> +            out = self.d_console("show bonding config %d" % bond_port)
> +            self.verify("Bonding mode: %d" % mode in out,
> +                        "Bonding mode display error when create bonded device")
> +            self.verify("Slaves: []" in out,
> +                        "Slaves display error when create bonded device")
> +            self.verify("Active Slaves: []" in out,
> +                        "Active Slaves display error when create bonded device")
> +            self.verify("Primary: []" not in out,
> +                        "Primary display error when create bonded device")
> +            out = self.d_console("show port info %d" % bond_port)
> +            self.verify("Connect to socket: %d" % socket in out,
> +                        "Bonding port connect socket error")
> +            self.verify("Link status: down" in out,
> +                        "Bonding port default link status error")
> +            self.verify("Link speed: 0 Mbps" in out,
> +                        "Bonding port default link speed error")
> +
> +        return bond_port
> +
> +    def add_slave(self, bond_port, invert_verify=False, expected_str='',
> +                  *slave_ports):
> +        """
> +        Add ports into the bonding device as slaves.
> +        """
> +        if len(slave_ports) <= 0:
> +            utils.RED("No port exist when add slave to bonded device")
> +        for slave_id in slave_ports:
> +            cmd = "add bonding slave %d %d" % (slave_id, bond_port)
> +            out = self.d_console(cmd)
> +            if expected_str:
> +                self.verify(expected_str in out,
> +                            "message <{0}> is missing".format(expected_str))
> +            slaves = self.get_bonding_info(bond_port, 'slaves')
> +            if not invert_verify:
> +                self.verify(str(slave_id) in slaves,
> +                            "Add port as bonding slave failed")
> +            else:
> +                err = "Add port as bonding slave successfully,should fail"
> +                self.verify(str(slave_id) not in slaves, err)
> +
> +    def remove_slaves(self, bond_port, invert_verify=False, *slave_port):
> +        """
> +        Remove the specified slave port from the bonding device.
> +        """
> +        if len(slave_port) <= 0:
> +            msg = "No port exist when remove slave from bonded device"
> +            self.logger.error(msg)
> +        for slave_id in slave_port:
> +            cmd = "remove bonding slave %d %d" % (int(slave_id), bond_port)
> +            self.d_console(cmd)
> +            slaves = self.get_bonding_info(bond_port, 'slaves')
> +            if not invert_verify:
> +                self.verify(str(slave_id) not in slaves,
> +                            "Remove slave to fail from bonding device")
> +            else:
> +                err = (
> +                    "Remove slave successfully from bonding device, "
> +                    "should be failed")
> +                self.verify(str(slave_id) in slaves, err)
> +
> +    def remove_all_slaves(self, bond_port):
> +        """
> +        Remove all slaves of specified bound device.
> +        """
> +        all_slaves = self.get_bonding_info(bond_port, 'slaves')
> +        if not all_slaves:
> +            return
> +        all_slaves = all_slaves.split()
> +        if len(all_slaves) == 0:
> +            return
> +        self.remove_slaves(bond_port, False, *all_slaves)
> +
> +    def set_primary_slave(self, bond_port, slave_port, invert_verify=False):
> +        """
> +        Set the primary slave for the bonding device.
> +        """
> +        cmd = "set bonding primary %d %d" % (slave_port, bond_port)
> +        self.d_console(cmd)
> +        out = self.get_bonding_info(bond_port, 'primary')
> +        if not invert_verify:
> +            self.verify(str(slave_port) in out,
> +                        "Set bonding primary port failed")
> +        else:
> +            err = "Set bonding primary port successfully, should not success"
> +            self.verify(str(slave_port) not in out, err)
> +
> +    def set_bonding_mode(self, bond_port, mode):
> +        """
> +        Set the bonding mode for port_id.
> +        """
> +        cmd = "set bonding mode %d %d" % (mode, bond_port)
> +        self.d_console(cmd)
> +        mode_value = self.get_bonding_info(bond_port, 'mode')
> +        self.verify(str(mode) in mode_value, "Set bonding mode failed")
> +
> +    def set_bonding_mac(self, bond_port, mac):
> +        """
> +        Set the MAC for the bonding device.
> +        """
> +        cmd = "set bonding mac_addr %s %s" % (bond_port, mac)
> +        self.d_console(cmd)
> +        new_mac = self.get_port_mac(bond_port)
> +        self.verify(new_mac == mac, "Set bonding mac failed")
> +
> +    def get_port_mac(self, bond_port, query_type):
> +        bond_port_mac = self.get_port_info(bond_port, query_type)
> +        return bond_port_mac
> +
> +    def set_bonding_balance_policy(self, bond_port, policy):
> +        """
> +        Set the balance transmit policy for the bonding device.
> +        """
> +        cmd = "set bonding balance_xmit_policy %d %s" % (bond_port, policy)
> +        self.d_console(cmd)
> +        new_policy = self.get_bonding_info(bond_port, 'balance_policy')
> +        policy = "BALANCE_XMIT_POLICY_LAYER" + policy.lstrip('l')
> +        self.verify(new_policy == policy, "Set bonding balance policy failed")
> +
> +    @property
> +    def is_perf(self):
> +        return self.parent._enable_perf
> --
> 1.9.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [dts] [PATCH V1]pmd_bonding: common methods used by bonding suites
  2019-02-28  6:03   ` Mo, YufengX
@ 2019-02-28  8:40     ` Chen, Zhaoyan
  0 siblings, 0 replies; 5+ messages in thread
From: Chen, Zhaoyan @ 2019-02-28  8:40 UTC (permalink / raw)
  To: Mo, YufengX, dts; +Cc: Mo, YufengX, Chen, Zhaoyan

Acked-by: Chen, Zhaoyan <zhaoyan.chen@intel.com>



Regards,
Zhaoyan Chen


> -----Original Message-----
> From: dts [mailto:dts-bounces@dpdk.org] On Behalf Of Mo, YufengX
> Sent: Thursday, February 28, 2019 2:04 PM
> To: dts@dpdk.org
> Cc: Mo, YufengX <yufengx.mo@intel.com>
> Subject: Re: [dts] [PATCH V1]pmd_bonding: common methods used by bonding
> suites
> 
> 
> Tested-by: Mo, YufengX <yufengx.mo@intel.com>
> 
> > -----Original Message-----
> > From: Mo, YufengX
> > Sent: Thursday, February 28, 2019 1:50 PM
> > To: dts@dpdk.org
> > Cc: Mo, YufengX <yufengx.mo@intel.com>
> > Subject: [dts][PATCH V1]pmd_bonding: common methods used by bonding
> > suites
> >
> >
> > It is a module that common methods are ran by
> > pmd_bonded_8023ad/pmd_stacked_bonded
> > suites, including:
> >     boot up testpmd
> >     sending packets
> >     query testpmd information of ports
> >     set testpmd port status
> >     commond dpdk bonding commands
> >
> > Signed-off-by: yufengmx <yufengx.mo@intel.com>
> > ---
> >  tests/bonding.py | 831
> > +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 831 insertions(+)
> >  create mode 100644 tests/bonding.py
> >
> > diff --git a/tests/bonding.py b/tests/bonding.py new file mode 100644
> > index 0000000..fb2a69e
> > --- /dev/null
> > +++ b/tests/bonding.py
> > @@ -0,0 +1,831 @@
> > +# 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.
> > +
> > +import os
> > +import time
> > +import re
> > +import struct
> > +import socket
> > +from socket import htonl
> > +
> > +from packet import Packet
> > +from scapy.sendrecv import sendp
> > +from scapy.utils import wrpcap
> > +
> > +import utils
> > +from exception import TimeoutException, VerifyFailure from pmd_output
> > +import PmdOutput from settings import HEADER_SIZE
> > +
> > +# define bonding mode
> > +MODE_ROUND_ROBIN = 0
> > +MODE_ACTIVE_BACKUP = 1
> > +MODE_XOR_BALANCE = 2
> > +MODE_BROADCAST = 3
> > +MODE_LACP = 4
> > +MODE_TLB_BALANCE = 5
> > +MODE_ALB_BALANCE = 6
> > +
> > +# define packet size
> > +FRAME_SIZE_64 = 64
> > +FRAME_SIZE_128 = 128
> > +FRAME_SIZE_256 = 256
> > +FRAME_SIZE_512 = 512
> > +FRAME_SIZE_1024 = 1024
> > +FRAME_SIZE_1518 = 1518
> > +
> > +class PmdBonding(object):
> > +    ''' common methods for testpmd bonding '''
> > +
> > +    def __init__(self, **kwargs):
> > +        # set parent instance
> > +        self.parent = kwargs.get('parent')
> > +        # set target source code directory
> > +        self.target_source = self.parent.dut.base_dir
> > +        # set logger
> > +        self.logger = self.parent.logger
> > +        self.verify = self.parent.verify
> > +        # select packet generator
> > +        self.pktgen_name = 'ixia' if self.is_perf else 'scapy'
> > +        # traffic default config
> > +        self.default_pkt_size = kwargs.get('pkt_size') or FRAME_SIZE_64
> > +        self.default_src_mac = kwargs.get('src_mac')
> > +        self.default_src_ip = kwargs.get('src_ip')
> > +        self.default_src_port = kwargs.get('src_port')
> > +        self.default_dst_ip = kwargs.get('dst_ip')
> > +        self.default_dst_port = kwargs.get('dst_port')
> > +        self.default_pkt_name = kwargs.get('pkt_name')
> > +        # testpmd
> > +        self.testpmd = PmdOutput(self.parent.dut)
> > +        self.testpmd_status = 'close'
> > +    #
> > +    # On tester platform, packet transmission
> > +    #
> > +    def mac_str_to_int(self, mac_str):
> > +        """ convert the MAC type from the string into the int. """
> > +        mac_hex = '0x'
> > +        for mac_part in mac_str.split(':'):
> > +            mac_hex += mac_part
> > +        return int(mac_hex, 16)
> > +
> > +    def mac_int_to_str(self, mac_int):
> > +        """ Translate the MAC type from the string into the int. """
> > +        temp = hex(mac_int)[2:]
> > +        b = []
> > +        [b.append(temp[n:n+2]) for n in range(len(temp)) if n % 2 == 0]
> > +        new_mac = ":".join(b)
> > +        return new_mac
> > +
> > +    def ip_str_to_int(self, ip_str):
> > +        """
> > +        convert the IP type from the string into the int.
> > +        """
> > +        ip_int = socket.ntohl(struct.unpack(
> > +                                "I", socket.inet_aton(str(ip_str)))[0])
> > +        return ip_int
> > +
> > +    def ip_int_to_str(self, ip_int):
> > +        """
> > +        convert the IP type from the int into the string.
> > +        """
> > +        ip_str = socket.inet_ntoa(struct.pack('I', socket.htonl(ip_int)))
> > +        return ip_str
> > +
> > +    def increase_ip(self, ip, step=1):
> > +        ''' ip: string format '''
> > +        _ip_int = self.ip_str_to_int(ip)
> > +        new_ip = self.ip_int_to_str(_ip_int + step)
> > +        return new_ip
> > +
> > +    def increase_mac(self, mac, step=1):
> > +        ''' mac: string format '''
> > +        _mac_int = self.mac_str_to_int(mac)
> > +        new_mac = self.mac_int_to_str(_mac_int+step)
> > +        return new_mac
> > +
> > +    def increase_port(self, port, step=1):
> > +        ''' port: int format '''
> > +        new_port = port + step
> > +        return new_port
> > +
> > +    def increase_mac_ip_port(self, step=1):
> > +        # get source port setting
> > +        mac, ip, port = (self.default_src_mac,
> > +                         self.default_src_ip,
> > +                         self.default_src_port)
> > +        return (self.increase_mac(mac, step),
> > +                self.increase_ip(ip, step),
> > +                self.increase_port(port, step))
> > +
> > +    def get_pkt_len(self, pkt_type):
> > +        ''' get packet payload size '''
> > +        frame_size = self.default_pkt_size
> > +        headers_size = sum(map(lambda x: HEADER_SIZE[x],
> > +                               ['eth', 'ip', pkt_type]))
> > +        pktlen = frame_size - headers_size
> > +        return pktlen
> > +
> > +    def set_stream_to_slave_port(self, dut_port_id):
> > +        '''
> > +        use framework/packet.py module to create one stream, send stream to
> > +        slave port
> > +        '''
> > +        # get dst port mac address
> > +        pkt_name = self.default_pkt_name
> > +        destport = self.default_dst_port
> > +        destip = self.default_dst_ip
> > +        dst_mac = self.get_port_info(dut_port_id, 'mac')
> > +        # packet size
> > +        pktlen = self.get_pkt_len(pkt_name)
> > +        # set stream configuration
> > +        srcmac, srcip, srcport = self.increase_mac_ip_port(0)
> > +        pkt_config = {
> > +        'type': pkt_name.upper(),
> > +        'pkt_layers': {
> > +            # Ether(dst=nutmac, src=srcmac)
> > +            'ether': {'src': srcmac, 'dst': dst_mac},
> > +            # IP(dst=destip, src=srcip, len=%s)
> > +            'ipv4': {'src': srcip, 'dst': destip},
> > +            # pkt_name(sport=srcport, dport=destport)
> > +            pkt_name: {'src': srcport, 'dst': destport},
> > +            # Raw(load='\x50'*%s)
> > +            'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}}
> > +        # create packet
> > +        streams = []
> > +        # keep a copy of pcap for debug
> > +        savePath = os.sep.join([self.target_source,
> > +                                "pkt_{0}.pcap".format(pkt_name)])
> > +        pkt_type = pkt_config.get('type')
> > +        pkt_layers = pkt_config.get('pkt_layers')
> > +        pkt = Packet(pkt_type=pkt_type.upper())
> > +        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 set_stream_to_bond_port(self, bond_port, slaves):
> > +        '''
> > +        : use framework/packet.py module to create multiple streams
> > +          send streams from bond port to slaves
> > +        :param bond_port:
> > +            bonded device port id
> > +        :param slaves:
> > +            slaves port id
> > +        '''
> > +        pkt_configs = []
> > +        # get dst port mac address
> > +        pkt_name = self.default_pkt_name
> > +        destport = self.default_dst_port
> > +        destip = self.default_dst_ip
> > +        dst_mac = self.get_port_info(bond_port, 'mac')
> > +        # packet size
> > +        pktlen = self.get_pkt_len(pkt_type)
> > +        # set stream configuration
> > +        for packet_id in range(len(slaves['active'])):
> > +            srcmac, srcip, srcport = self.increase_mac_ip_port(packet_id)
> > +            pkt_configs.append({
> > +            'type': pkt_name.upper(),
> > +            'pkt_layers': {
> > +                # Ether(dst=nutmac, src=srcmac)
> > +                'ether': {'src': srcmac, 'dst': dst_mac},
> > +                # IP(dst=destip, src=srcip, len=%s)
> > +                'ipv4': {'src': srcip, 'dst': destip},
> > +                # pkt_name(sport=srcport, dport=destport)
> > +                pkt_name: {'src': srcport, 'dst': destport},
> > +                # Raw(load='\x50'*%s)
> > +                'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}})
> > +        # create packet
> > +        streams = []
> > +        for values in pkt_configs:
> > +            # keep a copy of pcap for debug
> > +            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.upper())
> > +            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 send_packets_by_scapy(self, **kwargs):
> > +        tx_iface = kwargs.get('port topo')[0]
> > +        # set interface ready to send packet
> > +        cmd = "ifconfig {0} up".format(tx_iface)
> > +        self.parent.tester.send_expect(cmd, '# ', 30)
> > +        send_pkts = kwargs.get('stream')
> > +        # stream config
> > +        stream_configs = kwargs.get('traffic configs')
> > +        count = stream_configs.get('count')
> > +        interval = stream_configs.get('interval', 0.01)
> > +        # run traffic
> > +        sendp(send_pkts, iface=tx_iface, inter=interval, verbose=False,
> > +              count=count)
> > +
> > +    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')
> > +        traffic_time = kwargs.get('traffic_time', 0)
> > +        rate_percent = kwargs.get('rate_percent', float(100))
> > +        #---------------------------------------------------------------
> > +        send_pkts = []
> > +        self.tgen_input = []
> > +        tgen_input = self.tgen_input
> > +        # generate packet contain multi stream
> > +        for pkt in self.packet_types.values():
> > +            send_pkts.append(pkt.pktgen.pkt)
> > +        ixia_pkt = os.sep.join([self.target_source, 'bonding_ixia.pcap'])
> > +        wrpcap(ixia_pkt, send_pkts)
> > +        #----------------------------------------------------------------
> > +        # set packet for send
> > +        # pause frame basic configuration
> > +        pause_time = 65535
> > +        pause_rate = 0.50
> > +        # run ixia testing
> > +        frame_size = self.default_pkt_size
> > +        # calculate number of packets
> > +        expect_pps = self.parent.wirespeed(self.parent.nic, frame_size, 1) * \
> > +                                                                    1000000.0
> > +        # get line rate
> > +        linerate = expect_pps * (frame_size + 20) * 8
> > +        # calculate default sleep time for one pause frame
> > +        sleep = (1 / linerate) * pause_time * 512
> > +        # calculate packets dropped in sleep time
> > +        self.n_pkts = int((sleep / (1 / expect_pps)) * (1 / pause_rate))
> > +        #----------------------------------------------------------------
> > +        tester_port = self.parent.tester.get_local_port(self.parent.dut_ports[0])
> > +        tgen_input.append((tester_port, tester_port,ixia_pkt))
> > +        # run latency stat statistics
> > +        self.parent.tester.loop_traffic_generator_throughput(tgen_input,
> > +
> > + self.rate_percent)
> > +
> > +    def stop_ixia(self, data_types='packets'):
> > +        tester_inst = self.parent.tester
> > +        # get ixia statistics
> > +        line_rate = tester_inst.get_port_line_rate()
> > +        rx_bps, rx_pps = \
> > +            tester_inst.stop_traffic_generator_throughput_loop(self.tgen_input)
> > +        output = tester_inst.traffic_get_port_stats(self.tgen_input)
> > +        self.cur_data['ixia statistics'] = []
> > +        append = self.cur_data['ixia statistics'].append
> > +        append('send packets: {0}'.format(output[0]))
> > +        append('line_rate: {0}'.format(line_rate[0]))
> > +        append('rate_percent: {0}%'.format(self.rate_percent))
> > +
> > +    def get_pktgen(self, name):
> > +        pkt_gens = {
> > +            'ixia':     self.send_packets_by_ixia,
> > +            'scapy':    self.send_packets_by_scapy,}
> > +        pkt_generator = pkt_gens.get(name)
> > +
> > +        return pkt_generator
> > +
> > +    def send_packet(self, traffic_config):
> > +        """
> > +        stream transmission on specified link topology
> > +        """
> > +        time.sleep(2)
> > +        # start traffic
> > +        self.logger.info("begin transmission ...")
> > +        pktgen = self.get_pktgen(self.pktgen_name)
> > +        result = pktgen(**traffic_config)
> > +        # end traffic
> > +        self.logger.info("complete transmission")
> > +
> > +        return result
> > +    #
> > +    # On dut, dpdk testpmd common methods
> > +    #
> > +    def check_process_status(self, process_name='testpmd'):
> > +        cmd = "ps aux | grep -i %s | grep -v grep | awk {'print $2'}"%(
> > +                                                                process_name)
> > +        out = self.parent.dut.alt_session.send_expect(cmd, "# ", 10)
> > +        status = True if out != "" else False
> > +        return status
> > +
> > +    def check_process_exist(self, process_name='testpmd'):
> > +        status = self.check_process_status(process_name)
> > +        if not status:
> > +            msg = "{0} process exceptional quit".format(process_name)
> > +            out = self.parent.dut.session.session.get_output_all()
> > +            self.logger.info(out)
> > +            raise VerifyFailure(msg)
> > +
> > +    def d_console(self, cmds):
> > +        ''' wrap up testpmd command interactive console '''
> > +        if len(cmds) == 0:
> > +            return
> > +        # check if cmds is string
> > +        if isinstance(cmds, str):
> > +            timeout = 10
> > +            cmds = [[cmds, '', timeout]]
> > +        # check if cmds is only one command
> > +        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 'testpmd> '
> > +            else:
> > +                check_output = False
> > +                expected_str = expected_items or 'testpmd> '
> > +            timeout = int(item[2]) if len(item) == 3 else 5
> > +            #----------------------------------------------------------------
> > +            # run command on session
> > +            try:
> > +                console = self.testpmd.execute_cmd
> > +                msg_pipe = self.testpmd.get_output
> > +                output = console(item[0], expected_str, timeout)
> > +                output = msg_pipe(timeout) if not output else output
> > +            except TimeoutException:
> > +                try:
> > +                    # check if testpmd quit
> > +                    self.check_process_exist()
> > +                except Exception as e:
> > +                    self.testpmd_status = 'close'
> > +                msg = "execute '{0}' timeout".format(item[0])
> > +                output = out = self.parent.dut.session.session.get_output_all()
> > +                self.logger.error(output)
> > +                raise Exception(msg)
> > +
> > +            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:
> > +                    fmt = "unexpected '{0}' is not in output"
> > +                    msg = fmt.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 preset_testpmd(self, core_mask, options='', eal_param=''):
> > +        try:
> > +            self.testpmd.start_testpmd( core_mask,
> > +                                        param=' '.join(options),
> > +                                        eal_param=eal_param)
> > +        except TimeoutException:
> > +            # check if testpmd quit
> > +            try:
> > +                self.check_process_exist()
> > +            except Exception as e:
> > +                self.testpmd_status = 'close'
> > +            msg = "execute '{0}' timeout".format(item[0])
> > +            self.logger.error(msg_pipe(timeout))
> > +            raise TimeoutException(msg)
> > +        # wait lsc event udpate done
> > +        time.sleep(10)
> > +        # check if testpmd has bootep up
> > +        if self.check_process_status():
> > +            self.logger.info("testpmd boot up successful")
> > +        else:
> > +            raise VerifyFailure("testpmd boot up failed")
> > +        self.d_console(self.preset_testpmd_cmds)
> > +        self.preset_testpmd_cmds = []
> > +        time.sleep(1)
> > +
> > +    def start_testpmd(self, eal_option=''):
> > +        if self.testpmd_status == 'running':
> > +            return
> > +        # boot up testpmd
> > +        hw_mask = 'all'
> > +        options = ''
> > +        self.preset_testpmd_cmds = ['port stop all', '', 15]
> > +        self.preset_testpmd(hw_mask, options, eal_param=eal_option)
> > +        self.testpmd_status = 'running'
> > +
> > +    def stop_testpmd(self):
> > +        time.sleep(1)
> > +        testpmd_cmds =[['port stop all', '', 15],
> > +                       ['show port stats all', ''],
> > +                       ['stop', ''],]
> > +        output = self.d_console(testpmd_cmds)
> > +        time.sleep(1)
> > +        return output
> > +
> > +    def close_testpmd(self):
> > +        if self.testpmd_status == 'close':
> > +            return None
> > +        output = self.stop_testpmd()
> > +        time.sleep(1)
> > +        self.testpmd.quit()
> > +        time.sleep(10)
> > +        if self.check_process_status():
> > +            raise VerifyFailure("testpmd close failed")
> > +        else:
> > +            self.logger.info("close testpmd successful")
> > +        self.testpmd_status = 'close'
> > +        return output
> > +
> > +    def start_ports(self, port='all'):
> > +        """
> > +        Start a port which the testpmd can see.
> > +        """
> > +        timeout = 12 if port=='all' else 5
> > +        cmds =[
> > +           ["port start %s" % str(port), " ", timeout],
> > +           # to avoid lsc event message interfere normal status
> > +           [" ", '', timeout]]
> > +        self.d_console(cmds)
> > +
> > +    def get_stats(self, portid, flow=['rx', 'tx']):
> > +        """
> > +        get one port statistics of testpmd
> > +        """
> > +        _portid = int(portid) if isinstance(portid, (str, unicode)) else portid
> > +        info = self.testpmd.get_pmd_stats(_portid)
> > +        _kwd = ["-packets", "-errors", "-bytes"]
> > +        stats = {}
> > +        if isinstance(flow, list):
> > +            for item in flow:
> > +                for item2 in _kwd:
> > +                    name = item.upper() + item2
> > +                    stats[name] = int(info[name])
> > +        elif isinstance(flow, (str, unicode)):
> > +            for item in _kwd:
> > +                name = flow.upper() + item
> > +                stats[name] = int(info[name])
> > +        else:
> > +            msg = 'unknown data type'
> > +            raise Exception(msg)
> > +
> > +        return stats
> > +
> > +    def get_all_stats(self, ports):
> > +        """
> > +        Get a group of ports statistics, which testpmd can display.
> > +        """
> > +        stats = {}
> > +        attrs = ['tx', 'rx']
> > +        for port_id in ports:
> > +            stats[port_id] = self.get_stats(port_id, attrs)
> > +
> > +        return stats
> > +
> > +    def set_tester_port_status(self, port_name, status):
> > +        """
> > +        Do some operations to the network interface port,
> > +        such as "up" or "down".
> > +        """
> > +        eth = self.parent.tester.get_interface(port_name)
> > +        self.parent.tester.admin_ports_linux(eth, status)
> > +        time.sleep(5)
> > +
> > +    def set_dut_peer_port_by_id(self, port_id, status):
> > +        # stop peer port on tester
> > +        intf = self.parent.tester.get_local_port(self.parent.dut_ports[port_id])
> > +        self.set_tester_port_status(intf, status)
> > +        time.sleep(5)
> > +        cur_status = self.get_port_info(port_id, 'link_status')
> > +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> > +        if cur_status != status:
> > +            self.logger.warning("expected status is
> > + [{0}]".format(status))
> > +
> > +    def set_dut_port_status(self, port_id, status):
> > +        opt = 'link-up' if status == 'up' else 'link-down'
> > +        # stop slave link by force
> > +        cmd = "set {0} port {1}".format(opt, port_id)
> > +        self.d_console(cmd)
> > +        time.sleep(5)
> > +        cur_status = self.get_port_info(port_id, 'link_status')
> > +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> > +        if cur_status != status:
> > +            self.logger.warning("expected status is [{0}]".format(status))
> > +    #
> > +    # testpmd bonding commands
> > +    #
> > +    def get_value_from_str(self, key_str, regx_str, string):
> > +        """
> > +        Get some values from the given string by the regular expression.
> > +        """
> > +        if isinstance(key_str, (unicode, str)):
> > +            pattern = r"(?<=%s)%s" % (key_str, regx_str)
> > +            s = re.compile(pattern)
> > +            res = s.search(string)
> > +            if type(res).__name__ == 'NoneType':
> > +                msg = "{0} hasn't match anything".format(key_str)
> > +                self.logger.warning(msg)
> > +                return ' '
> > +            else:
> > +                return res.group(0)
> > +        elif isinstance(key_str, (list, tuple)):
> > +            for key in key_str:
> > +                pattern = r"(?<=%s)%s" % (key, regx_str)
> > +                s = re.compile(pattern)
> > +                res = s.search(string)
> > +                if type(res).__name__ == 'NoneType':
> > +                    continue
> > +                else:
> > +                    return res.group(0)
> > +            else:
> > +                self.logger.warning("all key_str hasn't match anything")
> > +                return ' '
> > +
> > +    def _get_detail_from_port_info(self, port_id, args):
> > +        """
> > +        Get the detail info from the output of pmd cmd
> > +            'show port info <port num>'.
> > +        """
> > +        key_str, regx_str = args
> > +        out = self.d_console("show port info %d" % port_id)
> > +        find_value = self.get_value_from_str(key_str, regx_str, out)
> > +        return find_value
> > +
> > +    def get_detail_from_port_info(self, port_id, args):
> > +        if isinstance(args[0], (list, tuple)):
> > +            return [self._get_detail_from_port_info(port_id, sub_args)
> > +                        for sub_args in args]
> > +        else:
> > +            return self._get_detail_from_port_info(port_id, args)
> > +
> > +    def get_port_info(self, port_id, info_type):
> > +        '''
> > +        Get the specified port information by its output message format
> > +        '''
> > +        info_set = {
> > +        'mac':              ["MAC address: ", "([0-9A-F]{2}:){5}[0-9A-F]{2}"],
> > +        'connect_socket':   ["Connect to socket: ", "\d+"],
> > +        'memory_socket':    ["memory allocation on the socket: ", "\d+"],
> > +        'link_status':      ["Link status: ", "\S+"],
> > +        'link_speed':       ["Link speed: ", "\d+"],
> > +        'link_duplex':      ["Link duplex: ", "\S+"],
> > +        'promiscuous_mode': ["Promiscuous mode: ", "\S+"],
> > +        'allmulticast_mode':["Allmulticast mode: ", "\S+"],
> > +        'vlan_offload':     [
> > +                        ["strip ", "\S+"],
> > +                        ['filter', "\S+"],
> > +                        ['qinq\(extend\) ', "\S+"]],
> > +        'queue_config':     [
> > +                        ["Max possible RX queues: ", "\d+"],
> > +                        ['Max possible number of RXDs per queue: ', "\d+"],
> > +                        ['Min possible number of RXDs per queue: ', "\d+"],
> > +                        ["Max possible TX queues: ", "\d+"],
> > +                        ['Max possible number of TXDs per queue: ', "\d+"],
> > +                        ['Min possible number of TXDs per queue: ', "\d+"],]
> > +        }
> > +
> > +        if info_type in info_set.keys():
> > +            return self.get_detail_from_port_info(port_id, info_set[info_type])
> > +        else:
> > +            msg = os.linesep.join([
> > +                "support query items including::",
> > +                os.linesep.join(info_set.keys())])
> > +            self.logger.warning(msg)
> > +            return None
> > +    #
> > +    # On dut, dpdk testpmd common bonding methods
> > +    #
> > +    def get_bonding_config(self, config_content, args):
> > +        """
> > +        Get bonding info by command "show bonding config".
> > +        """
> > +        key_str, regx_str = args
> > +        find_value = self.get_value_from_str(key_str, regx_str, config_content)
> > +        return find_value
> > +
> > +    def get_info_from_bond_config(self, config_content, args):
> > +        """
> > +        Get active slaves of the bonding device which you choose.
> > +        """
> > +        search_args = args if isinstance(args[0], (list, tuple)) else [args]
> > +        for search_args in search_args:
> > +            try:
> > +                info = self.get_bonding_config(config_content, search_args)
> > +                break
> > +            except Exception as e:
> > +                self.logger.info(e)
> > +        else:
> > +            info = None
> > +
> > +        return info
> > +
> > +    def get_bonding_info(self, bond_port, info_types):
> > +        ''' Get the specified port information by its output message format '''
> > +        info_set = {
> > +            'mode':          ["Bonding mode: ", "\d*"],
> > +            'agg_mode':      ["IEEE802.3AD Aggregator Mode: ", "\S*"],
> > +            'balance_policy':["Balance Xmit Policy: ", "\S+"],
> > +            'slaves':        [["Slaves \(\d\): \[", "\d*( \d*)*"],
> > +                              ["Slaves: \[", "\d*( \d*)*"]],
> > +            'active_slaves': [["Active Slaves \(\d\): \[", "\d*( \d*)*"],
> > +                              ["Acitve Slaves: \[", "\d*( \d*)*"]],
> > +            'primary':       ["Primary: \[", "\d*"]}
> > +        # get all config information
> > +        config_content = self.d_console("show bonding config %d" % bond_port)
> > +        if isinstance(info_types, (list or tuple)):
> > +            query_values = []
> > +            for info_type in info_types:
> > +                if info_type in info_set.keys():
> > +                    find_value = self.get_info_from_bond_config(
> > +                                            config_content, info_set[info_type])
> > +                    if info_type in ['active_slaves', 'slaves']:
> > +                        find_value = [value for value in find_value.split(' ')
> > +                                        if value]
> > +                else:
> > +                    find_value = None
> > +                query_values.append(find_value)
> > +            return query_values
> > +        else:
> > +            info_type = info_types
> > +            if info_type in info_set.keys():
> > +                find_value = self.get_info_from_bond_config(
> > +                                            config_content, info_set[info_type])
> > +                if info_type in ['active_slaves', 'slaves']:
> > +                    find_value = [value for value in find_value.split(' ')
> > +                                        if value]
> > +                return find_value
> > +            else:
> > +                return None
> > +
> > +    def get_active_slaves(self, bond_port):
> > +        primary_port = int(self.get_bonding_info(bond_port, 'primary'))
> > +        active_slaves = self.get_bonding_info(bond_port,
> > + 'active_slaves')
> > +
> > +        return int(primary_port), [int(slave) for slave in
> > + active_slaves]
> > +
> > +    def create_bonded_device(self, mode=0, socket=0, verify_detail=False):
> > +        """
> > +        Create a bonding device with the parameters you specified.
> > +        """
> > +        cmd = "create bonded device %d %d" % (mode, socket)
> > +        out = self.d_console(cmd)
> > +        err_fmt = "Create bonded device on mode [%d] socket [%d] failed"
> > +        self.verify("Created new bonded device" in out, err_fmt% (mode, socket))
> > +        fmts = [
> > +             "Created new bonded device net_bond_testpmd_[\d] on \(port ",
> > +             "Created new bonded device net_bonding_testpmd_[\d] on \(port ",
> > +             "Created new bonded device eth_bond_testpmd_[\d] on \(port "]
> > +        bond_port = self.get_value_from_str(fmts, "\d+", out)
> > +        bond_port = int(bond_port)
> > +
> > +        if verify_detail:
> > +            out = self.d_console("show bonding config %d" % bond_port)
> > +            self.verify("Bonding mode: %d" % mode in out,
> > +                        "Bonding mode display error when create bonded device")
> > +            self.verify("Slaves: []" in out,
> > +                        "Slaves display error when create bonded device")
> > +            self.verify("Active Slaves: []" in out,
> > +                        "Active Slaves display error when create bonded device")
> > +            self.verify("Primary: []" not in out,
> > +                        "Primary display error when create bonded device")
> > +            out = self.d_console("show port info %d" % bond_port)
> > +            self.verify("Connect to socket: %d" % socket in out,
> > +                        "Bonding port connect socket error")
> > +            self.verify("Link status: down" in out,
> > +                        "Bonding port default link status error")
> > +            self.verify("Link speed: 0 Mbps" in out,
> > +                        "Bonding port default link speed error")
> > +
> > +        return bond_port
> > +
> > +    def add_slave(self, bond_port, invert_verify=False, expected_str='',
> > +                  *slave_ports):
> > +        """
> > +        Add ports into the bonding device as slaves.
> > +        """
> > +        if len(slave_ports) <= 0:
> > +            utils.RED("No port exist when add slave to bonded device")
> > +        for slave_id in slave_ports:
> > +            cmd = "add bonding slave %d %d" % (slave_id, bond_port)
> > +            out = self.d_console(cmd)
> > +            if expected_str:
> > +                self.verify(expected_str in out,
> > +                            "message <{0}> is missing".format(expected_str))
> > +            slaves = self.get_bonding_info(bond_port, 'slaves')
> > +            if not invert_verify:
> > +                self.verify(str(slave_id) in slaves,
> > +                            "Add port as bonding slave failed")
> > +            else:
> > +                err = "Add port as bonding slave successfully,should fail"
> > +                self.verify(str(slave_id) not in slaves, err)
> > +
> > +    def remove_slaves(self, bond_port, invert_verify=False, *slave_port):
> > +        """
> > +        Remove the specified slave port from the bonding device.
> > +        """
> > +        if len(slave_port) <= 0:
> > +            msg = "No port exist when remove slave from bonded device"
> > +            self.logger.error(msg)
> > +        for slave_id in slave_port:
> > +            cmd = "remove bonding slave %d %d" % (int(slave_id), bond_port)
> > +            self.d_console(cmd)
> > +            slaves = self.get_bonding_info(bond_port, 'slaves')
> > +            if not invert_verify:
> > +                self.verify(str(slave_id) not in slaves,
> > +                            "Remove slave to fail from bonding device")
> > +            else:
> > +                err = (
> > +                    "Remove slave successfully from bonding device, "
> > +                    "should be failed")
> > +                self.verify(str(slave_id) in slaves, err)
> > +
> > +    def remove_all_slaves(self, bond_port):
> > +        """
> > +        Remove all slaves of specified bound device.
> > +        """
> > +        all_slaves = self.get_bonding_info(bond_port, 'slaves')
> > +        if not all_slaves:
> > +            return
> > +        all_slaves = all_slaves.split()
> > +        if len(all_slaves) == 0:
> > +            return
> > +        self.remove_slaves(bond_port, False, *all_slaves)
> > +
> > +    def set_primary_slave(self, bond_port, slave_port, invert_verify=False):
> > +        """
> > +        Set the primary slave for the bonding device.
> > +        """
> > +        cmd = "set bonding primary %d %d" % (slave_port, bond_port)
> > +        self.d_console(cmd)
> > +        out = self.get_bonding_info(bond_port, 'primary')
> > +        if not invert_verify:
> > +            self.verify(str(slave_port) in out,
> > +                        "Set bonding primary port failed")
> > +        else:
> > +            err = "Set bonding primary port successfully, should not success"
> > +            self.verify(str(slave_port) not in out, err)
> > +
> > +    def set_bonding_mode(self, bond_port, mode):
> > +        """
> > +        Set the bonding mode for port_id.
> > +        """
> > +        cmd = "set bonding mode %d %d" % (mode, bond_port)
> > +        self.d_console(cmd)
> > +        mode_value = self.get_bonding_info(bond_port, 'mode')
> > +        self.verify(str(mode) in mode_value, "Set bonding mode
> > + failed")
> > +
> > +    def set_bonding_mac(self, bond_port, mac):
> > +        """
> > +        Set the MAC for the bonding device.
> > +        """
> > +        cmd = "set bonding mac_addr %s %s" % (bond_port, mac)
> > +        self.d_console(cmd)
> > +        new_mac = self.get_port_mac(bond_port)
> > +        self.verify(new_mac == mac, "Set bonding mac failed")
> > +
> > +    def get_port_mac(self, bond_port, query_type):
> > +        bond_port_mac = self.get_port_info(bond_port, query_type)
> > +        return bond_port_mac
> > +
> > +    def set_bonding_balance_policy(self, bond_port, policy):
> > +        """
> > +        Set the balance transmit policy for the bonding device.
> > +        """
> > +        cmd = "set bonding balance_xmit_policy %d %s" % (bond_port, policy)
> > +        self.d_console(cmd)
> > +        new_policy = self.get_bonding_info(bond_port, 'balance_policy')
> > +        policy = "BALANCE_XMIT_POLICY_LAYER" + policy.lstrip('l')
> > +        self.verify(new_policy == policy, "Set bonding balance policy
> > + failed")
> > +
> > +    @property
> > +    def is_perf(self):
> > +        return self.parent._enable_perf
> > --
> > 1.9.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [dts] [PATCH V1]pmd_bonding: common methods used by bonding suites
  2019-02-28  5:50 ` [dts] [PATCH V1]pmd_bonding: " yufengmx
  2019-02-28  6:03   ` Mo, YufengX
@ 2019-02-28 11:00   ` Tu, Lijuan
  1 sibling, 0 replies; 5+ messages in thread
From: Tu, Lijuan @ 2019-02-28 11:00 UTC (permalink / raw)
  To: Mo, YufengX, dts; +Cc: Mo, YufengX

.git/rebase-apply/patch:213: trailing whitespace.
        :param slaves:
.git/rebase-apply/patch:273: trailing whitespace.
        rate_percent = kwargs.get('rate_percent', float(100))
.git/rebase-apply/patch:288: trailing whitespace.
        # run ixia testing
.git/rebase-apply/patch:324: trailing whitespace.

.git/rebase-apply/patch:326: trailing whitespace.

warning: squelched 6 whitespace errors
warning: 11 lines add whitespace errors.

> -----Original Message-----
> From: dts [mailto:dts-bounces@dpdk.org] On Behalf Of yufengmx
> Sent: Thursday, February 28, 2019 1:50 PM
> To: dts@dpdk.org
> Cc: Mo, YufengX <yufengx.mo@intel.com>
> Subject: [dts] [PATCH V1]pmd_bonding: common methods used by bonding
> suites
> 
> 
> It is a module that common methods are ran by
> pmd_bonded_8023ad/pmd_stacked_bonded
> suites, including:
>     boot up testpmd
>     sending packets
>     query testpmd information of ports
>     set testpmd port status
>     commond dpdk bonding commands
> 
> Signed-off-by: yufengmx <yufengx.mo@intel.com>
> ---
>  tests/bonding.py | 831
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 831 insertions(+)
>  create mode 100644 tests/bonding.py
> 
> diff --git a/tests/bonding.py b/tests/bonding.py new file mode 100644 index
> 0000000..fb2a69e
> --- /dev/null
> +++ b/tests/bonding.py
> @@ -0,0 +1,831 @@
> +# 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.
> +
> +import os
> +import time
> +import re
> +import struct
> +import socket
> +from socket import htonl
> +
> +from packet import Packet
> +from scapy.sendrecv import sendp
> +from scapy.utils import wrpcap
> +
> +import utils
> +from exception import TimeoutException, VerifyFailure from pmd_output
> +import PmdOutput from settings import HEADER_SIZE
> +
> +# define bonding mode
> +MODE_ROUND_ROBIN = 0
> +MODE_ACTIVE_BACKUP = 1
> +MODE_XOR_BALANCE = 2
> +MODE_BROADCAST = 3
> +MODE_LACP = 4
> +MODE_TLB_BALANCE = 5
> +MODE_ALB_BALANCE = 6
> +
> +# define packet size
> +FRAME_SIZE_64 = 64
> +FRAME_SIZE_128 = 128
> +FRAME_SIZE_256 = 256
> +FRAME_SIZE_512 = 512
> +FRAME_SIZE_1024 = 1024
> +FRAME_SIZE_1518 = 1518
> +
> +class PmdBonding(object):
> +    ''' common methods for testpmd bonding '''
> +
> +    def __init__(self, **kwargs):
> +        # set parent instance
> +        self.parent = kwargs.get('parent')
> +        # set target source code directory
> +        self.target_source = self.parent.dut.base_dir
> +        # set logger
> +        self.logger = self.parent.logger
> +        self.verify = self.parent.verify
> +        # select packet generator
> +        self.pktgen_name = 'ixia' if self.is_perf else 'scapy'
> +        # traffic default config
> +        self.default_pkt_size = kwargs.get('pkt_size') or FRAME_SIZE_64
> +        self.default_src_mac = kwargs.get('src_mac')
> +        self.default_src_ip = kwargs.get('src_ip')
> +        self.default_src_port = kwargs.get('src_port')
> +        self.default_dst_ip = kwargs.get('dst_ip')
> +        self.default_dst_port = kwargs.get('dst_port')
> +        self.default_pkt_name = kwargs.get('pkt_name')
> +        # testpmd
> +        self.testpmd = PmdOutput(self.parent.dut)
> +        self.testpmd_status = 'close'
> +    #
> +    # On tester platform, packet transmission
> +    #
> +    def mac_str_to_int(self, mac_str):
> +        """ convert the MAC type from the string into the int. """
> +        mac_hex = '0x'
> +        for mac_part in mac_str.split(':'):
> +            mac_hex += mac_part
> +        return int(mac_hex, 16)
> +
> +    def mac_int_to_str(self, mac_int):
> +        """ Translate the MAC type from the string into the int. """
> +        temp = hex(mac_int)[2:]
> +        b = []
> +        [b.append(temp[n:n+2]) for n in range(len(temp)) if n % 2 == 0]
> +        new_mac = ":".join(b)
> +        return new_mac
> +
> +    def ip_str_to_int(self, ip_str):
> +        """
> +        convert the IP type from the string into the int.
> +        """
> +        ip_int = socket.ntohl(struct.unpack(
> +                                "I", socket.inet_aton(str(ip_str)))[0])
> +        return ip_int
> +
> +    def ip_int_to_str(self, ip_int):
> +        """
> +        convert the IP type from the int into the string.
> +        """
> +        ip_str = socket.inet_ntoa(struct.pack('I', socket.htonl(ip_int)))
> +        return ip_str
> +
> +    def increase_ip(self, ip, step=1):
> +        ''' ip: string format '''
> +        _ip_int = self.ip_str_to_int(ip)
> +        new_ip = self.ip_int_to_str(_ip_int + step)
> +        return new_ip
> +
> +    def increase_mac(self, mac, step=1):
> +        ''' mac: string format '''
> +        _mac_int = self.mac_str_to_int(mac)
> +        new_mac = self.mac_int_to_str(_mac_int+step)
> +        return new_mac
> +
> +    def increase_port(self, port, step=1):
> +        ''' port: int format '''
> +        new_port = port + step
> +        return new_port
> +
> +    def increase_mac_ip_port(self, step=1):
> +        # get source port setting
> +        mac, ip, port = (self.default_src_mac,
> +                         self.default_src_ip,
> +                         self.default_src_port)
> +        return (self.increase_mac(mac, step),
> +                self.increase_ip(ip, step),
> +                self.increase_port(port, step))
> +
> +    def get_pkt_len(self, pkt_type):
> +        ''' get packet payload size '''
> +        frame_size = self.default_pkt_size
> +        headers_size = sum(map(lambda x: HEADER_SIZE[x],
> +                               ['eth', 'ip', pkt_type]))
> +        pktlen = frame_size - headers_size
> +        return pktlen
> +
> +    def set_stream_to_slave_port(self, dut_port_id):
> +        '''
> +        use framework/packet.py module to create one stream, send stream to
> +        slave port
> +        '''
> +        # get dst port mac address
> +        pkt_name = self.default_pkt_name
> +        destport = self.default_dst_port
> +        destip = self.default_dst_ip
> +        dst_mac = self.get_port_info(dut_port_id, 'mac')
> +        # packet size
> +        pktlen = self.get_pkt_len(pkt_name)
> +        # set stream configuration
> +        srcmac, srcip, srcport = self.increase_mac_ip_port(0)
> +        pkt_config = {
> +        'type': pkt_name.upper(),
> +        'pkt_layers': {
> +            # Ether(dst=nutmac, src=srcmac)
> +            'ether': {'src': srcmac, 'dst': dst_mac},
> +            # IP(dst=destip, src=srcip, len=%s)
> +            'ipv4': {'src': srcip, 'dst': destip},
> +            # pkt_name(sport=srcport, dport=destport)
> +            pkt_name: {'src': srcport, 'dst': destport},
> +            # Raw(load='\x50'*%s)
> +            'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}}
> +        # create packet
> +        streams = []
> +        # keep a copy of pcap for debug
> +        savePath = os.sep.join([self.target_source,
> +                                "pkt_{0}.pcap".format(pkt_name)])
> +        pkt_type = pkt_config.get('type')
> +        pkt_layers = pkt_config.get('pkt_layers')
> +        pkt = Packet(pkt_type=pkt_type.upper())
> +        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 set_stream_to_bond_port(self, bond_port, slaves):
> +        '''
> +        : use framework/packet.py module to create multiple streams
> +          send streams from bond port to slaves
> +        :param bond_port:
> +            bonded device port id
> +        :param slaves:
> +            slaves port id
> +        '''
> +        pkt_configs = []
> +        # get dst port mac address
> +        pkt_name = self.default_pkt_name
> +        destport = self.default_dst_port
> +        destip = self.default_dst_ip
> +        dst_mac = self.get_port_info(bond_port, 'mac')
> +        # packet size
> +        pktlen = self.get_pkt_len(pkt_type)
> +        # set stream configuration
> +        for packet_id in range(len(slaves['active'])):
> +            srcmac, srcip, srcport = self.increase_mac_ip_port(packet_id)
> +            pkt_configs.append({
> +            'type': pkt_name.upper(),
> +            'pkt_layers': {
> +                # Ether(dst=nutmac, src=srcmac)
> +                'ether': {'src': srcmac, 'dst': dst_mac},
> +                # IP(dst=destip, src=srcip, len=%s)
> +                'ipv4': {'src': srcip, 'dst': destip},
> +                # pkt_name(sport=srcport, dport=destport)
> +                pkt_name: {'src': srcport, 'dst': destport},
> +                # Raw(load='\x50'*%s)
> +                'raw': {'payload': ['58'] * self.get_pkt_len(pkt_name)}}})
> +        # create packet
> +        streams = []
> +        for values in pkt_configs:
> +            # keep a copy of pcap for debug
> +            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.upper())
> +            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 send_packets_by_scapy(self, **kwargs):
> +        tx_iface = kwargs.get('port topo')[0]
> +        # set interface ready to send packet
> +        cmd = "ifconfig {0} up".format(tx_iface)
> +        self.parent.tester.send_expect(cmd, '# ', 30)
> +        send_pkts = kwargs.get('stream')
> +        # stream config
> +        stream_configs = kwargs.get('traffic configs')
> +        count = stream_configs.get('count')
> +        interval = stream_configs.get('interval', 0.01)
> +        # run traffic
> +        sendp(send_pkts, iface=tx_iface, inter=interval, verbose=False,
> +              count=count)
> +
> +    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')
> +        traffic_time = kwargs.get('traffic_time', 0)
> +        rate_percent = kwargs.get('rate_percent', float(100))
> +        #---------------------------------------------------------------
> +        send_pkts = []
> +        self.tgen_input = []
> +        tgen_input = self.tgen_input
> +        # generate packet contain multi stream
> +        for pkt in self.packet_types.values():
> +            send_pkts.append(pkt.pktgen.pkt)
> +        ixia_pkt = os.sep.join([self.target_source, 'bonding_ixia.pcap'])
> +        wrpcap(ixia_pkt, send_pkts)
> +        #----------------------------------------------------------------
> +        # set packet for send
> +        # pause frame basic configuration
> +        pause_time = 65535
> +        pause_rate = 0.50
> +        # run ixia testing
> +        frame_size = self.default_pkt_size
> +        # calculate number of packets
> +        expect_pps = self.parent.wirespeed(self.parent.nic, frame_size, 1) * \
> +                                                                    1000000.0
> +        # get line rate
> +        linerate = expect_pps * (frame_size + 20) * 8
> +        # calculate default sleep time for one pause frame
> +        sleep = (1 / linerate) * pause_time * 512
> +        # calculate packets dropped in sleep time
> +        self.n_pkts = int((sleep / (1 / expect_pps)) * (1 / pause_rate))
> +        #----------------------------------------------------------------
> +        tester_port = self.parent.tester.get_local_port(self.parent.dut_ports[0])
> +        tgen_input.append((tester_port, tester_port,ixia_pkt))
> +        # run latency stat statistics
> +        self.parent.tester.loop_traffic_generator_throughput(tgen_input,
> +
> + self.rate_percent)
> +
> +    def stop_ixia(self, data_types='packets'):
> +        tester_inst = self.parent.tester
> +        # get ixia statistics
> +        line_rate = tester_inst.get_port_line_rate()
> +        rx_bps, rx_pps = \
> +            tester_inst.stop_traffic_generator_throughput_loop(self.tgen_input)
> +        output = tester_inst.traffic_get_port_stats(self.tgen_input)
> +        self.cur_data['ixia statistics'] = []
> +        append = self.cur_data['ixia statistics'].append
> +        append('send packets: {0}'.format(output[0]))
> +        append('line_rate: {0}'.format(line_rate[0]))
> +        append('rate_percent: {0}%'.format(self.rate_percent))
> +
> +    def get_pktgen(self, name):
> +        pkt_gens = {
> +            'ixia':     self.send_packets_by_ixia,
> +            'scapy':    self.send_packets_by_scapy,}
> +        pkt_generator = pkt_gens.get(name)
> +
> +        return pkt_generator
> +
> +    def send_packet(self, traffic_config):
> +        """
> +        stream transmission on specified link topology
> +        """
> +        time.sleep(2)
> +        # start traffic
> +        self.logger.info("begin transmission ...")
> +        pktgen = self.get_pktgen(self.pktgen_name)
> +        result = pktgen(**traffic_config)
> +        # end traffic
> +        self.logger.info("complete transmission")
> +
> +        return result
> +    #
> +    # On dut, dpdk testpmd common methods
> +    #
> +    def check_process_status(self, process_name='testpmd'):
> +        cmd = "ps aux | grep -i %s | grep -v grep | awk {'print $2'}"%(
> +                                                                process_name)
> +        out = self.parent.dut.alt_session.send_expect(cmd, "# ", 10)
> +        status = True if out != "" else False
> +        return status
> +
> +    def check_process_exist(self, process_name='testpmd'):
> +        status = self.check_process_status(process_name)
> +        if not status:
> +            msg = "{0} process exceptional quit".format(process_name)
> +            out = self.parent.dut.session.session.get_output_all()
> +            self.logger.info(out)
> +            raise VerifyFailure(msg)
> +
> +    def d_console(self, cmds):
> +        ''' wrap up testpmd command interactive console '''
> +        if len(cmds) == 0:
> +            return
> +        # check if cmds is string
> +        if isinstance(cmds, str):
> +            timeout = 10
> +            cmds = [[cmds, '', timeout]]
> +        # check if cmds is only one command
> +        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 'testpmd> '
> +            else:
> +                check_output = False
> +                expected_str = expected_items or 'testpmd> '
> +            timeout = int(item[2]) if len(item) == 3 else 5
> +            #----------------------------------------------------------------
> +            # run command on session
> +            try:
> +                console = self.testpmd.execute_cmd
> +                msg_pipe = self.testpmd.get_output
> +                output = console(item[0], expected_str, timeout)
> +                output = msg_pipe(timeout) if not output else output
> +            except TimeoutException:
> +                try:
> +                    # check if testpmd quit
> +                    self.check_process_exist()
> +                except Exception as e:
> +                    self.testpmd_status = 'close'
> +                msg = "execute '{0}' timeout".format(item[0])
> +                output = out = self.parent.dut.session.session.get_output_all()
> +                self.logger.error(output)
> +                raise Exception(msg)
> +
> +            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:
> +                    fmt = "unexpected '{0}' is not in output"
> +                    msg = fmt.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 preset_testpmd(self, core_mask, options='', eal_param=''):
> +        try:
> +            self.testpmd.start_testpmd( core_mask,
> +                                        param=' '.join(options),
> +                                        eal_param=eal_param)
> +        except TimeoutException:
> +            # check if testpmd quit
> +            try:
> +                self.check_process_exist()
> +            except Exception as e:
> +                self.testpmd_status = 'close'
> +            msg = "execute '{0}' timeout".format(item[0])
> +            self.logger.error(msg_pipe(timeout))
> +            raise TimeoutException(msg)
> +        # wait lsc event udpate done
> +        time.sleep(10)
> +        # check if testpmd has bootep up
> +        if self.check_process_status():
> +            self.logger.info("testpmd boot up successful")
> +        else:
> +            raise VerifyFailure("testpmd boot up failed")
> +        self.d_console(self.preset_testpmd_cmds)
> +        self.preset_testpmd_cmds = []
> +        time.sleep(1)
> +
> +    def start_testpmd(self, eal_option=''):
> +        if self.testpmd_status == 'running':
> +            return
> +        # boot up testpmd
> +        hw_mask = 'all'
> +        options = ''
> +        self.preset_testpmd_cmds = ['port stop all', '', 15]
> +        self.preset_testpmd(hw_mask, options, eal_param=eal_option)
> +        self.testpmd_status = 'running'
> +
> +    def stop_testpmd(self):
> +        time.sleep(1)
> +        testpmd_cmds =[['port stop all', '', 15],
> +                       ['show port stats all', ''],
> +                       ['stop', ''],]
> +        output = self.d_console(testpmd_cmds)
> +        time.sleep(1)
> +        return output
> +
> +    def close_testpmd(self):
> +        if self.testpmd_status == 'close':
> +            return None
> +        output = self.stop_testpmd()
> +        time.sleep(1)
> +        self.testpmd.quit()
> +        time.sleep(10)
> +        if self.check_process_status():
> +            raise VerifyFailure("testpmd close failed")
> +        else:
> +            self.logger.info("close testpmd successful")
> +        self.testpmd_status = 'close'
> +        return output
> +
> +    def start_ports(self, port='all'):
> +        """
> +        Start a port which the testpmd can see.
> +        """
> +        timeout = 12 if port=='all' else 5
> +        cmds =[
> +           ["port start %s" % str(port), " ", timeout],
> +           # to avoid lsc event message interfere normal status
> +           [" ", '', timeout]]
> +        self.d_console(cmds)
> +
> +    def get_stats(self, portid, flow=['rx', 'tx']):
> +        """
> +        get one port statistics of testpmd
> +        """
> +        _portid = int(portid) if isinstance(portid, (str, unicode)) else portid
> +        info = self.testpmd.get_pmd_stats(_portid)
> +        _kwd = ["-packets", "-errors", "-bytes"]
> +        stats = {}
> +        if isinstance(flow, list):
> +            for item in flow:
> +                for item2 in _kwd:
> +                    name = item.upper() + item2
> +                    stats[name] = int(info[name])
> +        elif isinstance(flow, (str, unicode)):
> +            for item in _kwd:
> +                name = flow.upper() + item
> +                stats[name] = int(info[name])
> +        else:
> +            msg = 'unknown data type'
> +            raise Exception(msg)
> +
> +        return stats
> +
> +    def get_all_stats(self, ports):
> +        """
> +        Get a group of ports statistics, which testpmd can display.
> +        """
> +        stats = {}
> +        attrs = ['tx', 'rx']
> +        for port_id in ports:
> +            stats[port_id] = self.get_stats(port_id, attrs)
> +
> +        return stats
> +
> +    def set_tester_port_status(self, port_name, status):
> +        """
> +        Do some operations to the network interface port,
> +        such as "up" or "down".
> +        """
> +        eth = self.parent.tester.get_interface(port_name)
> +        self.parent.tester.admin_ports_linux(eth, status)
> +        time.sleep(5)
> +
> +    def set_dut_peer_port_by_id(self, port_id, status):
> +        # stop peer port on tester
> +        intf = self.parent.tester.get_local_port(self.parent.dut_ports[port_id])
> +        self.set_tester_port_status(intf, status)
> +        time.sleep(5)
> +        cur_status = self.get_port_info(port_id, 'link_status')
> +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> +        if cur_status != status:
> +            self.logger.warning("expected status is
> + [{0}]".format(status))
> +
> +    def set_dut_port_status(self, port_id, status):
> +        opt = 'link-up' if status == 'up' else 'link-down'
> +        # stop slave link by force
> +        cmd = "set {0} port {1}".format(opt, port_id)
> +        self.d_console(cmd)
> +        time.sleep(5)
> +        cur_status = self.get_port_info(port_id, 'link_status')
> +        self.logger.info("port {0} is [{1}]".format(port_id, cur_status))
> +        if cur_status != status:
> +            self.logger.warning("expected status is [{0}]".format(status))
> +    #
> +    # testpmd bonding commands
> +    #
> +    def get_value_from_str(self, key_str, regx_str, string):
> +        """
> +        Get some values from the given string by the regular expression.
> +        """
> +        if isinstance(key_str, (unicode, str)):
> +            pattern = r"(?<=%s)%s" % (key_str, regx_str)
> +            s = re.compile(pattern)
> +            res = s.search(string)
> +            if type(res).__name__ == 'NoneType':
> +                msg = "{0} hasn't match anything".format(key_str)
> +                self.logger.warning(msg)
> +                return ' '
> +            else:
> +                return res.group(0)
> +        elif isinstance(key_str, (list, tuple)):
> +            for key in key_str:
> +                pattern = r"(?<=%s)%s" % (key, regx_str)
> +                s = re.compile(pattern)
> +                res = s.search(string)
> +                if type(res).__name__ == 'NoneType':
> +                    continue
> +                else:
> +                    return res.group(0)
> +            else:
> +                self.logger.warning("all key_str hasn't match anything")
> +                return ' '
> +
> +    def _get_detail_from_port_info(self, port_id, args):
> +        """
> +        Get the detail info from the output of pmd cmd
> +            'show port info <port num>'.
> +        """
> +        key_str, regx_str = args
> +        out = self.d_console("show port info %d" % port_id)
> +        find_value = self.get_value_from_str(key_str, regx_str, out)
> +        return find_value
> +
> +    def get_detail_from_port_info(self, port_id, args):
> +        if isinstance(args[0], (list, tuple)):
> +            return [self._get_detail_from_port_info(port_id, sub_args)
> +                        for sub_args in args]
> +        else:
> +            return self._get_detail_from_port_info(port_id, args)
> +
> +    def get_port_info(self, port_id, info_type):
> +        '''
> +        Get the specified port information by its output message format
> +        '''
> +        info_set = {
> +        'mac':              ["MAC address: ", "([0-9A-F]{2}:){5}[0-9A-F]{2}"],
> +        'connect_socket':   ["Connect to socket: ", "\d+"],
> +        'memory_socket':    ["memory allocation on the socket: ", "\d+"],
> +        'link_status':      ["Link status: ", "\S+"],
> +        'link_speed':       ["Link speed: ", "\d+"],
> +        'link_duplex':      ["Link duplex: ", "\S+"],
> +        'promiscuous_mode': ["Promiscuous mode: ", "\S+"],
> +        'allmulticast_mode':["Allmulticast mode: ", "\S+"],
> +        'vlan_offload':     [
> +                        ["strip ", "\S+"],
> +                        ['filter', "\S+"],
> +                        ['qinq\(extend\) ', "\S+"]],
> +        'queue_config':     [
> +                        ["Max possible RX queues: ", "\d+"],
> +                        ['Max possible number of RXDs per queue: ', "\d+"],
> +                        ['Min possible number of RXDs per queue: ', "\d+"],
> +                        ["Max possible TX queues: ", "\d+"],
> +                        ['Max possible number of TXDs per queue: ', "\d+"],
> +                        ['Min possible number of TXDs per queue: ', "\d+"],]
> +        }
> +
> +        if info_type in info_set.keys():
> +            return self.get_detail_from_port_info(port_id, info_set[info_type])
> +        else:
> +            msg = os.linesep.join([
> +                "support query items including::",
> +                os.linesep.join(info_set.keys())])
> +            self.logger.warning(msg)
> +            return None
> +    #
> +    # On dut, dpdk testpmd common bonding methods
> +    #
> +    def get_bonding_config(self, config_content, args):
> +        """
> +        Get bonding info by command "show bonding config".
> +        """
> +        key_str, regx_str = args
> +        find_value = self.get_value_from_str(key_str, regx_str, config_content)
> +        return find_value
> +
> +    def get_info_from_bond_config(self, config_content, args):
> +        """
> +        Get active slaves of the bonding device which you choose.
> +        """
> +        search_args = args if isinstance(args[0], (list, tuple)) else [args]
> +        for search_args in search_args:
> +            try:
> +                info = self.get_bonding_config(config_content, search_args)
> +                break
> +            except Exception as e:
> +                self.logger.info(e)
> +        else:
> +            info = None
> +
> +        return info
> +
> +    def get_bonding_info(self, bond_port, info_types):
> +        ''' Get the specified port information by its output message format '''
> +        info_set = {
> +            'mode':          ["Bonding mode: ", "\d*"],
> +            'agg_mode':      ["IEEE802.3AD Aggregator Mode: ", "\S*"],
> +            'balance_policy':["Balance Xmit Policy: ", "\S+"],
> +            'slaves':        [["Slaves \(\d\): \[", "\d*( \d*)*"],
> +                              ["Slaves: \[", "\d*( \d*)*"]],
> +            'active_slaves': [["Active Slaves \(\d\): \[", "\d*( \d*)*"],
> +                              ["Acitve Slaves: \[", "\d*( \d*)*"]],
> +            'primary':       ["Primary: \[", "\d*"]}
> +        # get all config information
> +        config_content = self.d_console("show bonding config %d" % bond_port)
> +        if isinstance(info_types, (list or tuple)):
> +            query_values = []
> +            for info_type in info_types:
> +                if info_type in info_set.keys():
> +                    find_value = self.get_info_from_bond_config(
> +                                            config_content, info_set[info_type])
> +                    if info_type in ['active_slaves', 'slaves']:
> +                        find_value = [value for value in find_value.split(' ')
> +                                        if value]
> +                else:
> +                    find_value = None
> +                query_values.append(find_value)
> +            return query_values
> +        else:
> +            info_type = info_types
> +            if info_type in info_set.keys():
> +                find_value = self.get_info_from_bond_config(
> +                                            config_content, info_set[info_type])
> +                if info_type in ['active_slaves', 'slaves']:
> +                    find_value = [value for value in find_value.split(' ')
> +                                        if value]
> +                return find_value
> +            else:
> +                return None
> +
> +    def get_active_slaves(self, bond_port):
> +        primary_port = int(self.get_bonding_info(bond_port, 'primary'))
> +        active_slaves = self.get_bonding_info(bond_port,
> + 'active_slaves')
> +
> +        return int(primary_port), [int(slave) for slave in
> + active_slaves]
> +
> +    def create_bonded_device(self, mode=0, socket=0, verify_detail=False):
> +        """
> +        Create a bonding device with the parameters you specified.
> +        """
> +        cmd = "create bonded device %d %d" % (mode, socket)
> +        out = self.d_console(cmd)
> +        err_fmt = "Create bonded device on mode [%d] socket [%d] failed"
> +        self.verify("Created new bonded device" in out, err_fmt% (mode, socket))
> +        fmts = [
> +             "Created new bonded device net_bond_testpmd_[\d] on \(port ",
> +             "Created new bonded device net_bonding_testpmd_[\d] on \(port ",
> +             "Created new bonded device eth_bond_testpmd_[\d] on \(port "]
> +        bond_port = self.get_value_from_str(fmts, "\d+", out)
> +        bond_port = int(bond_port)
> +
> +        if verify_detail:
> +            out = self.d_console("show bonding config %d" % bond_port)
> +            self.verify("Bonding mode: %d" % mode in out,
> +                        "Bonding mode display error when create bonded device")
> +            self.verify("Slaves: []" in out,
> +                        "Slaves display error when create bonded device")
> +            self.verify("Active Slaves: []" in out,
> +                        "Active Slaves display error when create bonded device")
> +            self.verify("Primary: []" not in out,
> +                        "Primary display error when create bonded device")
> +            out = self.d_console("show port info %d" % bond_port)
> +            self.verify("Connect to socket: %d" % socket in out,
> +                        "Bonding port connect socket error")
> +            self.verify("Link status: down" in out,
> +                        "Bonding port default link status error")
> +            self.verify("Link speed: 0 Mbps" in out,
> +                        "Bonding port default link speed error")
> +
> +        return bond_port
> +
> +    def add_slave(self, bond_port, invert_verify=False, expected_str='',
> +                  *slave_ports):
> +        """
> +        Add ports into the bonding device as slaves.
> +        """
> +        if len(slave_ports) <= 0:
> +            utils.RED("No port exist when add slave to bonded device")
> +        for slave_id in slave_ports:
> +            cmd = "add bonding slave %d %d" % (slave_id, bond_port)
> +            out = self.d_console(cmd)
> +            if expected_str:
> +                self.verify(expected_str in out,
> +                            "message <{0}> is missing".format(expected_str))
> +            slaves = self.get_bonding_info(bond_port, 'slaves')
> +            if not invert_verify:
> +                self.verify(str(slave_id) in slaves,
> +                            "Add port as bonding slave failed")
> +            else:
> +                err = "Add port as bonding slave successfully,should fail"
> +                self.verify(str(slave_id) not in slaves, err)
> +
> +    def remove_slaves(self, bond_port, invert_verify=False, *slave_port):
> +        """
> +        Remove the specified slave port from the bonding device.
> +        """
> +        if len(slave_port) <= 0:
> +            msg = "No port exist when remove slave from bonded device"
> +            self.logger.error(msg)
> +        for slave_id in slave_port:
> +            cmd = "remove bonding slave %d %d" % (int(slave_id), bond_port)
> +            self.d_console(cmd)
> +            slaves = self.get_bonding_info(bond_port, 'slaves')
> +            if not invert_verify:
> +                self.verify(str(slave_id) not in slaves,
> +                            "Remove slave to fail from bonding device")
> +            else:
> +                err = (
> +                    "Remove slave successfully from bonding device, "
> +                    "should be failed")
> +                self.verify(str(slave_id) in slaves, err)
> +
> +    def remove_all_slaves(self, bond_port):
> +        """
> +        Remove all slaves of specified bound device.
> +        """
> +        all_slaves = self.get_bonding_info(bond_port, 'slaves')
> +        if not all_slaves:
> +            return
> +        all_slaves = all_slaves.split()
> +        if len(all_slaves) == 0:
> +            return
> +        self.remove_slaves(bond_port, False, *all_slaves)
> +
> +    def set_primary_slave(self, bond_port, slave_port, invert_verify=False):
> +        """
> +        Set the primary slave for the bonding device.
> +        """
> +        cmd = "set bonding primary %d %d" % (slave_port, bond_port)
> +        self.d_console(cmd)
> +        out = self.get_bonding_info(bond_port, 'primary')
> +        if not invert_verify:
> +            self.verify(str(slave_port) in out,
> +                        "Set bonding primary port failed")
> +        else:
> +            err = "Set bonding primary port successfully, should not success"
> +            self.verify(str(slave_port) not in out, err)
> +
> +    def set_bonding_mode(self, bond_port, mode):
> +        """
> +        Set the bonding mode for port_id.
> +        """
> +        cmd = "set bonding mode %d %d" % (mode, bond_port)
> +        self.d_console(cmd)
> +        mode_value = self.get_bonding_info(bond_port, 'mode')
> +        self.verify(str(mode) in mode_value, "Set bonding mode failed")
> +
> +    def set_bonding_mac(self, bond_port, mac):
> +        """
> +        Set the MAC for the bonding device.
> +        """
> +        cmd = "set bonding mac_addr %s %s" % (bond_port, mac)
> +        self.d_console(cmd)
> +        new_mac = self.get_port_mac(bond_port)
> +        self.verify(new_mac == mac, "Set bonding mac failed")
> +
> +    def get_port_mac(self, bond_port, query_type):
> +        bond_port_mac = self.get_port_info(bond_port, query_type)
> +        return bond_port_mac
> +
> +    def set_bonding_balance_policy(self, bond_port, policy):
> +        """
> +        Set the balance transmit policy for the bonding device.
> +        """
> +        cmd = "set bonding balance_xmit_policy %d %s" % (bond_port, policy)
> +        self.d_console(cmd)
> +        new_policy = self.get_bonding_info(bond_port, 'balance_policy')
> +        policy = "BALANCE_XMIT_POLICY_LAYER" + policy.lstrip('l')
> +        self.verify(new_policy == policy, "Set bonding balance policy
> + failed")
> +
> +    @property
> +    def is_perf(self):
> +        return self.parent._enable_perf
> --
> 1.9.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-02-28 11:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-28  5:50 [dts] [PATCH V1][pmd bonding]: common methods used by bonding suites yufengmx
2019-02-28  5:50 ` [dts] [PATCH V1]pmd_bonding: " yufengmx
2019-02-28  6:03   ` Mo, YufengX
2019-02-28  8:40     ` Chen, Zhaoyan
2019-02-28 11:00   ` Tu, Lijuan

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).