Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repair the static DPDK PTF tests. #4210

Merged
merged 2 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/disabled/ci-dpdk-ptf-p4testgen-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,5 @@ jobs:
- name: Run DPDK PTF tests using P4Testgen
working-directory: p4c/build
run: |
sudo $IPDK_INSTALL_DIR/sbin/set_hugepages.sh
sudo -E ctest -j1 --output-on-failure --schedule-random -R testgen-p4c-pna-ptf
sudo $IPDK_INSTALL_DIR/sbin/set_hugepages.sh
sudo -E ctest -j1 --output-on-failure --schedule-random -R "testgen-p4c-pna-ptf|dpdk-ptf"
6 changes: 5 additions & 1 deletion backends/dpdk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ set (P4_16_SUITES

#### DPDK-PTF Tests
# PTF tests for DPDK are only enabled when both infrap4d and dpdk-target are installed.
set(DPDK_PTF_TEST_SUITES "${P4C_SOURCE_DIR}/testdata/p4_16_samples/pna-dpdk-add_on_miss0.p4")
set(DPDK_PTF_TEST_SUITES
"${P4C_SOURCE_DIR}/testdata/p4_16_samples/pna-dpdk-add_on_miss0.p4"
"${P4C_SOURCE_DIR}/testdata/p4_16_samples/pna-dpdk-small_sample.p4"
)

# Check for infrap4d.
find_program(INFRAP4D infrap4d PATHS ${IPDK_INSTALL_DIR}/sbin)
# DPDK_SDK_INSTALL is the path to the dpdk-target install directory
Expand Down
13 changes: 0 additions & 13 deletions backends/dpdk/run-dpdk-ptf-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,6 @@ def build_and_load_pipeline(
testutils.log.error("Failed to build pipeline")
return returncode

# Load pipeline.
# NOTE: in generated PTF tests, the pipelines are loaded in individual test cases.
# This should be commented when working with Testgen.
# command = (
# f"{self.options.ipdk_install_dir}/bin/p4rt-ctl "
# "set-pipe br0 "
# f"{conf_bin} "
# f"{info_name} "
# )
# returncode = self.bridge.ns_exec(command, timeout=30)
# if returncode != testutils.SUCCESS:
# testutils.log.error("Failed to load pipeline")
# return returncode
return testutils.SUCCESS

def run_ptf(self, P4RUNTIME_PORT: int, info_name, conf_bin) -> int:
Expand Down
1 change: 1 addition & 0 deletions cmake/Linters.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ endif()
file(
GLOB_RECURSE P4C_PYTHON_LINT_LIST FOLLOW_SYMLINKS
backends/*.py
testdata/*.py
tools/*.py
)
list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/p4tools/submodules")
Expand Down
218 changes: 108 additions & 110 deletions testdata/p4_16_samples/pna-dpdk-add_on_miss0.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,170 +16,168 @@

# Andy Fingerhut, [email protected]

import logging
import pprint
import queue
import time
from enum import Enum
from pathlib import Path

import ptf
import ptf.testutils as tu
from ptf.base_tests import BaseTest
import p4runtime_sh.shell as sh
import p4runtime_sh.utils as shutils
import p4runtime_sh.p4runtime as p4rt
from ptf import testutils as ptfutils
from ptf.mask import Mask
from ptf.packet import *

# The base_test.py path is relative to the test file, this should be improved
FILE_DIR = Path(__file__).resolve().parent
BASE_TEST_PATH = FILE_DIR.joinpath("../../backends/dpdk/base_test.py")
sys.path.append(str(BASE_TEST_PATH))
import base_test as bt

# Bsed on https://github.com/jafingerhut/p4-guide/blob/master/ipdk/23.01/add_on_miss0/ptf-tests/ptf-test1.py
# This global variable ensure that the forwarding pipeline will only be pushed once in one tes
PIPELINE_PUSHED = False


logger = logging.getLogger(None)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
class AbstractTest(bt.P4RuntimeTest):
EnumColor = Enum("EnumColor", ["GREEN", "YELLOW", "RED"], start=0)

pp = pprint.PrettyPrinter(indent=4)


class AbstractIdleTimeoutTest(BaseTest):
def setUp(self):
# Setting up PTF dataplane
self.dataplane = ptf.dataplane_instance
self.dataplane.flush()

logging.info("AbstractIdleTimeoutTest.setUp()")
grpc_addr = tu.test_param_get("grpcaddr")
if grpc_addr is None:
grpc_addr = 'localhost:9559'
certs_dir = '/usr/share/stratum/certs'
root_certificate = certs_dir + '/ca.crt'
private_key = certs_dir + '/client.key'
certificate_chain = certs_dir + '/client.crt'
# Default to insecure
ssl_opts = p4rt.SSLOptions(True, root_certificate, certificate_chain,
private_key)
sh.setup(device_id=1,
grpc_addr=grpc_addr,
election_id=(0, 1),
ssl_options=ssl_opts)
bt.P4RuntimeTest.setUp(self)
global PIPELINE_PUSHED
if not PIPELINE_PUSHED:
success = bt.P4RuntimeTest.updateConfig(self)
assert success
PIPELINE_PUSHED = True
packet_wait_time = ptfutils.test_param_get("packet_wait_time")
if not packet_wait_time:
self.packet_wait_time = 0.1
else:
self.packet_wait_time = float(packet_wait_time)

def tearDown(self):
logging.info("IdleTimeoutTest.tearDown()")
sh.teardown()
bt.P4RuntimeTest.tearDown(self)

def setupCtrlPlane(self):
pass

def sendPacket(self):
pass

def verifyPackets(self):
pass

@bt.autocleanup
def runTestImpl(self):
self.setupCtrlPlane()
logger.info("Sending Packet ...")
bt.testutils.log.info("Sending Packet ...")
self.sendPacket()
logger.info("Verifying Packet ...")
bt.testutils.log.info("Verifying Packet ...")
self.verifyPackets()


#############################################################
# Define a few small helper functions that help construct
# parameters for the table_add() method.
#############################################################

def entry_count(table_name):
te = sh.TableEntry(table_name)
n = 0
for x in te.read():
n = n + 1
return n

def init_key_from_read_tableentry(read_te):
new_te = sh.TableEntry(read_te.name)
# This is only written to work for tables where all key fields are
# match_kind exact.
for f in read_te.match._fields:
new_te.match[f] = '%d' % (int.from_bytes(read_te.match[f].exact.value, 'big'))
return new_te

def delete_all_entries(tname):
te = sh.TableEntry(tname)
for e in te.read():
d = init_key_from_read_tableentry(e)
d.delete()

def add_ipv4_host_entry_action_send(ipv4_addr_str, port_int):
te = sh.TableEntry('ipv4_host')(action='send')
te.match['dst_addr'] = '%s' % (ipv4_addr_str)
te.action['port'] = '%d' % (port_int)
te.insert()

class OneEntryTest(AbstractTest):
def entry_count(self, table_name):
req, table = self.make_table_read_request(table_name)
n = 0
for response in self.response_dump_helper(req):
n += len(response.entities)
return n

class OneEntryTest(AbstractIdleTimeoutTest):
def delete_all_table_entries(self, table_name):
req, _ = self.make_table_read_request(table_name)
for response in self.response_dump_helper(req):
for entity in response.entities:
assert entity.HasField("table_entry")
self.delete_table_entry(entity.table_entry)
return
return

def sendPacket(self):
in_dmac = 'ee:30:ca:9d:1e:00'
in_smac = 'ee:cd:00:7e:70:00'
ip_src_addr = '1.1.1.1'
ip_dst_addr = '2.2.2.2'
in_dmac = "ee:30:ca:9d:1e:00"
in_smac = "ee:cd:00:7e:70:00"
ip_src_addr = "1.1.1.1"
ip_dst_addr = "2.2.2.2"
ig_port = 0
sport = 59597
dport = 7503
pkt_in = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac,
ip_src=ip_src_addr, ip_dst=ip_dst_addr,
tcp_sport=sport, tcp_dport=dport)

pkt_in = ptfutils.simple_tcp_packet(
eth_src=in_smac,
eth_dst=in_dmac,
ip_src=ip_src_addr,
ip_dst=ip_dst_addr,
tcp_sport=sport,
tcp_dport=dport,
)

# Send in a first packet that should experience a miss on
# table ct_tcp_table, cause a new entry to be added by the
# data plane with a 30-second expiration time, and be
# forwarded with a change to its source MAC address that the
# add_on_miss0.p4 program uses to indicate that a miss
# occurred.
logging.info("Sending packet #1")
tu.send_packet(self, ig_port, pkt_in)
bt.testutils.log.info("Sending packet #1")
ptfutils.send_packet(self, ig_port, pkt_in)
first_pkt_time = time.time()

def verifyPackets(self):
in_dmac = 'ee:30:ca:9d:1e:00'
in_smac = 'ee:cd:00:7e:70:00'
ip_src_addr = '1.1.1.1'
ip_dst_addr = '2.2.2.2'
in_dmac = "ee:30:ca:9d:1e:00"
in_smac = "ee:cd:00:7e:70:00"
ip_src_addr = "1.1.1.1"
ip_dst_addr = "2.2.2.2"
sport = 59597
dport = 7503
# add_on_miss0.p4 replaces least significant 8 bits of source
# MAC address with 0xf1 on a hit of table ct_tcp_table, or
# 0xa5 on a miss.
out_smac_for_miss = in_smac[:-2] + 'a5'
out_smac_for_miss = in_smac[:-2] + "a5"
eg_port = 1
exp_pkt_for_miss = \
tu.simple_tcp_packet(eth_src=out_smac_for_miss, eth_dst=in_dmac,
ip_src=ip_src_addr, ip_dst=ip_dst_addr,
tcp_sport=sport, tcp_dport=dport)
exp_pkt_for_miss = ptfutils.simple_tcp_packet(
eth_src=out_smac_for_miss,
eth_dst=in_dmac,
ip_src=ip_src_addr,
ip_dst=ip_dst_addr,
tcp_sport=sport,
tcp_dport=dport,
)

# Check hit not tested for now for simplicity
# out_smac_for_hit = in_smac[:-2] + 'f1'
# out_smac_for_hit = in_smac[:-2] + "f1"
# exp_pkt_for_hit = \
# tu.simple_tcp_packet(eth_src=out_smac_for_hit, eth_dst=in_dmac,
# ptfutils.simple_tcp_packet(eth_src=out_smac_for_hit, eth_dst=in_dmac,
# ip_src=ip_src_addr, ip_dst=ip_dst_addr,
# tcp_sport=sport, tcp_dport=dport)


tu.verify_packets(self, exp_pkt_for_miss, [eg_port])
logging.info(" packet experienced a miss in ct_tcp_table as expected")
ptfutils.verify_packets(self, exp_pkt_for_miss, [eg_port])
bt.testutils.log.info("packet experienced a miss in ct_tcp_table as expected")

bt.testutils.log.info("Attempting to delete all entries in ipv4_host")
# TODO: This code does not seem functional?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jafingerhut I am unable to delete table entries on the P4Runtime interface for the DPDK SoftNIC. Even when I read the entries from the table, then try to delete those exact same entries I am getting a "not found" error. Is this a known issue?

The P4Runtme shell code also has the same issue, but it was never uncovered because it was only ever used on tables with no entries.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can double-check, but if I recall correctly, the current DPDK implementation does not support adding or deleting entries from the control plane specifically for add-on-miss tables. For add-on-miss tables, it only supports adding and deleting entries from the data plane.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good to know. Strangely enough I am able to add entries, but I can not remove them.

# self.delete_all_table_entries("MainControlImpl.ipv4_host")
# assert self.entry_count("MainControlImpl.ipv4_host") == 0

def setupCtrlPlane(self):
ig_port = 0
eg_port = 1

ip_src_addr = '1.1.1.1'
ip_dst_addr = '2.2.2.2'

logging.info("Attempting to delete all entries in ipv4_host")
delete_all_entries('ipv4_host')
logging.info("Attempting to add entries to ipv4_host")
add_ipv4_host_entry_action_send(ip_src_addr, ig_port)
add_ipv4_host_entry_action_send(ip_dst_addr, eg_port)
logging.info("Now ipv4_host contains %d entries"
"" % (entry_count('ipv4_host')))
ip_src_addr = 16843009 # "1.1.1.1"
ip_dst_addr = 33686018 # "2.2.2.2"
table_name = "MainControlImpl.ipv4_host"

assert self.entry_count(table_name) == 0
bt.testutils.log.info("Attempting to add entries to ipv4_host")
self.table_add(
(table_name, [self.Exact("hdr.ipv4.dst_addr", ip_src_addr)]),
("MainControlImpl.send", [("port", ig_port)]),
)
self.table_add(
(table_name, [self.Exact("hdr.ipv4.dst_addr", ip_dst_addr)]),
("MainControlImpl.send", [("port", eg_port)]),
)
entry_count = self.entry_count(table_name)
bt.testutils.log.info("Now ipv4_host contains %d entries" "", entry_count)
assert entry_count == 2

def runTest(self):
self.runTestImpl()





Loading
Loading