From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 7E561A04F5; Fri, 19 Jun 2020 07:00:57 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 59F4DE07; Fri, 19 Jun 2020 07:00:57 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 1DC193B5 for ; Fri, 19 Jun 2020 07:00:54 +0200 (CEST) IronPort-SDR: VQnslLJ/t5SYA7hfZxho3Uweq+2Sx46Z5jbtPw4KgOp8g5r/LhDQUm2gSc8nkH9acnCOq5jBgk JrJhhkWHeUvA== X-IronPort-AV: E=McAfee;i="6000,8403,9656"; a="141131057" X-IronPort-AV: E=Sophos;i="5.75,253,1589266800"; d="scan'208";a="141131057" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 Jun 2020 22:00:54 -0700 IronPort-SDR: Bkm+oIueNm41OMPizU0rSowRmN85V0Vk3Y+7e1V6f48HOUKum6gfPaVqfwNkz2dW8a/mdviZXF uHp5sYJnhjfA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.75,253,1589266800"; d="scan'208";a="262212993" Received: from fmsmsx103.amr.corp.intel.com ([10.18.124.201]) by fmsmga007.fm.intel.com with ESMTP; 18 Jun 2020 22:00:53 -0700 Received: from fmsmsx158.amr.corp.intel.com (10.18.116.75) by FMSMSX103.amr.corp.intel.com (10.18.124.201) with Microsoft SMTP Server (TLS) id 14.3.439.0; Thu, 18 Jun 2020 22:00:53 -0700 Received: from shsmsx104.ccr.corp.intel.com (10.239.4.70) by fmsmsx158.amr.corp.intel.com (10.18.116.75) with Microsoft SMTP Server (TLS) id 14.3.439.0; Thu, 18 Jun 2020 22:00:53 -0700 Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.85]) by SHSMSX104.ccr.corp.intel.com ([10.239.4.70]) with mapi id 14.03.0439.000; Fri, 19 Jun 2020 13:00:50 +0800 From: "Tu, Lijuan" To: "Mo, YufengX" , "dts@dpdk.org" , "Chen, Zhaoyan" CC: "Mo, YufengX" Thread-Topic: [dts] [PATCH V3 1/1] tests/dcf_lifecycle: upload automation script Thread-Index: AQHWQ4a4+/h53/37J0CI/papDDSVM6jfZhrA Date: Fri, 19 Jun 2020 05:00:49 +0000 Message-ID: <8CE3E05A3F976642AAB0F4675D0AD20E0BC5FC5C@SHSMSX101.ccr.corp.intel.com> References: <20200616023450.16143-1-yufengx.mo@intel.com> <20200616023450.16143-2-yufengx.mo@intel.com> In-Reply-To: <20200616023450.16143-2-yufengx.mo@intel.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="iso-2022-jp" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V3 1/1] tests/dcf_lifecycle: upload automation script X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" Applied, thanks -----Original Message----- From: dts On Behalf Of yufengmx Sent: 2020=1B$BG/=1B(B6=1B$B7n=1B(B16=1B$BF|=1B(B 10:35 To: dts@dpdk.org; Chen, Zhaoyan Cc: Mo, YufengX Subject: [dts] [PATCH V3 1/1] tests/dcf_lifecycle: upload automation script The DCF is a device configuration function (DCF - driver) bound to one of t= he device's VFs which can act as a sole controlling entity to exercise adva= nce functionality (such as switch, ACL) for rest of the VNFs (virtual netwo= rk functions) under a DPDK based NFV deployment. The DCF can act as a special VF talking to the kernel PF over the same virt= channel mailbox to configure the underlying device (port) for the VFs. The test suite covers the lifecycle of DCF context in Kernel PF, such as la= unch, and exit, switch rules handling, resetting, and exception exit. Signed-off-by: yufengmx --- tests/TestSuite_dcf_lifecycle.py | 1175 ++++++++++++++++++++++++++++++ 1 file changed, 1175 insertions(+) create mode 100644 tests/TestSuite_dcf_lifecycle.py diff --git a/tests/TestSuite_dcf_lifecycle.py b/tests/TestSuite_dcf_lifecyc= le.py new file mode 100644 index 0000000..90a56e6 --- /dev/null +++ b/tests/TestSuite_dcf_lifecycle.py @@ -0,0 +1,1175 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2020 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without #=20 +modification, are permitted provided that the following conditions #=20 +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 #=20 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #=20 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR #=20 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT #=20 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, #=20 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT #=20 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, #=20 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY #=20 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT #=20 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #=20 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +DPDK Test suite dcf life cycle. + +The DCF is a device configuration function (DCF - driver) bound to one=20 +of the device's VFs which can act as a sole controlling entity to=20 +exercise advance functionality (such as switch, ACL) for rest of the=20 +VNFs (virtual network functions) under a DPDK based NFV deployment. + +The DCF can act as a special VF talking to the kernel PF over the same=20 +virtchannel mailbox to configure the underlying device (port) for the VFs. + +The test suite covers the lifecycle of DCF context in Kernel PF, such=20 +as launch, and exit, switch rules handling, resetting, and exception exit. +""" + +import re +import time +import traceback +from contextlib import contextmanager +from pprint import pformat +from functools import partial + + +from settings import HEADER_SIZE +from test_case import TestCase +from exception import VerifyFailure +from packet import Packet +from pmd_output import PmdOutput +import utils + + +class TestDcfLifeCycle(TestCase): + + @property + def target_dir(self): + target_dir =3D '/root' + self.dut.base_dir[1:] \ + if self.dut.base_dir.startswith('~') else \ + self.dut.base_dir + return target_dir + + def d_con(self, cmd): + _cmd =3D [cmd, '# ', 15] if isinstance(cmd, str) else cmd + return self.dut.send_expect(*_cmd) + + def d_a_con(self, cmds): + if isinstance(cmds, str): + _cmd =3D [cmds, '# ', 15] + return self.dut.alt_session.send_expect(*_cmd) + else: + return [self.dut.alt_session.send_expect(_cmd, '# ', 10)=20 + for _cmd in cmds] + + def vf_pmd2_con(self, cmd): + _cmd =3D [cmd, '# ', 15] if isinstance(cmd, str) else cmd + return self.vf_pmd2_session.session.send_expect(*_cmd) + + def get_ip_layer(self): + layer =3D {'ipv4': { + 'src': '192.168.0.2', + 'dst': '192.168.0.3', + }, } + return layer + + def get_mac_layer(self, dut_port_id=3D0, vf_id=3D0): + dmac =3D self.vf_ports_info[dut_port_id]['vfs_mac'][vf_id] \ + if vf_id is not None else self.dut.ports_info[dut_port_id]['ma= c'] + layer =3D {'ether': {'dst': dmac, }, } + return layer + + def get_pkt_len(self): + headers_size =3D sum([HEADER_SIZE[x] for x in ['eth', 'ip']]) + pktlen =3D 64 - headers_size + return pktlen + + def config_stream(self, dut_port_id=3D0, vf_id=3DNone): + pkt_layers =3D {'raw': {'payload': ['58'] * self.get_pkt_len()}} + pkt_layers.update(self.get_ip_layer()) + pkt_layers.update(self.get_mac_layer(dut_port_id, vf_id)) + pkt =3D Packet(pkt_type=3D'IP_RAW') + for layer in list(pkt_layers.keys()): + pkt.config_layer(layer, pkt_layers[layer]) + self.logger.info(pkt.pktgen.pkt.command()) + return pkt + + def send_packet_by_scapy(self, pkt, dut_port_id=3D0, count=3D1): + tester_port_id =3D self.tester.get_local_port(dut_port_id) + tx_iface =3D self.tester.get_interface(tester_port_id) + pkt.send_pkt(crb=3Dself.tester, tx_port=3Dtx_iface, count=3Dcount) + + def init_adq(self): + cmds =3D [ + "modprobe sch_mqprio", + "modprobe act_mirred", + "modprobe cls_flower", + ] + self.d_a_con(cmds) + + def set_adq_on_pf(self, dut_port_id=3D0): + ''' + Set ADQ on PF + ''' + msg =3D "Set ADQ on PF" + self.logger.info(msg) + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmds =3D [ + f"ethtool -K {intf} hw-tc-offload on", + f"tc qdisc add dev {intf} ingress", + f"tc qdisc show dev {intf}", + f"tc qdisc add dev {intf} root mqprio num_tc 4 map 0 0 0 0 1 1= 1 1 2 2 2 2 3 3 3 3 queues 4@0 4@4 8@8 8@16 hw 1 mode channel", + f"tc filter add dev {intf} protocol ip parent ffff: prio 1 flo= wer dst_ip 192.168.1.10 ip_proto tcp action gact pass", + f"tc filter show dev {intf} parent ffff:", + ] + output =3D self.d_a_con(cmds) + self.is_adq_set =3D True + return output + + def remove_adq_on_pf(self, dut_port_id=3D0): + ''' + Remove ADQ on PF + ''' + if not self.is_adq_set: + return + msg =3D "Remove ADQ on PF" + self.logger.info(msg) + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmds =3D [ + f"tc filter del dev {intf} parent ffff: pref 1 protocol ip", + f"tc filter show dev {intf} parent ffff:", + f"tc qdisc del dev {intf} root mqprio", + f"tc qdisc del dev {intf} ingress", + f"tc qdisc show dev {intf}", + f"ethtool -K {intf} hw-tc-offload off", + ] + self.d_a_con(cmds) + self.is_adq_set =3D False + + def set_adq_mac_vlan(self, dut_port_id=3D0): + ''' + change the ADQ commands to MAC-VLAN + ''' + msg =3D "change the ADQ commands to MAC-VLAN" + self.logger.info(msg) + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmds =3D [ + f"ethtool -K {intf} l2-fwd-offload on", + f"ip link add link macvlan0 link {intf} type macvlan", + f"ip ad", + f"ifconfig macvlan0 up", + ] + output =3D self.d_a_con(cmds) + self.is_adq_set =3D True + return output + + def remove_adq_mac_vlan(self, dut_port_id=3D0): + ''' + Remove MAC-VLAN commands + ''' + if not self.is_adq_set: + return + msg =3D "Remove MAC-VLAN commands" + self.logger.info(msg) + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmds =3D [ + "ip link del macvlan0", + f"ethtool -K {intf} l2-fwd-offload off", + ] + self.d_a_con(cmds) + self.is_adq_set =3D False + + def clear_dmesg(self): + cmd =3D 'dmesg -C' + self.d_a_con(cmd) + + def get_dmesg(self): + cmd =3D 'dmesg --color=3Dnever' + return self.d_a_con(cmd) or '' + + def vf_init(self): + self.vf_ports_info =3D {} + self.dut.setup_modules(self.target, 'vfio-pci', '') + + def vf_create(self): + max_vfs =3D 4 + for index, port_id in enumerate(self.dut_ports): + port_obj =3D self.dut.ports_info[port_id]['port'] + pf_driver =3D port_obj.default_driver + self.dut.generate_sriov_vfs_by_port( + port_id, max_vfs, driver=3Dpf_driver) + pf_pci =3D port_obj.pci + sriov_vfs_port =3D self.dut.ports_info[port_id].get('vfs_port'= ) + if not sriov_vfs_port: + msg =3D f"failed to create vf on dut port {pf_pci}" + self.logger.error(msg) + continue + for port in sriov_vfs_port: + port.bind_driver(driver=3D'vfio-pci') + vfs_mac =3D [ + "00:12:34:56:{1}:{0}".format( + str(vf_index).zfill(2), str(index + 1).zfill(2)) + for vf_index in range(max_vfs)] + self.vf_ports_info[port_id] =3D { + 'pf_pci': pf_pci, + 'vfs_pci': port_obj.get_sriov_vfs_pci(), + 'vfs_mac': vfs_mac, + 'src_mac': "02:00:00:00:00:0%d" % index, } + self.logger.debug(pformat(self.vf_ports_info)) + + def vf_destroy(self): + if not self.vf_ports_info: + return + for port_id, _ in self.vf_ports_info.items(): + self.dut.destroy_sriov_vfs_by_port(port_id) + port_obj =3D self.dut.ports_info[port_id]['port'] + port_obj.bind_driver(self.drivername) + self.vf_ports_info =3D None + + def vf_whitelist(self): + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[0] + pf1_vf1 =3D self.vf_ports_info[0].get('vfs_pci')[1] + pf1_vf2 =3D self.vf_ports_info[0].get('vfs_pci')[2] + pf2_vf0 =3D self.vf_ports_info[1].get('vfs_pci')[0] \ + if len(self.vf_ports_info) =3D=3D 2 else '' + whitelist =3D { + 'pf1_vf0_dcf': f"-w {pf1_vf0},cap=3Ddcf", + 'pf1_vf1_dcf': f"-w {pf1_vf1},cap=3Ddcf", + 'pf1_vf0_pf2_vf0_dcf': f"-w {pf1_vf0},cap=3Ddcf -w {pf2_vf0},c= ap=3Ddcf", + 'pf1_vf1_vf2': f"-w {pf1_vf1} -w {pf1_vf2}", + 'pf1_vf1': f"-w {pf1_vf1}", + 'pf2_vf0_dcf': f"-w {pf2_vf0},cap=3Ddcf", + } + return whitelist + + def vf_set_mac_addr(self, dut_port_id=3D0, vf_id=3D1): + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmd =3D f"ip link set {intf} vf 1 mac 00:01:02:03:04:05" + self.d_a_con(cmd) + self.vf_testpmd2_reset_port() + + def vf_set_trust(self, dut_port_id=3D0, vf_id=3D0, flag=3D'on'): + ''' + Set a VF as trust + ''' + intf =3D self.dut.ports_info[dut_port_id]['port'].intf_name + cmd =3D f"ip link set {intf} vf {vf_id} trust {flag}" + self.d_a_con(cmd) + + def vf_set_trust_off(self): + ''' + Turn off VF trust mode + ''' + self.vf_set_trust(flag=3D'off') + + def testpmd_set_flow_rule(self, dut_port_id=3D0, con_name=3D'vf_dcf'): + ''' + Set switch rule to VF from DCF + ''' + cmd =3D ( + 'flow create ' + '{port} ' + 'priority 0 ' + 'ingress pattern eth / ipv4 src is {ip_src} dst is {ip_dst} / = end ' + 'actions vf id {vf_id} / end' + ).format(**{ + 'port': dut_port_id, + 'vf_id': 1, + 'ip_src': self.get_ip_layer()['ipv4']['src'], + 'ip_dst': self.get_ip_layer()['ipv4']['dst'], + }) + + con =3D self.d_con if con_name =3D=3D 'vf_dcf' else self.vf_pmd2_c= on + output =3D con([cmd, "testpmd> ", 15]) + return output + + def init_vf_dcf_testpmd(self): + self.vf_dcf_testpmd =3D "{}/{}/app/testpmd".format( + self.target_dir, self.dut.target) + + def start_vf_dcf_testpmd(self, pmd_opiton): + whitelist_name, prefix =3D pmd_opiton + cores =3D self.corelist[:5] + core_mask =3D utils.create_mask(cores) + whitelist =3D self.vf_whitelist().get(whitelist_name) + cmd =3D ( + "{bin} " + "-v " + "-c {core_mask} " + "-n {mem_channel} " + "{whitelist} " + "--file-prefix=3D{prefix} " + "-- -i ").format(**{ + 'bin': self.vf_dcf_testpmd, + 'core_mask': core_mask, + 'mem_channel': self.dut.get_memory_channels(), + 'whitelist': whitelist, + 'prefix': prefix, }) + self.vf_dcf_pmd_start_output =3D self.d_con([cmd, "testpmd> ", 120= ]) + self.is_vf_dcf_pmd_on =3D True + time.sleep(1) + + def close_vf_dcf_testpmd(self): + if not self.is_vf_dcf_pmd_on: + return + try: + self.d_con(['quit', '# ', 15]) + except Exception as e: + self.logger.error(traceback.format_exc()) + self.is_vf_dcf_pmd_on =3D False + + def kill_vf_dcf_process(self): + ''' + Kill DCF process + ''' + cmd =3D "ps aux | grep testpmd" + self.d_a_con(cmd) + cmd =3D r"kill -9 `ps -ef | grep testpmd | grep -v grep | grep -v = testpmd_vf | awk '{print $2}'`" + self.d_a_con(cmd) + self.is_vf_dcf_pmd_on =3D False + time.sleep(2) + cmd =3D "ps aux | grep testpmd" + self.d_a_con(cmd) + + def vf_dcf_testpmd_set_flow_rule(self, dut_port_id=3D0): + return self.testpmd_set_flow_rule(dut_port_id) + + def get_vf_dcf_testpmd_start_output(self): + output =3D self.vf_dcf_pmd_start_output + msg =3D "vf dcf testpmd boot up output is empty" + self.verify(output, msg) + return output + + def create_vf_testpmd2(self): + self.vf_testpmd2 =3D "{}/{}/app/testpmd_vf".format( + self.target_dir, self.dut.target) + cmd =3D 'rm -f {vf_pmd2};cp {vf_dcf_pmd} {vf_pmd2}'.format( + **{'vf_dcf_pmd': self.vf_dcf_testpmd, 'vf_pmd2': self.vf_testp= md2}) + self.d_a_con(cmd) + + def init_vf_testpmd2(self): + self.create_vf_testpmd2() + self.vf_pmd2_session_name =3D 'vf_testpmd2' + self.vf_pmd2_session =3D self.dut.create_session( + self.vf_pmd2_session_name) + + def start_vf_testpmd2(self, pmd_opiton): + whitelist_name, prefix =3D pmd_opiton + cores =3D self.corelist[5:] + core_mask =3D utils.create_mask(cores) + whitelist =3D self.vf_whitelist().get(whitelist_name) + cmd =3D ( + "{bin} " + "-v " + "-c {core_mask} " + "-n {mem_channel} " + "{whitelist} " + "--file-prefix=3D{prefix} " + "-- -i ").format(**{ + 'bin': self.vf_testpmd2, + 'core_mask': core_mask, + 'mem_channel': self.dut.get_memory_channels(), + 'whitelist': whitelist, + 'prefix': prefix, }) + self.vf_pmd2_start_output =3D self.vf_pmd2_con([cmd, "testpmd> ", = 120]) + self.is_vf_pmd2_on =3D True + cmds =3D [ + 'set verbose 1', + 'set fwd mac', + 'start'] if prefix =3D=3D 'vf' else ['start'] + [self.vf_pmd2_con([cmd, "testpmd> ", 15]) for cmd in cmds] + time.sleep(1) + + def close_vf_testpmd2(self): + if not self.is_vf_pmd2_on: + return + try: + self.vf_pmd2_con(['quit', '# ', 15]) + except Exception as e: + self.logger.error(traceback.format_exc()) + self.is_vf_pmd2_on =3D False + + def vf_testpmd2_reset_port(self): + if not self.is_vf_pmd2_on: + return + cmds =3D [ + 'stop', + 'port stop all', + 'port reset all', + 'port start all', + 'start', + ] + [self.vf_pmd2_con([cmd, "testpmd> ", 15]) for cmd in cmds] + + def vf_testpmd2_set_flow_rule(self, dut_port_id=3D0): + self.testpmd_set_flow_rule(dut_port_id, con_name=3D'vf2') + + def vf_pmd2_clear_port_stats(self): + cmd =3D 'clear port stats all' + self.vf_pmd2_con([cmd, "testpmd> ", 15]) + + def check_vf_pmd2_stats(self, traffic, portid=3D0, is_traffic_valid=3D= True): + pmd =3D PmdOutput(self.dut, session=3Dself.vf_pmd2_session) + info =3D pmd.get_pmd_stats(portid) or {} + ori_pkt =3D info.get('RX-packets') or 0 + traffic() + info =3D pmd.get_pmd_stats(portid) or {} + rx_pkt =3D info.get('RX-packets') or 0 + check_pkt =3D rx_pkt - ori_pkt + if is_traffic_valid: + msg =3D f"port {portid} should receive packets, but no traffic= happen" + self.verify(check_pkt and check_pkt > 0, msg) + else: + msg =3D f"port {portid} should not receive packets" + self.verify(not check_pkt, msg) + return rx_pkt + + def get_vf_testpmd2_start_output(self): + output =3D self.vf_pmd2_start_output + msg =3D "vf testpmd2 boot up output is empty" + self.verify(output, msg) + return output + + def check_vf_pmd2_traffic(self, func_name, topo=3DNone, flag=3DFalse): + dut_port_id, vf_id =3D topo if topo else [0, 1] + pkt =3D self.config_stream(dut_port_id, vf_id) + traffic =3D partial(self.send_packet_by_scapy, pkt, dut_port_id, 1= ) + self.vf_pmd2_clear_port_stats() + self.check_vf_pmd2_stats(traffic) + status_change_func =3D getattr(self, func_name) + status_change_func() + self.check_vf_pmd2_stats(traffic, is_traffic_valid=3Dflag) + + def run_test_pre(self, pmd_opitons): + pri_pmd_option =3D pmd_opitons[0] + self.start_vf_dcf_testpmd(pri_pmd_option) + if len(pmd_opitons) =3D=3D 1: # if only one pmd + return + slave_pmd_option =3D pmd_opitons[1] + self.start_vf_testpmd2(slave_pmd_option) + + def run_test_post(self): + # close all binary processes + self.close_vf_testpmd2() + self.close_vf_dcf_testpmd() + time.sleep(5) + + def check_support_dcf_mode_01_result(self): + dcf_output =3D self.get_vf_dcf_testpmd_start_output() + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[0] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf0} (socket {self.socket})", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dcf_output, msg) + + def verify_support_dcf_mode_01(self): + ''' + Generate 1 trust VF on 1 PF, and request 1 DCF on the trust VF. + PF should grant DCF mode to it. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'vf']] + self.run_test_pre(pmd_opts) + self.check_support_dcf_mode_01_result() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_support_dcf_mode_02_result(self): + dcf_output =3D self.get_vf_dcf_testpmd_start_output() + vf2_output =3D self.get_vf_testpmd2_start_output() + # vf 1 testpmd + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[0] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf0} (socket {self.socket})", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dcf_output, msg) + # vf 2 testpmd + pf2_vf0 =3D self.vf_ports_info[1].get('vfs_pci')[0] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f2_vf0} (socket {self.socket})", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in vf2_output, msg) + + def verify_support_dcf_mode_02(self): + ''' + Generate 2 trust VFs on 2 PFs, each trust VF request DCF. + Each PF should grant DCF mode to them. + ''' + except_content =3D None + try: + self.vf_set_trust() + self.vf_set_trust(dut_port_id=3D1) + pmd_opts =3D [['pf1_vf0_dcf', 'dcf1'], ['pf2_vf0_dcf', 'dcf2']= ] + self.run_test_pre(pmd_opts) + self.check_support_dcf_mode_02_result() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_support_dcf_mode_03_result(self): + dcf_output =3D self.get_vf_dcf_testpmd_start_output() + dmesg_output =3D self.get_dmesg() + # vf testpmd + pf1_vf1 =3D self.vf_ports_info[0].get('vfs_pci')[1] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf1} (socket {self.socket})", + "ice_dcf_get_vf_resource(): Failed to get response of OP_GET_V= F_RESOURCE", + "ice_dcf_init_hw(): Failed to get VF resource", + "ice_dcf_dev_init(): Failed to init DCF hardware", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dcf_output, msg) + # dmesg content + pf1 =3D self.vf_ports_info[0].get('pf_pci') + expected_strs =3D [ + f"ice {pf1}: VF 1 requested DCF capability, but only VF 0 is a= llowed to request DCF capability", + f"ice {pf1}: VF 1 failed opcode 3, retval: -5", + ] + msg =3D 'no dmesg output' + self.verify(dmesg_output, msg) + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dmesg_output, msg) + + def verify_support_dcf_mode_03(self): + except_content =3D None + try: + self.vf_set_trust(vf_id=3D1) + self.clear_dmesg() + pmd_opts =3D [['pf1_vf1_dcf', 'vf']] + self.run_test_pre(pmd_opts) + self.check_support_dcf_mode_03_result() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + self.vf_set_trust(vf_id=3D1, flag=3D'off') + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_support_dcf_mode_04_result(self): + dcf_output =3D self.get_vf_dcf_testpmd_start_output() + dmesg_output =3D self.get_dmesg() + # vf testpmd + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[0] + expected_strs =3D [ + "ice_dcf_get_vf_resource(): Failed to get response of OP_GET_V= F_RESOURCE", + "ice_dcf_init_hw(): Failed to get VF resource", + "ice_dcf_dev_init(): Failed to init DCF hardware", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dcf_output, msg) + # dmesg content + pf1 =3D self.vf_ports_info[0].get('pf_pci') + expected_strs =3D [ + f"ice {pf1}: VF needs to be trusted to configure DCF capabilit= y", + f"ice {pf1}: VF 0 failed opcode 3, retval: -5", + ] + msg =3D 'no dmesg output' + self.verify(dmesg_output, msg) + for expected_str in expected_strs: + msg =3D "'{}' not display".format(expected_str) + self.verify(expected_str in dmesg_output, msg) + + def verify_support_dcf_mode_04(self): + except_content =3D None + try: + self.vf_set_trust_off() + self.clear_dmesg() + pmd_opts =3D [['pf1_vf0_dcf', 'vf']] + self.run_test_pre(pmd_opts) + self.check_support_dcf_mode_04_result() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_support_dcf_mode_05(self): + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf'], ['pf1_vf1', 'vf']] + self.run_test_pre(pmd_opts) + self.vf_dcf_testpmd_set_flow_rule() + self.check_vf_pmd2_traffic('close_vf_dcf_testpmd') + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_handle_switch_filter_01(self): + ''' + If turn trust mode off, when DCF launched. The DCF rules should be= removed. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf'], ['pf1_vf1', 'vf']] + self.run_test_pre(pmd_opts) + self.vf_dcf_testpmd_set_flow_rule() + self.check_vf_pmd2_traffic('vf_set_trust_off') + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_handle_switch_filter_02(self): + ''' + If kill DCF process, when DCF launched. The DCF rules should be re= moved. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf'], ['pf1_vf1', 'vf']] + self.run_test_pre(pmd_opts) + self.vf_dcf_testpmd_set_flow_rule() + self.check_vf_pmd2_traffic('kill_vf_dcf_process', flag=3DTrue) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_pmd2_create_dcf_failed(self, vf_id=3D0): + vf2_output =3D self.get_vf_testpmd2_start_output() + pf1_vf =3D self.vf_ports_info[0].get('vfs_pci')[vf_id] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf} (socket {self.socket})", + ] + for expected_str in expected_strs: + msg =3D "Expect: the second testpmd can't be launched" + self.verify(expected_str not in vf2_output, msg) + expected_strs =3D [ + f"EAL: Requested device {pf1_vf} cannot be used" + ] + for expected_str in expected_strs: + msg =3D "Expect: the second testpmd can't be launched" + self.verify(expected_str in vf2_output, msg) + + def verify_handle_switch_filter_03(self): + ''' + Launch 2nd DCF process on the same VF, PF shall reject the request= . + DPDK does not support to open 2nd DCF PMD driver on same VF. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf'], ['pf1_vf0_dcf', 'dcf2']] + self.run_test_pre(pmd_opts) + self.check_pmd2_create_dcf_failed() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_handle_switch_filter_04(self): + ''' + If DCF enabled, one of VF reset. DCF shall clean up all the rules = of this VF. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf'], ['pf1_vf1', 'vf']] + self.run_test_pre(pmd_opts) + self.vf_dcf_testpmd_set_flow_rule() + self.check_vf_pmd2_traffic('vf_set_mac_addr', flag=3DTrue) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_dcf_pmd_create_dcf_failed(self, vf_id=3D0): + dcf_output =3D self.get_vf_dcf_testpmd_start_output() + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[vf_id] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf0} (socket {self.socket})", + "ice_dcf_get_vf_resource(): Failed to get response of OP_GET_V= F_RESOURCE", + "ice_dcf_init_hw(): Failed to get VF resource", + "ice_dcf_dev_init(): Failed to init DCF hardware", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display, PF should reject DCF mode.".format(= expected_str) + self.verify(expected_str in dcf_output, msg) + + def check_dcf_pmd_create_dcf_success(self): + output =3D self.get_vf_dcf_testpmd_start_output() + pf1_vf0 =3D self.vf_ports_info[0].get('vfs_pci')[0] + expected_strs =3D [ + f"Probe PCI driver: net_ice_dcf ({self.dcf_dev_id}) device: {p= f1_vf0} (socket {self.socket})", + "ice_load_pkg_type(): Active package is", + ] + for expected_str in expected_strs: + msg =3D "'{}' not display, DCF mode should be grant".format(ex= pected_str) + self.verify(expected_str in output, msg) + + def verify_dcf_with_adq_01(self): + ''' + When ADQ set on PF, PF should reject the DCF mode. Remove the ADQ = setting, + PF shall accept DCF mode. + + Host kernel version is required 4.19+, and MACVLAN offload should = be set off + ''' + except_content =3D None + try: + self.vf_set_trust() + self.set_adq_on_pf() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + # Expect: testpmd can't be launched. PF should reject DCF mode= . + self.run_test_pre(pmd_opts) + self.check_dcf_pmd_create_dcf_failed() + self.run_test_post() + # Expect: testpmd can launch successfully. DCF mode can be gra= nt + self.remove_adq_on_pf() + self.run_test_pre(pmd_opts) + self.check_dcf_pmd_create_dcf_success() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_on_pf() + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_dcf_with_adq_failed_result(self, output): + expected_strs =3D [ + "Exclusivity flag on", + "RTNETLINK answers: Operation not supported", ] + for _output in output: + if any(expected_str in output for expected_str in expected_str= s): + msg =3D "exclusive action occurs correctly" + self.logger.info(msg) + break + else: + msg =3D "Expect: ADQ command can't be success" + self.verify(False, msg) + + def check_dcf_with_adq_result1(self, output): + check_strs =3D [ + "Exclusivity flag on", + "RTNETLINK answers: Device or resource busy", ] + for _output in output: + status =3D all(check_str not in _output for check_str in check= _strs) + msg =3D "ADQ setting on PF shall be successful, but failed" + self.verify(status, msg) + + def verify_dcf_with_adq_02(self): + ''' + When DCF mode enabled, ADQ setting on PF shall fail. + Exit DCF mode, ADQ setting on PF shall be successful. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + self.run_test_pre(pmd_opts) + # Expect: ADQ command can't be success + output =3D self.set_adq_on_pf() + self.remove_adq_on_pf() + self.run_test_post() + self.check_dcf_with_adq_failed_result(output) + # Expect: ADQ can be set. + output =3D self.set_adq_on_pf() + self.check_dcf_with_adq_result1(output) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_on_pf() + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_dcf_with_adq_03(self): + ''' + Configure the DCF on 1 PF port and configure ADQ on the other PF p= ort. + Then turn off DCF, other PF's should not be impact. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + self.run_test_pre(pmd_opts) + # run PF1 DCF mode, ADQ can be set. + output =3D self.set_adq_on_pf(1) + self.remove_adq_on_pf(1) + self.run_test_post() + self.check_dcf_with_adq_result1(output) + # quit PF1 DCF mode, ADQ can be set. + output =3D self.set_adq_on_pf(1) + self.check_dcf_with_adq_result1(output) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_on_pf(1) + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_dcf_with_l2fwd_01(self): + ''' + When L2 forwarding set, PF should reject the DCF mode. + Remove L2 forwarding set, PF shall accept the DCF mode. + ''' + except_content =3D None + try: + self.vf_set_trust() + self.set_adq_mac_vlan() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + # Expect: testpmd can't be launched. PF should reject DCF mode= . + self.run_test_pre(pmd_opts) + self.check_dcf_pmd_create_dcf_failed() + self.run_test_post() + # Expect: testpmd can launch successfully. DCF mode can be gra= nt + self.remove_adq_mac_vlan() + self.run_test_pre(pmd_opts) + self.check_dcf_pmd_create_dcf_success() + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_mac_vlan() + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def check_dcf_with_l2fwd_adp_failed_result(self, output): + expected_str =3D "Could not change any device features" + status =3D any(expected_str in _output for _output in output) + msg =3D "When DCF mode enabled, PF should set L2 forwarding failed= ." + self.verify(status, msg) + + def check_dcf_with_l2fwd_adp_result(self, output): + check_strs =3D [ + "Exclusivity flag on", + "RTNETLINK answers: Device or resource busy", ] + for _output in output: + status =3D all(check_str not in _output for check_str in check= _strs) + msg =3D "PF should set L2 forwarding successful, but failed" + self.verify(status, msg) + + def verify_dcf_with_l2fwd_02(self): + ''' + When DCF mode enabled, PF can't set L2 forwarding. + Exit DCF mode, PF can set L2 forwarding. + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + self.run_test_pre(pmd_opts) + # When DCF mode enabled, PF can't set L2 forwarding. + output =3D self.set_adq_mac_vlan() + self.remove_adq_mac_vlan() + self.run_test_post() + self.check_dcf_with_l2fwd_adp_failed_result(output) + # Exit DCF mode, PF can set L2 forwarding. + output =3D self.set_adq_mac_vlan() + self.check_dcf_with_l2fwd_adp_result(output) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_mac_vlan() + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_dcf_with_l2fwd_03(self): + ''' + Configure the DCF on 1 PF port and configure MAC-VLAN on the other= PF port. + Then turn off DCF, other PF's MAC-VLAN filter should not be impact= . + ''' + except_content =3D None + try: + self.vf_set_trust() + pmd_opts =3D [['pf1_vf0_dcf', 'dcf']] + self.run_test_pre(pmd_opts) + # run PF1 DCF mode, PF2 can set L2 forwarding. + output =3D self.set_adq_mac_vlan(1) + self.remove_adq_mac_vlan(1) + self.run_test_post() + self.check_dcf_with_l2fwd_adp_result(output) + # Exit PF1 DCF mode, PF2 can set L2 forwarding. + output =3D self.set_adq_mac_vlan(1) + self.check_dcf_with_l2fwd_adp_result(output) + except Exception as e: + self.logger.error(traceback.format_exc()) + except_content =3D e + finally: + self.remove_adq_mac_vlan(1) + self.run_test_post() + # re-raise verify exception result + if except_content: + raise VerifyFailure(except_content) + + def verify_supported_nic(self): + supported_drivers =3D ['ice'] + result =3D all([self.dut.ports_info[index]['port'].default_driver = in + supported_drivers + for index in self.dut_ports]) + msg =3D "current nic <{0}> is not supported".format(self.nic) + self.verify(result, msg) + + def preset_pmd_res(self): + self.dcf_dev_id =3D '8086:1889' + self.socket =3D self.dut.get_numa_id(self.dut_ports[0]) + self.corelist =3D self.dut.get_core_list( + "1S/14C/1T", socket=3Dself.socket)[4:] + + def clear_flags(self): + self.is_vf_dcf_pmd_on =3D self.is_vf_pmd2_on =3D False + self.vf_dcf_pmd_start_output =3D self.vf_pmd2_start_output =3D Non= e + + def init_suite(self): + self.is_vf_dcf_pmd_on =3D self.is_vf_pmd2_on =3D self.is_adq_set = =3D \ + self.vf_pmd2_session =3D None + self.vf_dcf_pmd_start_output =3D self.vf_pmd2_start_output =3D Non= e + self.vf_init() + + def preset_test_environment(self): + cmds =3D [ + "uname -a", + "modinfo ice | grep version:", ] + self.d_a_con(cmds) + self.init_adq() + self.init_vf_dcf_testpmd() + self.init_vf_testpmd2() + self.preset_pmd_res() + self.vf_create() + + def destroy_resource(self): + try: + self.vf_destroy() + finally: + msg =3D "close vf devices" + self.logger.info(msg) + if self.vf_pmd2_session: + self.dut.close_session(self.vf_pmd2_session) + self.vf_pmd2_session =3D None + # + # Test cases. + # + + def set_up_all(self): + """ + Run at the start of each test suite. + """ + self.init_suite() + self.dut_ports =3D self.dut.get_ports(self.nic) + self.verify(len(self.dut_ports) >=3D 1, "Not enough ports") + self.verify_supported_nic() + # prepare testing environment + self.preset_test_environment() + + def tear_down_all(self): + """ + Run after each test suite. + """ + self.destroy_resource() + + def set_up(self): + """ + Run before each test case. + """ + pass + + def tear_down(self): + """ + Run after each test case. + """ + self.dut.kill_all() + self.clear_flags() + + def test_support_dcf_mode_01(self): + ''' + DCF on 1 trust VF on 1 PF + ''' + msg =3D "begin : DCF on 1 trust VF on 1 PF" + self.logger.info(msg) + self.verify_support_dcf_mode_01() + + def test_support_dcf_mode_02(self): + ''' + DCF on 2 PFs, 1 trust VF on each PF + ''' + self.verify(len(self.dut_ports) >=3D 2, "2 ports at least") + msg =3D "begin : DCF on 2 PFs, 1 trust VF on each PF" + self.logger.info(msg) + self.verify_support_dcf_mode_02() + + def test_support_dcf_mode_03(self): + ''' + Check only VF zero can get DCF mode + ''' + msg =3D "begin : Check only VF zero can get DCF mode" + self.logger.info(msg) + self.verify_support_dcf_mode_03() + + def test_support_dcf_mode_04(self): + ''' + Check only trusted VF can get DCF mode + ''' + msg =3D "begin : Check only trusted VF can get DCF mode" + self.logger.info(msg) + self.verify_support_dcf_mode_04() + + def test_support_dcf_mode_05(self): + ''' + DCF graceful exit + ''' + msg =3D "begin : DCF graceful exit" + self.logger.info(msg) + self.verify_support_dcf_mode_05() + + def test_handle_switch_filter_01(self): + ''' + Turn trust mode off, when DCF launched + ''' + msg =3D "begin : Turn trust mode off, when DCF launched" + self.logger.info(msg) + self.verify_handle_switch_filter_01() + + def test_handle_switch_filter_02(self): + ''' + Kill DCF process + ''' + msg =3D "begin : Kill DCF process" + self.logger.info(msg) + self.verify_handle_switch_filter_02() + + def test_handle_switch_filter_03(self): + ''' + Launch 2nd DCF process on the same VF + ''' + msg =3D "begin : Launch 2nd DCF process on the same VF" + self.logger.info(msg) + self.verify_handle_switch_filter_03() + + def test_handle_switch_filter_04(self): + ''' + DCF enabled, one of VF reset + ''' + msg =3D "begin : DCF enabled, one of VF reset" + self.logger.info(msg) + self.verify_handle_switch_filter_04() + + def test_dcf_with_adq_01(self): + ''' + When ADQ set on PF, PF should reject the DCF mode + ''' + msg =3D "begin : When ADQ set on PF, PF should reject the DCF mode= " + self.logger.info(msg) + self.verify_dcf_with_adq_01() + + def test_dcf_with_adq_02(self): + ''' + When DCF mode enabled, ADQ setting on PF shall fail + ''' + msg =3D "begin : When DCF mode enabled, ADQ setting on PF shall fa= il" + self.logger.info(msg) + self.verify_dcf_with_adq_02() + + def test_dcf_with_adq_03(self): + ''' + DCF and ADQ can be enabled on different PF + ''' + self.verify(len(self.dut_ports) >=3D 2, "2 ports at least") + msg =3D "begin : DCF and ADQ can be enabled on different PF" + self.logger.info(msg) + self.verify_dcf_with_adq_03() + + def test_dcf_with_l2fwd_01(self): + ''' + When L2 forwarding set, PF should reject the DCF mode + ''' + msg =3D "begin : When L2 forwarding set, PF should reject the DCF = mode" + self.logger.info(msg) + self.verify_dcf_with_l2fwd_01() + + def test_dcf_with_l2fwd_02(self): + ''' + When DCF mode enabled, PF can't set L2 forwarding + ''' + msg =3D "begin : When DCF mode enabled, PF can't set L2 forwarding= " + self.logger.info(msg) + self.verify_dcf_with_l2fwd_02() + + def test_dcf_with_l2fwd_03(self): + ''' + DCF and L2 forwarding can be enabled on different PF + ''' + self.verify(len(self.dut_ports) >=3D 2, "2 ports at least") + msg =3D "begin : DCF and L2 forwarding can be enabled on different= PF" + self.logger.info(msg) + self.verify_dcf_with_l2fwd_03() -- 2.21.0