From 86cfe709fb0e73bbd6c04a3d6ee34e345578afea Mon Sep 17 00:00:00 2001 From: pfamelis Date: Fri, 14 Oct 2022 16:54:00 +0300 Subject: [PATCH] feat: add netx22-p4 workshop files --- src/tests/netx22-p4/README.md | 73 ++ src/tests/netx22-p4/__init__.py | 14 + src/tests/netx22-p4/deploy_specs.sh | 17 + src/tests/netx22-p4/mininet/topo-simple.py | 96 +++ src/tests/netx22-p4/p4/bmv2.json | 669 ++++++++++++++++++ src/tests/netx22-p4/p4/main.p4 | 162 +++++ src/tests/netx22-p4/p4/p4info.txt | 88 +++ src/tests/netx22-p4/run_test_01_bootstrap.sh | 21 + .../netx22-p4/run_test_02_create_service.sh | 17 + .../netx22-p4/run_test_03_delete_service.sh | 17 + src/tests/netx22-p4/run_test_04_cleanup.sh | 17 + src/tests/netx22-p4/setup.sh | 8 + src/tests/netx22-p4/tests/.gitignore | 2 + src/tests/netx22-p4/tests/BuildDescriptors.py | 35 + src/tests/netx22-p4/tests/LoadDescriptors.py | 40 ++ src/tests/netx22-p4/tests/Objects.py | 276 ++++++++ src/tests/netx22-p4/tests/__init__.py | 14 + .../tests/test_functional_bootstrap.py | 95 +++ .../tests/test_functional_cleanup.py | 76 ++ .../tests/test_functional_create_service.py | 62 ++ .../tests/test_functional_delete_service.py | 58 ++ 21 files changed, 1857 insertions(+) create mode 100644 src/tests/netx22-p4/README.md create mode 100644 src/tests/netx22-p4/__init__.py create mode 100644 src/tests/netx22-p4/deploy_specs.sh create mode 100755 src/tests/netx22-p4/mininet/topo-simple.py create mode 100644 src/tests/netx22-p4/p4/bmv2.json create mode 100644 src/tests/netx22-p4/p4/main.p4 create mode 100644 src/tests/netx22-p4/p4/p4info.txt create mode 100755 src/tests/netx22-p4/run_test_01_bootstrap.sh create mode 100755 src/tests/netx22-p4/run_test_02_create_service.sh create mode 100755 src/tests/netx22-p4/run_test_03_delete_service.sh create mode 100755 src/tests/netx22-p4/run_test_04_cleanup.sh create mode 100755 src/tests/netx22-p4/setup.sh create mode 100644 src/tests/netx22-p4/tests/.gitignore create mode 100644 src/tests/netx22-p4/tests/BuildDescriptors.py create mode 100644 src/tests/netx22-p4/tests/LoadDescriptors.py create mode 100644 src/tests/netx22-p4/tests/Objects.py create mode 100644 src/tests/netx22-p4/tests/__init__.py create mode 100644 src/tests/netx22-p4/tests/test_functional_bootstrap.py create mode 100644 src/tests/netx22-p4/tests/test_functional_cleanup.py create mode 100644 src/tests/netx22-p4/tests/test_functional_create_service.py create mode 100644 src/tests/netx22-p4/tests/test_functional_delete_service.py diff --git a/src/tests/netx22-p4/README.md b/src/tests/netx22-p4/README.md new file mode 100644 index 000000000..51884787d --- /dev/null +++ b/src/tests/netx22-p4/README.md @@ -0,0 +1,73 @@ +# Network X 22 Demo - P4 driver, Basic connectivity functionality + +This functional test shows the P4 driver with a basic connectivity test between 2 hosts connected to a single P4 switch, using the TeraFlow Cloud-native SDN Controller. + +## Functional test folder + +This functional test can be found in folder `src/tests/netx22-p4/`. + +## P4 source and Mininet topology + +This test is designed to operate with a mininet deployment that contains 2 hosts and a BMv2 switch, such a topology can be found in the 'src/tests/netx22-p4/mininet' folder. +Additionally the P4 source code, along with its compiled artifacts are present in the 'src/tests/netx22-p4/mininet' folder. + +## Deployment and Dependencies + +To run this functional test, it is assumed you have deployed a MicroK8s-based Kubernetes environment and a TeraFlowSDN +controller instance as described in the [Tutorial: Deployment Guide](./1-0-deployment.md), and you configured the Python +environment as described in +[Tutorial: Run Experiments Guide > 2.1. Configure Python Environment](./2-1-python-environment.md). +Remember to source the scenario settings appropriately, e.g., `cd ~/tfs-ctrl && source my_deploy.sh` in each terminal +you open. + +Additionally mininet should be installed, we suggest using the mininet packaged in the [Next-Gen SDN Tutorial][https://github.com/opennetworkinglab/ngsdn-tutorial], as it provides an easy way to deploy mininet dockerized and comes with the BMv2Stratum software switch. + +## Test Execution + +### Mininet +To execute this functional test, first make sure that mininet is running the correct topology. +If you have used the Next-Gen SDN Tutorial for it, you may add the topology provided in the mininet folder of the ngsdn tutorial and add the following make rule to its Makefile +``` +start-simple: NGSDN_TOPO_PY := topo-simple.py +start-simple: _start +``` + +After that run +``` +make start-simple +make mn-cli +``` +You will be prompted with the mininet cli. Run the following and let it run until the end of the experiment +``` +client ping server +``` + +### Teraflow + +In another terminal cd to the teraflow directory and run the following +``` +src/tests/netx22-p4/setup.sh +``` +This will copy the p4 artifacts to the device pod. + +Then you can bootstrap the device to the Teraflow Controller +``` +src/tests/netx22-p4/run_test_01_bootstrap.sh +``` + +Install the required rules to the p4 switch +``` +src/tests/netx22-p4/run_test_02_create_service.sh +``` +You should now check the mininet terminal. The two hosts should be pinging each other as intended. + +You can remove the rules from the p4 switch +``` +src/tests/netx22-p4/run_test_03_delete_service.sh +``` +The two hosts on the mininet terminal, should stop pinging. + +And remove the device from the Teraflow Controller +``` +src/tests/netx22-p4/run_test_04_cleanup.sh +``` diff --git a/src/tests/netx22-p4/__init__.py b/src/tests/netx22-p4/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/netx22-p4/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/tests/netx22-p4/deploy_specs.sh b/src/tests/netx22-p4/deploy_specs.sh new file mode 100644 index 000000000..b486474e2 --- /dev/null +++ b/src/tests/netx22-p4/deploy_specs.sh @@ -0,0 +1,17 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +export TFS_COMPONENTS="context device automation service compute monitoring webui" + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Set the neew Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" diff --git a/src/tests/netx22-p4/mininet/topo-simple.py b/src/tests/netx22-p4/mininet/topo-simple.py new file mode 100755 index 000000000..8a7ccaf62 --- /dev/null +++ b/src/tests/netx22-p4/mininet/topo-simple.py @@ -0,0 +1,96 @@ +#!/usr/bin/python + +# Copyright 2019-present Open Networking Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +from mininet.cli import CLI +from mininet.log import setLogLevel +from mininet.net import Mininet +from mininet.node import Host +from mininet.topo import Topo +from stratum import StratumBmv2Switch + +CPU_PORT = 255 + +class IPv4Host(Host): + """Host that can be configured with an IPv4 gateway (default route). + """ + + def config(self, mac=None, ip=None, defaultRoute=None, lo='up', gw=None, + **_params): + super(IPv4Host, self).config(mac, ip, defaultRoute, lo, **_params) + self.cmd('arp -s 192.168.1.1 11:22:33:44:55:77') + self.cmd('ip -4 addr flush dev %s' % self.defaultIntf()) + self.cmd('ip -6 addr flush dev %s' % self.defaultIntf()) + self.cmd('ip -4 link set up %s' % self.defaultIntf()) + self.cmd('ip -4 addr add %s dev %s' % (ip, self.defaultIntf())) + if gw: + self.cmd('ip -4 route add default via %s' % gw) + # Disable offload + for attr in ["rx", "tx", "sg"]: + cmd = "/sbin/ethtool --offload %s %s off" % ( + self.defaultIntf(), attr) + self.cmd(cmd) + + def updateIP(): + return ip.split('/')[0] + + self.defaultIntf().updateIP = updateIP + +class TutorialTopo(Topo): + """Basic Server-Client topology with IPv4 hosts""" + + def __init__(self, *args, **kwargs): + Topo.__init__(self, *args, **kwargs) + + # Spines + # gRPC port 50001 + switch1 = self.addSwitch('switch1', cls=StratumBmv2Switch, cpuport=CPU_PORT) + + # IPv4 hosts attached to switch 1 + client = self.addHost('client', cls=IPv4Host, mac="aa:bb:cc:dd:ee:11", + ip='10.0.0.1/24', gw='10.0.0.100') + server = self.addHost('server', cls=IPv4Host, mac="aa:bb:cc:dd:ee:22", + ip='10.0.0.2/24', gw='10.0.0.100') + self.addLink(client, switch1) # port 1 + self.addLink(server, switch1) # port 2 + + +def main(): + net = Mininet(topo=TutorialTopo(), controller=None) + net.start() + client = net.hosts[0] + server = net.hosts[1] + client.setARP('10.0.0.2', 'aa:bb:cc:dd:ee:22') + server.setARP('10.0.0.1', 'aa:bb:cc:dd:ee:11') + CLI(net) + net.stop() + print '#' * 80 + print 'ATTENTION: Mininet was stopped! Perhaps accidentally?' + print 'No worries, it will restart automatically in a few seconds...' + print 'To access again the Mininet CLI, use `make mn-cli`' + print 'To detach from the CLI (without stopping), press Ctrl-D' + print 'To permanently quit Mininet, use `make stop`' + print '#' * 80 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='Mininet topology script for 2x2 fabric with stratum_bmv2 and IPv4 hosts') + args = parser.parse_args() + setLogLevel('info') + + main() diff --git a/src/tests/netx22-p4/p4/bmv2.json b/src/tests/netx22-p4/p4/bmv2.json new file mode 100644 index 000000000..b1e3fa8ce --- /dev/null +++ b/src/tests/netx22-p4/p4/bmv2.json @@ -0,0 +1,669 @@ +{ + "header_types" : [ + { + "name" : "scalars_0", + "id" : 0, + "fields" : [ + ["tmp", 1, false], + ["local_metadata_t.is_multicast", 1, false], + ["_padding_0", 6, false] + ] + }, + { + "name" : "standard_metadata", + "id" : 1, + "fields" : [ + ["ingress_port", 9, false], + ["egress_spec", 9, false], + ["egress_port", 9, false], + ["clone_spec", 32, false], + ["instance_type", 32, false], + ["drop", 1, false], + ["recirculate_port", 16, false], + ["packet_length", 32, false], + ["enq_timestamp", 32, false], + ["enq_qdepth", 19, false], + ["deq_timedelta", 32, false], + ["deq_qdepth", 19, false], + ["ingress_global_timestamp", 48, false], + ["egress_global_timestamp", 48, false], + ["lf_field_list", 32, false], + ["mcast_grp", 16, false], + ["resubmit_flag", 32, false], + ["egress_rid", 16, false], + ["recirculate_flag", 32, false], + ["checksum_error", 1, false], + ["parser_error", 32, false], + ["priority", 3, false], + ["_padding", 2, false] + ] + }, + { + "name" : "ethernet_t", + "id" : 2, + "fields" : [ + ["dst_addr", 48, false], + ["src_addr", 48, false], + ["ether_type", 16, false] + ] + } + ], + "headers" : [ + { + "name" : "scalars", + "id" : 0, + "header_type" : "scalars_0", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "standard_metadata", + "id" : 1, + "header_type" : "standard_metadata", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "ethernet", + "id" : 2, + "header_type" : "ethernet_t", + "metadata" : false, + "pi_omit" : true + } + ], + "header_stacks" : [], + "header_union_types" : [], + "header_unions" : [], + "header_union_stacks" : [], + "field_lists" : [], + "errors" : [ + ["NoError", 1], + ["PacketTooShort", 2], + ["NoMatch", 3], + ["StackOutOfBounds", 4], + ["HeaderTooShort", 5], + ["ParserTimeout", 6], + ["ParserInvalidArgument", 7] + ], + "enums" : [], + "parsers" : [ + { + "name" : "parser", + "id" : 0, + "init_state" : "start", + "parse_states" : [ + { + "name" : "start", + "id" : 0, + "parser_ops" : [ + { + "parameters" : [ + { + "type" : "regular", + "value" : "ethernet" + } + ], + "op" : "extract" + } + ], + "transitions" : [ + { + "value" : "default", + "mask" : null, + "next_state" : null + } + ], + "transition_key" : [] + } + ] + } + ], + "parse_vsets" : [], + "deparsers" : [ + { + "name" : "deparser", + "id" : 0, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 148, + "column" : 8, + "source_fragment" : "DeparserImpl" + }, + "order" : ["ethernet"] + } + ], + "meter_arrays" : [], + "counter_arrays" : [], + "register_arrays" : [], + "calculations" : [], + "learn_lists" : [], + "actions" : [ + { + "name" : "IngressPipeImpl.drop", + "id" : 0, + "runtime_data" : [], + "primitives" : [ + { + "op" : "mark_to_drop", + "parameters" : [ + { + "type" : "header", + "value" : "standard_metadata" + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 77, + "column" : 8, + "source_fragment" : "mark_to_drop(standard_metadata)" + } + } + ] + }, + { + "name" : "IngressPipeImpl.drop", + "id" : 1, + "runtime_data" : [], + "primitives" : [ + { + "op" : "mark_to_drop", + "parameters" : [ + { + "type" : "header", + "value" : "standard_metadata" + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 77, + "column" : 8, + "source_fragment" : "mark_to_drop(standard_metadata)" + } + } + ] + }, + { + "name" : "IngressPipeImpl.set_egress_port", + "id" : 2, + "runtime_data" : [ + { + "name" : "port_num", + "bitwidth" : 9 + } + ], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + { + "type" : "runtime_data", + "value" : 0 + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 81, + "column" : 8, + "source_fragment" : "standard_metadata.egress_spec = port_num" + } + } + ] + }, + { + "name" : "IngressPipeImpl.set_egress_port", + "id" : 3, + "runtime_data" : [ + { + "name" : "port_num", + "bitwidth" : 9 + } + ], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + { + "type" : "runtime_data", + "value" : 0 + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 81, + "column" : 8, + "source_fragment" : "standard_metadata.egress_spec = port_num" + } + } + ] + }, + { + "name" : "IngressPipeImpl.set_multicast_group", + "id" : 4, + "runtime_data" : [ + { + "name" : "gid", + "bitwidth" : 16 + } + ], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + }, + { + "type" : "runtime_data", + "value" : 0 + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 89, + "column" : 8, + "source_fragment" : "standard_metadata.mcast_grp = gid" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "local_metadata_t.is_multicast"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "b2d", + "left" : null, + "right" : { + "type" : "bool", + "value" : true + } + } + } + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 90, + "column" : 8, + "source_fragment" : "local_metadata.is_multicast = true" + } + } + ] + }, + { + "name" : "IngressPipeImpl.set_multicast_group", + "id" : 5, + "runtime_data" : [ + { + "name" : "gid", + "bitwidth" : 16 + } + ], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + }, + { + "type" : "runtime_data", + "value" : 0 + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 89, + "column" : 8, + "source_fragment" : "standard_metadata.mcast_grp = gid" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "local_metadata_t.is_multicast"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "b2d", + "left" : null, + "right" : { + "type" : "bool", + "value" : true + } + } + } + } + ], + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 90, + "column" : 8, + "source_fragment" : "local_metadata.is_multicast = true" + } + } + ] + }, + { + "name" : "act", + "id" : 6, + "runtime_data" : [], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "tmp"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "b2d", + "left" : null, + "right" : { + "type" : "bool", + "value" : true + } + } + } + } + ] + } + ] + }, + { + "name" : "act_0", + "id" : 7, + "runtime_data" : [], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "tmp"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "b2d", + "left" : null, + "right" : { + "type" : "bool", + "value" : false + } + } + } + } + ] + } + ] + } + ], + "pipelines" : [ + { + "name" : "ingress", + "id" : 0, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 71, + "column" : 8, + "source_fragment" : "IngressPipeImpl" + }, + "init_table" : "IngressPipeImpl.l2_exact_table", + "tables" : [ + { + "name" : "IngressPipeImpl.l2_exact_table", + "id" : 0, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 95, + "column" : 10, + "source_fragment" : "l2_exact_table" + }, + "key" : [ + { + "match_type" : "exact", + "name" : "hdr.ethernet.dst_addr", + "target" : ["ethernet", "dst_addr"], + "mask" : null + } + ], + "match_type" : "exact", + "type" : "simple", + "max_size" : 1024, + "with_counters" : false, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [2, 4, 0], + "actions" : ["IngressPipeImpl.set_egress_port", "IngressPipeImpl.set_multicast_group", "IngressPipeImpl.drop"], + "base_default_next" : null, + "next_tables" : { + "__HIT__" : "tbl_act", + "__MISS__" : "tbl_act_0" + }, + "default_entry" : { + "action_id" : 0, + "action_const" : true, + "action_data" : [], + "action_entry_const" : true + } + }, + { + "name" : "tbl_act", + "id" : 1, + "key" : [], + "match_type" : "exact", + "type" : "simple", + "max_size" : 1024, + "with_counters" : false, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [6], + "actions" : ["act"], + "base_default_next" : "node_5", + "next_tables" : { + "act" : "node_5" + }, + "default_entry" : { + "action_id" : 6, + "action_const" : true, + "action_data" : [], + "action_entry_const" : true + } + }, + { + "name" : "tbl_act_0", + "id" : 2, + "key" : [], + "match_type" : "exact", + "type" : "simple", + "max_size" : 1024, + "with_counters" : false, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [7], + "actions" : ["act_0"], + "base_default_next" : "node_5", + "next_tables" : { + "act_0" : "node_5" + }, + "default_entry" : { + "action_id" : 7, + "action_const" : true, + "action_data" : [], + "action_entry_const" : true + } + }, + { + "name" : "IngressPipeImpl.l2_ternary_table", + "id" : 3, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 109, + "column" : 10, + "source_fragment" : "l2_ternary_table" + }, + "key" : [ + { + "match_type" : "ternary", + "name" : "hdr.ethernet.dst_addr", + "target" : ["ethernet", "dst_addr"], + "mask" : null + } + ], + "match_type" : "ternary", + "type" : "simple", + "max_size" : 1024, + "with_counters" : false, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [3, 5, 1], + "actions" : ["IngressPipeImpl.set_egress_port", "IngressPipeImpl.set_multicast_group", "IngressPipeImpl.drop"], + "base_default_next" : null, + "next_tables" : { + "IngressPipeImpl.set_egress_port" : null, + "IngressPipeImpl.set_multicast_group" : null, + "IngressPipeImpl.drop" : null + }, + "default_entry" : { + "action_id" : 1, + "action_const" : true, + "action_data" : [], + "action_entry_const" : true + } + } + ], + "action_profiles" : [], + "conditionals" : [ + { + "name" : "node_5", + "id" : 0, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 122, + "column" : 12, + "source_fragment" : "!l2_exact_table.apply().hit" + }, + "expression" : { + "type" : "expression", + "value" : { + "op" : "not", + "left" : null, + "right" : { + "type" : "expression", + "value" : { + "op" : "d2b", + "left" : null, + "right" : { + "type" : "field", + "value" : ["scalars", "tmp"] + } + } + } + } + }, + "false_next" : null, + "true_next" : "IngressPipeImpl.l2_ternary_table" + } + ] + }, + { + "name" : "egress", + "id" : 1, + "source_info" : { + "filename" : "p4src/main.p4", + "line" : 134, + "column" : 8, + "source_fragment" : "EgressPipeImpl" + }, + "init_table" : null, + "tables" : [], + "action_profiles" : [], + "conditionals" : [] + } + ], + "checksums" : [], + "force_arith" : [], + "extern_instances" : [], + "field_aliases" : [ + [ + "queueing_metadata.enq_timestamp", + ["standard_metadata", "enq_timestamp"] + ], + [ + "queueing_metadata.enq_qdepth", + ["standard_metadata", "enq_qdepth"] + ], + [ + "queueing_metadata.deq_timedelta", + ["standard_metadata", "deq_timedelta"] + ], + [ + "queueing_metadata.deq_qdepth", + ["standard_metadata", "deq_qdepth"] + ], + [ + "intrinsic_metadata.ingress_global_timestamp", + ["standard_metadata", "ingress_global_timestamp"] + ], + [ + "intrinsic_metadata.egress_global_timestamp", + ["standard_metadata", "egress_global_timestamp"] + ], + [ + "intrinsic_metadata.lf_field_list", + ["standard_metadata", "lf_field_list"] + ], + [ + "intrinsic_metadata.mcast_grp", + ["standard_metadata", "mcast_grp"] + ], + [ + "intrinsic_metadata.resubmit_flag", + ["standard_metadata", "resubmit_flag"] + ], + [ + "intrinsic_metadata.egress_rid", + ["standard_metadata", "egress_rid"] + ], + [ + "intrinsic_metadata.recirculate_flag", + ["standard_metadata", "recirculate_flag"] + ], + [ + "intrinsic_metadata.priority", + ["standard_metadata", "priority"] + ] + ], + "program" : "p4src/main.p4", + "__meta__" : { + "version" : [2, 18], + "compiler" : "https://github.com/p4lang/p4c" + } +} \ No newline at end of file diff --git a/src/tests/netx22-p4/p4/main.p4 b/src/tests/netx22-p4/p4/main.p4 new file mode 100644 index 000000000..fb94bdfd2 --- /dev/null +++ b/src/tests/netx22-p4/p4/main.p4 @@ -0,0 +1,162 @@ +/* + * Copyright 2019-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +typedef bit<9> port_num_t; +typedef bit<48> mac_addr_t; +typedef bit<16> mcast_group_id_t; + +//------------------------------------------------------------------------------ +// HEADER DEFINITIONS +//------------------------------------------------------------------------------ + +header ethernet_t { + mac_addr_t dst_addr; + mac_addr_t src_addr; + bit<16> ether_type; +} + +struct parsed_headers_t { + ethernet_t ethernet; +} + +struct local_metadata_t { + bool is_multicast; +} + + +//------------------------------------------------------------------------------ +// INGRESS PIPELINE +//------------------------------------------------------------------------------ + +parser ParserImpl (packet_in packet, + out parsed_headers_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) +{ + state start { + transition parse_ethernet; + } + + state parse_ethernet { + packet.extract(hdr.ethernet); + transition accept; + } +} + + +control VerifyChecksumImpl(inout parsed_headers_t hdr, + inout local_metadata_t meta) +{ + apply { /* EMPTY */ } +} + + +control IngressPipeImpl (inout parsed_headers_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) { + + // Drop action shared by many tables. + action drop() { + mark_to_drop(standard_metadata); + } + + action set_egress_port(port_num_t port_num) { + standard_metadata.egress_spec = port_num; + } + + action set_multicast_group(mcast_group_id_t gid) { + // gid will be used by the Packet Replication Engine (PRE) in the + // Traffic Manager--located right after the ingress pipeline, to + // replicate a packet to multiple egress ports, specified by the control + // plane by means of P4Runtime MulticastGroupEntry messages. + standard_metadata.mcast_grp = gid; + local_metadata.is_multicast = true; + } + + // --- l2_exact_table ------------------ + + table l2_exact_table { + key = { + hdr.ethernet.dst_addr: exact; + } + actions = { + set_egress_port; + set_multicast_group; + @defaultonly drop; + } + const default_action = drop; + } + + // --- l2_ternary_table ------------------ + + table l2_ternary_table { + key = { + hdr.ethernet.dst_addr: ternary; + } + actions = { + set_egress_port; + set_multicast_group; + @defaultonly drop; + } + const default_action = drop; + } + + apply { + if (!l2_exact_table.apply().hit) { + // ...if an entry is NOT found, apply the ternary one in case + // this is a multicast/broadcast NDP NS packet. + l2_ternary_table.apply(); + } + } +} + +//------------------------------------------------------------------------------ +// EGRESS PIPELINE +//------------------------------------------------------------------------------ + +control EgressPipeImpl (inout parsed_headers_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) { + apply { /* EMPTY */ } +} + + +control ComputeChecksumImpl(inout parsed_headers_t hdr, + inout local_metadata_t local_metadata) +{ + apply { /* EMPTY */ } +} + + +control DeparserImpl(packet_out packet, in parsed_headers_t hdr) { + apply { + packet.emit(hdr.ethernet); + } +} + + +V1Switch( + ParserImpl(), + VerifyChecksumImpl(), + IngressPipeImpl(), + EgressPipeImpl(), + ComputeChecksumImpl(), + DeparserImpl() +) main; diff --git a/src/tests/netx22-p4/p4/p4info.txt b/src/tests/netx22-p4/p4/p4info.txt new file mode 100644 index 000000000..4048dfcd3 --- /dev/null +++ b/src/tests/netx22-p4/p4/p4info.txt @@ -0,0 +1,88 @@ +pkg_info { + arch: "v1model" +} +tables { + preamble { + id: 33605373 + name: "IngressPipeImpl.l2_exact_table" + alias: "l2_exact_table" + } + match_fields { + id: 1 + name: "hdr.ethernet.dst_addr" + bitwidth: 48 + match_type: EXACT + } + action_refs { + id: 16812802 + } + action_refs { + id: 16841371 + } + action_refs { + id: 16796182 + annotations: "@defaultonly" + scope: DEFAULT_ONLY + } + const_default_action_id: 16796182 + size: 1024 +} +tables { + preamble { + id: 33573501 + name: "IngressPipeImpl.l2_ternary_table" + alias: "l2_ternary_table" + } + match_fields { + id: 1 + name: "hdr.ethernet.dst_addr" + bitwidth: 48 + match_type: TERNARY + } + action_refs { + id: 16812802 + } + action_refs { + id: 16841371 + } + action_refs { + id: 16796182 + annotations: "@defaultonly" + scope: DEFAULT_ONLY + } + const_default_action_id: 16796182 + size: 1024 +} +actions { + preamble { + id: 16796182 + name: "IngressPipeImpl.drop" + alias: "drop" + } +} +actions { + preamble { + id: 16812802 + name: "IngressPipeImpl.set_egress_port" + alias: "set_egress_port" + } + params { + id: 1 + name: "port_num" + bitwidth: 9 + } +} +actions { + preamble { + id: 16841371 + name: "IngressPipeImpl.set_multicast_group" + alias: "set_multicast_group" + } + params { + id: 1 + name: "gid" + bitwidth: 16 + } +} +type_info { +} diff --git a/src/tests/netx22-p4/run_test_01_bootstrap.sh b/src/tests/netx22-p4/run_test_01_bootstrap.sh new file mode 100755 index 000000000..a3aeaa2b6 --- /dev/null +++ b/src/tests/netx22-p4/run_test_01_bootstrap.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# make sure to source the following scripts: +# - my_deploy.sh +# - tfs_runtime_env_vars.sh + +source tfs_runtime_env_vars.sh +python -m pytest --verbose src/tests/netx22-p4/tests/test_functional_bootstrap.py diff --git a/src/tests/netx22-p4/run_test_02_create_service.sh b/src/tests/netx22-p4/run_test_02_create_service.sh new file mode 100755 index 000000000..eb2b2d1ab --- /dev/null +++ b/src/tests/netx22-p4/run_test_02_create_service.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source tfs_runtime_env_vars.sh +python -m pytest --verbose src/tests/netx22-p4/tests/test_functional_create_service.py diff --git a/src/tests/netx22-p4/run_test_03_delete_service.sh b/src/tests/netx22-p4/run_test_03_delete_service.sh new file mode 100755 index 000000000..918073c84 --- /dev/null +++ b/src/tests/netx22-p4/run_test_03_delete_service.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source tfs_runtime_env_vars.sh +python -m pytest --verbose src/tests/netx22-p4/tests/test_functional_delete_service.py diff --git a/src/tests/netx22-p4/run_test_04_cleanup.sh b/src/tests/netx22-p4/run_test_04_cleanup.sh new file mode 100755 index 000000000..9e70d02ad --- /dev/null +++ b/src/tests/netx22-p4/run_test_04_cleanup.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source tfs_runtime_env_vars.sh +python -m pytest --verbose src/tests/netx22-p4/tests/test_functional_cleanup.py diff --git a/src/tests/netx22-p4/setup.sh b/src/tests/netx22-p4/setup.sh new file mode 100755 index 000000000..07fe22e6a --- /dev/null +++ b/src/tests/netx22-p4/setup.sh @@ -0,0 +1,8 @@ +#! /bin/bash + +export POD_NAME=$(kubectl get pods -n=tfs | grep device | awk '{print $1}') + +kubectl exec ${POD_NAME} -n=tfs -- mkdir /root/p4 + +kubectl cp src/tests/netx22-p4/p4/p4info.txt tfs/${POD_NAME}:/root/p4 +kubectl cp src/tests/netx22-p4/p4/bmv2.json tfs/${POD_NAME}:/root/p4 diff --git a/src/tests/netx22-p4/tests/.gitignore b/src/tests/netx22-p4/tests/.gitignore new file mode 100644 index 000000000..76cb708d1 --- /dev/null +++ b/src/tests/netx22-p4/tests/.gitignore @@ -0,0 +1,2 @@ +# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc. +Credentials.py diff --git a/src/tests/netx22-p4/tests/BuildDescriptors.py b/src/tests/netx22-p4/tests/BuildDescriptors.py new file mode 100644 index 000000000..5c5419190 --- /dev/null +++ b/src/tests/netx22-p4/tests/BuildDescriptors.py @@ -0,0 +1,35 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, json, sys +from .Objects import CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +def main(): + with open('tests/ofc22/descriptors_emulated.json', 'w', encoding='UTF-8') as f: + devices = [] + for device,connect_rules in DEVICES: + device = copy.deepcopy(device) + device['device_config']['config_rules'].extend(connect_rules) + devices.append(device) + + f.write(json.dumps({ + 'contexts': CONTEXTS, + 'topologies': TOPOLOGIES, + 'devices': devices, + 'links': LINKS + })) + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/netx22-p4/tests/LoadDescriptors.py b/src/tests/netx22-p4/tests/LoadDescriptors.py new file mode 100644 index 000000000..33bc699af --- /dev/null +++ b/src/tests/netx22-p4/tests/LoadDescriptors.py @@ -0,0 +1,40 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json, logging, sys +from common.Settings import get_setting +from context.client.ContextClient import ContextClient +from common.proto.context_pb2 import Context, Device, Link, Topology +from device.client.DeviceClient import DeviceClient + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +def main(): + context_client = ContextClient( + get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + device_client = DeviceClient( + get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + + with open('tests/ofc22/descriptors.json', 'r', encoding='UTF-8') as f: + descriptors = json.loads(f.read()) + + for context in descriptors['contexts' ]: context_client.SetContext (Context (**context )) + for topology in descriptors['topologies']: context_client.SetTopology(Topology(**topology)) + for device in descriptors['devices' ]: device_client .AddDevice (Device (**device )) + for link in descriptors['links' ]: context_client.SetLink (Link (**link )) + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/netx22-p4/tests/Objects.py b/src/tests/netx22-p4/tests/Objects.py new file mode 100644 index 000000000..09b3aced8 --- /dev/null +++ b/src/tests/netx22-p4/tests/Objects.py @@ -0,0 +1,276 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Dict, List, Tuple +from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Device import ( + json_device_connect_rules, json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, + json_device_connect_rules, json_device_id, json_device_p4_disabled, + json_device_emulated_tapi_disabled, json_device_id, json_device_packetrouter_disabled, json_device_tapi_disabled) +from common.tools.object_factory.ConfigRule import ( + json_config_rule_set, json_config_rule_delete) +from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id +from common.tools.object_factory.Link import json_link, json_link_id +from common.tools.object_factory.Topology import json_topology, json_topology_id +from common.proto.kpi_sample_types_pb2 import KpiSampleType + +# ----- Context -------------------------------------------------------------------------------------------------------- +CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) +CONTEXT = json_context(DEFAULT_CONTEXT_UUID) + +# ----- Topology ------------------------------------------------------------------------------------------------------- +TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) +TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) + +# ----- Monitoring Samples --------------------------------------------------------------------------------------------- +PACKET_PORT_SAMPLE_TYPES = [ + KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED, + KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED, + KpiSampleType.KPISAMPLETYPE_BYTES_TRANSMITTED, + KpiSampleType.KPISAMPLETYPE_BYTES_RECEIVED, +] + + +# ----- Devices -------------------------------------------------------------------------------------------------------- + +CUR_PATH = os.path.dirname(os.path.abspath(__file__)) + +DEVICE_SW1_UUID = 'SW1' +DEVICE_SW1_TIMEOUT = 60 +DEVICE_SW1_ID = json_device_id(DEVICE_SW1_UUID) +DEVICE_SW1 = json_device_p4_disabled(DEVICE_SW1_UUID) + +DEVICE_SW1_DPID = 1 +DEVICE_SW1_NAME = DEVICE_SW1_UUID +DEVICE_SW1_IP_ADDR = '10.0.2.10' +DEVICE_SW1_PORT = '50001' +DEVICE_SW1_VENDOR = 'Open Networking Foundation' +DEVICE_SW1_HW_VER = 'BMv2 simple_switch' +DEVICE_SW1_SW_VER = 'Stratum' + +DEVICE_SW1_BIN_PATH = '/root/p4/bmv2.json' +DEVICE_SW1_INFO_PATH = '/root/p4/p4info.txt' + +DEVICE_SW1_CONNECT_RULES = json_device_connect_rules( + DEVICE_SW1_IP_ADDR, + DEVICE_SW1_PORT, + { + 'id': DEVICE_SW1_DPID, + 'name': DEVICE_SW1_NAME, + 'vendor': DEVICE_SW1_VENDOR, + 'hw_ver': DEVICE_SW1_HW_VER, + 'sw_ver': DEVICE_SW1_SW_VER, + 'timeout': DEVICE_SW1_TIMEOUT, + 'p4bin': DEVICE_SW1_BIN_PATH, + 'p4info': DEVICE_SW1_INFO_PATH + } +) + + +################################## TABLE ENTRIES ################################## + + +DEVICE_SW1_CONFIG_TABLE_ENTRIES = [ + json_config_rule_set( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_exact_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:11' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '1' + } + ] + } + ), + json_config_rule_set( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_exact_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:22' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '2' + } + ] + } + ) +] + + +""" +DEVICE_SW1_CONFIG_TABLE_ENTRIES = [ + json_config_rule_set( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_ternary_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:11 &&& ff:ff:ff:ff:ff:ff' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '1' + } + ], + 'priority': 1 + } + ), + json_config_rule_set( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_ternary_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:22 &&& ff:ff:ff:ff:ff:ff' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '2' + } + ], + 'priority': 1 + } + ), +] +""" + +################################## TABLE DECONF ################################## + + +DEVICE_SW1_DECONF_TABLE_ENTRIES = [ + json_config_rule_delete( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_exact_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:11' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '1' + } + ] + } + ), + json_config_rule_delete( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_exact_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:22' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '2' + } + ] + } + ) +] + + + +""" +DEVICE_SW1_DECONF_TABLE_ENTRIES = [ + json_config_rule_delete( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_ternary_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:11 &&& ff:ff:ff:ff:ff:ff' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '1' + } + ], + 'priority': 1 + } + ), + json_config_rule_delete( + 'table', + { + 'table-name': 'IngressPipeImpl.l2_ternary_table', + 'match-fields': [ + { + 'match-field': 'hdr.ethernet.dst_addr', + 'match-value': 'aa:bb:cc:dd:ee:22 &&& ff:ff:ff:ff:ff:ff' + } + ], + 'action-name': 'IngressPipeImpl.set_egress_port', + 'action-params': [ + { + 'action-param': 'port_num', + 'action-value': '2' + } + ], + 'priority': 1 + } + ), +] +""" + +# ----- Links ---------------------------------------------------------------------------------------------------------- + +# ----- WIM Service Settings ------------------------------------------------------------------------------------------- + +# ----- Object Collections --------------------------------------------------------------------------------------------- + +CONTEXTS = [CONTEXT] +TOPOLOGIES = [TOPOLOGY] + +DEVICES = [ + (DEVICE_SW1, DEVICE_SW1_CONNECT_RULES, DEVICE_SW1_CONFIG_TABLE_ENTRIES, DEVICE_SW1_DECONF_TABLE_ENTRIES), +] + +LINKS = [] diff --git a/src/tests/netx22-p4/tests/__init__.py b/src/tests/netx22-p4/tests/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/netx22-p4/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/tests/netx22-p4/tests/test_functional_bootstrap.py b/src/tests/netx22-p4/tests/test_functional_bootstrap.py new file mode 100644 index 000000000..0254ffd26 --- /dev/null +++ b/src/tests/netx22-p4/tests/test_functional_bootstrap.py @@ -0,0 +1,95 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from common.proto.context_pb2 import ConfigActionEnum, Context, ContextId, Device, Empty, Link, Topology, DeviceOperationalStatusEnum +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +def test_prepare_scenario(context_client : ContextClient): # pylint: disable=redefined-outer-name + + # ----- Create Contexts and Topologies ----------------------------------------------------------------------------- + for context in CONTEXTS: + context_uuid = context['context_id']['context_uuid']['uuid'] + LOGGER.info('Adding Context {:s}'.format(context_uuid)) + response = context_client.SetContext(Context(**context)) + assert response.context_uuid.uuid == context_uuid + + for topology in TOPOLOGIES: + context_uuid = topology['topology_id']['context_id']['context_uuid']['uuid'] + topology_uuid = topology['topology_id']['topology_uuid']['uuid'] + LOGGER.info('Adding Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + response = context_client.SetTopology(Topology(**topology)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.topology_uuid.uuid == topology_uuid + context_id = json_context_id(context_uuid) + +def test_scenario_ready(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + +def test_devices_bootstraping( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Create Devices --------------------------------------------------------------- + for device, connect_rules, config_rules, _ in DEVICES: + device_uuid = device['device_id']['device_uuid']['uuid'] + LOGGER.info('Adding Device {:s}'.format(device_uuid)) + + device_p4_with_connect_rules = copy.deepcopy(device) + device_p4_with_connect_rules['device_config']['config_rules'].extend(connect_rules) + response = device_client.AddDevice(Device(**device_p4_with_connect_rules)) + assert response.device_uuid.uuid == device_uuid + +def test_devices_bootstrapped(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure bevices are created ----------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) diff --git a/src/tests/netx22-p4/tests/test_functional_cleanup.py b/src/tests/netx22-p4/tests/test_functional_cleanup.py new file mode 100644 index 000000000..32f716f1c --- /dev/null +++ b/src/tests/netx22-p4/tests/test_functional_cleanup.py @@ -0,0 +1,76 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, check_events +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from common.proto.context_pb2 import ConfigActionEnum, ContextId, Device, DeviceId, Empty, LinkId, TopologyId, DeviceOperationalStatusEnum +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +def test_scenario_cleanup( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Delete Devices and Validate Collected Events --------------------------------------------------------------- + for device, _, _, deconf_rules in DEVICES: + + device_id = device['device_id'] + device_uuid = device_id['device_uuid']['uuid'] + LOGGER.info('Deleting Device {:s}'.format(device_uuid)) + device_client.DeleteDevice(DeviceId(**device_id)) + #expected_events.append(('DeviceEvent', EVENT_REMOVE, json_device_id(device_uuid))) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + # ----- Delete Topologies and Validate Collected Events ------------------------------------------------------------ + for topology in TOPOLOGIES: + topology_id = topology['topology_id'] + context_uuid = topology_id['context_id']['context_uuid']['uuid'] + topology_uuid = topology_id['topology_uuid']['uuid'] + LOGGER.info('Deleting Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + context_client.RemoveTopology(TopologyId(**topology_id)) + context_id = json_context_id(context_uuid) + #expected_events.append(('TopologyEvent', EVENT_REMOVE, json_topology_id(topology_uuid, context_id=context_id))) + + # ----- Delete Contexts and Validate Collected Events -------------------------------------------------------------- + for context in CONTEXTS: + context_id = context['context_id'] + context_uuid = context_id['context_uuid']['uuid'] + LOGGER.info('Deleting Context {:s}'.format(context_uuid)) + context_client.RemoveContext(ContextId(**context_id)) + #expected_events.append(('ContextEvent', EVENT_REMOVE, json_context_id(context_uuid))) diff --git a/src/tests/netx22-p4/tests/test_functional_create_service.py b/src/tests/netx22-p4/tests/test_functional_create_service.py new file mode 100644 index 000000000..501536bdb --- /dev/null +++ b/src/tests/netx22-p4/tests/test_functional_create_service.py @@ -0,0 +1,62 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from common.proto.context_pb2 import Context, ContextId, Device, Empty, Link, Topology +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES +from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceId,\ + DeviceOperationalStatusEnum + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +def test_rules_entry( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Create Devices --------------------------------------------------------------- + for device, connect_rules, config_rules, _ in DEVICES: + # Enable device + device_p4_with_operational_status = copy.deepcopy(device) + device_p4_with_operational_status['device_operational_status'] = \ + DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED + device_client.ConfigureDevice(Device(**device_p4_with_operational_status)) + + device_data = context_client.GetDevice(DeviceId(**json_device_id('SW1'))) + + # Insert table entries + device_p4_with_config_rules = copy.deepcopy(device) + device_p4_with_config_rules['device_config']['config_rules'].extend(config_rules) + device_client.ConfigureDevice(Device(**device_p4_with_config_rules)) diff --git a/src/tests/netx22-p4/tests/test_functional_delete_service.py b/src/tests/netx22-p4/tests/test_functional_delete_service.py new file mode 100644 index 000000000..1be7e3240 --- /dev/null +++ b/src/tests/netx22-p4/tests/test_functional_delete_service.py @@ -0,0 +1,58 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, check_events +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from common.proto.context_pb2 import ConfigActionEnum, ContextId, Device, DeviceId, Empty, LinkId, TopologyId, DeviceOperationalStatusEnum +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +def test_rules_delete( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Delete Devices and Validate Collected Events --------------------------------------------------------------- + for device, _, _, deconf_rules in DEVICES: + + device_p4_with_deconf_rules = copy.deepcopy(device) + device_p4_with_deconf_rules['device_config']['config_rules'].extend(deconf_rules) + device_client.ConfigureDevice(Device(**device_p4_with_deconf_rules)) + + device_p4_with_operational_status = copy.deepcopy(device) + device_p4_with_operational_status['device_operational_status'] = \ + DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED + device_client.ConfigureDevice(Device(**device_p4_with_operational_status)) -- GitLab