From c43f6ed97544191ca804a8885caa3af8906e7f65 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Wed, 16 Oct 2024 18:55:00 -0400 Subject: [PATCH 1/6] Move optional steps for testing VMs out of README.md file This may help avoid new users being confused that they must follow those instructions. Signed-off-by: Andy Fingerhut --- vm-ubuntu-20.04/README.md | 217 +--------------- vm-ubuntu-20.04/create-vm-image-for-others.md | 210 ++++++++++++++++ vm-ubuntu-24.04/README.md | 235 +----------------- vm-ubuntu-24.04/create-vm-image-for-others.md | 214 ++++++++++++++++ 4 files changed, 435 insertions(+), 441 deletions(-) create mode 100644 vm-ubuntu-20.04/create-vm-image-for-others.md create mode 100644 vm-ubuntu-24.04/create-vm-image-for-others.md diff --git a/vm-ubuntu-20.04/README.md b/vm-ubuntu-20.04/README.md index ade3084c..6a1eb7ff 100644 --- a/vm-ubuntu-20.04/README.md +++ b/vm-ubuntu-20.04/README.md @@ -1,10 +1,12 @@ # Creating the VM + + Below are the steps to create a brand new VM using Vagrant: + Install [Vagrant](https://developer.hashicorp.com/vagrant/docs/installation) on your system if it's not already installed. - + Navigate to the directory where you want to create the new VM. + + In a shell/terminal window, change to this `vm-ubuntu-20.04` + directory inside the `tutorials` directory. + Run the below command in the terminal. - ``` + ```bash vagrant up ``` @@ -13,214 +15,9 @@ - You can update these packages using `apt upgrade` within the VM. + Alternatively, a development VM can be created by running - ``` + ```bash vagrant up dev ``` -*Note* : that creating a development VM can take one to several -hours, depending upon the speed of your computer and Internet -connection. - -Below are steps that were performed _after_ one of the commands above -was run on the host OS, before creating the VM images. Some of these -steps could probably be automated with programs, and changes to the -`vagrant` scripts that can do so are welcome. I performed these steps -manually to create a VM image, simply to avoid the experimentation and -time required to automate them. I typically only create new VM images -once per month. - -+ Log in as user p4 (password p4) -+ Click "Upgrade" in the pop-up window asking if you want to upgrade - the system, if asked. This will download the latest Linux kernel - version released for Ubuntu 20.04, and other updated packages. -+ Reboot the system. -+ This is optional, but if you want to save a little disk space, use - - ``` - sudo apt purge - ``` - - to remove older version of Linux - kernel, if the upgrade installed a newer one. -+ Clean the local repository of retrieved package files to free up disk space - - ``` - sudo apt clean - ``` - -+ Log in as user p4 (password p4) -+ Start menu -> Preferences -> LXQt settings -> Monitor settings - + Change resolution from initial 800x600 to 1024x768. Apply the changes. - + Close monitor settings window - + *Note*: For some reason I do not know, these settings seem to be - undone, even if I use the "Save" button. They are temporarily in - effect if I shut down the system and log back in, but then in a few - seconds it switches back to 800x600. Strange. -+ Start menu -> Preferences -> LXQt settings -> Desktop - + In "Wallpaper mode" popup menu, choose "Center on the screen". - + Click Apply button - + Close "Desktop preferences" window -+ Several of the icons on the desktop have an exclamation mark on - them. If you try double-clicking those icons, it pops up a window - saying "This file 'Wireshark' seems to be a desktop entry. What do - you want to do with it?" with buttons for "Open", "Execute", and - "Cancel". Clicking "Execute" executes the associated command. - If you do a mouse middle click on one of these desktop icons, a - popup menu appears where the second-to-bottom choice is "Trust this - executable". Selecting that causes the exclamation mark to go away, - and future double-clicks of the icon execute the program without - first popping up a window to choose between Open/Execute/Cancel. I - did that for each of these desktop icons: - + Terminal - + Wireshark -+ Log off - -+ Log in as user vagrant (password vagrant) -+ Change monitor settings and wallpaper mode as described above for - user p4. -+ Open a terminal. - + Run the command - - ``` - ./clean.sh - ``` - which removes about 6 to 7 GBytes of - files created while building the projects. -+ Log off - - -# Notes on test results for the VM - -I have run the tests below on every VM image I release, before -releasing it. You need not run them again, unless you are curious how -to do so. - - -## p4c testing results - -Steps to run the p4c tests: - -+ Log in as user vagrant (password vagrant) -+ In a new terminal, execute these commands: - -If you are testing on a Release VM image, first get a copy of the p4c -source code using the following command. This is unnecessary with a -Development VM image, as there is already a `p4c` directory with the -version of source code used to create that image already included in -the home directory of the `vagrant` user account: - -```bash -# for Release VM image only -git clone --recursive https://github.com/p4lang/p4c -``` - -The following steps are common for both Release and Development VM -images: - -```bash -# Compile p4c again from source, since the clean.sh step reduced disk -# space by deleting the p4c/build directory. -git clone https://github.com/jafingerhut/p4-guide -cd p4c -~/p4-guide/bin/build-p4c.sh - -# Run the p4c tests -cd build -make -j2 check |& tee make-check-out.txt -``` - -As of 2024-08-01, the p4c compiler passes all of its included tests, -except for some of the p4testgen tests (if those are enabled). - - -## Send ping packets in the solution to `basic` exercise of `p4lang/tutorials` repository - -With the version of the [tutorials](https://github.com/p4lang/tutorials) repository -that comes pre-installed in the `p4` user account of this VM, the -following tests pass. - -First log in as the user `p4` (password `p4`) and open a terminal -window. -```bash -$ cd tutorials/exercises/basic -$ cp solution/basic.p4 basic.p4 -$ make run -``` - -If at the end of many lines of logging output you see a prompt -`mininet>`, you can try entering the command `h1 ping h2` to ping from -virtual host `h1` in the exercise to `h2`, and it should report a -successful ping every second. It will not stop on its own. You can -type Control-C to stop it and return to the `mininet>` prompt, and you -can type Control-D to exit from mininet and get back to the original -shell prompt. To ensure that any processes started by the above steps -are terminated, you can run this command: -```bash -$ make stop -``` - - -# Creating a single file image of the VM - -These notes are primarily here as a reminder for people creating VM -images for distribution. If you downloaded a VM image, these steps -were already performed, and there is no reason you need to perform -them again. - -For the particular case of creating the VM named: - -+ 'P4 Tutorial Development 2024-08-01' -+ created on August 1, 2024 - -here were the host OS details, in case it turns out that matters to -the finished VM image for some reason: - -+ Windows 10 Enterprise -+ VirtualBox 6.1.30 r148432 -+ Vagrant 2.2.18 - -In the VirtualBox GUI interface: - -+ Choose menu item File -> Export Appliance ... -+ Select the VM named 'P4 Tutorial Development 2024-08-01' and click - Continue button - -+ Format - + I used: Open Virtualization Format 1.0 - + Other available options were: - + Open Virtualization Format 0.9 - + Open Virtualization Format 2.0 -+ Target file - + I used: /Users/andy/Documents/P4 Tutorials Development 2024-08-01.ova -+ Mac Address Policy - + I used: Include only NAT network adapter MAC addresses - + Other available options were: - + Include all network adapter MAC addresses - + Strip all network adapter MAC addresses -+ Additionally - + Write Manifest file: checked - + Include ISO image files: unchecked - -Clicked "Continue" button. - -Virtual system settings: - -+ Name: P4 Tutorial 2024-08-01 -+ Product: I left this blank -+ Product-URL: I left this blank -+ Vendor: P4.org - P4 Language Consortium -+ Vendor-URL: https://p4.org -+ Version: 2024-08-01 -+ Description: - -``` -Open source P4 development tools built from latest source code as of 2024-Aug-01 and packaged into an Ubuntu 20.04 Desktop Linux VM for the AMD64 architecture. -``` - -+ License - -``` -Open source code available hosted at https://github.com/p4lang is released under the Apache 2.0 license. Libraries it depends upon, such as Protobuf, Thrift, gRPC, Ubuntu Linux, etc. are released under their own licenses. -``` - -Clicked "Export" button. +*Note* that creating a development VM can take several hours, +depending upon the speed of your computer and Internet connection. diff --git a/vm-ubuntu-20.04/create-vm-image-for-others.md b/vm-ubuntu-20.04/create-vm-image-for-others.md new file mode 100644 index 00000000..6f050d1f --- /dev/null +++ b/vm-ubuntu-20.04/create-vm-image-for-others.md @@ -0,0 +1,210 @@ +# Creating a VM image for distribution to others + +If you want to run `p4c` tests, see the next section on how to do so, +_before_ running the commands in this section, since the commands in +this section will delete files that take one to several hours to +create while compiling p4c, needed for running p4c tests. + +Below are steps that were performed _after_ the commands in the +README.md file were run on the host OS, before creating the VM images. +Some of these steps could probably be automated with programs, and +changes to the `vagrant` scripts that can do so are welcome. I +performed these steps manually to create a VM image, simply to avoid +the experimentation and time required to automate them. I typically +only create new VM images once per month. + ++ Log in as user p4 (password p4) ++ Click "Upgrade" in the pop-up window asking if you want to upgrade + the system, if asked. This will download the latest Linux kernel + version released for Ubuntu 20.04, and other updated packages. ++ Reboot the system. ++ This is optional, but if you want to save a little disk space, use + + ``` + sudo apt purge + ``` + + to remove older version of Linux + kernel, if the upgrade installed a newer one. ++ Clean the local repository of retrieved package files to free up disk space + + ``` + sudo apt clean + ``` + ++ Log in as user p4 (password p4) ++ Start menu -> Preferences -> LXQt settings -> Monitor settings + + Change resolution from initial 800x600 to 1024x768. Apply the changes. + + Close monitor settings window + + *Note*: For some reason I do not know, these settings seem to be + undone, even if I use the "Save" button. They are temporarily in + effect if I shut down the system and log back in, but then in a few + seconds it switches back to 800x600. Strange. ++ Start menu -> Preferences -> LXQt settings -> Desktop + + In "Wallpaper mode" popup menu, choose "Center on the screen". + + Click Apply button + + Close "Desktop preferences" window ++ Several of the icons on the desktop have an exclamation mark on + them. If you try double-clicking those icons, it pops up a window + saying "This file 'Wireshark' seems to be a desktop entry. What do + you want to do with it?" with buttons for "Open", "Execute", and + "Cancel". Clicking "Execute" executes the associated command. + If you do a mouse middle click on one of these desktop icons, a + popup menu appears where the second-to-bottom choice is "Trust this + executable". Selecting that causes the exclamation mark to go away, + and future double-clicks of the icon execute the program without + first popping up a window to choose between Open/Execute/Cancel. I + did that for each of these desktop icons: + + Terminal + + Wireshark ++ Log off + ++ Log in as user vagrant (password vagrant) ++ Change monitor settings and wallpaper mode as described above for + user p4. ++ Open a terminal. + + Run the command + + ``` + ./clean.sh + ``` + which removes about 6 to 7 GBytes of + files created while building the projects. ++ Log off + + +# Notes on test results for the VM + +I have run the tests below on every VM image I release, before +releasing it. You need not run them again, unless you are curious how +to do so. + + +## p4c testing results + +Steps to run the p4c tests: + ++ Log in as user vagrant (password vagrant) ++ In a new terminal, execute these commands: + +If you are testing on a Release VM image, first get a copy of the p4c +source code using the following command. This is unnecessary with a +Development VM image, as there is already a `p4c` directory with the +version of source code used to create that image already included in +the home directory of the `vagrant` user account: + +```bash +# for Release VM image only +git clone --recursive https://github.com/p4lang/p4c +``` + +The following steps are common for both Release and Development VM +images: + +```bash +# Compile p4c again from source, since the clean.sh step reduced disk +# space by deleting the p4c/build directory. +git clone https://github.com/jafingerhut/p4-guide +cd p4c +~/p4-guide/bin/build-p4c.sh + +# Run the p4c tests +cd build +make -j2 check |& tee make-check-out.txt +``` + +As of 2024-08-01, the p4c compiler passes all of its included tests, +except for some of the p4testgen tests (if those are enabled). + + +## Send ping packets in the solution to `basic` exercise of `p4lang/tutorials` repository + +With the version of the [tutorials](https://github.com/p4lang/tutorials) repository +that comes pre-installed in the `p4` user account of this VM, the +following tests pass. + +First log in as the user `p4` (password `p4`) and open a terminal +window. +```bash +$ cd tutorials/exercises/basic +$ cp solution/basic.p4 basic.p4 +$ make run +``` + +If at the end of many lines of logging output you see a prompt +`mininet>`, you can try entering the command `h1 ping h2` to ping from +virtual host `h1` in the exercise to `h2`, and it should report a +successful ping every second. It will not stop on its own. You can +type Control-C to stop it and return to the `mininet>` prompt, and you +can type Control-D to exit from mininet and get back to the original +shell prompt. To ensure that any processes started by the above steps +are terminated, you can run this command: +```bash +$ make stop +``` + + +# Creating a single file image of the VM + +These notes are primarily here as a reminder for people creating VM +images for distribution. If you downloaded a VM image, these steps +were already performed, and there is no reason you need to perform +them again. + +For the particular case of creating the VM named: + ++ 'P4 Tutorial Development 2024-08-01' ++ created on August 1, 2024 + +here were the host OS details, in case it turns out that matters to +the finished VM image for some reason: + ++ Windows 10 Enterprise ++ VirtualBox 6.1.30 r148432 ++ Vagrant 2.2.18 + +In the VirtualBox GUI interface: + ++ Choose menu item File -> Export Appliance ... ++ Select the VM named 'P4 Tutorial Development 2024-08-01' and click + Continue button + ++ Format + + I used: Open Virtualization Format 1.0 + + Other available options were: + + Open Virtualization Format 0.9 + + Open Virtualization Format 2.0 ++ Target file + + I used: /Users/andy/Documents/P4 Tutorials Development 2024-08-01.ova ++ Mac Address Policy + + I used: Include only NAT network adapter MAC addresses + + Other available options were: + + Include all network adapter MAC addresses + + Strip all network adapter MAC addresses ++ Additionally + + Write Manifest file: checked + + Include ISO image files: unchecked + +Clicked "Continue" button. + +Virtual system settings: + ++ Name: P4 Tutorial 2024-08-01 ++ Product: I left this blank ++ Product-URL: I left this blank ++ Vendor: P4.org - P4 Language Consortium ++ Vendor-URL: https://p4.org ++ Version: 2024-08-01 ++ Description: + +``` +Open source P4 development tools built from latest source code as of 2024-Aug-01 and packaged into an Ubuntu 20.04 Desktop Linux VM for the AMD64 architecture. +``` + ++ License + +``` +Open source code available hosted at https://github.com/p4lang is released under the Apache 2.0 license. Libraries it depends upon, such as Protobuf, Thrift, gRPC, Ubuntu Linux, etc. are released under their own licenses. +``` + +Clicked "Export" button. diff --git a/vm-ubuntu-24.04/README.md b/vm-ubuntu-24.04/README.md index 31466a58..9a2fb11c 100644 --- a/vm-ubuntu-24.04/README.md +++ b/vm-ubuntu-24.04/README.md @@ -1,10 +1,7 @@ # Introduction -This directory is still new and a bit experimental at this point. -Feel free to try it out and report problems if you find any. - -Known issues that anyone who knows how to fix is welcome to suggest -improvements: +Known minor issues that anyone who knows how to fix is welcome to +suggest improvements: + The desktop icon for the Wireshark application does not seem to exist, at least not under the name that worked for Ubuntu 20.04, so @@ -15,7 +12,8 @@ improvements: + Below are the steps to create a brand new VM using Vagrant: + Install [Vagrant](https://developer.hashicorp.com/vagrant/docs/installation) on your system if it's not already installed. - + Navigate to the directory where you want to create the new VM. + + In a shell/terminal window, change to this `vm-ubuntu-24.04` + directory inside the `tutorials` directory. + Run the below command in the terminal. ```bash @@ -30,228 +28,3 @@ improvements: *Note* that creating a development VM can take several hours, depending upon the speed of your computer and Internet connection. - - -# Creating a VM image for distribution to others - -If you are creating the VM for your own use, there is no need to read -further below. All later instructions are for those who wish to -create a VM image for others to download and use. - -Some of these steps could probably be automated with programs, and -changes to the `vagrant` scripts that can do so are welcome. I -perform these steps manually to create a VM image, simply to avoid the -experimentation and time required to automate them. I typically only -create new VM images once per month. - -+ Log in as user p4 (password p4) -+ Upgrade Ubuntu packages if newer ones are available: - - ```bash - sudo apt update - sudo apt upgrade - ``` - -+ Reboot the system. -+ This is optional, but if you want to save a little disk space, use - - ```bash - sudo apt purge - ``` - - to remove older version of Linux - kernel, if the upgrade installed a newer one. -+ Clean the local repository of retrieved package files to free up disk space - - ```bash - sudo apt clean - ``` - -+ Log in as user p4 (password p4) -+ Start menu -> Preferences -> LXQt settings -> Monitor settings - + Change resolution from initial 800x600 to 1024x768. Apply the changes. - + Close monitor settings window - + *Note*: For some reason I do not know, these settings seem to be - undone, even if I use the "Save" button. They are temporarily in - effect if I shut down the system and log back in, but then in a few - seconds it switches back to 800x600. Strange. -+ Start menu -> Preferences -> LXQt settings -> Desktop - + Click "Background" tab - + To the right of "Wallpaper image file" name, click "Browse" - button. Find and choose "lxqt-default-wallpaper.png" from the - list and click "Open". - + In "Wallpaper mode" popup menu, choose "Center on the screen". - + Click Apply button - + Close "Desktop preferences" window -+ Start menu -> Preferences -> LXQt settings -> Appearance - + Click "Icons Theme" in left column - + Click "Ubuntu-Mono-Light ..." in right column. - + Click "Apply" button, then "Close" button. -+ Several of the icons on the desktop have an exclamation mark on - them. If you try double-clicking those icons, it pops up a window - saying "This file 'Wireshark' seems to be a desktop entry. What do - you want to do with it?" with buttons for "Open", "Execute", and - "Cancel". Clicking "Execute" executes the associated command. - If you do a mouse middle click on one of these desktop icons, a - popup menu appears where the second-to-bottom choice is "Trust this - executable". Selecting that causes the exclamation mark to go away, - and future double-clicks of the icon execute the program without - first popping up a window to choose between Open/Execute/Cancel. I - did that for each of these desktop icons: - + Terminal - + Wireshark -+ Log off - -+ Log in as user vagrant (password vagrant) -+ Change monitor settings and wallpaper mode as described above for - user p4. -+ Open a terminal. - + Run the command - - ```bash - ./clean.sh - ``` - which removes about 6 to 7 GBytes of - files created while building the projects. -+ Log off - - -# Notes on test results for the VM - -I have run the tests below on every VM image I release, before -releasing it. You need not run them again, unless you are curious how -to do so. - - -## p4c testing results - -Steps to run the p4c tests: - -+ Log in as user vagrant (password vagrant) -+ In a new terminal, execute these commands: - -If you are testing on a Release VM image, first get a copy of the p4c -source code using the following command. This is unnecessary with a -Development VM image, as there is already a `p4c` directory with the -version of source code used to create that image already included in -the home directory of the `vagrant` user account: - -```bash -# for Release VM image only -git clone --recursive https://github.com/p4lang/p4c -``` - -The following steps are common for both Release and Development VM -images: - -```bash -# Compile p4c again from source, since the clean.sh step reduced disk -# space by deleting the p4c/build directory. -git clone https://github.com/jafingerhut/p4-guide -cd p4c -~/p4-guide/bin/build-p4c.sh - -# Run the p4c tests -cd build -make -j2 check |& tee out1.txt - -# The above fails about 500 tests that require root. Re-run those tests -# as root using the next command. -sudo PATH=${PATH} VIRTUAL_ENV=${VIRTUAL_ENV} ${P4GUIDE_SUDO_OPTS} make -j2 recheck |& tee out2.txt -``` - -As of 2024-08-01, the p4c compiler passes all but about 15 of its -included tests when built using the steps above. - - -## Send ping packets in the solution to `basic` exercise of `p4lang/tutorials` repository - -With the version of the [tutorials](https://github.com/p4lang/tutorials) repository -that comes pre-installed in the `p4` user account of this VM, the -following tests pass. - -First log in as the user `p4` (password `p4`) and open a terminal -window. -```bash -$ cd tutorials/exercises/basic -$ cp solution/basic.p4 basic.p4 -$ make run -``` - -If at the end of many lines of logging output you see a prompt -`mininet>`, you can try entering the command `h1 ping h2` to ping from -virtual host `h1` in the exercise to `h2`, and it should report a -successful ping every second. It will not stop on its own. You can -type Control-C to stop it and return to the `mininet>` prompt, and you -can type Control-D to exit from mininet and get back to the original -shell prompt. To ensure that any processes started by the above steps -are terminated, you can run this command: -```bash -$ make stop -``` - - -# Creating a single file image of the VM - -These notes are primarily here as a reminder for people creating VM -images for distribution. If you downloaded a VM image, these steps -were already performed, and there is no reason you need to perform -them again. - -For the particular case of creating the VM named: - -+ 'P4 Tutorial Development 2024-08-01' -+ created on August 1, 2024 - -here were the host OS details, in case it turns out that matters to -the finished VM image for some reason: - -+ Windows 10 Enterprise -+ VirtualBox 6.1.30 r148432 -+ Vagrant 2.2.18 - -In the VirtualBox GUI interface: - -+ Choose menu item File -> Export Appliance ... -+ Select the VM named 'P4 Tutorial Development 2024-08-01' and click - Continue button - -+ Format - + I used: Open Virtualization Format 1.0 - + Other available options were: - + Open Virtualization Format 0.9 - + Open Virtualization Format 2.0 -+ Target file - + I used: /Users/andy/Documents/P4 Tutorials Development 2024-08-01.ova -+ Mac Address Policy - + I used: Include only NAT network adapter MAC addresses - + Other available options were: - + Include all network adapter MAC addresses - + Strip all network adapter MAC addresses -+ Additionally - + Write Manifest file: checked - + Include ISO image files: unchecked - -Clicked "Continue" button. - -Virtual system settings: - -+ Name: P4 Tutorial 2024-08-01 -+ Product: I left this blank -+ Product-URL: I left this blank -+ Vendor: P4.org - P4 Language Consortium -+ Vendor-URL: https://p4.org -+ Version: 2024-08-01 -+ Description: - -``` -Open source P4 development tools built from latest source code as of 2024-Aug-01 and packaged into an Ubuntu 24.04 Desktop Linux VM for the AMD64 architecture. -``` - -+ License - -``` -Open source code available hosted at https://github.com/p4lang is released under the Apache 2.0 license. Libraries it depends upon, such as Protobuf, Thrift, gRPC, Ubuntu Linux, etc. are released under their own licenses. -``` - -Clicked "Export" button. diff --git a/vm-ubuntu-24.04/create-vm-image-for-others.md b/vm-ubuntu-24.04/create-vm-image-for-others.md new file mode 100644 index 00000000..2e38ca84 --- /dev/null +++ b/vm-ubuntu-24.04/create-vm-image-for-others.md @@ -0,0 +1,214 @@ +# Creating a VM image for distribution to others + +If you want to run `p4c` tests, see the next section on how to do so, +_before_ running the commands in this section, since the commands in +this section will delete files that take one to several hours to +create while compiling p4c, needed for running p4c tests. + +If you are creating the VM for your own use, there is no need to read +further below. All later instructions are for those who wish to +create a VM image for others to download and use. + +Some of these steps could probably be automated with programs, and +changes to the `vagrant` scripts that can do so are welcome. I +perform these steps manually to create a VM image, simply to avoid the +experimentation and time required to automate them. I typically only +create new VM images once per month. + ++ Log in as user p4 (password p4) ++ Upgrade Ubuntu packages if newer ones are available: + + ```bash + sudo apt update + sudo apt upgrade + ``` + ++ Reboot the system. ++ This is optional, but if you want to save a little disk space, use + + ```bash + sudo apt purge + ``` + + to remove older version of Linux + kernel, if the upgrade installed a newer one. ++ Clean the local repository of retrieved package files to free up disk space + + ```bash + sudo apt clean + ``` + ++ Log in as user p4 (password p4) ++ Start menu -> Preferences -> LXQt settings -> Monitor settings + + Change resolution from initial 800x600 to 1024x768. Apply the changes. + + Close monitor settings window + + *Note*: For some reason I do not know, these settings seem to be + undone, even if I use the "Save" button. They are temporarily in + effect if I shut down the system and log back in, but then in a few + seconds it switches back to 800x600. Strange. ++ Start menu -> Preferences -> LXQt settings -> Desktop + + Click "Background" tab + + To the right of "Wallpaper image file" name, click "Browse" + button. Find and choose "lxqt-default-wallpaper.png" from the + list and click "Open". + + In "Wallpaper mode" popup menu, choose "Center on the screen". + + Click Apply button + + Close "Desktop preferences" window ++ Start menu -> Preferences -> LXQt settings -> Appearance + + Click "Icons Theme" in left column + + Click "Ubuntu-Mono-Light ..." in right column. + + Click "Apply" button, then "Close" button. ++ Several of the icons on the desktop have an exclamation mark on + them. If you try double-clicking those icons, it pops up a window + saying "This file 'Wireshark' seems to be a desktop entry. What do + you want to do with it?" with buttons for "Open", "Execute", and + "Cancel". Clicking "Execute" executes the associated command. + If you do a mouse middle click on one of these desktop icons, a + popup menu appears where the second-to-bottom choice is "Trust this + executable". Selecting that causes the exclamation mark to go away, + and future double-clicks of the icon execute the program without + first popping up a window to choose between Open/Execute/Cancel. I + did that for each of these desktop icons: + + Terminal + + Wireshark ++ Log off + ++ Log in as user vagrant (password vagrant) ++ Change monitor settings and wallpaper mode as described above for + user p4. ++ Open a terminal. + + Run the command + + ```bash + ./clean.sh + ``` + which removes about 6 to 7 GBytes of + files created while building the projects. ++ Log off + + +# Notes on test results for the VM + +I have run the tests below on every VM image I release, before +releasing it. You need not run them again, unless you are curious how +to do so. + + +## p4c testing results + +Steps to run the p4c tests: + ++ Log in as user vagrant (password vagrant) ++ In a new terminal, execute these commands: + +```bash +# Compile p4c again from source, since the clean.sh step reduced disk +# space by deleting the p4c/build directory. +git clone https://github.com/jafingerhut/p4-guide +cd p4c +~/p4-guide/bin/build-p4c.sh + +# Run the p4c tests +cd build +make -j2 check |& tee out1.txt + +# The above fails about 500 tests that require root. Re-run those tests +# as root using the next command. +sudo PATH=${PATH} VIRTUAL_ENV=${VIRTUAL_ENV} ${P4GUIDE_SUDO_OPTS} make -j2 recheck |& tee out2.txt +``` + +As of 2024-08-01, the p4c compiler passes all but about 15 of its +included tests when built using the steps above. + + +## Send ping packets in the solution to `basic` exercise of `p4lang/tutorials` repository + +With the version of the [tutorials](https://github.com/p4lang/tutorials) repository +that comes pre-installed in the `p4` user account of this VM, the +following tests pass. + +First log in as the user `p4` (password `p4`) and open a terminal +window. +```bash +$ cd tutorials/exercises/basic +$ cp solution/basic.p4 basic.p4 +$ make run +``` + +If at the end of many lines of logging output you see a prompt +`mininet>`, you can try entering the command `h1 ping h2` to ping from +virtual host `h1` in the exercise to `h2`, and it should report a +successful ping every second. It will not stop on its own. You can +type Control-C to stop it and return to the `mininet>` prompt, and you +can type Control-D to exit from mininet and get back to the original +shell prompt. To ensure that any processes started by the above steps +are terminated, you can run this command: +```bash +$ make stop +``` + + +# Creating a single file image of the VM + +These notes are primarily here as a reminder for people creating VM +images for distribution. If you downloaded a VM image, these steps +were already performed, and there is no reason you need to perform +them again. + +For the particular case of creating the VM named: + ++ 'P4 Tutorial Development 2024-08-01' ++ created on August 1, 2024 + +here were the host OS details, in case it turns out that matters to +the finished VM image for some reason: + ++ Windows 10 Enterprise ++ VirtualBox 6.1.30 r148432 ++ Vagrant 2.2.18 + +In the VirtualBox GUI interface: + ++ Choose menu item File -> Export Appliance ... ++ Select the VM named 'P4 Tutorial Development 2024-08-01' and click + Continue button + ++ Format + + I used: Open Virtualization Format 1.0 + + Other available options were: + + Open Virtualization Format 0.9 + + Open Virtualization Format 2.0 ++ Target file + + I used: /Users/andy/Documents/P4 Tutorials Development 2024-08-01.ova ++ Mac Address Policy + + I used: Include only NAT network adapter MAC addresses + + Other available options were: + + Include all network adapter MAC addresses + + Strip all network adapter MAC addresses ++ Additionally + + Write Manifest file: checked + + Include ISO image files: unchecked + +Clicked "Continue" button. + +Virtual system settings: + ++ Name: P4 Tutorial 2024-08-01 ++ Product: I left this blank ++ Product-URL: I left this blank ++ Vendor: P4.org - P4 Language Consortium ++ Vendor-URL: https://p4.org ++ Version: 2024-08-01 ++ Description: + +``` +Open source P4 development tools built from latest source code as of 2024-Aug-01 and packaged into an Ubuntu 24.04 Desktop Linux VM for the AMD64 architecture. +``` + ++ License + +``` +Open source code available hosted at https://github.com/p4lang is released under the Apache 2.0 license. Libraries it depends upon, such as Protobuf, Thrift, gRPC, Ubuntu Linux, etc. are released under their own licenses. +``` + +Clicked "Export" button. From 322494984d045d085562715f3e7fb3fece7ff1d5 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Thu, 30 Jan 2025 21:17:19 +0000 Subject: [PATCH 2/6] Add 2025-Jan-30 versions of source code for install.sh Signed-off-by: Andy Fingerhut --- vm-ubuntu-24.04/install.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vm-ubuntu-24.04/install.sh b/vm-ubuntu-24.04/install.sh index ccc7728f..a7eae56b 100755 --- a/vm-ubuntu-24.04/install.sh +++ b/vm-ubuntu-24.04/install.sh @@ -27,11 +27,12 @@ print_usage() { 1>&2 echo "" 1>&2 echo "Dates supported:" 1>&2 echo " 2025-Jan-01" + 1>&2 echo " 2025-Jan-30" } if [ $# -eq 0 ] then - VERSION="2025-Jan-01" + VERSION="2025-Jan-30" echo "No version specified. Defaulting to ${VERSION}" elif [ $# -eq 1 ] then @@ -49,6 +50,13 @@ case ${VERSION} in export INSTALL_P4C_SOURCE_VERSION="1dc0afae2207f4bb9f5ab45f105ed569cc1ac89b" export INSTALL_PTF_SOURCE_VERSION="c554f83685186be4cfa9387eb5d6d700d2bbd7c0" ;; + 2025-Jan-30) + # 2025-Jan-01 versions: + export INSTALL_BEHAVIORAL_MODEL_SOURCE_VERSION="892c42198082d3252f4c6facc7363c02ca1d71d2" + export INSTALL_PI_SOURCE_VERSION="2bb40f7ab800b91b26f3aed174bbbfc739a37ffa" + export INSTALL_P4C_SOURCE_VERSION="2776b1948529bc7e8ccfb2f6ea2a9c1ab1f68796" + export INSTALL_PTF_SOURCE_VERSION="c554f83685186be4cfa9387eb5d6d700d2bbd7c0" + ;; latest) echo "Using the latest version of all p4lang repository source code." ;; From 12bc6bf9ca5ac4bb7960eda2b2c2ce066679000a Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Thu, 8 Oct 2026 00:42:35 -0400 Subject: [PATCH 3/6] Use black to update formatting and fix many warnings found by flake8 Signed-off-by: Andy Fingerhut --- exercises/basic/ptf/basic_fwd.py | 206 +++++--- exercises/basic/receive.py | 40 +- exercises/basic/send.py | 21 +- exercises/basic_tunnel/myTunnel_header.py | 10 +- exercises/basic_tunnel/ptf/basic_tunnel.py | 66 ++- exercises/basic_tunnel/receive.py | 20 +- exercises/basic_tunnel/send.py | 49 +- exercises/calc/calc.py | 81 +-- exercises/ecn/receive.py | 18 +- exercises/ecn/send.py | 27 +- exercises/flowcache/mycontroller.py | 510 ++++++++++-------- exercises/flowcache/solution/mycontroller.py | 519 +++++++++++-------- exercises/link_monitor/probe_hdrs.py | 25 +- exercises/link_monitor/receive.py | 23 +- exercises/link_monitor/send.py | 32 +- exercises/load_balance/receive.py | 40 +- exercises/load_balance/send.py | 21 +- exercises/mri/receive.py | 46 +- exercises/mri/send.py | 65 +-- exercises/p4runtime/mycontroller.py | 178 ++++--- exercises/p4runtime/solution/mycontroller.py | 211 +++++--- exercises/qos/receive.py | 4 +- exercises/qos/send.py | 27 +- exercises/source_routing/receive.py | 65 ++- exercises/source_routing/send.py | 43 +- utils/mininet/appcontroller.py | 108 ++-- utils/mininet/apptopo.py | 78 ++- utils/mininet/multi_switch_mininet.py | 264 ++++++---- utils/mininet/p4_mininet.py | 86 +-- utils/mininet/shortest_path.py | 55 +- utils/mininet/single_switch_mininet.py | 146 ++++-- utils/netstat.py | 4 +- utils/p4_mininet.py | 81 +-- utils/p4apprunner.py | 303 ++++++----- utils/p4runtime_lib/bmv2.py | 2 +- utils/p4runtime_lib/convert.py | 110 ++-- utils/p4runtime_lib/error_utils.py | 38 +- utils/p4runtime_lib/helper.py | 98 ++-- utils/p4runtime_lib/simple_controller.py | 209 +++++--- utils/p4runtime_lib/switch.py | 49 +- utils/p4runtime_switch.py | 48 +- utils/run_exercise.py | 429 ++++++++------- vm-ubuntu-20.04/py3localpath.py | 6 +- 43 files changed, 2658 insertions(+), 1803 deletions(-) diff --git a/exercises/basic/ptf/basic_fwd.py b/exercises/basic/ptf/basic_fwd.py index 54e52a6a..2e129c22 100644 --- a/exercises/basic/ptf/basic_fwd.py +++ b/exercises/basic/ptf/basic_fwd.py @@ -14,18 +14,18 @@ # Import p4runtime_lib from the tutorials repo utils directory sys.path.append( - os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../../utils/')) + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../utils/") +) import p4runtime_lib.bmv2 import p4runtime_lib.helper from p4runtime_lib.switch import ShutdownAllSwitchConnections - # Configure logging logger = logging.getLogger(None) ch = logging.StreamHandler() ch.setLevel(logging.INFO) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s" + " - %(message)s") ch.setFormatter(formatter) logger.addHandler(ch) @@ -40,7 +40,7 @@ def setUp(self): # Get test parameters grpc_addr = tu.test_param_get("grpcaddr") if grpc_addr is None: - grpc_addr = 'localhost:9559' + grpc_addr = "localhost:9559" p4info_txt_fname = tu.test_param_get("p4info") p4prog_binary_fname = tu.test_param_get("config") @@ -49,10 +49,11 @@ def setUp(self): # Connect to the switch via gRPC self.sw = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s1', + name="s1", address=grpc_addr, device_id=0, - proto_dump_file='logs/s1-p4runtime-requests.txt') + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) # Establish as master controller self.sw.MasterArbitrationUpdate() @@ -60,134 +61,162 @@ def setUp(self): # Load the P4 program onto the switch self.sw.SetForwardingPipelineConfig( p4info=self.p4info_helper.p4info, - bmv2_json_file_path=p4prog_binary_fname) + bmv2_json_file_path=p4prog_binary_fname + ) def tearDown(self): logging.debug("BasicFwdTest.tearDown()") ShutdownAllSwitchConnections() - -###################################################################### -# Helper function to add entries to ipv4_lpm table -###################################################################### + ###################################################################### + # Helper function to add entries to ipv4_lpm table + ###################################################################### def add_ipv4_lpm_entry(self, ipv4_addr_str, prefix_len, dst_mac_str, port): table_entry = self.p4info_helper.buildTableEntry( - table_name='MyIngress.ipv4_lpm', - match_fields={ - 'hdr.ipv4.dstAddr': (ipv4_addr_str, prefix_len) - }, - action_name='MyIngress.ipv4_forward', - action_params={ - 'dstAddr': dst_mac_str, - 'port': port - }) + table_name="MyIngress.ipv4_lpm", + match_fields={"hdr.ipv4.dstAddr": (ipv4_addr_str, prefix_len)}, + action_name="MyIngress.ipv4_forward", + action_params={"dstAddr": dst_mac_str, "port": port}, + ) self.sw.WriteTableEntry(table_entry) class DropTest(BasicFwdTest): """Test that packets are dropped when no table entries are installed.""" + def runTest(self): - in_dmac = 'ee:30:ca:9d:1e:00' - in_smac = 'ee:cd:00:7e:70:00' - ip_dst = '10.0.1.1' + in_dmac = "ee:30:ca:9d:1e:00" + in_smac = "ee:cd:00:7e:70:00" + ip_dst = "10.0.1.1" ig_port = 1 - pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac, - ip_dst=ip_dst, ip_ttl=64) + pkt = tu.simple_tcp_packet( + eth_src=in_smac, eth_dst=in_dmac, ip_dst=ip_dst, ip_ttl=64 + ) tu.send_packet(self, ig_port, pkt) tu.verify_no_other_packets(self) class FwdTest(BasicFwdTest): """Test that a packet is forwarded correctly with one table entry.""" + def runTest(self): - in_dmac = 'ee:30:ca:9d:1e:00' - in_smac = 'ee:cd:00:7e:70:00' - ip_dst = '10.0.1.1' + in_dmac = "ee:30:ca:9d:1e:00" + in_smac = "ee:cd:00:7e:70:00" + ip_dst = "10.0.1.1" ig_port = 1 eg_port = 2 - out_dmac = '08:00:00:00:02:22' + out_dmac = "08:00:00:00:02:22" # Add a forwarding entry self.add_ipv4_lpm_entry(ip_dst, 32, out_dmac, eg_port) # Send packet - pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac, - ip_dst=ip_dst, ip_ttl=64) + pkt = tu.simple_tcp_packet( + eth_src=in_smac, eth_dst=in_dmac, ip_dst=ip_dst, ip_ttl=64 + ) # Expected: srcAddr = old dstAddr, dstAddr = new MAC, TTL decremented - exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=out_dmac, - ip_dst=ip_dst, ip_ttl=63) + exp_pkt = tu.simple_tcp_packet( + eth_src=in_dmac, eth_dst=out_dmac, ip_dst=ip_dst, ip_ttl=63 + ) tu.send_packet(self, ig_port, pkt) tu.verify_packets(self, exp_pkt, [eg_port]) class MultiEntryTest(BasicFwdTest): """Test multiple LPM entries route to different ports correctly.""" + def runTest(self): - in_dmac = 'ee:30:ca:9d:1e:00' - in_smac = 'ee:cd:00:7e:70:00' + in_dmac = "ee:30:ca:9d:1e:00" + in_smac = "ee:cd:00:7e:70:00" ig_port = 0 entries = [] - entries.append({'ip_dst': '10.0.1.1', - 'prefix_len': 32, - 'pkt_dst': '10.0.1.1', - 'eg_port': 1, - 'out_dmac': '08:00:00:00:01:11'}) - entries.append({'ip_dst': '10.0.2.0', - 'prefix_len': 24, - 'pkt_dst': '10.0.2.99', - 'eg_port': 2, - 'out_dmac': '08:00:00:00:02:22'}) - entries.append({'ip_dst': '10.0.3.0', - 'prefix_len': 24, - 'pkt_dst': '10.0.3.1', - 'eg_port': 3, - 'out_dmac': '08:00:00:00:03:33'}) + entries.append( + { + "ip_dst": "10.0.1.1", + "prefix_len": 32, + "pkt_dst": "10.0.1.1", + "eg_port": 1, + "out_dmac": "08:00:00:00:01:11", + } + ) + entries.append( + { + "ip_dst": "10.0.2.0", + "prefix_len": 24, + "pkt_dst": "10.0.2.99", + "eg_port": 2, + "out_dmac": "08:00:00:00:02:22", + } + ) + entries.append( + { + "ip_dst": "10.0.3.0", + "prefix_len": 24, + "pkt_dst": "10.0.3.1", + "eg_port": 3, + "out_dmac": "08:00:00:00:03:33", + } + ) # Add all entries for e in entries: - self.add_ipv4_lpm_entry(e['ip_dst'], e['prefix_len'], - e['out_dmac'], e['eg_port']) + self.add_ipv4_lpm_entry( + e["ip_dst"], e["prefix_len"], e["out_dmac"], e["eg_port"] + ) # Test each entry ttl_in = 64 for e in entries: - pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac, - ip_dst=e['pkt_dst'], ip_ttl=ttl_in) - exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=e['out_dmac'], - ip_dst=e['pkt_dst'], - ip_ttl=ttl_in - 1) + pkt = tu.simple_tcp_packet( + eth_src=in_smac, eth_dst=in_dmac, ip_dst=e["pkt_dst"], + ip_ttl=ttl_in + ) + exp_pkt = tu.simple_tcp_packet( + eth_src=in_dmac, + eth_dst=e["out_dmac"], + ip_dst=e["pkt_dst"], + ip_ttl=ttl_in - 1, + ) tu.send_packet(self, ig_port, pkt) - tu.verify_packets(self, exp_pkt, [e['eg_port']]) + tu.verify_packets(self, exp_pkt, [e["eg_port"]]) ttl_in -= 10 class LpmTiebreakerTest(BasicFwdTest): """Test that longest-prefix match wins for overlapping routes.""" + def runTest(self): - in_dmac = 'ee:30:ca:9d:1e:00' - in_smac = 'ee:cd:00:7e:70:00' + in_dmac = "ee:30:ca:9d:1e:00" + in_smac = "ee:cd:00:7e:70:00" ig_port = 0 - less_specific_out_dmac = '08:00:00:00:11:11' + less_specific_out_dmac = "08:00:00:00:11:11" less_specific_eg_port = 1 - more_specific_out_dmac = '08:00:00:00:22:22' + more_specific_out_dmac = "08:00:00:00:22:22" more_specific_eg_port = 2 # Two overlapping routes: /16 and /24. Packet should match /24. - self.add_ipv4_lpm_entry('10.0.0.0', 16, - less_specific_out_dmac, less_specific_eg_port) - self.add_ipv4_lpm_entry('10.0.1.0', 24, - more_specific_out_dmac, more_specific_eg_port) - - pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac, - ip_dst='10.0.1.99', ip_ttl=64) - exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=more_specific_out_dmac, - ip_dst='10.0.1.99', ip_ttl=63) + self.add_ipv4_lpm_entry( + "10.0.0.0", 16, less_specific_out_dmac, less_specific_eg_port + ) + self.add_ipv4_lpm_entry( + "10.0.1.0", 24, more_specific_out_dmac, more_specific_eg_port + ) + + pkt = tu.simple_tcp_packet( + eth_src=in_smac, eth_dst=in_dmac, ip_dst="10.0.1.99", ip_ttl=64 + ) + exp_pkt = tu.simple_tcp_packet( + eth_src=in_dmac, + eth_dst=more_specific_out_dmac, + ip_dst="10.0.1.99", + ip_ttl=63, + ) tu.send_packet(self, ig_port, pkt) tu.verify_packets(self, exp_pkt, [more_specific_eg_port]) @@ -195,21 +224,24 @@ def runTest(self): class TtlBoundaryTest(BasicFwdTest): """Test forwarding behavior when input IPv4 TTL is at boundary value 1.""" + def runTest(self): - in_dmac = 'ee:30:ca:9d:1e:00' - in_smac = 'ee:cd:00:7e:70:00' - ip_dst = '10.0.9.9' + in_dmac = "ee:30:ca:9d:1e:00" + in_smac = "ee:cd:00:7e:70:00" + ip_dst = "10.0.9.9" ig_port = 1 eg_port = 3 - out_dmac = '08:00:00:00:09:99' + out_dmac = "08:00:00:00:09:99" self.add_ipv4_lpm_entry(ip_dst, 32, out_dmac, eg_port) - pkt = tu.simple_tcp_packet(eth_src=in_smac, eth_dst=in_dmac, - ip_dst=ip_dst, ip_ttl=1) - exp_pkt = tu.simple_tcp_packet(eth_src=in_dmac, eth_dst=out_dmac, - ip_dst=ip_dst, ip_ttl=0) + pkt = tu.simple_tcp_packet( + eth_src=in_smac, eth_dst=in_dmac, ip_dst=ip_dst, ip_ttl=1 + ) + exp_pkt = tu.simple_tcp_packet( + eth_src=in_dmac, eth_dst=out_dmac, ip_dst=ip_dst, ip_ttl=0 + ) tu.send_packet(self, ig_port, pkt) tu.verify_packets(self, exp_pkt, [eg_port]) @@ -217,16 +249,18 @@ def runTest(self): class NonIpv4DropTest(BasicFwdTest): """Test non-IPv4 traffic bypasses IPv4 LPM forwarding logic.""" + def runTest(self): ig_port = 1 pkt = tu.simple_arp_packet( - eth_dst='ff:ff:ff:ff:ff:ff', - eth_src='00:de:ad:be:ef:01', + eth_dst="ff:ff:ff:ff:ff:ff", + eth_src="00:de:ad:be:ef:01", arp_op=1, - ip_snd='10.0.1.10', - ip_tgt='10.0.1.1', - hw_snd='00:de:ad:be:ef:01', - hw_tgt='00:00:00:00:00:00') + ip_snd="10.0.1.10", + ip_tgt="10.0.1.1", + hw_snd="00:de:ad:be:ef:01", + hw_tgt="00:00:00:00:00:00", + ) tu.send_packet(self, ig_port, pkt) tu.verify_packets(self, pkt, [0]) diff --git a/exercises/basic/receive.py b/exercises/basic/receive.py index 2e10e6ee..c202806a 100755 --- a/exercises/basic/receive.py +++ b/exercises/basic/receive.py @@ -14,50 +14,52 @@ IPOption, ShortField, get_if_list, - sniff + sniff, ) from scapy.layers.inet import _IPOption_HDR def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] + fields_desc = [ + _IPOption_HDR, + FieldLenField("length", None, fmt="B", length_of="swids", + adjust=lambda pkt, x: x + 4), + ShortField("count", 0), + FieldListField( + "swids", [], IntField("", 0), length_from=lambda pkt: pkt.count * 4 + ), + ] + + def handle_pkt(pkt): if TCP in pkt and pkt[TCP].dport == 1234: print("got a packet") pkt.show2() - # hexdump(pkt) + # hexdump(pkt) sys.stdout.flush() def main(): - ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i] + ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] print("sniffing on %s" % iface) sys.stdout.flush() - sniff(iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/basic/send.py b/exercises/basic/send.py index ee98ff47..e1a77a67 100755 --- a/exercises/basic/send.py +++ b/exercises/basic/send.py @@ -11,20 +11,20 @@ def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def main(): - if len(sys.argv)<3: + if len(sys.argv) < 3: print('pass 2 arguments: ""') exit(1) @@ -32,11 +32,16 @@ def main(): iface = get_if() print("sending on interface %s to %s" % (iface, str(addr))) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] + pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + pkt = ( + pkt + / IP(dst=addr) + / TCP(dport=1234, sport=random.randint(49152, 65535)) + / sys.argv[2] + ) pkt.show2() sendp(pkt, iface=iface, verbose=False) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/basic_tunnel/myTunnel_header.py b/exercises/basic_tunnel/myTunnel_header.py index 30fdedf9..e61bf677 100644 --- a/exercises/basic_tunnel/myTunnel_header.py +++ b/exercises/basic_tunnel/myTunnel_header.py @@ -3,21 +3,19 @@ # SPDX-License-Identifier: GPL-2.0-only -from scapy.all import * +from scapy.all import Packet, Ether, IP, ShortField, bind_layers TYPE_MYTUNNEL = 0x1212 TYPE_IPV4 = 0x0800 + class MyTunnel(Packet): name = "MyTunnel" - fields_desc = [ - ShortField("pid", 0), - ShortField("dst_id", 0) - ] + fields_desc = [ShortField("pid", 0), ShortField("dst_id", 0)] + def mysummary(self): return self.sprintf("pid=%pid%, dst_id=%dst_id%") bind_layers(Ether, MyTunnel, type=TYPE_MYTUNNEL) bind_layers(MyTunnel, IP, pid=TYPE_IPV4) - diff --git a/exercises/basic_tunnel/ptf/basic_tunnel.py b/exercises/basic_tunnel/ptf/basic_tunnel.py index adbe6ab8..05e871e8 100644 --- a/exercises/basic_tunnel/ptf/basic_tunnel.py +++ b/exercises/basic_tunnel/ptf/basic_tunnel.py @@ -35,7 +35,6 @@ class MyTunnel(Packet): import p4runtime_lib.helper from p4runtime_lib.switch import ShutdownAllSwitchConnections - # Configure Logging logger = logging.getLogger(None) handler = logging.StreamHandler() @@ -60,29 +59,31 @@ def setUp(self): # Create P4Info helper for building the table entries self.p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_txt_fname) - + # Connect to the switch via gRPC self.sw = p4runtime_lib.bmv2.Bmv2SwitchConnection( name="s1", address=grpc_addr, device_id=0, - proto_dump_file="logs/s1-p4runtime-requests.txt") + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) # Establish as master controller self.sw.MasterArbitrationUpdate() # Load the P4 Program onto the switch self.sw.SetForwardingPipelineConfig( - p4info=self.p4info_helper.p4info, bmv2_json_file_path=p4prog_binary_fname) + p4info=self.p4info_helper.p4info, + bmv2_json_file_path=p4prog_binary_fname + ) def tearDown(self): logging.debug("BasicTunnelTest.tearDown()") ShutdownAllSwitchConnections() - -###################################################################### -# Helper function to add entries to ipv4_lpm table -###################################################################### + ###################################################################### + # Helper function to add entries to ipv4_lpm table + ###################################################################### def add_ipv4_lpm_entry(self, ipv4_addr_str, prefix_len, dst_mac_str, port): table_entry = self.p4info_helper.buildTableEntry( @@ -104,7 +105,9 @@ def add_tunnel_entry(self, dst_id, port): class Ipv4DropOnMissTest(BasicTunnelTest): - """Verify that a plain IPv4 packet is dropped when no LPM table entry exists.""" + """Verify that a plain IPv4 packet is dropped when no LPM table + entry exists.""" + def runTest(self): pkt = tu.simple_tcp_packet( eth_src="ee:cd:00:7e:70:00", @@ -117,7 +120,9 @@ def runTest(self): class Ipv4ForwardTest(BasicTunnelTest): - """Verify that a plain IPv4 packet is forwarded correctly with one table entry.""" + """Verify that a plain IPv4 packet is forwarded correctly with one + table entry.""" + def runTest(self): in_dmac = "ee:30:ca:9d:1e:00" in_smac = "ee:cd:00:7e:70:00" @@ -138,10 +143,13 @@ def runTest(self): class TunnelForwardTest(BasicTunnelTest): - """Verify that a tunneled packet is forwarded correctly when a valid table entry exists.""" + """Verify that a tunneled packet is forwarded correctly when a + valid table entry exists.""" + def runTest(self): in_pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", + type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=2) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) @@ -153,10 +161,13 @@ def runTest(self): class TunnelDropOnMissTest(BasicTunnelTest): - """Verify that a tunneled packet is dropped when no matching table entry exists.""" + """Verify that a tunneled packet is dropped when no matching table + entry exists.""" + def runTest(self): in_pkt = ( - Ether(src="00:11:22:33:44:66", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:66", dst="ff:ff:ff:ff:ff:ff", + type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=77) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) @@ -168,6 +179,7 @@ def runTest(self): class TtlBoundaryTest(BasicTunnelTest): """Verify IPv4 TTL is decremented to 0 correctly when input TTL is 1.""" + def runTest(self): in_dmac = "ee:30:ca:9d:1e:00" in_smac = "ee:cd:00:7e:70:00" @@ -179,24 +191,25 @@ def runTest(self): self.add_ipv4_lpm_entry(ip_dst, 32, out_dmac, eg_port) pkt = tu.simple_tcp_packet( - eth_src=in_smac, eth_dst=in_dmac, - ip_dst=ip_dst, ip_ttl=1 + eth_src=in_smac, eth_dst=in_dmac, ip_dst=ip_dst, ip_ttl=1 ) exp_pkt = tu.simple_tcp_packet( - eth_src=in_dmac, eth_dst=out_dmac, - ip_dst=ip_dst, ip_ttl=0 + eth_src=in_dmac, eth_dst=out_dmac, ip_dst=ip_dst, ip_ttl=0 ) tu.send_packet(self, ig_port, pkt) tu.verify_packets(self, exp_pkt, [eg_port]) class TunnelUnknownProtoTest(BasicTunnelTest): - """Verify tunnel packet with non-IPv4 proto_id is still forwarded by dst_id.""" + """Verify tunnel packet with non-IPv4 proto_id is still forwarded + by dst_id.""" + def runTest(self): self.add_tunnel_entry(dst_id=5, port=2) pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", + type=TYPE_MYTUNNEL) / MyTunnel(proto_id=0x9999, dst_id=5) / "unknown-proto-payload" ) @@ -205,7 +218,9 @@ def runTest(self): class MixedTrafficTest(BasicTunnelTest): - """Verify IPv4 and tunnel traffic are handled independently correctly via separate tables.""" + """Verify IPv4 and tunnel traffic are handled independently + correctly via separate tables.""" + def runTest(self): in_dmac = "ee:30:ca:9d:1e:00" in_smac = "ee:cd:00:7e:70:00" @@ -220,19 +235,18 @@ def runTest(self): # test plain IPv4 which should hit ipv4_lpm table ipv4_pkt = tu.simple_tcp_packet( - eth_src=in_smac, eth_dst=in_dmac, - ip_dst=ip_dst, ip_ttl=64 + eth_src=in_smac, eth_dst=in_dmac, ip_dst=ip_dst, ip_ttl=64 ) exp_ipv4_pkt = tu.simple_tcp_packet( - eth_src=in_dmac, eth_dst=out_dmac, - ip_dst=ip_dst, ip_ttl=63 + eth_src=in_dmac, eth_dst=out_dmac, ip_dst=ip_dst, ip_ttl=63 ) tu.send_packet(self, 1, ipv4_pkt) tu.verify_packets(self, exp_ipv4_pkt, [ipv4_port]) # test tunnel packet which should hit myTunnel_exact table tunnel_pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", + type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=2) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) diff --git a/exercises/basic_tunnel/receive.py b/exercises/basic_tunnel/receive.py index 6a6cf210..942c89ed 100755 --- a/exercises/basic_tunnel/receive.py +++ b/exercises/basic_tunnel/receive.py @@ -11,33 +11,33 @@ def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def handle_pkt(pkt): if MyTunnel in pkt or (TCP in pkt and pkt[TCP].dport == 1234): print("got a packet") pkt.show2() -# hexdump(pkt) -# print "len(pkt) = ", len(pkt) + # hexdump(pkt) + # print "len(pkt) = ", len(pkt) sys.stdout.flush() def main(): - ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i] + ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] print("sniffing on %s" % iface) sys.stdout.flush() - sniff(iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/basic_tunnel/send.py b/exercises/basic_tunnel/send.py index e06bd955..3c00fd84 100755 --- a/exercises/basic_tunnel/send.py +++ b/exercises/basic_tunnel/send.py @@ -12,42 +12,61 @@ def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def main(): parser = argparse.ArgumentParser() - parser.add_argument('ip_addr', type=str, help="The destination IP address to use") - parser.add_argument('message', type=str, help="The message to include in packet") - parser.add_argument('--dst_id', type=int, default=None, help='The myTunnel dst_id to use, if unspecified then myTunnel header will not be included in packet') + parser.add_argument("ip_addr", type=str, + help="The destination IP address to use") + parser.add_argument("message", type=str, + help="The message to include in packet") + parser.add_argument( + "--dst_id", + type=int, + default=None, + help="""The myTunnel dst_id to use, if unspecified then + myTunnel header will not be included in packet""", + ) args = parser.parse_args() addr = socket.gethostbyname(args.ip_addr) dst_id = args.dst_id iface = get_if() - if (dst_id is not None): + if dst_id is not None: print("sending on interface {} to dst_id {}".format(iface, str(dst_id))) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt / MyTunnel(dst_id=dst_id) / IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / args.message + pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + pkt = ( + pkt + / MyTunnel(dst_id=dst_id) + / IP(dst=addr) + / TCP(dport=1234, sport=random.randint(49152, 65535)) + / args.message + ) else: print("sending on interface {} to IP addr {}".format(iface, str(addr))) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt / IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / args.message + pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + pkt = ( + pkt + / IP(dst=addr) + / TCP(dport=1234, sport=random.randint(49152, 65535)) + / args.message + ) pkt.show2() -# hexdump(pkt) -# print "len(pkt) = ", len(pkt) + # hexdump(pkt) + # print "len(pkt) = ", len(pkt) sendp(pkt, iface=iface, verbose=False) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/calc/calc.py b/exercises/calc/calc.py index a3c93035..895a6bef 100644 --- a/exercises/calc/calc.py +++ b/exercises/calc/calc.py @@ -13,95 +13,112 @@ StrFixedLenField, XByteField, bind_layers, - srp1 + srp1, ) + # Define custom packet class for P4calc class P4calc(Packet): name = "P4calc" # Define fields for the P4calc packet - fields_desc = [ StrFixedLenField("P", "P", length=1), - StrFixedLenField("Four", "4", length=1), - XByteField("version", 0x01), - StrFixedLenField("op", "+", length=1), - IntField("operand_a", 0), - IntField("operand_b", 0), - IntField("result", 0xDEADBABE)] + fields_desc = [ + StrFixedLenField("P", "P", length=1), + StrFixedLenField("Four", "4", length=1), + XByteField("version", 0x01), + StrFixedLenField("op", "+", length=1), + IntField("operand_a", 0), + IntField("operand_b", 0), + IntField("result", 0xDEADBABE), + ] + # Bind custom packet class to Ethernet type 0x1234 bind_layers(Ether, P4calc, type=0x1234) + # Custom exception for number parsing error class NumParseError(Exception): pass + # Custom exception for operator parsing error class OpParseError(Exception): pass + # Token class for representing parsed tokens class Token: def __init__(self, type, value=None): self.type = type self.value = value + # Parser function for parsing number literals def num_parser(s, i, ts): - pattern = "^\s*([0-9]+)\s*" - match = re.match(pattern,s[i:]) + pattern = r"^\s*([0-9]+)\s*" + match = re.match(pattern, s[i:]) if match: - ts.append(Token('num', match.group(1))) + ts.append(Token("num", match.group(1))) return i + match.end(), ts - raise NumParseError('Expected number literal.') + raise NumParseError("Expected number literal.") + # Parser function for parsing binary operators def op_parser(s, i, ts): - pattern = "^\s*([-+&|^])\s*" - match = re.match(pattern,s[i:]) + pattern = r"^\s*([-+&|^])\s*" + match = re.match(pattern, s[i:]) if match: - ts.append(Token('op', match.group(1))) + ts.append(Token("op", match.group(1))) return i + match.end(), ts raise OpParseError("Expected binary operator '-', '+', '&', '|', or '^'.") + # Function to create a sequence of parsers def make_seq(p1, p2): def parse(s, i, ts): - i,ts2 = p1(s,i,ts) - return p2(s,i,ts2) + i, ts2 = p1(s, i, ts) + return p2(s, i, ts2) + return parse def main(): - p = make_seq(num_parser, make_seq(op_parser,num_parser)) # Create parser for number and operator sequence - s = '' - iface = 'eth0' + # Create parser for number and operator sequence + p = make_seq(num_parser, make_seq(op_parser, num_parser)) + s = "" + iface = "eth0" while True: - s = input('> ') + s = input("> ") if s == "quit": break print(s) try: - i,ts = p(s,0,[]) + i, ts = p(s, 0, []) # Construct packet using parsed tokens - pkt = Ether(dst='00:04:00:00:00:00', type=0x1234) / P4calc(op=ts[1].value, - operand_a=int(ts[0].value), - operand_b=int(ts[2].value)) - pkt = pkt/' ' - - resp = srp1(pkt, iface=iface, timeout=1, verbose=False) # Send packet and receive response + pkt = Ether(dst="00:04:00:00:00:00", type=0x1234) / P4calc( + op=ts[1].value, operand_a=int(ts[0].value), + operand_b=int(ts[2].value) + ) + pkt = pkt / " " + + # Send packet and receive response + resp = srp1(pkt, iface=iface, timeout=1, verbose=False) if resp: - p4calc=resp[P4calc] + p4calc = resp[P4calc] if p4calc: - print(p4calc.result) # Print the result from the response packet + # Print the result from the response packet + print(p4calc.result) else: print("cannot find P4calc header in the packet") else: print("Didn't receive response") except Exception as error: - print(error) # Print any exceptions that occur during parsing or packet handling + # Print any exceptions that occur during parsing or packet + # handling + print(error) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/ecn/receive.py b/exercises/ecn/receive.py index 85b3b6d0..282ebb35 100755 --- a/exercises/ecn/receive.py +++ b/exercises/ecn/receive.py @@ -9,30 +9,30 @@ def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def handle_pkt(pkt): print("got a packet") pkt.show2() -# hexdump(pkt) + # hexdump(pkt) sys.stdout.flush() def main(): - iface = 'eth0' + iface = "eth0" print("sniffing on %s" % iface) sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/ecn/send.py b/exercises/ecn/send.py index 298f1496..866a3456 100755 --- a/exercises/ecn/send.py +++ b/exercises/ecn/send.py @@ -12,36 +12,41 @@ def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def main(): - if len(sys.argv)<4: + if len(sys.argv) < 4: print('pass 2 arguments: "" ') exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, tos=1) / UDP(dport=4321, sport=1234) / sys.argv[2] + pkt = ( + Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + / IP(dst=addr, tos=1) + / UDP(dport=4321, sport=1234) + / sys.argv[2] + ) pkt.show2() - #hexdump(pkt) + # hexdump(pkt) try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) + for i in range(int(sys.argv[3])): + sendp(pkt, iface=iface) + sleep(1) except KeyboardInterrupt: raise -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/flowcache/mycontroller.py b/exercises/flowcache/mycontroller.py index 4d2a76dd..0062a821 100755 --- a/exercises/flowcache/mycontroller.py +++ b/exercises/flowcache/mycontroller.py @@ -14,7 +14,7 @@ from collections import Counter from datetime import datetime, timedelta -from scapy.all import * +from scapy.all import Ether, IP import grpc @@ -22,7 +22,7 @@ # Probably there's a better way of doing this. sys.path.append( os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../utils/')) + "../../utils/")) import p4runtime_lib.bmv2 import p4runtime_lib.helper from p4runtime_lib.switch import ShutdownAllSwitchConnections @@ -32,15 +32,16 @@ global_data = {} -global_data['CPU_PORT'] = 510 -global_data['CPU_PORT_CLONE_SESSION_ID'] = 57 -global_data['NUM_PORTS'] = 3 -global_data['index'] = 0 +global_data["CPU_PORT"] = 510 +global_data["CPU_PORT_CLONE_SESSION_ID"] = 57 +global_data["NUM_PORTS"] = 3 +global_data["index"] = 0 global_data["10.0.1.1"] = "08:00:00:00:01:11" global_data["10.0.2.2"] = "08:00:00:00:02:22" global_data["10.0.3.3"] = "08:00:00:00:03:33" -## The notification database keeps track of the received idle notifications and triggers the deletion of stale flow rules. +# The notification database keeps track of the received idle +# notifications and triggers the deletion of stale flow rules. notif_db = {} # The lookup table is defined to simplify reachability and provide connectivity @@ -65,84 +66,83 @@ } } + def ipv4ToInt(addr): """Take an argument 'addr' containing an IPv4 address written as a string in dotted decimal notation, e.g. '10.1.2.3', and convert it to an integer.""" - bytes_ = [int(b, 10) for b in addr.split('.')] + bytes_ = [int(b, 10) for b in addr.split(".")] assert len(bytes_) == 4 # Note: The bytes() call below will throw exception if any # elements of bytes_ is outside of the range [0, 255]], so no need # to add a separate check for that here. - return int.from_bytes(bytes(bytes_), byteorder='big') + return int.from_bytes(bytes(bytes_), byteorder="big") + def intToIpv4(n): """Take an argument 'n' containing a 32-bit IPv4 address as an integer in the range [0, 2^32-1], and return a string in dotted decimal notation.""" - return "%d.%d.%d.%d" % ((n >> 24) & 0xff, - (n >> 16) & 0xff, - (n >> 8) & 0xff, - n & 0xff) + return "%d.%d.%d.%d" % ( + (n >> 24) & 0xFF, + (n >> 16) & 0xFF, + (n >> 8) & 0xFF, + n & 0xFF, + ) + def flowCacheEntryToDebugStr(table_entry, include_action=False): - # TODO: The match fields are hardcoded to specific indices to retrieve specific parameters, such as hdr.ipv4.srcAddr and its value. - src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, byteorder='big')) - dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, byteorder='big')) - proto = int.from_bytes(table_entry.match[0].exact.value, byteorder='big') - return ("(SA=%s, DA=%s, proto=%d)" - "" % (src_ip, dst_ip, proto)) + # TODO: The match fields are hardcoded to specific indices to + # retrieve specific parameters, such as hdr.ipv4.srcAddr and its + # value. + src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, + byteorder="big")) + dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, + byteorder="big")) + proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") + return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) + def decodePacketInMetadata(pktin_info, packet): pktin_field_to_val = {} for md in packet.metadata: md_id_int = md.metadata_id - md_val_int = int.from_bytes(md.value, byteorder='big') + md_val_int = int.from_bytes(md.value, byteorder="big") assert md_id_int in pktin_info md_field_info = pktin_info[md_id_int] - pktin_field_to_val[md_field_info['name']] = md_val_int - ret = {'metadata': pktin_field_to_val, - 'payload': packet.payload} + pktin_field_to_val[md_field_info["name"]] = md_val_int + ret = {"metadata": pktin_field_to_val, "payload": packet.payload} print("decodePacketInMetadata: ret=%s" % (ret)) return ret + def serializableEnumDict(p4info_data, name): type_info = p4info_data.type_info name_to_int = {} int_to_name = {} for member in type_info.serializable_enums[name].members: name = member.name - int_val = int.from_bytes(member.value, byteorder='big') + int_val = int.from_bytes(member.value, byteorder="big") name_to_int[name] = int_val int_to_name[int_val] = name - print("serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" - "" % (name, name_to_int, int_to_name)) + print( + "serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" + "" % (name, name_to_int, int_to_name) + ) return name_to_int, int_to_name -def decodePacketInMetadata(pktin_info, packet): - pktin_field_to_val = {} - for md in packet.metadata: - md_id_int = md.metadata_id - md_val_int = int.from_bytes(md.value, byteorder='big') - assert md_id_int in pktin_info - md_field_info = pktin_info[md_id_int] - pktin_field_to_val[md_field_info['name']] = md_val_int - ret = {'metadata': pktin_field_to_val, - 'payload': packet.payload} - print("decodePacketInMetadata: ret=%s" % (ret)) - return ret def getObj(p4info_obj_map, obj_type, name): key = (obj_type, name) return p4info_obj_map.get(key, None) + def controllerPacketMetadataDictKeyId(p4info_obj_map, name): cpm_info = getObj(p4info_obj_map, "controller_packet_metadata", name) - assert cpm_info != None + assert cpm_info is not None ret = {} for md in cpm_info.metadata: - id = md.id - ret[md.id] = {'id': md.id, 'name': md.name, 'bitwidth': md.bitwidth} + ret[md.id] = {"id": md.id, "name": md.name, "bitwidth": md.bitwidth} return ret @@ -164,12 +164,18 @@ def makeP4infoObjMap(p4info_data): del p4info_obj_map[key] return p4info_obj_map + def writeCloneSession(sw, clone_session_id, replicas): - # Size 0 bmv2 does not support truncation for clones, issue behavioral-model #996 - clone_entry = global_data['p4info_helper'].buildCloneSessionEntry(clone_session_id, replicas, 0) + # Size 0 bmv2 does not support truncation for clones, issue + # behavioral-model #996 + clone_entry = global_data["p4info_helper"].buildCloneSessionEntry( + clone_session_id, replicas, 0 + ) sw.WritePREEntry(clone_entry) -def addFlowRule( ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, new_dscp, decrement_ttl_bool, dst_eth_addr): + +def addFlowRule(ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, + new_dscp, decrement_ttl_bool, dst_eth_addr): """ Install flow rule in flow cache table @@ -188,42 +194,56 @@ def addFlowRule( ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, new_dscp, else: x = 0 - table_entry = global_data['p4info_helper'].buildTableEntry( + table_entry = global_data["p4info_helper"].buildTableEntry( table_name="MyIngress.flow_cache", match_fields={ "hdr.ipv4.protocol": protocol, "hdr.ipv4.srcAddr": src_ip_addr, - "hdr.ipv4.dstAddr": dst_ip_addr + "hdr.ipv4.dstAddr": dst_ip_addr, }, action_name="MyIngress.cached_action", action_params={ "port": port, "decrement_ttl": x, "new_dscp": new_dscp, - "dst_eth_addr": dst_eth_addr + "dst_eth_addr": dst_eth_addr, }, # TODO: Add idle timeout ) ingress_sw.WriteTableEntry(table_entry) + def createFlowRule(notif): - # TODO: This function generates a flow entry to trigger deletion. - # The match fields are populated using values retrieved from the IDLE notification. - # Hardcoded values are configured to extract specific parameters, such as hdr.ipv4.protocol, from the IDLE notification. - table_entry = global_data['p4info_helper'].buildTableEntry( + # TODO: This function generates a flow entry to trigger deletion. + # The match fields are populated using values retrieved from the + # IDLE notification. Hardcoded values are configured to extract + # specific parameters, such as hdr.ipv4.protocol, from the IDLE + # notification. + te = notif["idle"].table_entry[0] + table_entry = global_data["p4info_helper"].buildTableEntry( table_name="MyIngress.flow_cache", match_fields={ - "hdr.ipv4.protocol": int.from_bytes(notif["idle"].table_entry[0].match[0].exact.value,byteorder='big'), - "hdr.ipv4.srcAddr": int(ipaddress.IPv4Address(notif["idle"].table_entry[0].match[1].exact.value)), - "hdr.ipv4.dstAddr": int(ipaddress.IPv4Address(notif["idle"].table_entry[0].match[2].exact.value)) + "hdr.ipv4.protocol": int.from_bytes( + te.match[0].exact.value, byteorder="big" + ), + "hdr.ipv4.srcAddr": int( + ipaddress.IPv4Address(te.match[1].exact.value) + ), + "hdr.ipv4.dstAddr": int( + ipaddress.IPv4Address(te.match[2].exact.value) + ), }, ) return table_entry + def deleteFlowRule(sw, table_entry): sw.DeleteTableEntry(table_entry) - print("Deleted flow_cache entry on %s. %s" - "" % (sw.name, flowCacheEntryToDebugStr(table_entry))) + print( + "Deleted flow_cache entry on %s. %s" + "" % (sw.name, flowCacheEntryToDebugStr(table_entry)) + ) + def addNotification(sw_name, flow_rule): # Add notification to notification DB @@ -233,6 +253,7 @@ def addNotification(sw_name, flow_rule): } notif_db[sw_name].append(notification) + def checkFlowRule(sw_name, flow_rule): # Checks if a flow rule is already in the notification DB # to avoid storing multiple notifications for the same flow rule @@ -242,34 +263,43 @@ def checkFlowRule(sw_name, flow_rule): for notif in notif_db[sw_name]: if notif["flow_rule"] == flow_rule: return True - + return False + def isExpired(timestamp, timeout): return datetime.now() - timestamp > timedelta(seconds=timeout) + def cleanExpiredNotifiction(sw_name, timeout=5): - # Removes expired notifications + # Removes expired notifications if sw_name not in notif_db: return False # Filter the notifications to remove expired ones notif_db[sw_name] = [ - notif for notif in notif_db[sw_name] + notif + for notif in notif_db[sw_name] if not isExpired(notif["timestamp"], timeout) ] return True + def packetOutMetadataList(opcode, reserved1, operand0): # This function does not use the generated contents of the P4Info # file to map PacketOut metadata fields to indices. If you change # the PacketOut metadata format in the P4 program, this code must # be manually updated to match. - return [{"value": opcode, "bitwidth": 8}, - {"value": reserved1, "bitwidth": 8}, - {"value": operand0, "bitwidth": 32}] + return [ + {"value": opcode, "bitwidth": 8}, + {"value": reserved1, "bitwidth": 8}, + {"value": operand0, "bitwidth": 32}, + ] -def sendPacketOut(sw ,payload, metadatas): + +def sendPacketOut(sw, payload, metadatas): # TODO: Implement the function logic to send a packet-out message + pass + def readTableRules(p4info_helper, sw): """ @@ -278,19 +308,22 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print('\n----- Reading tables rules for %s -----' % sw.name) + print("\n----- Reading tables rules for %s -----" % (sw.name)) for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate # the IDs in the entry to names print(entry) - print('-----') + print("-----") + def printCounter(p4info_helper, sw, counter_name, index): """ - Reads the specified counter at the given index from the switch. In our - program, the index is derived from the first 6 bits of the IP destination address. + Reads the specified counter at the given index from the switch. In + our program, the index is derived from the first 6 bits of the IP + destination address. + If the index is 0, it will return all values from the counter. :param p4info_helper: the P4Info helper @@ -299,122 +332,148 @@ def printCounter(p4info_helper, sw, counter_name, index): :param index: the counter index (in our case, first 6 bits of the IP) """ try: - for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): + for response in sw.ReadCounters( + p4info_helper.get_counters_id(counter_name), index + ): for entity in response.entities: counter = entity.counter_entry - print("%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - )) + print( + "%s %s %d: %d packets (%d bytes)" + % ( + sw.name, + counter_name, + index, + counter.data.packet_count, + counter.data.byte_count, + ) + ) except grpc.RpcError as e: - print(f"[gRPC Error in printCounter for {sw.name}]") - printGrpcError(e) + print(f"[gRPC Error in printCounter for {sw.name}]") + printGrpcError(e) - if e.code() == grpc.StatusCode.UNKNOWN: + if e.code() == grpc.StatusCode.UNKNOWN: print(f"Unknown gRPC error from {sw.name}. Retrying...") time.sleep(2) except Exception as e: - print(f"[Unexpected Error in printCounter for {sw.name}]: {e}") - traceback.print_exc() - time.sleep(2) + print(f"[Unexpected Error in printCounter for {sw.name}]: {e}") + traceback.print_exc() + time.sleep(2) + def processPacket(message): - payload = message["packet-in"].payload - packet = message["packet-in"] - print("Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name)) - if len(payload) > 0: - i = 0 - pkt = Ether(payload) - ip_proto = pkt[IP].proto - ip_sa_str = pkt[IP].src - src_ip_addr = ipv4ToInt(ip_sa_str) - ip_da_str = pkt[IP].dst - dst_ip_addr = ipv4ToInt(ip_da_str) - pktinfo = decodePacketInMetadata(global_data['cpm_packetin_id2data'], packet) - debug_packetin = False - if debug_packetin: - i += 1 - print("") - print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" - "" % (type(payload))) - print(payload) - print(pktinfo) - print("Scapy decode:") - print(pkt) - print("IPv4 proto %d (type %s)" - "" % (ip_proto, type(ip_proto))) - print("IPv4 SA %08x (type %s)" - "" % (src_ip_addr, type(src_ip_addr))) - print("IPv4 DA %08x (type %s)" - "" % (dst_ip_addr, type(dst_ip_addr))) - if pktinfo['metadata']['punt_reason'] == global_data['punt_reason_name2int']['FLOW_UNKNOWN']: - dest_port_int = lookup_table[message["sw"].name][ip_da_str] - decrement_ttl_bool = True - new_dscp_int = 5 - global_data['index'] = int(pkt[IP].dst.split('.')[3]) - dst_eth_addr = global_data[ip_da_str] - metadatas = packetOutMetadataList( - global_data['controller_opcode_name2int']['SEND_TO_PORT_IN_OPERAND0'], - 0, dest_port_int) - sendPacketOut(message["sw"], payload, metadatas) - addFlowRule(message["sw"], - src_ip_addr, - dst_ip_addr, - ip_proto, - dest_port_int, - new_dscp_int, - decrement_ttl_bool, - dst_eth_addr) - - print("For switch %s flow (SA=%s, DA=%s, proto=%d)" - " added table entry to send packets" - " to port %d with new DSCP %d" - "" % (message["sw"].name, ip_sa_str, ip_da_str, - ip_proto, dest_port_int, new_dscp_int)) + payload = message["packet-in"].payload + packet = message["packet-in"] + print("Received PacketIn message of length %d bytes from switch %s" + "" % (len(payload), message["sw"].name)) + if len(payload) > 0: + i = 0 + pkt = Ether(payload) + ip_proto = pkt[IP].proto + ip_sa_str = pkt[IP].src + src_ip_addr = ipv4ToInt(ip_sa_str) + ip_da_str = pkt[IP].dst + dst_ip_addr = ipv4ToInt(ip_da_str) + pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], + packet) + debug_packetin = False + if debug_packetin: + i += 1 + print("") + print("pktin %d of %d" % (i, len(payload))) + print("type(pktin.packet.payload)=%s" "" % (type(payload))) + print(payload) + print(pktinfo) + print("Scapy decode:") + print(pkt) + print("IPv4 proto %d (type %s)" % (ip_proto, type(ip_proto))) + print("IPv4 SA %08x (type %s)" % (src_ip_addr, type(src_ip_addr))) + print("IPv4 DA %08x (type %s)" % (dst_ip_addr, type(dst_ip_addr))) + if ( + pktinfo["metadata"]["punt_reason"] + == global_data["punt_reason_name2int"]["FLOW_UNKNOWN"] + ): + dest_port_int = lookup_table[message["sw"].name][ip_da_str] + decrement_ttl_bool = True + new_dscp_int = 5 + global_data["index"] = int(pkt[IP].dst.split(".")[3]) + dst_eth_addr = global_data[ip_da_str] + metadatas = packetOutMetadataList( + global_data["controller_opcode_name2int"]["SEND_TO_PORT_IN_OPERAND0"], + 0, + dest_port_int, + ) + sendPacketOut(message["sw"], payload, metadatas) + addFlowRule(message["sw"], + src_ip_addr, + dst_ip_addr, + ip_proto, + dest_port_int, + new_dscp_int, + decrement_ttl_bool, + dst_eth_addr) + + print("For switch %s flow (SA=%s, DA=%s, proto=%d)" + " added table entry to send packets" + " to port %d with new DSCP %d" + "" % (message["sw"].name, ip_sa_str, ip_da_str, + ip_proto, dest_port_int, new_dscp_int)) + async def processNotif(notif_queue): - while True: - notif = await notif_queue.get() - debug_notif = False + while True: + notif = await notif_queue.get() + debug_notif = False + if debug_notif: + print(notif) + pprint.pprint(notif_db) + if notif["type"] == "packet-in": + processPacket(notif) + printCounter( + global_data["p4info_helper"], + notif["sw"], + "MyIngress.ingressPktOutCounter", + global_data["index"], + ) + printCounter( + global_data["p4info_helper"], + notif["sw"], + "MyEgress.egressPktInCounter", + global_data["index"], + ) if debug_notif: - print(notif) - pprint.pprint(notif_db) - if notif["type"] == "packet-in": - processPacket(notif) - printCounter(global_data ['p4info_helper'], notif["sw"], 'MyIngress.ingressPktOutCounter', global_data ['index']) - printCounter(global_data ['p4info_helper'], notif["sw"], 'MyEgress.egressPktInCounter', global_data ['index']) - if debug_notif: - readTableRules(global_data ['p4info_helper'], notif["sw"]) - elif notif["type"] == "idle-notif": - # TODO: For extra credit, you can experiment with adjusting the stale time for notifications (e.g., 10 seconds) - # and optimize the behavior of the notification database (notif_db). - if notif["sw"].name not in notif_db: - notif_db[notif["sw"].name] = [] - else: - # Check if a notification is older than 10 seconds - cleanExpiredNotifiction(notif["sw"].name, 10) - - table_entry = createFlowRule(notif) - - if not checkFlowRule(notif["sw"].name, table_entry): - addNotification(notif["sw"].name, table_entry) - deleteFlowRule(notif["sw"], table_entry) - else: - print("Received idle timeout notification for switch=%s %s" - " It is duplicate of recently processed notification, so ignoring it." - "" % (notif["sw"].name, - flowCacheEntryToDebugStr(table_entry))) - notif_queue.task_done() - -async def packetInHandler(notif_queue,sw): + readTableRules(global_data["p4info_helper"], notif["sw"]) + elif notif["type"] == "idle-notif": + # TODO: For extra credit, you can experiment with + # adjusting the stale time for notifications (e.g., 10 + # seconds) and optimize the behavior of the notification + # database (notif_db). + if notif["sw"].name not in notif_db: + notif_db[notif["sw"].name] = [] + else: + # Check if a notification is older than 10 seconds + cleanExpiredNotifiction(notif["sw"].name, 10) + + table_entry = createFlowRule(notif) + + if not checkFlowRule(notif["sw"].name, table_entry): + addNotification(notif["sw"].name, table_entry) + deleteFlowRule(notif["sw"], table_entry) + else: + print("Received idle timeout notification for switch=%s %s" + " It is duplicate of recently processed notification," + " so ignoring it." + "" % (notif["sw"].name, + flowCacheEntryToDebugStr(table_entry))) + notif_queue.task_done() + + +async def packetInHandler(notif_queue, sw): # TODO: Implement the function logic to handle a packet-in message while True: try: packet_in = await asyncio.to_thread(sw.PacketIn) - #print(f"Received packet: {packet_in}") + # print(f"Received packet: {packet_in}") message = {"type": "packet-in", "sw": sw, "packet-in": packet_in} await notif_queue.put(message) @@ -431,7 +490,8 @@ async def packetInHandler(notif_queue,sw): traceback.print_exc() await asyncio.sleep(2) -async def idleTimeHandler(notif_queue,sw): + +async def idleTimeHandler(notif_queue, sw): # TODO: Implement the function logic to handle idle timeout notification while True: idle_notif = await asyncio.to_thread(sw.IdleTimeoutNotification) @@ -439,40 +499,44 @@ async def idleTimeHandler(notif_queue,sw): await notif_queue.put(message) await asyncio.sleep(5) + def printGrpcError(e): - print("gRPC Error:", e.details(), end=' ') + print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=' ') + print("(%s)" % status_code.name, end=" ") traceback = sys.exc_info()[2] print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + async def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file - global_data ['p4info_helper'] = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) - p4info_helper = global_data ['p4info_helper'] + global_data["p4info_helper"] = \ + p4runtime_lib.helper.P4InfoHelper(p4info_file_path) + p4info_helper = global_data["p4info_helper"] try: # Create a switch connection object for s1,s2,s3; # this is backed by a P4Runtime gRPC connection. # Also, dump all P4Runtime messages sent to switch to given txt files. s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s1', - address='127.0.0.1:50051', + name="s1", + address="127.0.0.1:50051", device_id=0, - proto_dump_file='logs/s1-p4runtime-requests.txt') + proto_dump_file="logs/s1-p4runtime-requests.txt") s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s2', - address='127.0.0.1:50052', + name="s2", + address="127.0.0.1:50052", device_id=1, - proto_dump_file='logs/s2-p4runtime-requests.txt') + proto_dump_file="logs/s2-p4runtime-requests.txt") s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s3', - address='127.0.0.1:50053', + name="s3", + address="127.0.0.1:50053", device_id=2, - proto_dump_file='logs/s3-p4runtime-requests.txt') + proto_dump_file="logs/s3-p4runtime-requests.txt") - # Send master arbitration update message to establish this controller as - # master (required by P4Runtime before performing any other write operation) + # Send master arbitration update message to establish this + # controller as master (required by P4Runtime before + # performing any other write operation) s1.MasterArbitrationUpdate() s2.MasterArbitrationUpdate() s3.MasterArbitrationUpdate() @@ -488,27 +552,34 @@ async def main(p4info_file_path, bmv2_file_path): bmv2_json_file_path=bmv2_file_path) print("Installed P4 Program using SetForwardingPipelineConfig on s3") - global_data['p4info_obj_map'] = makeP4infoObjMap(p4info_helper.p4info) - global_data['cpm_packetin_id2data'] = \ - controllerPacketMetadataDictKeyId(global_data['p4info_obj_map'], - "packet_in") + global_data["p4info_obj_map"] = makeP4infoObjMap(p4info_helper.p4info) + global_data["cpm_packetin_id2data"] = controllerPacketMetadataDictKeyId( + global_data["p4info_obj_map"], "packet_in" + ) - global_data['punt_reason_name2int'], global_data['punt_reason_int2name'] = \ - serializableEnumDict(p4info_helper.p4info, 'PuntReason_t') - global_data['controller_opcode_name2int'], global_data['controller_opcode_int2name'] = \ - serializableEnumDict(p4info_helper.p4info, 'ControllerOpcode_t') + global_data["punt_reason_name2int"], global_data["punt_reason_int2name"] = ( + serializableEnumDict(p4info_helper.p4info, "PuntReason_t") + ) + ( + global_data["controller_opcode_name2int"], + global_data["controller_opcode_int2name"], + ) = serializableEnumDict(p4info_helper.p4info, "ControllerOpcode_t") try: - replicas = [{ "egress_port": global_data['CPU_PORT'], "instance": 1 }] - writeCloneSession(s1, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - writeCloneSession(s2, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - writeCloneSession(s3, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - - except shp4rt.P4RuntimeWriteException as e: - print("Got exception trying to configure clone session %d." - " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data['CPU_PORT_CLONE_SESSION_ID'])) + replicas = [{"egress_port": global_data["CPU_PORT"], + "instance": 1}] + writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + + except shp4rt.P4RuntimeWriteException: + print("Got exception trying to configure clone session %d." + " Assuming it was initialized already in an earlier" + " run of the controller." + "" % (global_data["CPU_PORT_CLONE_SESSION_ID"])) notif_queue = asyncio.Queue() @@ -522,33 +593,48 @@ async def main(p4info_file_path, bmv2_file_path): proc_notif = asyncio.create_task(processNotif(notif_queue)) - await asyncio.gather(pkt_s1,pkt_s2,pkt_s3,idle_notif_s1, idle_notif_s2, idle_notif_s3, proc_notif) + await asyncio.gather(pkt_s1, pkt_s2, pkt_s3, idle_notif_s1, + idle_notif_s2, idle_notif_s3, proc_notif) except KeyboardInterrupt: print(" Shutting down.") except grpc.RpcError as e: print(f"gRPC error occurred: {e}") - print(f"Status code: {e.code()}") # e.g., StatusCode.UNAVAILABLE or StatusCode.INVALID_ARGUMENT + # e.g., StatusCode.UNAVAILABLE or StatusCode.INVALID_ARGUMENT + print(f"Status code: {e.code()}") print(f"Details: {e.details()}") ShutdownAllSwitchConnections() -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/flowcache.p4.p4info.txtpb') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/flowcache.json') + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="P4Runtime Controller") + parser.add_argument( + "--p4info", + help="p4info proto in text format from p4c", + type=str, + action="store", + required=False, + default="./build/flowcache.p4.p4info.txtpb", + ) + parser.add_argument( + "--bmv2-json", + help="BMv2 JSON file from p4c", + type=str, + action="store", + required=False, + default="./build/flowcache.json", + ) args = parser.parse_args() if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info) + print("\np4info file not found: %s\nHave you run 'make'?" + "" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" + "" % (args.bmv2_json)) parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/flowcache/solution/mycontroller.py b/exercises/flowcache/solution/mycontroller.py index 0e517b3d..f182edf0 100755 --- a/exercises/flowcache/solution/mycontroller.py +++ b/exercises/flowcache/solution/mycontroller.py @@ -14,7 +14,7 @@ from collections import Counter from datetime import datetime, timedelta -from scapy.all import * +from scapy.all import Ether, IP import grpc @@ -22,7 +22,7 @@ # Probably there's a better way of doing this. sys.path.append( os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../utils/')) + "../../utils/")) import p4runtime_lib.bmv2 import p4runtime_lib.helper from p4runtime_lib.switch import ShutdownAllSwitchConnections @@ -32,15 +32,16 @@ global_data = {} -global_data['CPU_PORT'] = 510 -global_data['CPU_PORT_CLONE_SESSION_ID'] = 57 -global_data['NUM_PORTS'] = 3 -global_data['index'] = 0 +global_data["CPU_PORT"] = 510 +global_data["CPU_PORT_CLONE_SESSION_ID"] = 57 +global_data["NUM_PORTS"] = 3 +global_data["index"] = 0 global_data["10.0.1.1"] = "08:00:00:00:01:11" global_data["10.0.2.2"] = "08:00:00:00:02:22" global_data["10.0.3.3"] = "08:00:00:00:03:33" -## The notification database keeps track of the received idle notifications and triggers the deletion of stale flow rules. +# The notification database keeps track of the received idle +# notifications and triggers the deletion of stale flow rules. notif_db = {} # The lookup table is defined to simplify reachability and provide connectivity @@ -65,84 +66,83 @@ } } + def ipv4ToInt(addr): """Take an argument 'addr' containing an IPv4 address written as a string in dotted decimal notation, e.g. '10.1.2.3', and convert it to an integer.""" - bytes_ = [int(b, 10) for b in addr.split('.')] + bytes_ = [int(b, 10) for b in addr.split(".")] assert len(bytes_) == 4 # Note: The bytes() call below will throw exception if any # elements of bytes_ is outside of the range [0, 255]], so no need # to add a separate check for that here. - return int.from_bytes(bytes(bytes_), byteorder='big') + return int.from_bytes(bytes(bytes_), byteorder="big") + def intToIpv4(n): """Take an argument 'n' containing a 32-bit IPv4 address as an integer in the range [0, 2^32-1], and return a string in dotted decimal notation.""" - return "%d.%d.%d.%d" % ((n >> 24) & 0xff, - (n >> 16) & 0xff, - (n >> 8) & 0xff, - n & 0xff) + return "%d.%d.%d.%d" % ( + (n >> 24) & 0xFF, + (n >> 16) & 0xFF, + (n >> 8) & 0xFF, + n & 0xFF, + ) + def flowCacheEntryToDebugStr(table_entry, include_action=False): - # TODO: The match fields are hardcoded to specific indices to retrieve specific parameters, such as hdr.ipv4.srcAddr and its value. - src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, byteorder='big')) - dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, byteorder='big')) - proto = int.from_bytes(table_entry.match[0].exact.value, byteorder='big') - return ("(SA=%s, DA=%s, proto=%d)" - "" % (src_ip, dst_ip, proto)) + # TODO: The match fields are hardcoded to specific indices to + # retrieve specific parameters, such as hdr.ipv4.srcAddr and its + # value. + src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, + byteorder="big")) + dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, + byteorder="big")) + proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") + return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) + def decodePacketInMetadata(pktin_info, packet): pktin_field_to_val = {} for md in packet.metadata: md_id_int = md.metadata_id - md_val_int = int.from_bytes(md.value, byteorder='big') + md_val_int = int.from_bytes(md.value, byteorder="big") assert md_id_int in pktin_info md_field_info = pktin_info[md_id_int] - pktin_field_to_val[md_field_info['name']] = md_val_int - ret = {'metadata': pktin_field_to_val, - 'payload': packet.payload} + pktin_field_to_val[md_field_info["name"]] = md_val_int + ret = {"metadata": pktin_field_to_val, "payload": packet.payload} print("decodePacketInMetadata: ret=%s" % (ret)) return ret + def serializableEnumDict(p4info_data, name): type_info = p4info_data.type_info name_to_int = {} int_to_name = {} for member in type_info.serializable_enums[name].members: name = member.name - int_val = int.from_bytes(member.value, byteorder='big') + int_val = int.from_bytes(member.value, byteorder="big") name_to_int[name] = int_val int_to_name[int_val] = name - print("serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" - "" % (name, name_to_int, int_to_name)) + print( + "serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" + "" % (name, name_to_int, int_to_name) + ) return name_to_int, int_to_name -def decodePacketInMetadata(pktin_info, packet): - pktin_field_to_val = {} - for md in packet.metadata: - md_id_int = md.metadata_id - md_val_int = int.from_bytes(md.value, byteorder='big') - assert md_id_int in pktin_info - md_field_info = pktin_info[md_id_int] - pktin_field_to_val[md_field_info['name']] = md_val_int - ret = {'metadata': pktin_field_to_val, - 'payload': packet.payload} - print("decodePacketInMetadata: ret=%s" % (ret)) - return ret def getObj(p4info_obj_map, obj_type, name): key = (obj_type, name) return p4info_obj_map.get(key, None) + def controllerPacketMetadataDictKeyId(p4info_obj_map, name): cpm_info = getObj(p4info_obj_map, "controller_packet_metadata", name) - assert cpm_info != None + assert cpm_info is not None ret = {} for md in cpm_info.metadata: - id = md.id - ret[md.id] = {'id': md.id, 'name': md.name, 'bitwidth': md.bitwidth} + ret[md.id] = {"id": md.id, "name": md.name, "bitwidth": md.bitwidth} return ret @@ -164,12 +164,18 @@ def makeP4infoObjMap(p4info_data): del p4info_obj_map[key] return p4info_obj_map + def writeCloneSession(sw, clone_session_id, replicas): - # Size 0 bmv2 does not support truncation for clones, issue behavioral-model #996 - clone_entry = global_data['p4info_helper'].buildCloneSessionEntry(clone_session_id, replicas, 0) + # Size 0 bmv2 does not support truncation for clones, issue + # behavioral-model #996 + clone_entry = global_data["p4info_helper"].buildCloneSessionEntry( + clone_session_id, replicas, 0 + ) sw.WritePREEntry(clone_entry) -def addFlowRule( ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, new_dscp, decrement_ttl_bool, dst_eth_addr): + +def addFlowRule(ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, + new_dscp, decrement_ttl_bool, dst_eth_addr): """ Install flow rule in flow cache table @@ -188,43 +194,57 @@ def addFlowRule( ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, new_dscp, else: x = 0 - table_entry = global_data['p4info_helper'].buildTableEntry( + table_entry = global_data["p4info_helper"].buildTableEntry( table_name="MyIngress.flow_cache", match_fields={ "hdr.ipv4.protocol": protocol, "hdr.ipv4.srcAddr": src_ip_addr, - "hdr.ipv4.dstAddr": dst_ip_addr + "hdr.ipv4.dstAddr": dst_ip_addr, }, action_name="MyIngress.cached_action", action_params={ - "port": port, - "decrement_ttl": x, - "new_dscp": new_dscp, - "dst_eth_addr": dst_eth_addr + "port": port, + "decrement_ttl": x, + "new_dscp": new_dscp, + "dst_eth_addr": dst_eth_addr, }, # TODO: Add idle timeout - idle_timeout_ns = 3 * NSEC_PER_SEC - ) + idle_timeout_ns=3 * NSEC_PER_SEC, + ) ingress_sw.WriteTableEntry(table_entry) + def createFlowRule(notif): - # TODO: This function generates a flow entry to trigger deletion. - # The match fields are populated using values retrieved from the IDLE notification. - # Hardcoded values are configured to extract specific parameters, such as hdr.ipv4.protocol, from the IDLE notification. - table_entry = global_data['p4info_helper'].buildTableEntry( + # TODO: This function generates a flow entry to trigger deletion. + # The match fields are populated using values retrieved from the + # IDLE notification. Hardcoded values are configured to extract + # specific parameters, such as hdr.ipv4.protocol, from the IDLE + # notification. + te = notif["idle"].table_entry[0] + table_entry = global_data["p4info_helper"].buildTableEntry( table_name="MyIngress.flow_cache", match_fields={ - "hdr.ipv4.protocol": int.from_bytes(notif["idle"].table_entry[0].match[0].exact.value,byteorder='big'), - "hdr.ipv4.srcAddr": int(ipaddress.IPv4Address(notif["idle"].table_entry[0].match[1].exact.value)), - "hdr.ipv4.dstAddr": int(ipaddress.IPv4Address(notif["idle"].table_entry[0].match[2].exact.value)) + "hdr.ipv4.protocol": int.from_bytes( + te.match[0].exact.value, byteorder="big" + ), + "hdr.ipv4.srcAddr": int( + ipaddress.IPv4Address(te.match[1].exact.value) + ), + "hdr.ipv4.dstAddr": int( + ipaddress.IPv4Address(te.match[2].exact.value) + ), }, ) return table_entry + def deleteFlowRule(sw, table_entry): sw.DeleteTableEntry(table_entry) - print("Deleted flow_cache entry on %s. %s" - "" % (sw.name, flowCacheEntryToDebugStr(table_entry))) + print( + "Deleted flow_cache entry on %s. %s" + "" % (sw.name, flowCacheEntryToDebugStr(table_entry)) + ) + def addNotification(sw_name, flow_rule): # Add notification to notification DB @@ -234,6 +254,7 @@ def addNotification(sw_name, flow_rule): } notif_db[sw_name].append(notification) + def checkFlowRule(sw_name, flow_rule): # Checks if a flow rule is already in the notification DB # to avoid storing multiple notifications for the same flow rule @@ -243,36 +264,44 @@ def checkFlowRule(sw_name, flow_rule): for notif in notif_db[sw_name]: if notif["flow_rule"] == flow_rule: return True - + return False + def isExpired(timestamp, timeout): return datetime.now() - timestamp > timedelta(seconds=timeout) + def cleanExpiredNotifiction(sw_name, timeout=5): - # Removes expired notifications + # Removes expired notifications if sw_name not in notif_db: return False # Filter the notifications to remove expired ones notif_db[sw_name] = [ - notif for notif in notif_db[sw_name] + notif + for notif in notif_db[sw_name] if not isExpired(notif["timestamp"], timeout) ] return True + def packetOutMetadataList(opcode, reserved1, operand0): # This function does not use the generated contents of the P4Info # file to map PacketOut metadata fields to indices. If you change # the PacketOut metadata format in the P4 program, this code must # be manually updated to match. - return [{"value": opcode, "bitwidth": 8}, - {"value": reserved1, "bitwidth": 8}, - {"value": operand0, "bitwidth": 32}] + return [ + {"value": opcode, "bitwidth": 8}, + {"value": reserved1, "bitwidth": 8}, + {"value": operand0, "bitwidth": 32}, + ] + -def sendPacketOut(sw ,payload, metadatas): +def sendPacketOut(sw, payload, metadatas): # TODO: Implement the function logic to send a packet-out message sw.PacketOut(payload, metadatas) + def readTableRules(p4info_helper, sw): """ Reads the table entries from all tables on the switch. @@ -280,19 +309,22 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print('\n----- Reading tables rules for %s -----' % sw.name) + print("\n----- Reading tables rules for %s -----" % (sw.name)) for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate # the IDs in the entry to names print(entry) - print('-----') + print("-----") + def printCounter(p4info_helper, sw, counter_name, index): """ - Reads the specified counter at the given index from the switch. In our - program, the index is derived from the first 6 bits of the IP destination address. + Reads the specified counter at the given index from the switch. In + our program, the index is derived from the first 6 bits of the IP + destination address. + If the index is 0, it will return all values from the counter. :param p4info_helper: the P4Info helper @@ -301,122 +333,148 @@ def printCounter(p4info_helper, sw, counter_name, index): :param index: the counter index (in our case, first 6 bits of the IP) """ try: - for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): + for response in sw.ReadCounters( + p4info_helper.get_counters_id(counter_name), index + ): for entity in response.entities: counter = entity.counter_entry - print("%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - )) + print( + "%s %s %d: %d packets (%d bytes)" + % ( + sw.name, + counter_name, + index, + counter.data.packet_count, + counter.data.byte_count, + ) + ) except grpc.RpcError as e: - print(f"[gRPC Error in printCounter for {sw.name}]") - printGrpcError(e) + print(f"[gRPC Error in printCounter for {sw.name}]") + printGrpcError(e) - if e.code() == grpc.StatusCode.UNKNOWN: + if e.code() == grpc.StatusCode.UNKNOWN: print(f"Unknown gRPC error from {sw.name}. Retrying...") time.sleep(2) except Exception as e: - print(f"[Unexpected Error in printCounter for {sw.name}]: {e}") - traceback.print_exc() - time.sleep(2) + print(f"[Unexpected Error in printCounter for {sw.name}]: {e}") + traceback.print_exc() + time.sleep(2) + def processPacket(message): - payload = message["packet-in"].payload - packet = message["packet-in"] - print("Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name)) - if len(payload) > 0: - i = 0 - pkt = Ether(payload) - ip_proto = pkt[IP].proto - ip_sa_str = pkt[IP].src - src_ip_addr = ipv4ToInt(ip_sa_str) - ip_da_str = pkt[IP].dst - dst_ip_addr = ipv4ToInt(ip_da_str) - pktinfo = decodePacketInMetadata(global_data['cpm_packetin_id2data'], packet) - debug_packetin = False - if debug_packetin: - i += 1 - print("") - print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" - "" % (type(payload))) - print(payload) - print(pktinfo) - print("Scapy decode:") - print(pkt) - print("IPv4 proto %d (type %s)" - "" % (ip_proto, type(ip_proto))) - print("IPv4 SA %08x (type %s)" - "" % (src_ip_addr, type(src_ip_addr))) - print("IPv4 DA %08x (type %s)" - "" % (dst_ip_addr, type(dst_ip_addr))) - if pktinfo['metadata']['punt_reason'] == global_data['punt_reason_name2int']['FLOW_UNKNOWN']: - dest_port_int = lookup_table[message["sw"].name][ip_da_str] - decrement_ttl_bool = True - new_dscp_int = 5 - global_data['index'] = int(pkt[IP].dst.split('.')[3]) - dst_eth_addr = global_data[ip_da_str] - metadatas = packetOutMetadataList( - global_data['controller_opcode_name2int']['SEND_TO_PORT_IN_OPERAND0'], - 0, dest_port_int) - sendPacketOut(message["sw"], payload, metadatas) - addFlowRule(message["sw"], - src_ip_addr, - dst_ip_addr, - ip_proto, - dest_port_int, - new_dscp_int, - decrement_ttl_bool, - dst_eth_addr) - - print("For switch %s flow (SA=%s, DA=%s, proto=%d)" - " added table entry to send packets" - " to port %d with new DSCP %d" - "" % (message["sw"].name, ip_sa_str, ip_da_str, - ip_proto, dest_port_int, new_dscp_int)) + payload = message["packet-in"].payload + packet = message["packet-in"] + print("Received PacketIn message of length %d bytes from switch %s" + "" % (len(payload), message["sw"].name)) + if len(payload) > 0: + i = 0 + pkt = Ether(payload) + ip_proto = pkt[IP].proto + ip_sa_str = pkt[IP].src + src_ip_addr = ipv4ToInt(ip_sa_str) + ip_da_str = pkt[IP].dst + dst_ip_addr = ipv4ToInt(ip_da_str) + pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], + packet) + debug_packetin = False + if debug_packetin: + i += 1 + print("") + print("pktin %d of %d" % (i, len(payload))) + print("type(pktin.packet.payload)=%s" "" % (type(payload))) + print(payload) + print(pktinfo) + print("Scapy decode:") + print(pkt) + print("IPv4 proto %d (type %s)" % (ip_proto, type(ip_proto))) + print("IPv4 SA %08x (type %s)" % (src_ip_addr, type(src_ip_addr))) + print("IPv4 DA %08x (type %s)" % (dst_ip_addr, type(dst_ip_addr))) + if ( + pktinfo["metadata"]["punt_reason"] + == global_data["punt_reason_name2int"]["FLOW_UNKNOWN"] + ): + dest_port_int = lookup_table[message["sw"].name][ip_da_str] + decrement_ttl_bool = True + new_dscp_int = 5 + global_data["index"] = int(pkt[IP].dst.split(".")[3]) + dst_eth_addr = global_data[ip_da_str] + metadatas = packetOutMetadataList( + global_data["controller_opcode_name2int"]["SEND_TO_PORT_IN_OPERAND0"], + 0, + dest_port_int, + ) + sendPacketOut(message["sw"], payload, metadatas) + addFlowRule(message["sw"], + src_ip_addr, + dst_ip_addr, + ip_proto, + dest_port_int, + new_dscp_int, + decrement_ttl_bool, + dst_eth_addr) + + print("For switch %s flow (SA=%s, DA=%s, proto=%d)" + " added table entry to send packets" + " to port %d with new DSCP %d" + "" % (message["sw"].name, ip_sa_str, ip_da_str, + ip_proto, dest_port_int, new_dscp_int)) + async def processNotif(notif_queue): - while True: - notif = await notif_queue.get() - debug_notif = False + while True: + notif = await notif_queue.get() + debug_notif = False + if debug_notif: + print(notif) + pprint.pprint(notif_db) + if notif["type"] == "packet-in": + processPacket(notif) + printCounter( + global_data["p4info_helper"], + notif["sw"], + "MyIngress.ingressPktOutCounter", + global_data["index"], + ) + printCounter( + global_data["p4info_helper"], + notif["sw"], + "MyEgress.egressPktInCounter", + global_data["index"], + ) if debug_notif: - print(notif) - pprint.pprint(notif_db) - if notif["type"] == "packet-in": - processPacket(notif) - printCounter(global_data ['p4info_helper'], notif["sw"], 'MyIngress.ingressPktOutCounter', global_data ['index']) - printCounter(global_data ['p4info_helper'], notif["sw"], 'MyEgress.egressPktInCounter', global_data ['index']) - if debug_notif: - readTableRules(global_data ['p4info_helper'], notif["sw"]) - elif notif["type"] == "idle-notif": - # TODO: For extra credit, you can experiment with adjusting the stale time for notifications (e.g., 10 seconds) - # and optimize the behavior of the notification database (notif_db). - if notif["sw"].name not in notif_db: - notif_db[notif["sw"].name] = [] - else: - # Check if a notification is older than 10 seconds - cleanExpiredNotifiction(notif["sw"].name, 10) - - table_entry = createFlowRule(notif) - - if not checkFlowRule(notif["sw"].name, table_entry): - addNotification(notif["sw"].name, table_entry) - deleteFlowRule(notif["sw"], table_entry) - else: - print("Received idle timeout notification for switch=%s %s" - " It is duplicate of recently processed notification, so ignoring it." - "" % (notif["sw"].name, - flowCacheEntryToDebugStr(table_entry))) - notif_queue.task_done() - -async def packetInHandler(notif_queue,sw): + readTableRules(global_data["p4info_helper"], notif["sw"]) + elif notif["type"] == "idle-notif": + # TODO: For extra credit, you can experiment with + # adjusting the stale time for notifications (e.g., 10 + # seconds) and optimize the behavior of the notification + # database (notif_db). + if notif["sw"].name not in notif_db: + notif_db[notif["sw"].name] = [] + else: + # Check if a notification is older than 10 seconds + cleanExpiredNotifiction(notif["sw"].name, 10) + + table_entry = createFlowRule(notif) + + if not checkFlowRule(notif["sw"].name, table_entry): + addNotification(notif["sw"].name, table_entry) + deleteFlowRule(notif["sw"], table_entry) + else: + print("Received idle timeout notification for switch=%s %s" + " It is duplicate of recently processed notification," + " so ignoring it." + "" % (notif["sw"].name, + flowCacheEntryToDebugStr(table_entry))) + notif_queue.task_done() + + +async def packetInHandler(notif_queue, sw): # TODO: Implement the function logic to handle a packet-in message while True: try: packet_in = await asyncio.to_thread(sw.PacketIn) - #print(f"Received packet: {packet_in}") + # print(f"Received packet: {packet_in}") message = {"type": "packet-in", "sw": sw, "packet-in": packet_in} await notif_queue.put(message) @@ -433,7 +491,8 @@ async def packetInHandler(notif_queue,sw): traceback.print_exc() await asyncio.sleep(2) -async def idleTimeHandler(notif_queue,sw): + +async def idleTimeHandler(notif_queue, sw): # TODO: Implement the function logic to handle idle timeout notification while True: idle_notif = await asyncio.to_thread(sw.IdleTimeoutNotification) @@ -441,40 +500,44 @@ async def idleTimeHandler(notif_queue,sw): await notif_queue.put(message) await asyncio.sleep(5) + def printGrpcError(e): - print("gRPC Error:", e.details(), end=' ') + print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=' ') + print("(%s)" % status_code.name, end=" ") traceback = sys.exc_info()[2] print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + async def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file - global_data ['p4info_helper'] = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) - p4info_helper = global_data ['p4info_helper'] + global_data["p4info_helper"] = \ + p4runtime_lib.helper.P4InfoHelper(p4info_file_path) + p4info_helper = global_data["p4info_helper"] try: # Create a switch connection object for s1,s2,s3; # this is backed by a P4Runtime gRPC connection. # Also, dump all P4Runtime messages sent to switch to given txt files. s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s1', - address='127.0.0.1:50051', + name="s1", + address="127.0.0.1:50051", device_id=0, - proto_dump_file='logs/s1-p4runtime-requests.txt') + proto_dump_file="logs/s1-p4runtime-requests.txt") s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s2', - address='127.0.0.1:50052', + name="s2", + address="127.0.0.1:50052", device_id=1, - proto_dump_file='logs/s2-p4runtime-requests.txt') + proto_dump_file="logs/s2-p4runtime-requests.txt") s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s3', - address='127.0.0.1:50053', + name="s3", + address="127.0.0.1:50053", device_id=2, - proto_dump_file='logs/s3-p4runtime-requests.txt') + proto_dump_file="logs/s3-p4runtime-requests.txt") - # Send master arbitration update message to establish this controller as - # master (required by P4Runtime before performing any other write operation) + # Send master arbitration update message to establish this + # controller as master (required by P4Runtime before + # performing any other write operation) s1.MasterArbitrationUpdate() s2.MasterArbitrationUpdate() s3.MasterArbitrationUpdate() @@ -490,27 +553,34 @@ async def main(p4info_file_path, bmv2_file_path): bmv2_json_file_path=bmv2_file_path) print("Installed P4 Program using SetForwardingPipelineConfig on s3") - global_data['p4info_obj_map'] = makeP4infoObjMap(p4info_helper.p4info) - global_data['cpm_packetin_id2data'] = \ - controllerPacketMetadataDictKeyId(global_data['p4info_obj_map'], - "packet_in") + global_data["p4info_obj_map"] = makeP4infoObjMap(p4info_helper.p4info) + global_data["cpm_packetin_id2data"] = controllerPacketMetadataDictKeyId( + global_data["p4info_obj_map"], "packet_in" + ) - global_data['punt_reason_name2int'], global_data['punt_reason_int2name'] = \ - serializableEnumDict(p4info_helper.p4info, 'PuntReason_t') - global_data['controller_opcode_name2int'], global_data['controller_opcode_int2name'] = \ - serializableEnumDict(p4info_helper.p4info, 'ControllerOpcode_t') + global_data["punt_reason_name2int"], global_data["punt_reason_int2name"] = ( + serializableEnumDict(p4info_helper.p4info, "PuntReason_t") + ) + ( + global_data["controller_opcode_name2int"], + global_data["controller_opcode_int2name"], + ) = serializableEnumDict(p4info_helper.p4info, "ControllerOpcode_t") try: - replicas = [{ "egress_port": global_data['CPU_PORT'], "instance": 1 }] - writeCloneSession(s1, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - writeCloneSession(s2, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - writeCloneSession(s3, global_data['CPU_PORT_CLONE_SESSION_ID'], replicas) - - except shp4rt.P4RuntimeWriteException as e: - print("Got exception trying to configure clone session %d." - " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data['CPU_PORT_CLONE_SESSION_ID'])) + replicas = [{"egress_port": global_data["CPU_PORT"], + "instance": 1}] + writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], + replicas) + + except shp4rt.P4RuntimeWriteException: + print("Got exception trying to configure clone session %d." + " Assuming it was initialized already in an earlier" + " run of the controller." + "" % (global_data["CPU_PORT_CLONE_SESSION_ID"])) notif_queue = asyncio.Queue() @@ -524,33 +594,48 @@ async def main(p4info_file_path, bmv2_file_path): proc_notif = asyncio.create_task(processNotif(notif_queue)) - await asyncio.gather(pkt_s1,pkt_s2,pkt_s3,idle_notif_s1, idle_notif_s2, idle_notif_s3, proc_notif) + await asyncio.gather(pkt_s1, pkt_s2, pkt_s3, idle_notif_s1, + idle_notif_s2, idle_notif_s3, proc_notif) except KeyboardInterrupt: print(" Shutting down.") except grpc.RpcError as e: print(f"gRPC error occurred: {e}") - print(f"Status code: {e.code()}") # e.g., StatusCode.UNAVAILABLE or StatusCode.INVALID_ARGUMENT + # e.g., StatusCode.UNAVAILABLE or StatusCode.INVALID_ARGUMENT + print(f"Status code: {e.code()}") print(f"Details: {e.details()}") ShutdownAllSwitchConnections() -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/flowcache.p4.p4info.txtpb') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/flowcache.json') + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="P4Runtime Controller") + parser.add_argument( + "--p4info", + help="p4info proto in text format from p4c", + type=str, + action="store", + required=False, + default="./build/flowcache.p4.p4info.txtpb", + ) + parser.add_argument( + "--bmv2-json", + help="BMv2 JSON file from p4c", + type=str, + action="store", + required=False, + default="./build/flowcache.json", + ) args = parser.parse_args() if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info) + print("\np4info file not found: %s\nHave you run 'make'?" + "" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" + "" % (args.bmv2_json)) parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/link_monitor/probe_hdrs.py b/exercises/link_monitor/probe_hdrs.py index 614037fa..abb9729c 100755 --- a/exercises/link_monitor/probe_hdrs.py +++ b/exercises/link_monitor/probe_hdrs.py @@ -1,23 +1,29 @@ # SPDX-FileCopyrightText: 2019 Stephen Ibanez # # SPDX-License-Identifier: GPL-2.0-only -from scapy.all import * +from scapy.all import Packet, Ether, ByteField, BitField, IntField, bind_layers TYPE_PROBE = 0x812 + class Probe(Packet): - fields_desc = [ ByteField("hop_cnt", 0)] + fields_desc = [ByteField("hop_cnt", 0)] + class ProbeData(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("swid", 0, 7), - ByteField("port", 0), - IntField("byte_cnt", 0), - BitField("last_time", 0, 48), - BitField("cur_time", 0, 48)] + fields_desc = [ + BitField("bos", 0, 1), + BitField("swid", 0, 7), + ByteField("port", 0), + IntField("byte_cnt", 0), + BitField("last_time", 0, 48), + BitField("cur_time", 0, 48), + ] + class ProbeFwd(Packet): - fields_desc = [ ByteField("egress_spec", 0)] + fields_desc = [ByteField("egress_spec", 0)] + bind_layers(Ether, Probe, type=TYPE_PROBE) bind_layers(Probe, ProbeFwd, hop_cnt=0) @@ -25,4 +31,3 @@ class ProbeFwd(Packet): bind_layers(ProbeData, ProbeData, bos=0) bind_layers(ProbeData, ProbeFwd, bos=1) bind_layers(ProbeFwd, ProbeFwd) - diff --git a/exercises/link_monitor/receive.py b/exercises/link_monitor/receive.py index 6d363546..eb01f03f 100755 --- a/exercises/link_monitor/receive.py +++ b/exercises/link_monitor/receive.py @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-only -from probe_hdrs import * +from probe_hdrs import ProbeData, sniff def expand(x): @@ -13,19 +13,26 @@ def expand(x): x = x.payload yield x + def handle_pkt(pkt): if ProbeData in pkt: - data_layers = [l for l in expand(pkt) if l.name=='ProbeData'] + data_layers = [x for x in expand(pkt) if x.name == "ProbeData"] print("") for sw in data_layers: - utilization = 0 if sw.cur_time == sw.last_time else 8.0*sw.byte_cnt/(sw.cur_time - sw.last_time) - print("Switch {} - Port {}: {} Mbps".format(sw.swid, sw.port, utilization)) + utilization = ( + 0 + if sw.cur_time == sw.last_time + else 8.0 * sw.byte_cnt / (sw.cur_time - sw.last_time) + ) + print("Switch {} - Port {}: {} Mbps".format( + sw.swid, sw.port, utilization)) + def main(): - iface = 'eth0' + iface = "eth0" print("sniffing on {}".format(iface)) - sniff(iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/link_monitor/send.py b/exercises/link_monitor/send.py index 874e5902..b1eee7ab 100755 --- a/exercises/link_monitor/send.py +++ b/exercises/link_monitor/send.py @@ -6,29 +6,33 @@ import sys import time -from probe_hdrs import * +from scapy.all import Ether, sendp, get_if_hwaddr +from probe_hdrs import Probe, ProbeFwd def main(): - probe_pkt = Ether(dst='ff:ff:ff:ff:ff:ff', src=get_if_hwaddr('eth0')) / \ - Probe(hop_cnt=0) / \ - ProbeFwd(egress_spec=4) / \ - ProbeFwd(egress_spec=1) / \ - ProbeFwd(egress_spec=4) / \ - ProbeFwd(egress_spec=1) / \ - ProbeFwd(egress_spec=3) / \ - ProbeFwd(egress_spec=2) / \ - ProbeFwd(egress_spec=3) / \ - ProbeFwd(egress_spec=2) / \ - ProbeFwd(egress_spec=1) + probe_pkt = ( + Ether(dst="ff:ff:ff:ff:ff:ff", src=get_if_hwaddr("eth0")) + / Probe(hop_cnt=0) + / ProbeFwd(egress_spec=4) + / ProbeFwd(egress_spec=1) + / ProbeFwd(egress_spec=4) + / ProbeFwd(egress_spec=1) + / ProbeFwd(egress_spec=3) + / ProbeFwd(egress_spec=2) + / ProbeFwd(egress_spec=3) + / ProbeFwd(egress_spec=2) + / ProbeFwd(egress_spec=1) + ) while True: try: - sendp(probe_pkt, iface='eth0') + sendp(probe_pkt, iface="eth0") time.sleep(1) except KeyboardInterrupt: sys.exit() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/exercises/load_balance/receive.py b/exercises/load_balance/receive.py index 7672976c..b4d14575 100755 --- a/exercises/load_balance/receive.py +++ b/exercises/load_balance/receive.py @@ -13,49 +13,51 @@ IPOption, ShortField, get_if_list, - sniff + sniff, ) from scapy.layers.inet import _IPOption_HDR def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] + fields_desc = [ + _IPOption_HDR, + FieldLenField("length", None, fmt="B", length_of="swids", + adjust=lambda pkt, x: x + 4), + ShortField("count", 0), + FieldListField( + "swids", [], IntField("", 0), length_from=lambda pkt: pkt.count * 4 + ), + ] + + def handle_pkt(pkt): print("got a packet") pkt.show2() -# hexdump(pkt) + # hexdump(pkt) sys.stdout.flush() def main(): - ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i] + ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] print("sniffing on %s" % iface) sys.stdout.flush() - sniff(filter="tcp", iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(filter="tcp", iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/load_balance/send.py b/exercises/load_balance/send.py index ee98ff47..e1a77a67 100755 --- a/exercises/load_balance/send.py +++ b/exercises/load_balance/send.py @@ -11,20 +11,20 @@ def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + def main(): - if len(sys.argv)<3: + if len(sys.argv) < 3: print('pass 2 arguments: ""') exit(1) @@ -32,11 +32,16 @@ def main(): iface = get_if() print("sending on interface %s to %s" % (iface, str(addr))) - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') - pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2] + pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + pkt = ( + pkt + / IP(dst=addr) + / TCP(dport=1234, sport=random.randint(49152, 65535)) + / sys.argv[2] + ) pkt.show2() sendp(pkt, iface=iface, verbose=False) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/mri/receive.py b/exercises/mri/receive.py index 71c63a09..398d407a 100755 --- a/exercises/mri/receive.py +++ b/exercises/mri/receive.py @@ -13,55 +13,57 @@ PacketListField, ShortField, get_if_list, - sniff + sniff, ) from scapy.layers.inet import _IPOption_HDR def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] + fields_desc = [IntField("swid", 0), IntField("qdepth", 0)] + def extract_padding(self, p): - return "", p + return "", p + class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] + fields_desc = [ + _IPOption_HDR, + FieldLenField("length", None, fmt="B", length_of="swtraces", + adjust=lambda pkt, x: x * 2 + 4), + ShortField("count", 0), + PacketListField( + "swtraces", [], SwitchTrace, count_from=lambda pkt: (pkt.count * 1) + ), + ] + def handle_pkt(pkt): print("got a packet") pkt.show2() -# hexdump(pkt) + # hexdump(pkt) sys.stdout.flush() def main(): - iface = 'eth0' + iface = "eth0" print("sniffing on %s" % iface) sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/mri/send.py b/exercises/mri/send.py index d805efac..433f8803 100755 --- a/exercises/mri/send.py +++ b/exercises/mri/send.py @@ -20,70 +20,73 @@ ShortField, get_if_hwaddr, get_if_list, - sendp + sendp, ) from scapy.layers.inet import _IPOption_HDR def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class SwitchTrace(Packet): - fields_desc = [ IntField("swid", 0), - IntField("qdepth", 0)] + fields_desc = [IntField("swid", 0), IntField("qdepth", 0)] + def extract_padding(self, p): - return "", p + return "", p + class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swtraces", - adjust=lambda pkt,l:l*2+4), - ShortField("count", 0), - PacketListField("swtraces", - [], - SwitchTrace, - count_from=lambda pkt:(pkt.count*1)) ] + fields_desc = [ + _IPOption_HDR, + FieldLenField("length", None, fmt="B", length_of="swtraces", + adjust=lambda pkt, x: x * 2 + 4), + ShortField("count", 0), + PacketListField( + "swtraces", [], SwitchTrace, count_from=lambda pkt: (pkt.count * 1) + ), + ] def main(): - if len(sys.argv)<3: + if len(sys.argv) < 3: print('pass 2 arguments: ""') exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - dst=addr, options = IPOption_MRI(count=0, - swtraces=[])) / UDP( - dport=4321, sport=1234) / sys.argv[2] + pkt = ( + Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + / IP(dst=addr, options=IPOption_MRI(count=0, swtraces=[])) + / UDP(dport=4321, sport=1234) + / sys.argv[2] + ) - # pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( - # dst=addr, options = IPOption_MRI(count=2, - # swtraces=[SwitchTrace(swid=0,qdepth=0), SwitchTrace(swid=1,qdepth=0)])) / UDP( - # dport=4321, sport=1234) / sys.argv[2] + # pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP( + # dst=addr, options = IPOption_MRI(count=2, + # swtraces=[SwitchTrace(swid=0,qdepth=0), SwitchTrace(swid=1,qdepth=0)])) / UDP( + # dport=4321, sport=1234) / sys.argv[2] pkt.show2() - #hexdump(pkt) + # hexdump(pkt) try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) + for i in range(int(sys.argv[3])): + sendp(pkt, iface=iface) + sleep(1) except KeyboardInterrupt: raise -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/p4runtime/mycontroller.py b/exercises/p4runtime/mycontroller.py index b9e43665..113052cf 100755 --- a/exercises/p4runtime/mycontroller.py +++ b/exercises/p4runtime/mycontroller.py @@ -13,8 +13,8 @@ # Import P4Runtime lib from parent utils dir # Probably there's a better way of doing this. sys.path.append( - os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../utils/')) + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../utils/") +) import p4runtime_lib.bmv2 import p4runtime_lib.helper from p4runtime_lib.error_utils import printGrpcError @@ -24,8 +24,9 @@ SWITCH_TO_SWITCH_PORT = 2 -def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, - dst_eth_addr, dst_ip_addr): +def writeTunnelRules( + p4info_helper, ingress_sw, egress_sw, tunnel_id, dst_eth_addr, dst_ip_addr +): """ Installs three rules: 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that @@ -46,31 +47,32 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, # 1) Tunnel Ingress Rule table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.ipv4_lpm", - match_fields={ - "hdr.ipv4.dstAddr": (dst_ip_addr, 32) - }, + match_fields={"hdr.ipv4.dstAddr": (dst_ip_addr, 32)}, action_name="MyIngress.myTunnel_ingress", action_params={ "dst_id": tunnel_id, - }) + }, + ) ingress_sw.WriteTableEntry(table_entry) print("Installed ingress tunnel rule on %s" % ingress_sw.name) # 2) Tunnel Transit Rule - # The rule will need to be added to the myTunnel_exact table and match on - # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded - # using the myTunnel_forward action on the port connected to the next switch. + # The rule will need to be added to the myTunnel_exact table and + # match on the tunnel ID (hdr.myTunnel.dst_id). Traffic will need + # to be forwarded using the myTunnel_forward action on the port + # connected to the next switch. # - # For our simple topology, switch 1 and switch 2 are connected using a - # link attached to port 2 on both switches. We have defined a variable at - # the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output - # port for this action. + # For our simple topology, switch 1 and switch 2 are connected + # using a link attached to port 2 on both switches. We have + # defined a variable at the top of the file, + # SWITCH_TO_SWITCH_PORT, that you can use as the output port for + # this action. # - # We will only need a transit rule on the ingress switch because we are - # using a simple topology. In general, you'll need on transit rule for - # each switch in the path (except the last switch, which has the egress rule), - # and you will need to select the port dynamically for each switch based on - # your topology. + # We will only need a transit rule on the ingress switch because + # we are using a simple topology. In general, you'll need on + # transit rule for each switch in the path (except the last + # switch, which has the egress rule), and you will need to select + # the port dynamically for each switch based on your topology. # TODO build the transit rule # TODO install the transit rule on the ingress switch @@ -83,14 +85,10 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, # connected to. table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, + match_fields={"hdr.myTunnel.dst_id": tunnel_id}, action_name="MyIngress.myTunnel_egress", - action_params={ - "dstAddr": dst_eth_addr, - "port": SWITCH_TO_HOST_PORT - }) + action_params={"dstAddr": dst_eth_addr, "port": SWITCH_TO_HOST_PORT}, + ) egress_sw.WriteTableEntry(table_entry) print("Installed egress tunnel rule on %s" % egress_sw.name) @@ -102,21 +100,21 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print('\n----- Reading tables rules for %s -----' % sw.name) + print("\n----- Reading tables rules for %s -----" % sw.name) for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate # the IDs in the entry to names print(entry) - print('-----') + print("-----") def printCounter(p4info_helper, sw, counter_name, index): """ - Reads the specified counter at the specified index from the switch. In our - program, the index is the tunnel ID. If the index is 0, it will return all - values from the counter. + Reads the specified counter at the specified index from the + switch. In our program, the index is the tunnel ID. If the index + is 0, it will return all values from the counter. :param p4info_helper: the P4Info helper :param sw: the switch connection @@ -126,10 +124,17 @@ def printCounter(p4info_helper, sw, counter_name, index): for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry - print("%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - )) + print( + "%s %s %d: %d packets (%d bytes)" + % ( + sw.name, + counter_name, + index, + counter.data.packet_count, + counter.data.byte_count, + ) + ) + def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file @@ -140,49 +145,71 @@ def main(p4info_file_path, bmv2_file_path): # this is backed by a P4Runtime gRPC connection. # Also, dump all P4Runtime messages sent to switch to given txt files. s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s1', - address='127.0.0.1:50051', + name="s1", + address="127.0.0.1:50051", device_id=0, - proto_dump_file='logs/s1-p4runtime-requests.txt') + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s2', - address='127.0.0.1:50052', + name="s2", + address="127.0.0.1:50052", device_id=1, - proto_dump_file='logs/s2-p4runtime-requests.txt') + proto_dump_file="logs/s2-p4runtime-requests.txt", + ) - # Send master arbitration update message to establish this controller as - # master (required by P4Runtime before performing any other write operation) + # Send master arbitration update message to establish this + # controller as master (required by P4Runtime before + # performing any other write operation) s1.MasterArbitrationUpdate() s2.MasterArbitrationUpdate() # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s1.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s1") - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s2.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s2") # Write the rules that tunnel traffic from h1 to h2 - writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, - dst_eth_addr="08:00:00:00:02:22", dst_ip_addr="10.0.2.2") + writeTunnelRules( + p4info_helper, + ingress_sw=s1, + egress_sw=s2, + tunnel_id=100, + dst_eth_addr="08:00:00:00:02:22", + dst_ip_addr="10.0.2.2", + ) # Write the rules that tunnel traffic from h2 to h1 - writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, - dst_eth_addr="08:00:00:00:01:11", dst_ip_addr="10.0.1.1") - - # TODO Uncomment the following two lines to read table entries from s1 and s2 + writeTunnelRules( + p4info_helper, + ingress_sw=s2, + egress_sw=s1, + tunnel_id=200, + dst_eth_addr="08:00:00:00:01:11", + dst_ip_addr="10.0.1.1", + ) + + # TODO Uncomment the following two lines to read table entries + # from s1 and s2 # readTableRules(p4info_helper, s1) # readTableRules(p4info_helper, s2) # Print the tunnel counters every 2 seconds while True: sleep(2) - print('\n----- Reading tunnel counters -----') - printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 100) - printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100) - printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 200) - printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 200) + print("\n----- Reading tunnel counters -----") + printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", + 100) + printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", + 100) + printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", + 200) + printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", + 200) except KeyboardInterrupt: print(" Shutting down.") @@ -191,22 +218,35 @@ def main(p4info_file_path, bmv2_file_path): ShutdownAllSwitchConnections() -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.p4.p4info.txtpb') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.json') + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="P4Runtime Controller") + parser.add_argument( + "--p4info", + help="p4info proto in text format from p4c", + type=str, + action="store", + required=False, + default="./build/advanced_tunnel.p4.p4info.txtpb", + ) + parser.add_argument( + "--bmv2-json", + help="BMv2 JSON file from p4c", + type=str, + action="store", + required=False, + default="./build/advanced_tunnel.json", + ) args = parser.parse_args() if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info) + print("\np4info file not found: %s\nHave you run 'make'?" + "" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" + "" % (args.bmv2_json)) parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/p4runtime/solution/mycontroller.py b/exercises/p4runtime/solution/mycontroller.py index 05815cf0..86ffa666 100755 --- a/exercises/p4runtime/solution/mycontroller.py +++ b/exercises/p4runtime/solution/mycontroller.py @@ -13,8 +13,8 @@ # Import P4Runtime lib from parent utils dir # Probably there's a better way of doing this. sys.path.append( - os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../utils/')) + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../utils/") +) import p4runtime_lib.bmv2 import p4runtime_lib.helper from p4runtime_lib.switch import ShutdownAllSwitchConnections @@ -23,8 +23,9 @@ SWITCH_TO_SWITCH_PORT = 2 -def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, - dst_eth_addr, dst_ip_addr): +def writeTunnelRules( + p4info_helper, ingress_sw, egress_sw, tunnel_id, dst_eth_addr, dst_ip_addr +): """ Installs three rules: 1) An tunnel ingress rule on the ingress switch in the ipv4_lpm table that @@ -45,43 +46,41 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, # 1) Tunnel Ingress Rule table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.ipv4_lpm", - match_fields={ - "hdr.ipv4.dstAddr": (dst_ip_addr, 32) - }, + match_fields={"hdr.ipv4.dstAddr": (dst_ip_addr, 32)}, action_name="MyIngress.myTunnel_ingress", action_params={ "dst_id": tunnel_id, - }) + }, + ) ingress_sw.WriteTableEntry(table_entry) print("Installed ingress tunnel rule on %s" % ingress_sw.name) # 2) Tunnel Transit Rule - # The rule will need to be added to the myTunnel_exact table and match on - # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded - # using the myTunnel_forward action on the port connected to the next switch. + # The rule will need to be added to the myTunnel_exact table and + # match on the tunnel ID (hdr.myTunnel.dst_id). Traffic will need + # to be forwarded using the myTunnel_forward action on the port + # connected to the next switch. # - # For our simple topology, switch 1 and switch 2 are connected using a - # link attached to port 2 on both switches. We have defined a variable at - # the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output - # port for this action. + # For our simple topology, switch 1 and switch 2 are connected + # using a link attached to port 2 on both switches. We have + # defined a variable at the top of the file, + # SWITCH_TO_SWITCH_PORT, that you can use as the output port for + # this action. # - # We will only need a transit rule on the ingress switch because we are - # using a simple topology. In general, you'll need on transit rule for - # each switch in the path (except the last switch, which has the egress rule), - # and you will need to select the port dynamically for each switch based on - # your topology. + # We will only need a transit rule on the ingress switch because + # we are using a simple topology. In general, you'll need on + # transit rule for each switch in the path (except the last + # switch, which has the egress rule), and you will need to select + # the port dynamically for each switch based on your topology. # TODO build the transit rule # TODO install the transit rule on the ingress switch table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, + match_fields={"hdr.myTunnel.dst_id": tunnel_id}, action_name="MyIngress.myTunnel_forward", - action_params={ - "port": SWITCH_TO_SWITCH_PORT - }) + action_params={"port": SWITCH_TO_SWITCH_PORT}, + ) ingress_sw.WriteTableEntry(table_entry) print("Installed transit tunnel rule on %s" % ingress_sw.name) @@ -92,14 +91,10 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id, # connected to. table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.myTunnel_exact", - match_fields={ - "hdr.myTunnel.dst_id": tunnel_id - }, + match_fields={"hdr.myTunnel.dst_id": tunnel_id}, action_name="MyIngress.myTunnel_egress", - action_params={ - "dstAddr": dst_eth_addr, - "port": SWITCH_TO_HOST_PORT - }) + action_params={"dstAddr": dst_eth_addr, "port": SWITCH_TO_HOST_PORT}, + ) egress_sw.WriteTableEntry(table_entry) print("Installed egress tunnel rule on %s" % egress_sw.name) @@ -111,30 +106,38 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print('\n----- Reading tables rules for %s -----' % sw.name) + print("\n----- Reading tables rules for %s -----" % sw.name) for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate # the IDs in the entry to names table_name = p4info_helper.get_tables_name(entry.table_id) - print('%s: ' % table_name, end=' ') + print("%s: " % table_name, end=" ") for m in entry.match: - print(p4info_helper.get_match_field_name(table_name, m.field_id), end=' ') - print('%r' % (p4info_helper.get_match_field_value(m),), end=' ') + print( + p4info_helper.get_match_field_name(table_name, m.field_id), + end=" " + ) + print("%r" % (p4info_helper.get_match_field_value(m)), end=" ") action = entry.action.action action_name = p4info_helper.get_actions_name(action.action_id) - print('->', action_name, end=' ') + print("->", action_name, end=" ") for p in action.params: - print(p4info_helper.get_action_param_name(action_name, p.param_id), end=' ') - print('%r' % p.value, end=' ') + print( + p4info_helper.get_action_param_name(action_name, + p.param_id), + end=" ", + ) + print("%r" % p.value, end=" ") print() + def printCounter(p4info_helper, sw, counter_name, index): """ - Reads the specified counter at the specified index from the switch. In our - program, the index is the tunnel ID. If the index is 0, it will return all - values from the counter. + Reads the specified counter at the specified index from the + switch. In our program, the index is the tunnel ID. If the index + is 0, it will return all values from the counter. :param p4info_helper: the P4Info helper :param sw: the switch connection @@ -144,17 +147,26 @@ def printCounter(p4info_helper, sw, counter_name, index): for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry - print("%s %s %d: %d packets (%d bytes)" % ( - sw.name, counter_name, index, - counter.data.packet_count, counter.data.byte_count - )) + print( + "%s %s %d: %d packets (%d bytes)" + % ( + sw.name, + counter_name, + index, + counter.data.packet_count, + counter.data.byte_count, + ) + ) + def printGrpcError(e): - print("gRPC Error:", e.details(), end=' ') + print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=' ') + print("(%s)" % status_code.name, end=" ") traceback = sys.exc_info()[2] - print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, + traceback.tb_lineno)) + def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file @@ -165,49 +177,71 @@ def main(p4info_file_path, bmv2_file_path): # this is backed by a P4Runtime gRPC connection. # Also, dump all P4Runtime messages sent to switch to given txt files. s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s1', - address='127.0.0.1:50051', + name="s1", + address="127.0.0.1:50051", device_id=0, - proto_dump_file='logs/s1-p4runtime-requests.txt') + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( - name='s2', - address='127.0.0.1:50052', + name="s2", + address="127.0.0.1:50052", device_id=1, - proto_dump_file='logs/s2-p4runtime-requests.txt') + proto_dump_file="logs/s2-p4runtime-requests.txt", + ) - # Send master arbitration update message to establish this controller as - # master (required by P4Runtime before performing any other write operation) + # Send master arbitration update message to establish this + # controller as master (required by P4Runtime before + # performing any other write operation) s1.MasterArbitrationUpdate() s2.MasterArbitrationUpdate() # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s1.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s1") - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s2.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s2") # Write the rules that tunnel traffic from h1 to h2 - writeTunnelRules(p4info_helper, ingress_sw=s1, egress_sw=s2, tunnel_id=100, - dst_eth_addr="08:00:00:00:02:22", dst_ip_addr="10.0.2.2") + writeTunnelRules( + p4info_helper, + ingress_sw=s1, + egress_sw=s2, + tunnel_id=100, + dst_eth_addr="08:00:00:00:02:22", + dst_ip_addr="10.0.2.2", + ) # Write the rules that tunnel traffic from h2 to h1 - writeTunnelRules(p4info_helper, ingress_sw=s2, egress_sw=s1, tunnel_id=200, - dst_eth_addr="08:00:00:00:01:11", dst_ip_addr="10.0.1.1") + writeTunnelRules( + p4info_helper, + ingress_sw=s2, + egress_sw=s1, + tunnel_id=200, + dst_eth_addr="08:00:00:00:01:11", + dst_ip_addr="10.0.1.1", + ) - # TODO Uncomment the following two lines to read table entries from s1 and s2 + # TODO Uncomment the following two lines to read table entries + # from s1 and s2 readTableRules(p4info_helper, s1) readTableRules(p4info_helper, s2) # Print the tunnel counters every 2 seconds while True: sleep(2) - print('\n----- Reading tunnel counters -----') - printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 100) - printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100) - printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 200) - printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 200) + print("\n----- Reading tunnel counters -----") + printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", + 100) + printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", + 100) + printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", + 200) + printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", + 200) except KeyboardInterrupt: print(" Shutting down.") @@ -216,22 +250,35 @@ def main(p4info_file_path, bmv2_file_path): ShutdownAllSwitchConnections() -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='P4Runtime Controller') - parser.add_argument('--p4info', help='p4info proto in text format from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.p4.p4info.txtpb') - parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c', - type=str, action="store", required=False, - default='./build/advanced_tunnel.json') + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="P4Runtime Controller") + parser.add_argument( + "--p4info", + help="p4info proto in text format from p4c", + type=str, + action="store", + required=False, + default="./build/advanced_tunnel.p4.p4info.txtpb", + ) + parser.add_argument( + "--bmv2-json", + help="BMv2 JSON file from p4c", + type=str, + action="store", + required=False, + default="./build/advanced_tunnel.json", + ) args = parser.parse_args() if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info) + print("\np4info file not found: %s\nHave you run 'make'?" + "" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" + "" % (args.bmv2_json)) parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/qos/receive.py b/exercises/qos/receive.py index 6d54acfc..cbee9c16 100755 --- a/exercises/qos/receive.py +++ b/exercises/qos/receive.py @@ -16,11 +16,11 @@ def handle_pkt(pkt): def main(): - iface = 'eth0' + iface = "eth0" print("sniffing on %s" % iface) sys.stdout.flush() sniff(iface=iface, prn=lambda x: handle_pkt(x)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/qos/send.py b/exercises/qos/send.py index 2bab4be3..8818442b 100755 --- a/exercises/qos/send.py +++ b/exercises/qos/send.py @@ -25,8 +25,11 @@ def get_if(): def main(): parser = argparse.ArgumentParser() - parser.add_argument("--p", help="Protocol name To send TCP/UDP etc packets", type=str) - parser.add_argument("--des", help="IP address of the destination", type=str) + parser.add_argument( + "--p", help="Protocol name To send TCP/UDP etc packets", type=str + ) + parser.add_argument("--des", help="IP address of the destination", + type=str) parser.add_argument("--m", help="Raw Message", type=str) parser.add_argument("--dur", help="in seconds", type=str) args = parser.parse_args() @@ -34,8 +37,13 @@ def main(): if args.p and args.des and args.m and args.dur: addr = socket.gethostbyname(args.des) iface = get_if() - if args.p == 'UDP': - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, tos=1) / UDP(dport=4321, sport=1234) / args.m + if args.p == "UDP": + pkt = ( + Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + / IP(dst=addr, tos=1) + / UDP(dport=4321, sport=1234) + / args.m + ) pkt.show2() try: for i in range(int(args.dur)): @@ -43,8 +51,13 @@ def main(): sleep(1) except KeyboardInterrupt: raise - elif args.p == 'TCP': - pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") / IP(dst=addr, tos=1) / TCP() / args.m + elif args.p == "TCP": + pkt = ( + Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") + / IP(dst=addr, tos=1) + / TCP() + / args.m + ) pkt.show2() try: for i in range(int(args.dur)): @@ -54,5 +67,5 @@ def main(): raise -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/source_routing/receive.py b/exercises/source_routing/receive.py index 8e5e6b3f..249f6a10 100755 --- a/exercises/source_routing/receive.py +++ b/exercises/source_routing/receive.py @@ -5,57 +5,76 @@ # SPDX-License-Identifier: GPL-2.0-only import sys -from scapy.all import Ether, IPOption, Packet, bind_layers, get_if_list, sniff -from scapy.fields import * +from scapy.all import ( + Ether, + IPOption, + Packet, + bind_layers, + get_if_list, + sniff, +) +from scapy.fields import ( + BitField, + IntField, + FieldListField, + FieldLenField, + ShortField, + XShortField, +) from scapy.layers.inet import _IPOption_HDR def get_if(): - ifs=get_if_list() - iface=None + iface = None for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", - length_of="swids", - adjust=lambda pkt,l:l+4), - ShortField("count", 0), - FieldListField("swids", - [], - IntField("", 0), - length_from=lambda pkt:pkt.count*4) ] + fields_desc = [ + _IPOption_HDR, + FieldLenField("length", None, fmt="B", length_of="swids", + adjust=lambda pkt, x: x + 4), + ShortField("count", 0), + FieldListField("swids", [], IntField("", 0), + length_from=lambda pkt: pkt.count * 4), + ] + + def handle_pkt(pkt): print("got a packet") pkt.show2() -# hexdump(pkt) + # hexdump(pkt) sys.stdout.flush() + class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] + fields_desc = [BitField("bos", 0, 1), BitField("port", 0, 15)] + + class SourceRoutingTail(Packet): - fields_desc = [ XShortField("etherType", 0x800)] + fields_desc = [XShortField("etherType", 0x800)] + bind_layers(Ether, SourceRoute, type=0x1234) bind_layers(SourceRoute, SourceRoute, bos=0) bind_layers(SourceRoute, SourceRoutingTail, bos=1) + def main(): - iface = 'eth0' + iface = "eth0" print("sniffing on %s" % iface) sys.stdout.flush() - sniff(filter="udp and port 4321", iface = iface, - prn = lambda x: handle_pkt(x)) + sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/exercises/source_routing/send.py b/exercises/source_routing/send.py index 0ddd73fd..e02b636d 100755 --- a/exercises/source_routing/send.py +++ b/exercises/source_routing/send.py @@ -14,35 +14,36 @@ bind_layers, get_if_hwaddr, get_if_list, - sendp + sendp, ) -from scapy.fields import * +from scapy.fields import BitField def get_if(): - ifs=get_if_list() - iface=None # "h1-eth0" + iface = None # "h1-eth0" for i in get_if_list(): if "eth0" in i: - iface=i - break; + iface = i + break if not iface: print("Cannot find eth0 interface") exit(1) return iface + class SourceRoute(Packet): - fields_desc = [ BitField("bos", 0, 1), - BitField("port", 0, 15)] + fields_desc = [BitField("bos", 0, 1), BitField("port", 0, 15)] + bind_layers(Ether, SourceRoute, type=0x1234) bind_layers(SourceRoute, SourceRoute, bos=0) bind_layers(SourceRoute, IP, bos=1) + def main(): - if len(sys.argv)<2: - print('pass 2 arguments: ') + if len(sys.argv) < 2: + print("pass 2 arguments: ") exit(1) addr = socket.gethostbyname(sys.argv[1]) @@ -51,18 +52,22 @@ def main(): while True: print() - s = str(input('Type space separated port nums ' - '(example: "2 3 2 2 1") or "q" to quit: ')) + s = str( + input( + "Type space separated port nums " + '(example: "2 3 2 2 1") or "q" to quit: ' + ) + ) if s == "q": - break; + break print() i = 0 - pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff'); + pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") for p in s.split(" "): try: pkt = pkt / SourceRoute(bos=0, port=int(p)) - i = i+1 + i = i + 1 except ValueError: pass if pkt.haslayer(SourceRoute): @@ -72,10 +77,10 @@ def main(): pkt.show2() sendp(pkt, iface=iface, verbose=False) - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=3); - #pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=2); - #pkt = pkt / SourceRoute(bos=1, port=1) + # pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=3); + # pkt = pkt / SourceRoute(bos=0, port=2) / SourceRoute(bos=0, port=2); + # pkt = pkt / SourceRoute(bos=1, port=1) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/utils/mininet/appcontroller.py b/utils/mininet/appcontroller.py index 03f53381..2eb62c5d 100644 --- a/utils/mininet/appcontroller.py +++ b/utils/mininet/appcontroller.py @@ -8,36 +8,51 @@ class AppController: - def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): + def __init__(self, manifest=None, target=None, topo=None, net=None, + links=None): self.manifest = manifest self.target = target - self.conf = manifest['targets'][target] + self.conf = manifest["targets"][target] self.topo = topo self.net = net self.links = links def read_entries(self, filename): entries = [] - with open(filename, 'r') as f: + with open(filename, "r") as f: for line in f: line = line.strip() - if line == '': continue + if line == "": + continue entries.append(line) return entries def add_entries(self, thrift_port=9090, sw=None, entries=None): assert entries - if sw: thrift_port = sw.thrift_port + if sw: + thrift_port = sw.thrift_port - print('\n'.join(entries)) - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE) - p.communicate(input='\n'.join(entries)) + print("\n".join(entries)) + p = subprocess.Popen( + ["simple_switch_CLI", "--thrift-port", str(thrift_port)], + stdin=subprocess.PIPE, + ) + p.communicate(input="\n".join(entries)) def read_register(self, register, idx, thrift_port=9090, sw=None): - if sw: thrift_port = sw.thrift_port - p = subprocess.Popen(['simple_switch_CLI', '--thrift-port', str(thrift_port)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = [l for l in stdout.split('\n') if ' %s[%d]' % (register, idx) in l][0].split('= ', 1)[1] + if sw: + thrift_port = sw.thrift_port + p = subprocess.Popen( + ["simple_switch_CLI", "--thrift-port", str(thrift_port)], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = p.communicate(input="register_read %s %d" + "" % (register, idx)) + reg_val = [x for x in stdout.split("\n") if " %s[%d]" % (register, idx) in x][ + 0 + ].split("= ", 1)[1] return int(reg_val) def start(self): @@ -45,13 +60,17 @@ def start(self): entries = {} for sw in self.topo.switches(): entries[sw] = [] - if 'switches' in self.conf and sw in self.conf['switches'] and 'entries' in self.conf['switches'][sw]: - extra_entries = self.conf['switches'][sw]['entries'] - if type(extra_entries) == list: # array of entries + if ( + "switches" in self.conf + and sw in self.conf["switches"] + and "entries" in self.conf["switches"][sw] + ): + extra_entries = self.conf["switches"][sw]["entries"] + if isinstance(extra_entries, list): # array of entries entries[sw] += extra_entries - else: # path to file that contains entries + else: # path to file that contains entries entries[sw] += self.read_entries(extra_entries) - #entries[sw] += [ + # entries[sw] += [ # 'table_set_default send_frame _drop', # 'table_set_default forward _drop', # 'table_set_default ipv4_lpm _drop'] @@ -59,39 +78,46 @@ def start(self): for host_name in self.topo._host_links: h = self.net.get(host_name) for link in list(self.topo._host_links[host_name].values()): - sw = link['sw'] - #entries[sw].append('table_add send_frame rewrite_mac %d => %s' % (link['sw_port'], link['sw_mac'])) - #entries[sw].append('table_add forward set_dmac %s => %s' % (link['host_ip'], link['host_mac'])) - #entries[sw].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (link['host_ip'], link['host_ip'], link['sw_port'])) - iface = h.intfNames()[link['idx']] + sw = link["sw"] + # entries[sw].append('table_add send_frame rewrite_mac %d => %s' % (link['sw_port'], link['sw_mac'])) + # entries[sw].append('table_add forward set_dmac %s => %s' % (link['host_ip'], link['host_mac'])) + # entries[sw].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (link['host_ip'], link['host_ip'], link['sw_port'])) + iface = h.intfNames()[link["idx"]] # use mininet to set ip and mac to let it know the change - h.setIP(link['host_ip'], 24) - h.setMAC(link['host_mac']) - #h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd('arp -i %s -s %s %s' % (iface, link['sw_ip'], link['sw_mac'])) - h.cmd('ethtool --offload %s rx off tx off' % iface) - h.cmd('ip route add %s dev %s' % (link['sw_ip'], iface)) - h.setDefaultRoute("via %s" % link['sw_ip']) + h.setIP(link["host_ip"], 24) + h.setMAC(link["host_mac"]) + # h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) + h.cmd("arp -i %s -s %s %s" % (iface, link["sw_ip"], + link["sw_mac"])) + h.cmd("ethtool --offload %s rx off tx off" % iface) + h.cmd("ip route add %s dev %s" % (link["sw_ip"], iface)) + h.setDefaultRoute("via %s" % link["sw_ip"]) for h in self.net.hosts: h_link = list(self.topo._host_links[h.name].values())[0] for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0]=='h') - if not path: continue - if not path[1][0] == 's': continue # next hop is a switch - sw_link = self.topo._sw_links[sw.name][path[1]] - #entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) - #entries[sw.name].append('table_add forward set_dmac %s => %s' % (h_link['host_ip'], sw_link[1]['mac'])) - #entries[sw.name].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (h_link['host_ip'], h_link['host_ip'], sw_link[0]['port'])) + path = shortestpath.get(sw.name, h.name, + exclude=lambda n: n[0] == "h") + if not path: + continue + if not path[1][0] == "s": + continue # next hop is a switch + # sw_link = self.topo._sw_links[sw.name][path[1]] + # entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) + # entries[sw.name].append('table_add forward set_dmac %s => %s' % (h_link['host_ip'], sw_link[1]['mac'])) + # entries[sw.name].append('table_add ipv4_lpm set_nhop %s/32 => %s %d' % (h_link['host_ip'], h_link['host_ip'], sw_link[0]['port'])) for h2 in self.net.hosts: - if h == h2: continue - path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0]=='h') - if not path: continue + if h == h2: + continue + path = shortestpath.get(h.name, h2.name, + exclude=lambda n: n[0] == "h") + if not path: + continue h_link = self.topo._host_links[h.name][path[1]] h2_link = list(self.topo._host_links[h2.name].values())[0] - h.cmd('ip route add %s via %s' % (h2_link['host_ip'], h_link['sw_ip'])) - + h.cmd("ip route add %s via %s" + "" % (h2_link["host_ip"], h_link["sw_ip"])) print("**********") print("Configuring entries in p4 tables") diff --git a/utils/mininet/apptopo.py b/utils/mininet/apptopo.py index 8d055f11..eb0b6d33 100644 --- a/utils/mininet/apptopo.py +++ b/utils/mininet/apptopo.py @@ -6,20 +6,28 @@ class AppTopo(Topo): - def __init__(self, links, latencies={}, manifest=None, target=None, - log_dir="/tmp", bws={}, **opts): + def __init__( + self, + links, + latencies={}, + manifest=None, + target=None, + log_dir="/tmp", + bws={}, + **opts + ): Topo.__init__(self, **opts) nodes = sum(list(map(list, list(zip(*links)))), []) - host_names = sorted(list(set([n for n in nodes if n[0] == 'h']))) - sw_names = sorted(list(set([n for n in nodes if n[0] == 's']))) + host_names = sorted(list(set([n for n in nodes if n[0] == "h"]))) + sw_names = sorted(list(set([n for n in nodes if n[0] == "s"]))) sw_ports = dict([(sw, []) for sw in sw_names]) self._host_links = {} self._sw_links = dict([(sw, {}) for sw in sw_names]) for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" %(log_dir, sw_name)) + self.addSwitch(sw_name, log_file="%s/%s.log" % (log_dir, sw_name)) for host_name in host_names: host_num = int(host_name[1:]) @@ -27,48 +35,62 @@ def __init__(self, links, latencies={}, manifest=None, target=None, self.addHost(host_name) self._host_links[host_name] = {} - host_links = [l for l in links if l[0]==host_name or l[1]==host_name] + host_links = [x for x in links if x[0] == host_name or x[1] == host_name] sw_idx = 0 for link in host_links: sw = link[0] if link[0] != host_name else link[1] sw_num = int(sw[1:]) - assert sw[0]=='s', "Hosts should be connected to switches, not " + str(sw) + assert ( + sw[0] == "s" + ), "Hosts should be connected to switches, not " + str(sw) host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num) - delay_key = ''.join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else '0ms' + host_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num) + delay_key = "".join([host_name, sw]) + delay = latencies[delay_key] if delay_key in latencies else "0ms" bw = bws[delay_key] if delay_key in bws else None sw_ports[sw].append(host_name) self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac = host_mac, - host_ip = host_ip, - sw = sw, - sw_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num), - sw_ip = "10.0.%d.%d" % (sw_num, 254), - sw_port = sw_ports[sw].index(host_name)+1 - ) - self.addLink(host_name, sw, delay=delay, bw=bw, - addr1=host_mac, addr2=self._host_links[host_name][sw]['sw_mac']) + idx=sw_idx, + host_mac=host_mac, + host_ip=host_ip, + sw=sw, + sw_mac="00:00:00:00:%02x:%02x" % (sw_num, host_num), + sw_ip="10.0.%d.%d" % (sw_num, 254), + sw_port=sw_ports[sw].index(host_name) + 1, + ) + self.addLink( + host_name, + sw, + delay=delay, + bw=bw, + addr1=host_mac, + addr2=self._host_links[host_name][sw]["sw_mac"], + ) sw_idx += 1 - for link in links: # only check switch-switch links + for link in links: # only check switch-switch links sw1, sw2 = link - if sw1[0] != 's' or sw2[0] != 's': continue + if sw1[0] != "s" or sw2[0] != "s": + continue - delay_key = ''.join(sorted([sw1, sw2])) - delay = latencies[delay_key] if delay_key in latencies else '0ms' + delay_key = "".join(sorted([sw1, sw2])) + delay = latencies[delay_key] if delay_key in latencies else "0ms" bw = bws[delay_key] if delay_key in bws else None - self.addLink(sw1, sw2, delay=delay, bw=bw)#, max_queue_size=10) + self.addLink(sw1, sw2, delay=delay, bw=bw) # , max_queue_size=10) sw_ports[sw1].append(sw2) sw_ports[sw2].append(sw1) sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict(mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), port=sw_ports[sw1].index(sw2)+1) - sw2_port = dict(mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), port=sw_ports[sw2].index(sw1)+1) + sw1_port = dict( + mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), + port=sw_ports[sw1].index(sw2) + 1, + ) + sw2_port = dict( + mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), + port=sw_ports[sw2].index(sw1) + 1, + ) self._sw_links[sw1][sw2] = [sw1_port, sw2_port] self._sw_links[sw2][sw1] = [sw2_port, sw1_port] - diff --git a/utils/mininet/multi_switch_mininet.py b/utils/mininet/multi_switch_mininet.py index 8ebd5e8f..4ba14f66 100755 --- a/utils/mininet/multi_switch_mininet.py +++ b/utils/mininet/multi_switch_mininet.py @@ -19,26 +19,72 @@ from mininet.net import Mininet from p4_mininet import P4Host, P4Switch -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--bmv2-log', help='verbose messages in log file', action="store_true") -parser.add_argument('--cli', help="start the mininet cli", action="store_true") -parser.add_argument('--auto-control-plane', help='enable automatic control plane population', action="store_true") -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - action="store_true") -parser.add_argument('--manifest', '-m', help='Path to manifest file', - type=str, action="store", required=True) -parser.add_argument('--target', '-t', help='Target in manifest file to run', - type=str, action="store", required=True) -parser.add_argument('--log-dir', '-l', help='Location to save output to', - type=str, action="store", required=True) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) +parser = argparse.ArgumentParser(description="Mininet demo") +parser.add_argument( + "--behavioral-exe", + help="Path to behavioral executable", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--thrift-port", + help="Thrift server port for table updates", + type=int, + action="store", + default=9090, +) +parser.add_argument( + "--bmv2-log", help="verbose messages in log file", action="store_true" +) +parser.add_argument("--cli", help="start the mininet cli", action="store_true") +parser.add_argument( + "--auto-control-plane", + help="enable automatic control plane population", + action="store_true", +) +parser.add_argument( + "--json", + help="Path to JSON config file", + type=str, action="store", required=True +) +parser.add_argument( + "--pcap-dump", + help="Dump packets on interfaces to pcap files", + action="store_true" +) +parser.add_argument( + "--manifest", + "-m", + help="Path to manifest file", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--target", + "-t", + help="Target in manifest file to run", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--log-dir", + "-l", + help="Location to save output to", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--cli-message", + help="Message to print before starting CLI", + type=str, + action="store", + required=False, + default=False, +) args = parser.parse_args() @@ -46,114 +92,125 @@ next_thrift_port = args.thrift_port + def run_command(command): return os.WEXITSTATUS(os.system(command)) + def configureP4Switch(**switch_args): class ConfiguredP4Switch(P4Switch): def __init__(self, *opts, **kwargs): global next_thrift_port kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port + kwargs["thrift_port"] = next_thrift_port next_thrift_port += 1 P4Switch.__init__(self, *opts, **kwargs) + return ConfiguredP4Switch def main(): - with open(args.manifest, 'r') as f: + with open(args.manifest, "r") as f: manifest = json.load(f) - conf = manifest['targets'][args.target] - params = conf['parameters'] if 'parameters' in conf else {} + conf = manifest["targets"][args.target] + params = conf["parameters"] if "parameters" in conf else {} - os.environ.update(dict([(k_v[0], str(k_v[1])) for k_v in iter(params.items())])) + os.environ.update(dict([(k_v[0], str(k_v[1])) + for k_v in iter(params.items())])) def formatParams(s): for param in params: - s = re.sub('\$'+param+'(\W|$)', str(params[param]) + r'\1', s) - s = s.replace('${'+param+'}', str(params[param])) + s = re.sub("\$" + param + "(\W|$)", str(params[param]) + r"\1", s) + s = s.replace("${" + param + "}", str(params[param])) return s AppTopo = apptopo.AppTopo AppController = appcontroller.AppController - if 'topo_module' in conf: + if "topo_module" in conf: sys.path.insert(0, os.path.dirname(args.manifest)) - topo_module = importlib.import_module(conf['topo_module']) + topo_module = importlib.import_module(conf["topo_module"]) AppTopo = topo_module.CustomAppTopo - if 'controller_module' in conf: + if "controller_module" in conf: sys.path.insert(0, os.path.dirname(args.manifest)) - controller_module = importlib.import_module(conf['controller_module']) + controller_module = importlib.import_module(conf["controller_module"]) AppController = controller_module.CustomAppController if not os.path.isdir(args.log_dir): - if os.path.exists(args.log_dir): raise Exception('Log dir exists and is not a dir') + if os.path.exists(args.log_dir): + raise Exception("Log dir exists and is not a dir") os.mkdir(args.log_dir) - os.environ['P4APP_LOGDIR'] = args.log_dir - - - links = [l[:2] for l in conf['links']] - latencies = dict([(''.join(sorted(l[:2])), l[2]) for l in conf['links'] if len(l)>=3]) - bws = dict([(''.join(sorted(l[:2])), l[3]) for l in conf['links'] if len(l)>=4]) - - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'latency' not in host: continue + os.environ["P4APP_LOGDIR"] = args.log_dir + + links = [x[:2] for x in conf["links"]] + latencies = dict( + [("".join(sorted(x[:2])), x[2]) for x in conf["links"] if len(x) >= 3] + ) + bws = dict([("".join(sorted(x[:2])), x[3]) + for x in conf["links"] if len(x) >= 4]) + + for host_name in sorted(conf["hosts"].keys()): + host = conf["hosts"][host_name] + if "latency" not in host: + continue for a, b in links: - if a != host_name and b != host_name: continue + if a != host_name and b != host_name: + continue other = a if a != host_name else b - latencies[host_name+other] = host['latency'] + latencies[host_name + other] = host["latency"] - for l in latencies: - if isinstance(latencies[l], str): - latencies[l] = formatParams(latencies[l]) + for x in latencies: + if isinstance(latencies[x], str): + latencies[x] = formatParams(latencies[x]) else: - latencies[l] = str(latencies[l]) + "ms" - - bmv2_log = args.bmv2_log or ('bmv2_log' in conf and conf['bmv2_log']) - pcap_dump = args.pcap_dump or ('pcap_dump' in conf and conf['pcap_dump']) - - topo = AppTopo(links, latencies, manifest=manifest, target=args.target, - log_dir=args.log_dir, bws=bws) + latencies[x] = str(latencies[x]) + "ms" + + bmv2_log = args.bmv2_log or ("bmv2_log" in conf and conf["bmv2_log"]) + pcap_dump = args.pcap_dump or ("pcap_dump" in conf and conf["pcap_dump"]) + + topo = AppTopo( + links, + latencies, + manifest=manifest, + target=args.target, + log_dir=args.log_dir, + bws=bws, + ) switchClass = configureP4Switch( - sw_path=args.behavioral_exe, - json_path=args.json, - log_console=bmv2_log, - pcap_dump=pcap_dump) - net = Mininet(topo = topo, - link = TCLink, - host = P4Host, - switch = switchClass, - controller = None) + sw_path=args.behavioral_exe, + json_path=args.json, + log_console=bmv2_log, + pcap_dump=pcap_dump, + ) + net = Mininet(topo=topo, link=TCLink, host=P4Host, switch=switchClass, + controller=None) net.start() sleep(1) controller = None - if args.auto_control_plane or 'controller_module' in conf: + if args.auto_control_plane or "controller_module" in conf: controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) + topo=topo, net=net, links=links) controller.start() - for h in net.hosts: h.describe() if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: + with open(args.cli_message, "r") as message_file: print(message_file.read()) - if args.cli or ('cli' in conf and conf['cli']): + if args.cli or ("cli" in conf and conf["cli"]): CLI(net) stdout_files = dict() return_codes = [] host_procs = [] - def formatCmd(cmd): for h in net.hosts: cmd = cmd.replace(h.name, h.defaultIntf().updateIP()) @@ -169,59 +226,72 @@ def _wait_for_exit(p, host): stdout_files[host_name].flush() stdout_files[host_name].close() - print('\n'.join(["%s: %s"%(k_v1[0],k_v1[1]) for k_v1 in iter(params.items())]) + '\n') + print( + "\n".join(["%s: %s" % (k_v1[0], k_v1[1]) for k_v1 in iter(params.items())]) + + "\n" + ) - for host_name in sorted(conf['hosts'].keys()): - host = conf['hosts'][host_name] - if 'cmd' not in host: continue + for host_name in sorted(conf["hosts"].keys()): + host = conf["hosts"][host_name] + if "cmd" not in host: + continue h = net.get(host_name) - stdout_filename = os.path.join(args.log_dir, h.name + '.stdout') - stdout_files[h.name] = open(stdout_filename, 'w') - cmd = formatCmd(host['cmd']) + stdout_filename = os.path.join(args.log_dir, h.name + ".stdout") + stdout_files[h.name] = open(stdout_filename, "w") + cmd = formatCmd(host["cmd"]) print(h.name, cmd) - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) - if 'startup_sleep' in host: sleep(host['startup_sleep']) + p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, + preexec_fn=os.setpgrp) + if "startup_sleep" in host: + sleep(host["startup_sleep"]) - if 'wait' in host and host['wait']: + if "wait" in host and host["wait"]: _wait_for_exit(p, host_name) else: host_procs.append((p, host_name)) for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: + if "wait" in conf["hosts"][host_name] and conf["hosts"][host_name]["wait"]: _wait_for_exit(p, host_name) - for p, host_name in host_procs: - if 'wait' in conf['hosts'][host_name] and conf['hosts'][host_name]['wait']: + if "wait" in conf["hosts"][host_name] and conf["hosts"][host_name]["wait"]: continue if p.returncode is None: - run_command('pkill -INT -P %d' % p.pid) + run_command("pkill -INT -P %d" % p.pid) sleep(0.2) - rc = run_command('pkill -0 -P %d' % p.pid) # check if it's still running - if rc == 0: # the process group is still running, send TERM - sleep(1) # give it a little more time to exit gracefully - run_command('pkill -TERM -P %d' % p.pid) + # check if it's still running + rc = run_command("pkill -0 -P %d" % p.pid) + if rc == 0: # the process group is still running, send TERM + sleep(1) # give it a little more time to exit gracefully + run_command("pkill -TERM -P %d" % p.pid) _wait_for_exit(p, host_name) - if 'after' in conf and 'cmd' in conf['after']: - cmds = conf['after']['cmd'] if type(conf['after']['cmd']) == list else [conf['after']['cmd']] + if "after" in conf and "cmd" in conf["after"]: + cmds = ( + conf["after"]["cmd"] + if isinstance(conf["after"]["cmd"], list) + else [conf["after"]["cmd"]] + ) for cmd in cmds: os.system(cmd) - if controller: controller.stop() + if controller: + controller.stop() net.stop() -# if bmv2_log: -# os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) -# if pcap_dump: -# os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) + # if bmv2_log: + # os.system('bash -c "cp /tmp/p4s.s*.log \'%s\'"' % args.log_dir) + # if pcap_dump: + # os.system('bash -c "cp *.pcap \'%s\'"' % args.log_dir) bad_codes = [rc for rc in return_codes if rc != 0] - if len(bad_codes): sys.exit(1) + if len(bad_codes): + sys.exit(1) + -if __name__ == '__main__': - setLogLevel( 'info' ) +if __name__ == "__main__": + setLogLevel("info") main() diff --git a/utils/mininet/p4_mininet.py b/utils/mininet/p4_mininet.py index b6f39f5e..020c2b5e 100644 --- a/utils/mininet/p4_mininet.py +++ b/utils/mininet/p4_mininet.py @@ -18,7 +18,8 @@ def config(self, **params): r = super(P4Host, self).config(**params) for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) + cmd = ("/sbin/ethtool --offload %s %s off" + "" % (self.defaultIntf().name, off)) self.cmd(cmd) # disable IPv6 @@ -31,31 +32,41 @@ def config(self, **params): def describe(self, sw_addr=None, sw_mac=None): print("**********") print("Network configuration for: %s" % self.name) - print("Default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - )) + print( + "Default interface: %s\t%s\t%s" + % ( + self.defaultIntf().name, + self.defaultIntf().IP(), + self.defaultIntf().MAC(), + ) + ) if sw_addr is not None or sw_mac is not None: print("Default route to switch: %s (%s)" % (sw_addr, sw_mac)) print("**********") + class P4Switch(Switch): """P4 virtual switch""" + device_id = 0 - def __init__(self, name, sw_path = None, json_path = None, - log_file = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): + def __init__( + self, + name, + sw_path=None, + json_path=None, + log_file=None, + thrift_port=None, + pcap_dump=False, + log_console=False, + verbose=False, + device_id=None, + enable_debugger=False, + **kwargs + ): Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) + assert sw_path + assert json_path # make sure that the provided sw_path is valid pathCheck(sw_path) # make sure that the provided JSON file exists @@ -68,7 +79,7 @@ def __init__(self, name, sw_path = None, json_path = None, self.log_file = log_file if self.log_file is None: self.log_file = "/tmp/p4s.{}.log".format(self.name) - self.output = open(self.log_file, 'w') + self.output = open(self.log_file, "w") self.thrift_port = thrift_port self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger @@ -86,10 +97,11 @@ def setup(cls): pass def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" + """While the process is running (pid exists), we check if the + Thrift server has been started. If the Thrift server is ready, + we assume that the switch was started successfully. This is + only reliable if the Thrift server is started at the end of + the init process""" while True: if not os.path.exists(os.path.join("/proc", str(pid))): return False @@ -97,7 +109,7 @@ def check_switch_started(self, pid): sock.settimeout(0.5) result = sock.connect_ex(("localhost", self.thrift_port)) if result == 0: - return True + return True def start(self, controllers): "Start up a new P4 switch" @@ -105,47 +117,51 @@ def start(self, controllers): args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) + args.extend(["-i", str(port) + "@" + intf.name]) if self.pcap_dump: args.append("--pcap") # args.append("--useFiles") if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) + args.extend(["--thrift-port", str(self.thrift_port)]) if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) + args.extend(["--nanolog", self.nanomsg]) + args.extend(["--device-id", str(self.device_id)]) P4Switch.device_id += 1 args.append(self.json_path) if self.enable_debugger: args.append("--debugger") if self.log_console: args.append("--log-console") - info(' '.join(args) + "\n") + info(" ".join(args) + "\n") pid = None with tempfile.NamedTemporaryFile() as f: # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) + self.cmd( + " ".join(args) + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name + ) pid = int(f.read()) debug("P4 switch {} PID is {}.\n".format(self.name, pid)) sleep(1) if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name)) + error( + "P4 switch {} did not start correctly." + "Check the switch log file.\n".format(self.name) + ) exit(1) info("P4 switch {} has been started.\n".format(self.name)) def stop(self): "Terminate P4 switch." self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') + self.cmd("kill %" + self.sw_path) + self.cmd("wait") self.deleteIntfs() def attach(self, intf): "Connect a data port" - assert(0) + assert 0 def detach(self, intf): "Disconnect a data port" - assert(0) + assert 0 diff --git a/utils/mininet/shortest_path.py b/utils/mininet/shortest_path.py index 07d71f50..50d1cd42 100644 --- a/utils/mininet/shortest_path.py +++ b/utils/mininet/shortest_path.py @@ -17,43 +17,47 @@ def get(self, a, b, exclude=lambda node: False): return self._recPath(a, b, [], exclude) def _recPath(self, a, b, visited, exclude): - if a == b: return [a] + if a == b: + return [a] new_visited = visited + [a] paths = [] for neighbor in self.neighbors[a]: - if neighbor in new_visited: continue - if exclude(neighbor) and neighbor != b: continue + if neighbor in new_visited: + continue + if exclude(neighbor) and neighbor != b: + continue path = self._recPath(neighbor, b, new_visited, exclude) - if path: paths.append(path) + if path: + paths.append(path) paths.sort(key=len) return [a] + paths[0] if len(paths) else None -if __name__ == '__main__': - edges = [ - (1, 2), - (1, 3), - (1, 5), - (2, 4), - (3, 4), - (3, 5), - (3, 6), - (4, 6), - (5, 6), - (7, 8) +if __name__ == "__main__": + edges = [ + (1, 2), + (1, 3), + (1, 5), + (2, 4), + (3, 4), + (3, 5), + (3, 6), + (4, 6), + (5, 6), + (7, 8), ] sp = ShortestPath(edges) - assert sp.get(1, 1) == [1] # shortest path from node 1 to itself - assert sp.get(2, 2) == [2] # shortest path from node 2 to itself + assert sp.get(1, 1) == [1] # shortest path from node 1 to itself + assert sp.get(2, 2) == [2] # shortest path from node 2 to itself assert sp.get(1, 2) == [1, 2] assert sp.get(2, 1) == [2, 1] - assert sp.get(1, 3) == [1, 3] # shortest path from node 1 to node 3 - assert sp.get(3, 1) == [3, 1] # shortest path from node 3 to node 1 + assert sp.get(1, 3) == [1, 3] # shortest path from node 1 to node 3 + assert sp.get(3, 1) == [3, 1] # shortest path from node 3 to node 1 assert sp.get(4, 6) == [4, 6] assert sp.get(6, 4) == [6, 4] @@ -61,8 +65,10 @@ def _recPath(self, a, b, visited, exclude): assert sp.get(2, 6) == [2, 4, 6] assert sp.get(6, 2) == [6, 4, 2] - assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] # Multiple shortest paths from 1 to 6 - assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] # Multiple shortest paths from 6 to 1 + # Multiple shortest paths from 1 to 6 + assert sp.get(1, 6) in [[1, 3, 6], [1, 5, 6]] + # Multiple shortest paths from 6 to 1 + assert sp.get(6, 1) in [[6, 3, 1], [6, 5, 1]] assert sp.get(2, 5) == [2, 1, 5] assert sp.get(5, 2) == [5, 1, 2] @@ -73,6 +79,5 @@ def _recPath(self, a, b, visited, exclude): assert sp.get(7, 8) == [7, 8] assert sp.get(8, 7) == [8, 7] - assert sp.get(1, 7) == None # There is no path from node 1 to node 7 - assert sp.get(7, 2) == None # There is no path from node 7 to node 2 - + assert sp.get(1, 7) is None # There is no path from node 1 to node 7 + assert sp.get(7, 2) is None # There is no path from node 7 to node 2 diff --git a/utils/mininet/single_switch_mininet.py b/utils/mininet/single_switch_mininet.py index 446475ec..f10eb5c2 100755 --- a/utils/mininet/single_switch_mininet.py +++ b/utils/mininet/single_switch_mininet.py @@ -13,82 +13,127 @@ from mininet.topo import Topo from p4_mininet import P4Host, P4Switch -parser = argparse.ArgumentParser(description='Mininet demo') -parser.add_argument('--behavioral-exe', help='Path to behavioral executable', - type=str, action="store", required=True) -parser.add_argument('--thrift-port', help='Thrift server port for table updates', - type=int, action="store", default=9090) -parser.add_argument('--num-hosts', help='Number of hosts to connect to switch', - type=int, action="store", default=2) -parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3') -parser.add_argument('--json', help='Path to JSON config file', - type=str, action="store", required=True) -parser.add_argument('--log-file', help='Path to write the switch log file', - type=str, action="store", required=False) -parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', - type=str, action="store", required=False, default=False) -parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', - type=str, action="store", required=False, default=False) -parser.add_argument('--cli-message', help='Message to print before starting CLI', - type=str, action="store", required=False, default=False) +parser = argparse.ArgumentParser(description="Mininet demo") +parser.add_argument( + "--behavioral-exe", + help="Path to behavioral executable", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--thrift-port", + help="Thrift server port for table updates", + type=int, + action="store", + default=9090, +) +parser.add_argument( + "--num-hosts", + help="Number of hosts to connect to switch", + type=int, + action="store", + default=2, +) +parser.add_argument("--mode", choices=["l2", "l3"], type=str, default="l3") +parser.add_argument( + "--json", + help="Path to JSON config file", + type=str, + action="store", + required=True, +) +parser.add_argument( + "--log-file", + help="Path to write the switch log file", + type=str, + action="store", + required=False, +) +parser.add_argument( + "--pcap-dump", + help="Dump packets on interfaces to pcap files", + type=str, + action="store", + required=False, + default=False, +) +parser.add_argument( + "--switch-config", + help="simple_switch_CLI script to configure switch", + type=str, + action="store", + required=False, + default=False, +) +parser.add_argument( + "--cli-message", + help="Message to print before starting CLI", + type=str, + action="store", + required=False, + default=False, +) args = parser.parse_args() class SingleSwitchTopo(Topo): "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, - thrift_port, pcap_dump, n, **opts): + + def __init__(self, sw_path, json_path, log_file, thrift_port, pcap_dump, + n, **opts): # Initialize topology and default options Topo.__init__(self, **opts) - switch = self.addSwitch('s1', - sw_path = sw_path, - json_path = json_path, - log_console = True, - log_file = log_file, - thrift_port = thrift_port, - enable_debugger = False, - pcap_dump = pcap_dump) + switch = self.addSwitch( + "s1", + sw_path=sw_path, + json_path=json_path, + log_console=True, + log_file=log_file, + thrift_port=thrift_port, + enable_debugger=False, + pcap_dump=pcap_dump, + ) for h in range(n): - host = self.addHost('h%d' % (h + 1), - ip = "10.0.%d.10/24" % h, - mac = '00:04:00:00:00:%02x' %h) + host = self.addHost("h%d" % (h + 1), ip="10.0.%d.10/24" % h, + mac="00:04:00:00:00:%02x" % h) print("Adding host", str(host)) self.addLink(host, switch) + def main(): num_hosts = args.num_hosts mode = args.mode - topo = SingleSwitchTopo(args.behavioral_exe, - args.json, - args.log_file, - args.thrift_port, - args.pcap_dump, - num_hosts) - net = Mininet(topo = topo, - host = P4Host, - switch = P4Switch, - controller = None) + topo = SingleSwitchTopo( + args.behavioral_exe, + args.json, + args.log_file, + args.thrift_port, + args.pcap_dump, + num_hosts, + ) + net = Mininet(topo=topo, host=P4Host, switch=P4Switch, controller=None) net.start() - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in range(num_hosts)] sw_addr = ["10.0.%d.1" % n for n in range(num_hosts)] for n in range(num_hosts): - h = net.get('h%d' % (n + 1)) + h = net.get("h%d" % (n + 1)) if mode == "l2": h.setDefaultRoute("dev %s" % h.defaultIntf().name) else: h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) + h.setDefaultRoute("dev %s via %s" + "" % (h.defaultIntf().name, sw_addr[n])) for n in range(num_hosts): - h = net.get('h%d' % (n + 1)) + h = net.get("h%d" % (n + 1)) h.describe(sw_addr[n], sw_mac[n]) sleep(1) @@ -96,7 +141,7 @@ def main(): if args.switch_config is not None: print() print("Reading switch configuration script:", args.switch_config) - with open(args.switch_config, 'r') as config_file: + with open(args.switch_config, "r") as config_file: switch_config = config_file.read() print("Configuring switch...") @@ -109,12 +154,13 @@ def main(): print("Ready !") if args.cli_message is not None: - with open(args.cli_message, 'r') as message_file: + with open(args.cli_message, "r") as message_file: print(message_file.read()) - CLI( net ) + CLI(net) net.stop() -if __name__ == '__main__': - setLogLevel( 'info' ) + +if __name__ == "__main__": + setLogLevel("info") main() diff --git a/utils/netstat.py b/utils/netstat.py index 6779e956..983c48f4 100644 --- a/utils/netstat.py +++ b/utils/netstat.py @@ -6,7 +6,7 @@ def check_listening_on_port(port): - for c in psutil.net_connections(kind='inet'): - if c.status == 'LISTEN' and c.laddr[1] == port: + for c in psutil.net_connections(kind="inet"): + if c.status == "LISTEN" and c.laddr[1] == port: return True return False diff --git a/utils/p4_mininet.py b/utils/p4_mininet.py index ddb77392..d88dba5f 100644 --- a/utils/p4_mininet.py +++ b/utils/p4_mininet.py @@ -12,7 +12,8 @@ from mininet.node import Host, Switch from netstat import check_listening_on_port -SWITCH_START_TIMEOUT = 10 # seconds +SWITCH_START_TIMEOUT = 10 # seconds + class P4Host(Host): def config(self, **params): @@ -34,29 +35,39 @@ def config(self, **params): def describe(self): print("**********") print(self.name) - print("default interface: %s\t%s\t%s" %( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC() - )) + print( + "default interface: %s\t%s\t%s" + % ( + self.defaultIntf().name, + self.defaultIntf().IP(), + self.defaultIntf().MAC(), + ) + ) print("**********") + class P4Switch(Switch): """P4 virtual switch""" + device_id = 0 - def __init__(self, name, sw_path = None, json_path = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - log_file = None, - verbose = False, - device_id = None, - enable_debugger = False, - **kwargs): + def __init__( + self, + name, + sw_path=None, + json_path=None, + thrift_port=None, + pcap_dump=False, + log_console=False, + log_file=None, + verbose=False, + device_id=None, + enable_debugger=False, + **kwargs + ): Switch.__init__(self, name, **kwargs) - assert(sw_path) - assert(json_path) + assert sw_path + assert json_path # make sure that the provided sw_path is valid pathCheck(sw_path) # make sure that the provided JSON file exists @@ -67,10 +78,11 @@ def __init__(self, name, sw_path = None, json_path = None, self.json_path = json_path self.verbose = verbose logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') + self.output = open(logfile, "w") self.thrift_port = thrift_port if check_listening_on_port(self.thrift_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) + error("%s cannot bind port %d because it is bound by" + " another process\n" % (self.name, self.grpc_port)) exit(1) self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger @@ -92,10 +104,11 @@ def setup(cls): pass def check_switch_started(self, pid): - """While the process is running (pid exists), we check if the Thrift - server has been started. If the Thrift server is ready, we assume that - the switch was started successfully. This is only reliable if the Thrift - server is started at the end of the init process""" + """While the process is running (pid exists), we check if the + Thrift server has been started. If the Thrift server is ready, + we assume that the switch was started successfully. This is + only reliable if the Thrift server is started at the end of + the init process""" while True: if not os.path.exists(os.path.join("/proc", str(pid))): return False @@ -109,26 +122,28 @@ def start(self, controllers): args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) + args.extend(["-i", str(port) + "@" + intf.name]) if self.pcap_dump: args.append("--pcap %s" % self.pcap_dump) if self.thrift_port: - args.extend(['--thrift-port', str(self.thrift_port)]) + args.extend(["--thrift-port", str(self.thrift_port)]) if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) + args.extend(["--nanolog", self.nanomsg]) + args.extend(["--device-id", str(self.device_id)]) P4Switch.device_id += 1 args.append(self.json_path) if self.enable_debugger: args.append("--debugger") if self.log_console: args.append("--log-console") - info(' '.join(args) + "\n") + info(" ".join(args) + "\n") pid = None with tempfile.NamedTemporaryFile() as f: # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &') - self.cmd(' '.join(args) + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) + self.cmd( + " ".join(args) + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name + ) pid = int(f.read()) debug("P4 switch {} PID is {}.\n".format(self.name, pid)) if not self.check_switch_started(pid): @@ -139,14 +154,14 @@ def start(self, controllers): def stop(self): "Terminate P4 switch." self.output.flush() - self.cmd('kill %' + self.sw_path) - self.cmd('wait') + self.cmd("kill %" + self.sw_path) + self.cmd("wait") self.deleteIntfs() def attach(self, intf): "Connect a data port" - assert(0) + assert 0 def detach(self, intf): "Disconnect a data port" - assert(0) + assert 0 diff --git a/utils/p4apprunner.py b/utils/p4apprunner.py index ec14c16d..dac176fd 100755 --- a/utils/p4apprunner.py +++ b/utils/p4apprunner.py @@ -5,7 +5,6 @@ # SPDX-License-Identifier: Apache-2.0 - import argparse import json import os @@ -13,31 +12,55 @@ import tarfile from collections import OrderedDict -parser = argparse.ArgumentParser(description='p4apprunner') -parser.add_argument('--build-dir', help='Directory to build in.', - type=str, action='store', required=False, default='/tmp') -parser.add_argument('--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) -parser.add_argument('--manifest', help='Path to manifest file.', - type=str, action='store', required=False, default='./p4app.json') -parser.add_argument('app', help='.p4app package to run.', type=str) -parser.add_argument('target', help=('Target to run. Defaults to the first target ' - 'in the package.'), - nargs='?', type=str) +parser = argparse.ArgumentParser(description="p4apprunner") +parser.add_argument( + "--build-dir", + help="Directory to build in.", + type=str, + action="store", + required=False, + default="/tmp", +) +parser.add_argument( + "--quiet", + help="Suppress log messages.", + action="store_true", + required=False, + default=False, +) +parser.add_argument( + "--manifest", + help="Path to manifest file.", + type=str, + action="store", + required=False, + default="./p4app.json", +) +parser.add_argument("app", help=".p4app package to run.", type=str) +parser.add_argument( + "target", + help=("Target to run. Defaults to the first target " "in the package."), + nargs="?", + type=str, +) args = parser.parse_args() + def log(*items): - if args.quiet != True: + if args.quiet is not True: print(*items) + def log_error(*items): print(*items, file=sys.stderr) + def run_command(command): - log('>', command) + log(">", command) return os.WEXITSTATUS(os.system(command)) + class Manifest: def __init__(self, program_file, language, target, target_config): self.program_file = program_file @@ -45,83 +68,86 @@ def __init__(self, program_file, language, target, target_config): self.target = target self.target_config = target_config + def read_manifest(manifest_file): manifest = json.load(manifest_file, object_pairs_hook=OrderedDict) - if 'program' not in manifest: - log_error('No program defined in manifest.') + if "program" not in manifest: + log_error("No program defined in manifest.") sys.exit(1) - program_file = manifest['program'] + program_file = manifest["program"] - if 'language' not in manifest: - log_error('No language defined in manifest.') + if "language" not in manifest: + log_error("No language defined in manifest.") sys.exit(1) - language = manifest['language'] + language = manifest["language"] - if 'targets' not in manifest or len(manifest['targets']) < 1: - log_error('No targets defined in manifest.') + if "targets" not in manifest or len(manifest["targets"]) < 1: + log_error("No targets defined in manifest.") sys.exit(1) if args.target is not None: chosen_target = args.target - elif 'default-target' in manifest: - chosen_target = manifest['default-target'] + elif "default-target" in manifest: + chosen_target = manifest["default-target"] else: - chosen_target = list(manifest['targets'].keys())[0] + chosen_target = list(manifest["targets"].keys())[0] - if chosen_target not in manifest['targets']: - log_error('Target not found in manifest:', chosen_target) + if chosen_target not in manifest["targets"]: + log_error("Target not found in manifest:", chosen_target) sys.exit(1) - return Manifest(program_file, language, chosen_target, manifest['targets'][chosen_target]) + return Manifest(program_file, language, chosen_target, + manifest["targets"][chosen_target]) def run_compile_bmv2(manifest): - if 'run-before-compile' in manifest.target_config: - commands = manifest.target_config['run-before-compile'] + if "run-before-compile" in manifest.target_config: + commands = manifest.target_config["run-before-compile"] if not isinstance(commands, list): - log_error('run-before-compile should be a list:', commands) + log_error("run-before-compile should be a list:", commands) sys.exit(1) for command in commands: run_command(command) compiler_args = [] - if manifest.language == 'p4-14': - compiler_args.append('--p4v 14') - elif manifest.language == 'p4-16': - compiler_args.append('--p4v 16') + if manifest.language == "p4-14": + compiler_args.append("--p4v 14") + elif manifest.language == "p4-16": + compiler_args.append("--p4v 16") else: - log_error('Unknown language:', manifest.language) + log_error("Unknown language:", manifest.language) sys.exit(1) - if 'compiler-flags' in manifest.target_config: - flags = manifest.target_config['compiler-flags'] + if "compiler-flags" in manifest.target_config: + flags = manifest.target_config["compiler-flags"] if not isinstance(flags, list): - log_error('compiler-flags should be a list:', flags) + log_error("compiler-flags should be a list:", flags) sys.exit(1) compiler_args.extend(flags) # Compile the program. - output_file = manifest.program_file + '.json' + output_file = manifest.program_file + ".json" compiler_args.append('"%s"' % manifest.program_file) compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) + rv = run_command("p4c-bm2-ss %s" % " ".join(compiler_args)) - if 'run-after-compile' in manifest.target_config: - commands = manifest.target_config['run-after-compile'] + if "run-after-compile" in manifest.target_config: + commands = manifest.target_config["run-after-compile"] if not isinstance(commands, list): - log_error('run-after-compile should be a list:', commands) + log_error("run-after-compile should be a list:", commands) sys.exit(1) for command in commands: run_command(command) if rv != 0: - log_error('Compile failed.') + log_error("Compile failed.") sys.exit(1) return output_file + def run_mininet(manifest): output_file = run_compile_bmv2(manifest) @@ -130,182 +156,209 @@ def run_mininet(manifest): # We'll place the switch's log file in current (build) folder. cwd = os.getcwd() - log_file = os.path.join(cwd, manifest.program_file + '.log') - print ("*** Log file %s" % log_file) + log_file = os.path.join(cwd, manifest.program_file + ".log") + print("*** Log file %s" % log_file) switch_args.append('--log-file "%s"' % log_file) pcap_dir = os.path.join(cwd) - print ("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" '% pcap_dir) + print("*** Pcap folder %s" % pcap_dir) + switch_args.append('--pcap-dump "%s" ' % pcap_dir) # Generate a message that will be printed by the Mininet CLI to make # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: + message_file = "mininet_message.txt" + with open(message_file, "w") as message: print(file=message) - print('======================================================================', + print("========================================" + "==============================", file=message) + print("Welcome to the BMV2 Mininet CLI!", file=message) + print("========================================" + "==============================", file=message) + print("Your P4 program is installed into the BMV2 software switch", file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', + print("and your initial configuration is loaded. You can interact", file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) + print("with the network using the mininet CLI below.", file=message) print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI', file=message) + print("To inspect or change the switch configuration, connect to", + file=message) + print("its CLI from your host operating system using this command:", + file=message) + print(" simple_switch_CLI", file=message) print(file=message) - print('To view the switch log, run this command from your host OS:', file=message) - print(' tail -f %s' % log_file, file=message) + print("To view the switch log, run this command from your host OS:", + file=message) + print(" tail -f %s" % log_file, file=message) print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) + print("To view the switch output pcap, check the pcap files in %s:" + "" % (pcap_dir), file=message) + print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", + file=message) print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) + # print('To run the switch debugger, run this command from your host OS:', file=message) + # print(' bm_p4dbg' , file=message) + # print(file=message) switch_args.append('--cli-message "%s"' % message_file) - if 'num-hosts' in manifest.target_config: - switch_args.append('--num-hosts %s' % manifest.target_config['num-hosts']) + if "num-hosts" in manifest.target_config: + switch_args.append("--num-hosts %s" + "" % (manifest.target_config["num-hosts"])) - if 'switch-config' in manifest.target_config: - switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) + if "switch-config" in manifest.target_config: + switch_args.append( + '--switch-config "%s"' % manifest.target_config["switch-config"] + ) - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') + switch_args.append('--behavioral-exe "%s"' % "simple_switch") switch_args.append('--json "%s"' % output_file) program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command('python3 %s %s' % (program, ' '.join(switch_args))) + return run_command("python3 %s %s" % (program, " ".join(switch_args))) + def run_multiswitch(manifest): output_file = run_compile_bmv2(manifest) script_args = [] cwd = os.getcwd() - log_dir = os.path.join(cwd, cwd + '/logs') - print ("*** Log directory %s" % log_dir) + log_dir = os.path.join(cwd, cwd + "/logs") + print("*** Log directory %s" % log_dir) script_args.append('--log-dir "%s"' % log_dir) pcap_dir = os.path.join(cwd) - print ("*** Pcap directory %s" % cwd) + print("*** Pcap directory %s" % cwd) script_args.append('--manifest "%s"' % args.manifest) script_args.append('--target "%s"' % manifest.target) - if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: - script_args.append('--auto-control-plane' ) - script_args.append('--behavioral-exe "%s"' % 'simple_switch') + if ( + "auto-control-plane" in manifest.target_config + and manifest.target_config["auto-control-plane"] + ): + script_args.append("--auto-control-plane") + script_args.append('--behavioral-exe "%s"' % "simple_switch") script_args.append('--json "%s"' % output_file) - #script_args.append('--cli') + # script_args.append('--cli') # Generate a message that will be printed by the Mininet CLI to make # interacting with the simple switch a little easier. - message_file = 'mininet_message.txt' - with open(message_file, 'w') as message: + message_file = "mininet_message.txt" + with open(message_file, "w") as message: print(file=message) - print('======================================================================', + print("========================================" + "==============================", file=message) + print("Welcome to the BMV2 Mininet CLI!", file=message) + print("========================================" + "==============================", file=message) + print("Your P4 program is installed into the BMV2 software switch", file=message) - print('Welcome to the BMV2 Mininet CLI!', file=message) - print('======================================================================', + print("and your initial configuration is loaded. You can interact", file=message) - print('Your P4 program is installed into the BMV2 software switch', file=message) - print('and your initial configuration is loaded. You can interact', file=message) - print('with the network using the mininet CLI below.', file=message) + print("with the network using the mininet CLI below.", file=message) print(file=message) - print('To inspect or change the switch configuration, connect to', file=message) - print('its CLI from your host operating system using this command:', file=message) - print(' simple_switch_CLI --thrift-port ', file=message) + print("To inspect or change the switch configuration, connect to", + file=message) + print("its CLI from your host operating system using this command:", + file=message) + print(" simple_switch_CLI --thrift-port ", + file=message) print(file=message) - print('To view a switch log, run this command from your host OS:', file=message) - print(' tail -f %s/.log' % log_dir, file=message) + print("To view a switch log, run this command from your host OS:", + file=message) + print(" tail -f %s/.log" % log_dir, file=message) print(file=message) - print('To view the switch output pcap, check the pcap files in %s:' % pcap_dir, file=message) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap', file=message) + print("To view the switch output pcap, check the pcap files in %s:" + "" % (pcap_dir), file=message) + print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", + file=message) print(file=message) -# print('To run the switch debugger, run this command from your host OS:', file=message) -# print(' bm_p4dbg' , file=message) -# print(file=message) + # print('To run the switch debugger, run this command from your host OS:', file=message) + # print(' bm_p4dbg' , file=message) + # print(file=message) script_args.append('--cli-message "%s"' % message_file) program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command('python3 %s %s' % (program, ' '.join(script_args))) + return run_command("python3 %s %s" % (program, " ".join(script_args))) + def run_stf(manifest): output_file = run_compile_bmv2(manifest) - if not 'test' in manifest.target_config: - log_error('No STF test file provided.') + if "test" not in manifest.target_config: + log_error("No STF test file provided.") sys.exit(1) - stf_file = manifest.target_config['test'] + stf_file = manifest.target_config["test"] # Run the program using the BMV2 STF interpreter. stf_args = [] - stf_args.append('-v') + stf_args.append("-v") stf_args.append(os.path.join(args.build_dir, output_file)) stf_args.append(os.path.join(args.build_dir, stf_file)) program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command('python3 %s %s' % (program, ' '.join(stf_args))) + rv = run_command("python3 %s %s" % (program, " ".join(stf_args))) if rv != 0: sys.exit(1) return rv + def run_custom(manifest): output_file = run_compile_bmv2(manifest) - python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' + python_path = "PYTHONPATH=$PYTHONPATH:/scripts/mininet/" script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') + script_args.append('--behavioral-exe "%s"' % "simple_switch") script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') - if not 'program' in manifest.target_config: - log_error('No mininet program file provided.') - sys.exit(1) - program = manifest.target_config['program'] - rv = run_command('%s python3 %s %s' % (python_path, program, ' '.join(script_args))) + script_args.append('--cli "%s"' % "simple_switch_CLI") + if "program" not in manifest.target_config: + log_error("No mininet program file provided.") + sys.exit(1) + program = manifest.target_config["program"] + rv = run_command("%s python3 %s %s" + "" % (python_path, program, " ".join(script_args))) if rv != 0: sys.exit(1) return rv + def main(): - log('Entering build directory.') + log("Entering build directory.") os.chdir(args.build_dir) # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we # can process its contents. - log('Extracting package.') + log("Extracting package.") tar = tarfile.open(args.app) tar.extractall() tar.close() - log('Reading package manifest.') - with open(args.manifest, 'r') as manifest_file: + log("Reading package manifest.") + with open(args.manifest, "r") as manifest_file: manifest = read_manifest(manifest_file) # Dispatch to the backend implementation for this target. backend = manifest.target - if 'use' in manifest.target_config: - backend = manifest.target_config['use'] + if "use" in manifest.target_config: + backend = manifest.target_config["use"] - if backend == 'mininet': + if backend == "mininet": rc = run_mininet(manifest) - elif backend == 'multiswitch': + elif backend == "multiswitch": rc = run_multiswitch(manifest) - elif backend == 'stf': + elif backend == "stf": rc = run_stf(manifest) - elif backend == 'custom': + elif backend == "custom": rc = run_custom(manifest) - elif backend == 'compile-bmv2': + elif backend == "compile-bmv2": run_compile_bmv2(manifest) rc = 0 else: - log_error('Target specifies unknown backend:', backend) + log_error("Target specifies unknown backend:", backend) sys.exit(1) sys.exit(rc) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/utils/p4runtime_lib/bmv2.py b/utils/p4runtime_lib/bmv2.py index 4ddf1f5f..48ed069d 100644 --- a/utils/p4runtime_lib/bmv2.py +++ b/utils/p4runtime_lib/bmv2.py @@ -8,7 +8,7 @@ def buildDeviceConfig(bmv2_json_file_path=None): "Builds the device config for BMv2" with open(bmv2_json_file_path) as f: - device_config = f.read().encode('utf-8') + device_config = f.read().encode("utf-8") return device_config diff --git a/utils/p4runtime_lib/convert.py b/utils/p4runtime_lib/convert.py index 22b747bf..cef450e2 100644 --- a/utils/p4runtime_lib/convert.py +++ b/utils/p4runtime_lib/convert.py @@ -5,33 +5,44 @@ import re import socket -''' -This package contains several helper functions for encoding to and decoding from byte strings: +""" +This package contains several helper functions for encoding to and +decoding from byte strings: - integers - IPv4 address strings - Ethernet address strings -''' +""" + +mac_pattern = re.compile(r"^([\da-fA-F]{2}:){5}([\da-fA-F]{2})$") + -mac_pattern = re.compile(r'^([\da-fA-F]{2}:){5}([\da-fA-F]{2})$') def matchesMac(mac_addr_string): return mac_pattern.match(mac_addr_string) is not None + def encodeMac(mac_addr_string): - return bytes.fromhex(mac_addr_string.replace(':', '')) + return bytes.fromhex(mac_addr_string.replace(":", "")) + def decodeMac(encoded_mac_addr): - return ':'.join(s.hex() for s in encoded_mac_addr) + return ":".join(s.hex() for s in encoded_mac_addr) + + +ip_pattern = re.compile(r"^(\d{1,3}\.){3}(\d{1,3})$") + -ip_pattern = re.compile(r'^(\d{1,3}\.){3}(\d{1,3})$') def matchesIPv4(ip_addr_string): return ip_pattern.match(ip_addr_string) is not None + def encodeIPv4(ip_addr_string): return socket.inet_aton(ip_addr_string) + def decodeIPv4(encoded_ip_addr): return socket.inet_ntoa(encoded_ip_addr) + def matchesIPv6(ip_addr_string): try: socket.inet_pton(socket.AF_INET6, ip_addr_string) @@ -39,42 +50,52 @@ def matchesIPv6(ip_addr_string): except socket.error: return False + def encodeIPv6(ip_addr_string): return socket.inet_pton(socket.AF_INET6, ip_addr_string) + def decodeIPv6(encoded_ip_addr): - return socket.inet_ntop(socket.AF_INET6, encoded_ip_addr) + return socket.inet_ntop(socket.AF_INET6, encoded_ip_addr) + def bitwidthToBytes(bitwidth): return int(math.ceil(bitwidth / 8.0)) + def encodeNum(number, bitwidth): byte_len = bitwidthToBytes(bitwidth) # If number is negative, calculate the positive number that its # 2's complement encoding would look like in 'bitwidth' bits. orig_number = number if number < 0: - if number < -(2 ** (bitwidth-1)): - raise Exception("Negative number, %d, has 2's complement representation that does not fit in %d bits" % (number, bitwidth)) - number = (2 ** bitwidth) + number - num_str = '%x' % number + if number < -(2 ** (bitwidth - 1)): + raise Exception("Negative number, %d, has 2's complement" + " representation that does not fit in %d bits" + "" % (number, bitwidth)) + number = (2**bitwidth) + number + num_str = "%x" % number if orig_number < 0: - print("CONVERT_NEGATIVE_NUMBER debug: orig_number=%s number=%s bitwidth=%d num_str='%s'" + print("CONVERT_NEGATIVE_NUMBER debug: orig_number=%s number=%s" + " bitwidth=%d num_str='%s'" "" % (orig_number, number, bitwidth, num_str)) - if number >= 2 ** bitwidth: - raise Exception("Number, %d, does not fit in %d bits" % (number, bitwidth)) - return bytes.fromhex('0' * (byte_len * 2 - len(num_str)) + num_str) + if number >= 2**bitwidth: + raise Exception("Number, %d, does not fit in %d bits" + "" % (number, bitwidth)) + return bytes.fromhex("0" * (byte_len * 2 - len(num_str)) + num_str) + def decodeNum(encoded_number): return int(encoded_number.hex(), 16) + def encode(x, bitwidth): - 'Tries to infer the type of `x` and encode it' + "Tries to infer the type of `x` and encode it" byte_len = bitwidthToBytes(bitwidth) - if (type(x) == list or type(x) == tuple) and len(x) == 1: + if (isinstance(x, list) or isinstance(x, tuple)) and len(x) == 1: x = x[0] encoded_bytes = None - if type(x) == str: + if isinstance(x, str): if matchesMac(x): encoded_bytes = encodeMac(x) elif matchesIPv4(x): @@ -84,62 +105,63 @@ def encode(x, bitwidth): else: # Assume that the string is already encoded encoded_bytes = x - elif type(x) == int: + elif isinstance(x, int): encoded_bytes = encodeNum(x, bitwidth) else: raise Exception("Encoding objects of %r is not supported" % type(x)) - assert(len(encoded_bytes) == byte_len) + assert len(encoded_bytes) == byte_len return encoded_bytes -if __name__ == '__main__': + +if __name__ == "__main__": # TODO These tests should be moved out of main eventually # Test encoding and decoding for MAC address mac = "aa:bb:cc:dd:ee:ff" enc_mac = encodeMac(mac) - assert(enc_mac == '\xaa\xbb\xcc\xdd\xee\xff') + assert enc_mac == "\xaa\xbb\xcc\xdd\xee\xff" dec_mac = decodeMac(enc_mac) - assert(mac == dec_mac) + assert mac == dec_mac # Test encoding and decoding for IPv4 address ip0 = "10.0.0.1" enc_ipv4 = encodeIPv4(ip0) - assert(enc_ipv4 == '\x0a\x00\x00\x01') + assert enc_ipv4 == "\x0a\x00\x00\x01" dec_ipv4 = decodeIPv4(enc_ipv4) - assert(ip0 == dec_ipv4) + assert ip0 == dec_ipv4 # Test encoding and decoding for IPv6 address ip1 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" enc_ipv6 = encodeIPv6(ip1) - assert(enc_ipv6 == '\x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4') + assert enc_ipv6 == "\x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4" dec_ipv6 = decodeIPv6(enc_ipv6) - assert(ip1 == dec_ipv6) + assert ip1 == dec_ipv6 # Test encoding and decoding for a number num = 1337 byte_len = 5 enc_num = encodeNum(num, byte_len * 8) - assert(enc_num == '\x00\x00\x00\x05\x39') + assert enc_num == "\x00\x00\x00\x05\x39" dec_num = decodeNum(enc_num) - assert(num == dec_num) + assert num == dec_num - assert(matchesIPv4('10.0.0.1')) - assert(not matchesIPv4('10.0.0.1.5')) - assert(not matchesIPv4('1000.0.0.1')) - assert(not matchesIPv4('10001')) + assert matchesIPv4("10.0.0.1") + assert not matchesIPv4("10.0.0.1.5") + assert not matchesIPv4("1000.0.0.1") + assert not matchesIPv4("10001") - assert(matchesIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')) - assert(not matchesIPv6('241.54.113.65')) - assert(not matchesIPv6('::1::2')) - assert(not matchesIPv6('192.168.1.1')) + assert matchesIPv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + assert not matchesIPv6("241.54.113.65") + assert not matchesIPv6("::1::2") + assert not matchesIPv6("192.168.1.1") # Test generic encoding function - assert(encode(mac, 6 * 8) == enc_mac) - assert(encode(ip0, 4 * 8) == enc_ipv4) - assert(encode(ip1, 16 * 8) == enc_ipv6) - assert(encode(num, 5 * 8) == enc_num) - assert(encode((num,), 5 * 8) == enc_num) - assert(encode([num], 5 * 8) == enc_num) + assert encode(mac, 6 * 8) == enc_mac + assert encode(ip0, 4 * 8) == enc_ipv4 + assert encode(ip1, 16 * 8) == enc_ipv6 + assert encode(num, 5 * 8) == enc_num + assert encode((num,), 5 * 8) == enc_num + assert encode([num], 5 * 8) == enc_num num = 256 byte_len = 2 diff --git a/utils/p4runtime_lib/error_utils.py b/utils/p4runtime_lib/error_utils.py index 1325b0c3..85707b72 100644 --- a/utils/p4runtime_lib/error_utils.py +++ b/utils/p4runtime_lib/error_utils.py @@ -16,18 +16,20 @@ def __init__(self, message): super(P4RuntimeErrorFormatException, self).__init__(message) -# Parse the binary details of the gRPC error. This is required to print some -# helpful debugging information in tha case of batched Write / Read -# requests. Returns None if there are no useful binary details and throws -# P4RuntimeErrorFormatException if the error is not formatted -# properly. Otherwise, returns a list of tuples with the first element being the -# index of the operation in the batch that failed and the second element being -# the p4.Error Protobuf message. +# Parse the binary details of the gRPC error. This is required to +# print some helpful debugging information in tha case of batched +# Write / Read requests. Returns None if there are no useful binary +# details and throws P4RuntimeErrorFormatException if the error is not +# formatted properly. Otherwise, returns a list of tuples with the +# first element being the index of the operation in the batch that +# failed and the second element being the p4.Error Protobuf message. def parseGrpcErrorBinaryDetails(grpc_error): # Check if grpc_error is None or not an instance of grpc.RpcError if not grpc_error or not isinstance(grpc_error, grpc.RpcError): - raise P4RuntimeErrorFormatException(f"Invalid gRPC error object: {grpc_error}") - + raise P4RuntimeErrorFormatException( + f"Invalid gRPC error object: {grpc_error}" + ) + if grpc_error.code() != grpc.StatusCode.UNKNOWN: return None @@ -50,7 +52,8 @@ def parseGrpcErrorBinaryDetails(grpc_error): p4_error = p4runtime_pb2.Error() if not one_error_any.Unpack(p4_error): raise P4RuntimeErrorFormatException( - "Cannot convert Any message to p4.Error") + "Cannot convert Any message to p4.Error" + ) if p4_error.canonical_code == code_pb2.OK: continue indexed_p4_errors += [(idx, p4_error)] @@ -66,12 +69,12 @@ def parseGrpcErrorBinaryDetails(grpc_error): # batch) in order to print error code + user-facing message. See P4Runtime # documentation for more details on error-reporting. def printGrpcError(grpc_error): - print("gRPC Error", grpc_error.details(), end=' ') + print("gRPC Error", grpc_error.details(), end=" ") status_code = grpc_error.code() - print("({})".format(status_code.name), end=' ') + print("({})".format(status_code.name), end=" ") traceback = sys.exc_info()[2] - print("[{}:{}]".format( - traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + print("[{}:{}]".format(traceback.tb_frame.f_code.co_filename, + traceback.tb_lineno)) if status_code != grpc.StatusCode.UNKNOWN: return p4_errors = parseGrpcErrorBinaryDetails(grpc_error) @@ -79,7 +82,6 @@ def printGrpcError(grpc_error): return print("Errors in batch:") for idx, p4_error in p4_errors: - code_name = code_pb2._CODE.values_by_number[ - p4_error.canonical_code].name - print("\t* At index {}: {}, '{}'\n".format( - idx, code_name, p4_error.message)) + code_name = code_pb2._CODE.values_by_number[p4_error.canonical_code].name + print("\t* At index {}: {}, '{}'\n".format(idx, code_name, + p4_error.message)) diff --git a/utils/p4runtime_lib/helper.py b/utils/p4runtime_lib/helper.py index 55a79c44..daea2b73 100644 --- a/utils/p4runtime_lib/helper.py +++ b/utils/p4runtime_lib/helper.py @@ -15,8 +15,9 @@ def __init__(self, p4_info_filepath): p4info = p4info_pb2.P4Info() # Load the p4info file into a skeleton P4Info object with open(p4_info_filepath) as p4info_f: - google.protobuf.text_format.Merge(p4info_f.read(), p4info, - allow_unknown_field=True) + google.protobuf.text_format.Merge( + p4info_f.read(), p4info, allow_unknown_field=True + ) self.p4info = p4info def get(self, entity_type, name=None, id=None): @@ -26,16 +27,18 @@ def get(self, entity_type, name=None, id=None): for o in getattr(self.p4info, entity_type): pre = o.preamble if name: - if (pre.name == name or pre.alias == name): + if pre.name == name or pre.alias == name: return o else: if pre.id == id: return o if name: - raise AttributeError("Could not find %r of type %s" % (name, entity_type)) + raise AttributeError("Could not find %r of type %s" + "" % (name, entity_type)) else: - raise AttributeError("Could not find id %r of type %s" % (id, entity_type)) + raise AttributeError("Could not find id %r of type %s" + "" % (id, entity_type)) def get_id(self, entity_type, name): return self.get(entity_type, name=name).preamble.id @@ -47,8 +50,9 @@ def get_alias(self, entity_type, id): return self.get(entity_type, id=id).preamble.alias def __getattr__(self, attr): - # Synthesize convenience functions for name to id lookups for top-level entities - # e.g. get_tables_id(name_string) or get_actions_id(name_string) + # Synthesize convenience functions for name to id lookups for + # top-level entities e.g. get_tables_id(name_string) or + # get_actions_id(name_string) m = re.search(r"^get_(\w+)_id$", attr) if m: primitive = m.group(1) @@ -61,7 +65,8 @@ def __getattr__(self, attr): primitive = m.group(1) return lambda id: self.get_name(primitive, id) - raise AttributeError("%r object has no attribute %r" % (self.__class__, attr)) + raise AttributeError("%r object has no attribute %r" + "" % (self.__class__, attr)) def get_match_field(self, table_name, name=None, id=None): for t in self.p4info.tables: @@ -74,7 +79,9 @@ def get_match_field(self, table_name, name=None, id=None): elif id is not None: if mf.id == id: return mf - raise AttributeError("%r has no attribute %r" % (table_name, name if name is not None else id)) + raise AttributeError("%r has no attribute %r" + "" % (table_name, + name if name is not None else id)) def get_match_field_id(self, table_name, match_field_name): return self.get_match_field(table_name, name=match_field_name).id @@ -109,15 +116,15 @@ def get_match_field_pb(self, table_name, match_field_name, value): def get_match_field_value(self, match_field): match_type = match_field.WhichOneof("field_match_type") - if match_type == 'valid': + if match_type == "valid": return match_field.valid.value - elif match_type == 'exact': + elif match_type == "exact": return match_field.exact.value - elif match_type == 'lpm': + elif match_type == "lpm": return (match_field.lpm.value, match_field.lpm.prefix_len) - elif match_type == 'ternary': + elif match_type == "ternary": return (match_field.ternary.value, match_field.ternary.mask) - elif match_type == 'range': + elif match_type == "range": return (match_field.range.low, match_field.range.high) else: raise Exception("Unsupported match type with type %r" % match_type) @@ -133,7 +140,10 @@ def get_action_param(self, action_name, name=None, id=None): elif id is not None: if p.id == id: return p - raise AttributeError("action %r has no param %r, (has: %r)" % (action_name, name if name is not None else id, a.params)) + raise AttributeError( + "action %r has no param %r, (has: %r)" + % (action_name, name if name is not None else id, a.params) + ) def get_action_param_id(self, action_name, param_name): return self.get_action_param(action_name, name=param_name).id @@ -148,14 +158,16 @@ def get_action_param_pb(self, action_name, param_name, value): p4runtime_param.value = encode(value, p4info_param.bitwidth) return p4runtime_param - def buildTableEntry(self, - table_name, - match_fields=None, - default_action=False, - action_name=None, - action_params=None, - idle_timeout_ns=None, - priority=None): + def buildTableEntry( + self, + table_name, + match_fields=None, + default_action=False, + action_name=None, + action_params=None, + idle_timeout_ns=None, + priority=None, + ): table_entry = p4runtime_pb2.TableEntry() table_entry.table_id = self.get_tables_id(table_name) @@ -166,10 +178,12 @@ def buildTableEntry(self, table_entry.idle_timeout_ns = idle_timeout_ns if match_fields: - table_entry.match.extend([ - self.get_match_field_pb(table_name, match_field_name, value) - for match_field_name, value in match_fields.items() - ]) + table_entry.match.extend( + [ + self.get_match_field_pb(table_name, match_field_name, value) + for match_field_name, value in match_fields.items() + ] + ) if default_action: table_entry.is_default_action = True @@ -178,10 +192,12 @@ def buildTableEntry(self, action = table_entry.action.action action.action_id = self.get_actions_id(action_name) if action_params: - action.params.extend([ - self.get_action_param_pb(action_name, field_name, value) - for field_name, value in action_params.items() - ]) + action.params.extend( + [ + self.get_action_param_pb(action_name, field_name, value) + for field_name, value in action_params.items() + ] + ) return table_entry def buildMulticastGroupEntry(self, multicast_group_id, replicas): @@ -189,20 +205,24 @@ def buildMulticastGroupEntry(self, multicast_group_id, replicas): mc_entry.multicast_group_entry.multicast_group_id = multicast_group_id for replica in replicas: r = p4runtime_pb2.Replica() - r.egress_port = replica['egress_port'] - r.instance = replica['instance'] + r.egress_port = replica["egress_port"] + r.instance = replica["instance"] mc_entry.multicast_group_entry.replicas.extend([r]) return mc_entry - def buildCloneSessionEntry(self, clone_session_id, replicas, packet_length_bytes=0): + def buildCloneSessionEntry(self, clone_session_id, replicas, + packet_length_bytes=0): clone_entry = p4runtime_pb2.PacketReplicationEngineEntry() clone_entry.clone_session_entry.session_id = clone_session_id - clone_entry.clone_session_entry.packet_length_bytes = packet_length_bytes - clone_entry.clone_session_entry.class_of_service = 0 # PI currently supports only CoS=0 for clone session entry + clone_entry.clone_session_entry.packet_length_bytes = ( + packet_length_bytes + ) + clone_entry.clone_session_entry.class_of_service = ( + 0 # PI currently supports only CoS=0 for clone session entry + ) for replica in replicas: r = p4runtime_pb2.Replica() - r.egress_port = replica['egress_port'] - r.instance = replica['instance'] + r.egress_port = replica["egress_port"] + r.instance = replica["instance"] clone_entry.clone_session_entry.replicas.extend([r]) return clone_entry - diff --git a/utils/p4runtime_lib/simple_controller.py b/utils/p4runtime_lib/simple_controller.py index 35fcd795..291dbaf1 100755 --- a/utils/p4runtime_lib/simple_controller.py +++ b/utils/p4runtime_lib/simple_controller.py @@ -14,46 +14,71 @@ def error(msg): - print(' - ERROR! ' + msg, file=sys.stderr) + print(" - ERROR! " + msg, file=sys.stderr) + def info(msg): - print(' - ' + msg, file=sys.stdout) + print(" - " + msg, file=sys.stdout) class ConfException(Exception): pass + class InvalidFileContentException(ConfException): pass def main(): - parser = argparse.ArgumentParser(description='P4Runtime Simple Controller') - - parser.add_argument('-a', '--p4runtime-server-addr', - help='address and port of the switch\'s P4Runtime server (e.g. 192.168.0.1:50051)', - type=str, action="store", required=True) - parser.add_argument('-d', '--device-id', - help='Internal device ID to use in P4Runtime messages', - type=int, action="store", required=True) - parser.add_argument('-p', '--proto-dump-file', - help='path to file where to dump protobuf messages sent to the switch', - type=str, action="store", required=True) - parser.add_argument("-c", '--runtime-conf-file', - help="path to input runtime configuration file (JSON)", - type=str, action="store", required=True) + parser = argparse.ArgumentParser(description="P4Runtime Simple Controller") + + parser.add_argument( + "-a", + "--p4runtime-server-addr", + help=("address and port of the switch's P4Runtime server" + " (e.g. 192.168.0.1:50051)"), + type=str, + action="store", + required=True, + ) + parser.add_argument( + "-d", + "--device-id", + help="Internal device ID to use in P4Runtime messages", + type=int, + action="store", + required=True, + ) + parser.add_argument( + "-p", + "--proto-dump-file", + help="path to file where to dump protobuf messages sent to the switch", + type=str, + action="store", + required=True, + ) + parser.add_argument( + "-c", + "--runtime-conf-file", + help="path to input runtime configuration file (JSON)", + type=str, + action="store", + required=True, + ) args = parser.parse_args() if not os.path.exists(args.runtime_conf_file): parser.error("File %s does not exist!" % args.runtime_conf_file) workdir = os.path.dirname(os.path.abspath(args.runtime_conf_file)) - with open(args.runtime_conf_file, 'r') as sw_conf_file: - program_switch(addr=args.p4runtime_server_addr, - device_id=args.device_id, - sw_conf_file=sw_conf_file, - workdir=workdir, - proto_dump_fpath=args.proto_dump_file) + with open(args.runtime_conf_file, "r") as sw_conf_file: + program_switch( + addr=args.p4runtime_server_addr, + device_id=args.device_id, + sw_conf_file=sw_conf_file, + workdir=workdir, + proto_dump_fpath=args.proto_dump_file, + ) def check_switch_conf(sw_conf, workdir): @@ -63,11 +88,11 @@ def check_switch_conf(sw_conf, workdir): if "target" not in sw_conf: raise ConfException("missing key 'target'") - target = sw_conf['target'] + target = sw_conf["target"] if target not in target_choices: raise ConfException("unknown target '%s'" % target) - if target == 'bmv2': + if target == "bmv2": required_keys.append("bmv2_json") files_to_check.append("bmv2_json") @@ -81,14 +106,18 @@ def check_switch_conf(sw_conf, workdir): raise ConfException("file does not exist %s" % real_path) # check for file content (e.g. JSON format for bmv2_json) if conf_key == "bmv2_json": - with open(real_path, 'r') as f: + with open(real_path, "r") as f: try: json.load(f) # Check if the file can be parsed as JSON except json.JSONDecodeError as e: - raise InvalidFileContentException(f"Invalid JSON content in {real_path}: {e}") + raise InvalidFileContentException( + f"Invalid JSON content in {real_path}: {e}" + ) -def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath, runtime_json): +def program_switch( + addr, device_id, sw_conf_file, workdir, proto_dump_fpath, runtime_json +): sw_conf = json_load_byteified(sw_conf_file) try: check_switch_conf(sw_conf=sw_conf, workdir=workdir) @@ -96,17 +125,18 @@ def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath, run error("While parsing input runtime configuration: %s" % str(e)) return - info('Using P4Info file %s...' % sw_conf['p4info']) - p4info_fpath = os.path.join(workdir, sw_conf['p4info']) + info("Using P4Info file %s..." % sw_conf["p4info"]) + p4info_fpath = os.path.join(workdir, sw_conf["p4info"]) p4info_helper = helper.P4InfoHelper(p4info_fpath) - target = sw_conf['target'] + target = sw_conf["target"] info("Connecting to P4Runtime server on %s (%s)..." % (addr, target)) if target == "bmv2": - sw = bmv2.Bmv2SwitchConnection(address=addr, device_id=device_id, - proto_dump_file=proto_dump_fpath) + sw = bmv2.Bmv2SwitchConnection( + address=addr, device_id=device_id, proto_dump_file=proto_dump_fpath + ) else: raise Exception("Don't know how to connect to target %s" % target) @@ -114,30 +144,30 @@ def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath, run sw.MasterArbitrationUpdate() if target == "bmv2": - info("Setting pipeline config (%s)..." % sw_conf['bmv2_json']) - bmv2_json_fpath = os.path.join(workdir, sw_conf['bmv2_json']) + info("Setting pipeline config (%s)..." % sw_conf["bmv2_json"]) + bmv2_json_fpath = os.path.join(workdir, sw_conf["bmv2_json"]) sw.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json_fpath) else: raise Exception("Should not be here") - if 'table_entries' in sw_conf: - table_entries = sw_conf['table_entries'] + if "table_entries" in sw_conf: + table_entries = sw_conf["table_entries"] info("Inserting %d table entries..." % len(table_entries)) for entry in table_entries: info(tableEntryToString(entry)) validateTableEntry(entry, p4info_helper, runtime_json) insertTableEntry(sw, entry, p4info_helper) - if 'multicast_group_entries' in sw_conf: - group_entries = sw_conf['multicast_group_entries'] + if "multicast_group_entries" in sw_conf: + group_entries = sw_conf["multicast_group_entries"] info("Inserting %d group entries..." % len(group_entries)) for entry in group_entries: info(groupEntryToString(entry)) insertMulticastGroupEntry(sw, entry, p4info_helper) - if 'clone_session_entries' in sw_conf: - clone_entries = sw_conf['clone_session_entries'] + if "clone_session_entries" in sw_conf: + clone_entries = sw_conf["clone_session_entries"] info("Inserting %d clone entries..." % len(clone_entries)) for entry in clone_entries: info(cloneEntryToString(entry)) @@ -148,33 +178,32 @@ def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath, run def validateTableEntry(flow, p4info_helper, runtime_json): - table_name = flow['table'] - match_fields = flow.get('match') # None if not found - priority = flow.get('priority') # None if not found + table_name = flow["table"] + match_fields = flow.get("match") # None if not found + priority = flow.get("priority") # None if not found match_types_with_priority = [ p4info_pb2.MatchField.TERNARY, p4info_pb2.MatchField.RANGE, - p4info_pb2.MatchField.OPTIONAL + p4info_pb2.MatchField.OPTIONAL, ] if match_fields is not None and (priority is None or priority == 0): for match_field_name, _ in match_fields.items(): - p4info_match = p4info_helper.get_match_field( - table_name, match_field_name) + p4info_match = p4info_helper.get_match_field(table_name, + match_field_name) match_type = p4info_match.match_type if match_type in match_types_with_priority: - raise AssertionError( - "non-zero 'priority' field is required for all entries for table {} in {}" - .format(table_name, runtime_json) - ) + raise AssertionError("non-zero 'priority' field is required" + " for all entries for table {} in {}" + "".format(table_name, runtime_json)) def insertTableEntry(sw, flow, p4info_helper): - table_name = flow['table'] - match_fields = flow.get('match') # None if not found - action_name = flow['action_name'] - default_action = flow.get('default_action') # None if not found - action_params = flow['action_params'] - priority = flow.get('priority') # None if not found + table_name = flow["table"] + match_fields = flow.get("match") # None if not found + action_name = flow["action_name"] + default_action = flow.get("default_action") # None if not found + action_params = flow["action_params"] + priority = flow.get("priority") # None if not found table_entry = p4info_helper.buildTableEntry( table_name=table_name, @@ -182,7 +211,8 @@ def insertTableEntry(sw, flow, p4info_helper): default_action=default_action, action_name=action_name, action_params=action_params, - priority=priority) + priority=priority, + ) sw.WriteTableEntry(table_entry) @@ -194,7 +224,7 @@ def json_load_byteified(file_handle): def _byteify(data, ignore_dicts=False): # if this is a unicode string, return its string representation if isinstance(data, str): - return data.encode('utf-8') + return data.encode("utf-8") # if this is a list of values, return list of byteified values if isinstance(data, list): return [_byteify(item, ignore_dicts=True) for item in data] @@ -202,7 +232,8 @@ def _byteify(data, ignore_dicts=False): # but only if we haven't already byteified it if isinstance(data, dict) and not ignore_dicts: return { - _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) + _byteify(key, ignore_dicts=True): _byteify(value, + ignore_dicts=True) for key, value in data.items() } # if it's anything else, return it in its original form @@ -210,46 +241,60 @@ def _byteify(data, ignore_dicts=False): def tableEntryToString(flow): - if 'match' in flow: - match_str = ['%s=%s' % (match_name, str(flow['match'][match_name])) for match_name in - flow['match']] - match_str = ', '.join(match_str) - elif 'default_action' in flow and flow['default_action']: - match_str = '(default action)' + if "match" in flow: + match_str = [ + "%s=%s" % (match_name, str(flow["match"][match_name])) + for match_name in flow["match"] + ] + match_str = ", ".join(match_str) + elif "default_action" in flow and flow["default_action"]: + match_str = "(default action)" else: - match_str = '(any)' - params = ['%s=%s' % (param_name, str(flow['action_params'][param_name])) for param_name in - flow['action_params']] - params = ', '.join(params) - return "%s: %s => %s(%s)" % ( - flow['table'], match_str, flow['action_name'], params) + match_str = "(any)" + params = [ + "%s=%s" % (param_name, str(flow["action_params"][param_name])) + for param_name in flow["action_params"] + ] + params = ", ".join(params) + return ("%s: %s => %s(%s)" + "" % (flow["table"], match_str, flow["action_name"], params)) def groupEntryToString(rule): group_id = rule["multicast_group_id"] - replicas = ['%d' % replica["egress_port"] for replica in rule['replicas']] - ports_str = ', '.join(replicas) - return 'Group {0} => ({1})'.format(group_id, ports_str) + replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] + ports_str = ", ".join(replicas) + return "Group {0} => ({1})".format(group_id, ports_str) + def cloneEntryToString(rule): clone_id = rule["clone_session_id"] if "packet_length_bytes" in rule: - packet_length_bytes = str(rule["packet_length_bytes"])+"B" + packet_length_bytes = str(rule["packet_length_bytes"]) + "B" else: packet_length_bytes = "NO_TRUNCATION" - replicas = ['%d' % replica["egress_port"] for replica in rule['replicas']] - ports_str = ', '.join(replicas) - return 'Clone Session {0} => ({1}) ({2})'.format(clone_id, ports_str, packet_length_bytes) + replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] + ports_str = ", ".join(replicas) + return "Clone Session {0} => ({1}) ({2})".format( + clone_id, ports_str, packet_length_bytes + ) + def insertMulticastGroupEntry(sw, rule, p4info_helper): - mc_entry = p4info_helper.buildMulticastGroupEntry(rule["multicast_group_id"], rule['replicas']) + mc_entry = p4info_helper.buildMulticastGroupEntry( + rule["multicast_group_id"], rule["replicas"] + ) sw.WritePREEntry(mc_entry) + def insertCloneGroupEntry(sw, rule, p4info_helper): - clone_entry = p4info_helper.buildCloneSessionEntry(rule['clone_session_id'], rule['replicas'], - rule.get('packet_length_bytes', 0)) + clone_entry = p4info_helper.buildCloneSessionEntry( + rule["clone_session_id"], + rule["replicas"], + rule.get("packet_length_bytes", 0), + ) sw.WritePREEntry(clone_entry) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/utils/p4runtime_lib/switch.py b/utils/p4runtime_lib/switch.py index 50f021f8..e7b12e1a 100644 --- a/utils/p4runtime_lib/switch.py +++ b/utils/p4runtime_lib/switch.py @@ -14,10 +14,12 @@ # List of all active connections connections = [] + def ShutdownAllSwitchConnections(): for c in connections: c.shutdown() + class StreamDispatcher: def __init__(self, stream): self.stream = stream @@ -48,10 +50,11 @@ def _dispatch_loop(self): def stop(self): self.running = False - + + class SwitchConnection(object): - def __init__(self, name=None, address='127.0.0.1:50051', device_id=0, + def __init__(self, name=None, address="127.0.0.1:50051", device_id=0, proto_dump_file=None): self.name = name self.address = address @@ -63,19 +66,22 @@ def __init__(self, name=None, address='127.0.0.1:50051', device_id=0, self.channel = grpc.intercept_channel(self.channel, interceptor) self.client_stub = p4runtime_pb2_grpc.P4RuntimeStub(self.channel) self.requests_stream = IterableQueue() - self.stream_msg_resp = self.client_stub.StreamChannel(iter(self.requests_stream)) + self.stream_msg_resp = self.client_stub.StreamChannel( + iter(self.requests_stream) + ) self.dispatcher = StreamDispatcher(self.stream_msg_resp) self.proto_dump_file = proto_dump_file connections.append(self) @abstractmethod def buildDeviceConfig(self, **kwargs): - print("switch.py:SwitchConnection:buildDeviceConfig() should be overridden, but never called (?)", flush=True) + print("switch.py:SwitchConnection:buildDeviceConfig() should be" + " overridden, but never called (?)", flush=True,) assert False def shutdown(self): self.requests_stream.close() - self.dispatcher.stop() + self.dispatcher.stop() def MasterArbitrationUpdate(self, dry_run=False, **kwargs): request = p4runtime_pb2.StreamMessageRequest() @@ -99,7 +105,9 @@ def SetForwardingPipelineConfig(self, p4info, dry_run=False, **kwargs): config.p4info.CopyFrom(p4info) config.p4_device_config = device_config - request.action = p4runtime_pb2.SetForwardingPipelineConfigRequest.VERIFY_AND_COMMIT + request.action = ( + p4runtime_pb2.SetForwardingPipelineConfigRequest.VERIFY_AND_COMMIT + ) if dry_run: print("P4Runtime SetForwardingPipelineConfig:", request) else: @@ -192,9 +200,9 @@ def PacketOut(self, payload, metadatas): for meta in metadatas: item = p4runtime_pb2.PacketMetadata() item.metadata_id = i - item.value = meta["value"].to_bytes(meta["bitwidth"], 'big') + item.value = meta["value"].to_bytes(meta["bitwidth"], "big") metadata_list.append(item) - i +=1 + i += 1 packet_out.metadata.extend(metadata_list) request = p4runtime_pb2.StreamMessageRequest() @@ -207,35 +215,42 @@ def IdleTimeoutNotification(self, dry_run=False): print("P4 Runtime PacketIn: ", msg) else: return msg -class GrpcRequestLogger(grpc.UnaryUnaryClientInterceptor, - grpc.UnaryStreamClientInterceptor): + + +class GrpcRequestLogger( + grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor +): """Implementation of a gRPC interceptor that logs request to a file""" def __init__(self, log_file): self.log_file = log_file - with open(self.log_file, 'w') as f: + with open(self.log_file, "w") as f: # Clear content if it exists. f.write("") def log_message(self, method_name, body): - with open(self.log_file, 'a') as f: - ts = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + with open(self.log_file, "a") as f: + ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] msg = str(body) f.write("\n[%s] %s\n---\n" % (ts, method_name)) if len(msg) < MSG_LOG_MAX_LEN: f.write(str(body)) else: - f.write("Message too long (%d bytes)! Skipping log...\n" % len(msg)) - f.write('---\n') + f.write("Message too long (%d bytes)! Skipping log...\n" + "" % (len(msg))) + f.write("---\n") - def intercept_unary_unary(self, continuation, client_call_details, request): + def intercept_unary_unary(self, continuation, client_call_details, + request): self.log_message(client_call_details.method, request) return continuation(client_call_details, request) - def intercept_unary_stream(self, continuation, client_call_details, request): + def intercept_unary_stream(self, continuation, client_call_details, + request): self.log_message(client_call_details.method, request) return continuation(client_call_details, request) + class IterableQueue(Queue): _sentinel = object() diff --git a/utils/p4runtime_switch.py b/utils/p4runtime_switch.py index 35c6f831..11c73b11 100644 --- a/utils/p4runtime_switch.py +++ b/utils/p4runtime_switch.py @@ -16,21 +16,27 @@ class P4RuntimeSwitch(P4Switch): "BMv2 switch with gRPC support" + next_grpc_port = 50051 next_thrift_port = 9090 - def __init__(self, name, sw_path = None, json_path = None, - grpc_port = None, - thrift_port = None, - pcap_dump = False, - log_console = False, - verbose = False, - device_id = None, - enable_debugger = False, - log_file = None, - **kwargs): + def __init__( + self, + name, + sw_path=None, + json_path=None, + grpc_port=None, + thrift_port=None, + pcap_dump=False, + log_console=False, + verbose=False, + device_id=None, + enable_debugger=False, + log_file=None, + **kwargs + ): Switch.__init__(self, name, **kwargs) - assert (sw_path) + assert sw_path self.sw_path = sw_path # make sure that the provided sw_path is valid pathCheck(sw_path) @@ -57,12 +63,14 @@ def __init__(self, name, sw_path = None, json_path = None, P4RuntimeSwitch.next_thrift_port += 1 if check_listening_on_port(self.grpc_port): - error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port)) + error("%s cannot bind port %d because it is bound" + " by another process\n" + "" % (self.name, self.grpc_port)) exit(1) self.verbose = verbose logfile = "/tmp/p4s.{}.log".format(self.name) - self.output = open(logfile, 'w') + self.output = open(logfile, "w") self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger self.log_console = log_console @@ -86,7 +94,6 @@ def __init__(self, name, sw_path = None, json_path = None, if "priority_queues" in kwargs: self.priority_queues = kwargs["priority_queues"] - def check_switch_started(self, pid): for _ in range(SWITCH_START_TIMEOUT * 2): if not os.path.exists(os.path.join("/proc", str(pid))): @@ -100,12 +107,12 @@ def start(self, controllers): args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): - args.extend(['-i', str(port) + "@" + intf.name]) + args.extend(["-i", str(port) + "@" + intf.name]) if self.pcap_dump: args.append("--pcap %s" % self.pcap_dump) if self.nanomsg: - args.extend(['--nanolog', self.nanomsg]) - args.extend(['--device-id', str(self.device_id)]) + args.extend(["--nanolog", self.nanomsg]) + args.extend(["--device-id", str(self.device_id)]) P4Switch.device_id += 1 if self.json_path: args.append(self.json_path) @@ -116,21 +123,20 @@ def start(self, controllers): if self.log_console: args.append("--log-console") if self.thrift_port: - args.append('--thrift-port ' + str(self.thrift_port)) + args.append("--thrift-port " + str(self.thrift_port)) if self.grpc_port: args.append("-- --grpc-server-addr 0.0.0.0:" + str(self.grpc_port)) if self.cpu_port: args.append("--cpu-port " + str(self.cpu_port)) if self.priority_queues: args.append("--priority-queues " + str(self.priority_queues)) - cmd = ' '.join(args) + cmd = " ".join(args) info(cmd + "\n") print(cmd + "\n") - pid = None with tempfile.NamedTemporaryFile() as f: - self.cmd(cmd + ' >' + self.log_file + ' 2>&1 & echo $! >> ' + f.name) + self.cmd(cmd + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name) pid = int(f.read()) debug("P4 switch {} PID is {}.\n".format(self.name, pid)) if not self.check_switch_started(pid): diff --git a/utils/run_exercise.py b/utils/run_exercise.py index 672f1998..0026be54 100755 --- a/utils/run_exercise.py +++ b/utils/run_exercise.py @@ -26,12 +26,13 @@ def configureP4Switch(**switch_args): - """ Helper class that is called by mininet to initialize - the virtual P4 switches. The purpose is to ensure each - switch's thrift server is using a unique port. + """Helper class that is called by mininet to initialize + the virtual P4 switches. The purpose is to ensure each + switch's thrift server is using a unique port. """ - if "sw_path" in switch_args and 'grpc' in switch_args['sw_path']: - # If grpc appears in the BMv2 switch target, we assume will start P4Runtime + if "sw_path" in switch_args and "grpc" in switch_args["sw_path"]: + # If grpc appears in the BMv2 switch target, we assume will + # start P4Runtime class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch): def __init__(self, *opts, **kwargs): kwargs.update(switch_args) @@ -42,12 +43,13 @@ def describe(self): return ConfiguredP4RuntimeSwitch else: + class ConfiguredP4Switch(P4Switch): next_thrift_port = 9090 + def __init__(self, *opts, **kwargs): - global next_thrift_port kwargs.update(switch_args) - kwargs['thrift_port'] = ConfiguredP4Switch.next_thrift_port + kwargs["thrift_port"] = ConfiguredP4Switch.next_thrift_port ConfiguredP4Switch.next_thrift_port += 1 P4Switch.__init__(self, *opts, **kwargs) @@ -58,16 +60,17 @@ def describe(self): class ExerciseTopo(Topo): - """ The mininet topology class for the P4 tutorial exercises. - """ - def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): + """The mininet topology class for the P4 tutorial exercises.""" + + def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, + **opts): Topo.__init__(self, **opts) host_links = [] switch_links = [] # assumes host always comes first for host<-->switch links for link in links: - if link['node1'][0] == 'h': + if link["node1"][0] == "h": host_links.append(link) else: switch_links.append(link) @@ -75,18 +78,17 @@ def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): for sw, params in switches.items(): if "program" in params: switchClass = configureP4Switch( - sw_path=bmv2_exe, - json_path=params["program"], - log_console=True, - pcap_dump=pcap_dir) + sw_path=bmv2_exe, + json_path=params["program"], + log_console=True, + pcap_dump=pcap_dir, + ) else: # add default switch switchClass = None - switch_opts = { - "log_file": "%s/%s.log" % (log_dir, sw), - "cls": switchClass - } + switch_opts = {"log_file": "%s/%s.log" % (log_dir, sw), + "cls": switchClass} for key in ("cpu_port", "priority_queues"): if key in params: @@ -95,102 +97,118 @@ def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): self.addSwitch(sw, **switch_opts) for link in host_links: - host_name = link['node1'] - sw_name, sw_port = self.parse_switch_node(link['node2']) - host_ip = hosts[host_name]['ip'] - host_mac = hosts[host_name]['mac'] + host_name = link["node1"] + sw_name, sw_port = self.parse_switch_node(link["node2"]) + host_ip = hosts[host_name]["ip"] + host_mac = hosts[host_name]["mac"] self.addHost(host_name, ip=host_ip, mac=host_mac) - self.addLink(host_name, sw_name, - delay=link['latency'], bw=link['bandwidth'], - port2=sw_port) + self.addLink( + host_name, + sw_name, + delay=link["latency"], + bw=link["bandwidth"], + port2=sw_port, + ) for link in switch_links: - sw1_name, sw1_port = self.parse_switch_node(link['node1']) - sw2_name, sw2_port = self.parse_switch_node(link['node2']) - self.addLink(sw1_name, sw2_name, - port1=sw1_port, port2=sw2_port, - delay=link['latency'], bw=link['bandwidth']) - + sw1_name, sw1_port = self.parse_switch_node(link["node1"]) + sw2_name, sw2_port = self.parse_switch_node(link["node2"]) + self.addLink( + sw1_name, + sw2_name, + port1=sw1_port, + port2=sw2_port, + delay=link["latency"], + bw=link["bandwidth"], + ) def parse_switch_node(self, node): - assert(len(node.split('-')) == 2) - sw_name, sw_port = node.split('-') + assert len(node.split("-")) == 2 + sw_name, sw_port = node.split("-") try: sw_port = int(sw_port[1:]) except: - raise Exception('Invalid switch node in topology file: {}'.format(node)) + raise Exception("Invalid switch node in" + " topology file: {}".format(node)) return sw_name, sw_port class ExerciseRunner: """ - Attributes: - log_dir : string // directory for mininet log files - pcap_dir : string // directory for mininet switch pcap files - quiet : bool // determines if we print logger messages + Attributes: + log_dir : string // directory for mininet log files + pcap_dir : string // directory for mininet switch pcap files + quiet : bool // determines if we print logger messages - hosts : dict // mininet host names and their associated properties - switches : dict // mininet switch names and their associated properties - links : list // list of mininet link properties + hosts : dict // mininet host names and their associated properties + switches : dict // mininet switch names and their associated properties + links : list // list of mininet link properties - switch_json : string // json of the compiled p4 example - bmv2_exe : string // name or path of the p4 switch binary + switch_json : string // json of the compiled p4 example + bmv2_exe : string // name or path of the p4 switch binary - topo : Topo object // The mininet topology instance - net : Mininet object // The mininet instance + topo : Topo object // The mininet topology instance + net : Mininet object // The mininet instance """ + def logger(self, *items): if not self.quiet: - print(' '.join(items)) + print(" ".join(items)) - def format_latency(self, l): - """ Helper method for parsing link latencies from the topology json. """ - if isinstance(l, str): - return l + def format_latency(self, x): + """Helper method for parsing link latencies from the topology json.""" + if isinstance(x, str): + return x else: - return str(l) + "ms" - - - def __init__(self, topo_file, log_dir, pcap_dir, - switch_json, bmv2_exe='simple_switch', quiet=False): - """ Initializes some attributes and reads the topology json. Does not - actually run the exercise. Use run_exercise() for that. - - Arguments: - topo_file : string // A json file which describes the exercise's - mininet topology. - log_dir : string // Path to a directory for storing exercise logs - pcap_dir : string // Ditto, but for mininet switch pcap files - switch_json : string // Path to a compiled p4 json for bmv2 - bmv2_exe : string // Path to the p4 behavioral binary - quiet : bool // Enable/disable script debug messages + return str(x) + "ms" + + def __init__( + self, + topo_file, + log_dir, + pcap_dir, + switch_json, + bmv2_exe="simple_switch", + quiet=False, + ): + """Initializes some attributes and reads the topology json. Does not + actually run the exercise. Use run_exercise() for that. + + Arguments: + topo_file : string // A json file which describes the exercise's + mininet topology. + log_dir : string // Path to a directory for storing exercise logs + pcap_dir : string // Ditto, but for mininet switch pcap files + switch_json : string // Path to a compiled p4 json for bmv2 + bmv2_exe : string // Path to the p4 behavioral binary + quiet : bool // Enable/disable script debug messages """ self.quiet = quiet - self.logger('Reading topology file.') - with open(topo_file, 'r') as f: + self.logger("Reading topology file.") + with open(topo_file, "r") as f: topo = json.load(f) - self.hosts = topo['hosts'] - self.switches = topo['switches'] - self.links = self.parse_links(topo['links']) + self.hosts = topo["hosts"] + self.switches = topo["switches"] + self.links = self.parse_links(topo["links"]) # Ensure all the needed directories exist and are directories for dir_name in [log_dir, pcap_dir]: if not os.path.isdir(dir_name): if os.path.exists(dir_name): - raise Exception("'%s' exists and is not a directory!" % dir_name) + raise Exception("'%s' exists and is not a directory!" + "" % (dir_name)) os.mkdir(dir_name) self.log_dir = log_dir self.pcap_dir = pcap_dir self.switch_json = switch_json self.bmv2_exe = bmv2_exe - def run_exercise(self): - """ Sets up the mininet instance, programs the switches, - and starts the mininet CLI. This is the main method to run after - initializing the object. + """Sets up the mininet instance, programs the switches, + and starts the mininet CLI. This is the main method to run after + initializing the object. """ # Initialize mininet with the topology specified by the config self.create_network() @@ -208,125 +226,145 @@ def run_exercise(self): # stop right after the CLI is exited self.net.stop() - def parse_links(self, unparsed_links): - """ Given a list of links descriptions of the form [node1, node2, latency, bandwidth] - with the latency and bandwidth being optional, parses these descriptions - into dictionaries and store them as self.links + """Given a list of links descriptions of the form [node1, + node2, latency, bandwidth] with the latency and bandwidth + being optional, parses these descriptions into dictionaries + and store them as self.links """ links = [] for link in unparsed_links: # make sure each link's endpoints are ordered alphabetically - s, t, = link[0], link[1] + ( + s, + t, + ) = ( + link[0], + link[1], + ) if s > t: - s,t = t,s + s, t = t, s - link_dict = {'node1':s, - 'node2':t, - 'latency':'0ms', - 'bandwidth':None - } + link_dict = {"node1": s, "node2": t, "latency": "0ms", + "bandwidth": None} if len(link) > 2: - link_dict['latency'] = self.format_latency(link[2]) + link_dict["latency"] = self.format_latency(link[2]) if len(link) > 3: - link_dict['bandwidth'] = link[3] - - if link_dict['node1'][0] == 'h': - assert link_dict['node2'][0] == 's', 'Hosts should be connected to switches, not ' + str(link_dict['node2']) + link_dict["bandwidth"] = link[3] + + if link_dict["node1"][0] == "h": + assert ( + link_dict["node2"][0] == "s" + ), "Hosts should be connected to switches, not " + str( + link_dict["node2"] + ) links.append(link_dict) return links - def create_network(self): - """ Create the mininet network object, and store it as self.net. + """Create the mininet network object, and store it as self.net. - Side effects: - - Mininet topology instance stored as self.topo - - Mininet instance stored as self.net + Side effects: + - Mininet topology instance stored as self.topo + - Mininet instance stored as self.net """ self.logger("Building mininet topology.") defaultSwitchClass = configureP4Switch( - sw_path=self.bmv2_exe, - json_path=self.switch_json, - log_console=True, - pcap_dump=self.pcap_dir) - - self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir) - - self.net = Mininet(topo = self.topo, - link = TCLink, - host = P4Host, - switch = defaultSwitchClass, - controller = None) + sw_path=self.bmv2_exe, + json_path=self.switch_json, + log_console=True, + pcap_dump=self.pcap_dir, + ) + + self.topo = ExerciseTopo( + self.hosts, + self.switches, + self.links, + self.log_dir, + self.bmv2_exe, + self.pcap_dir, + ) + + self.net = Mininet( + topo=self.topo, + link=TCLink, + host=P4Host, + switch=defaultSwitchClass, + controller=None, + ) def program_switch_p4runtime(self, sw_name, sw_dict): - """ This method will use P4Runtime to program the switch using the - content of the runtime JSON file as input. + """This method will use P4Runtime to program the switch using the + content of the runtime JSON file as input. """ sw_obj = self.net.get(sw_name) grpc_port = sw_obj.grpc_port device_id = sw_obj.device_id - runtime_json = sw_dict['runtime_json'] - self.logger('Configuring switch %s using P4Runtime with file %s' % (sw_name, runtime_json)) - with open(runtime_json, 'r') as sw_conf_file: - outfile = '%s/%s-p4runtime-requests.txt' %(self.log_dir, sw_name) + runtime_json = sw_dict["runtime_json"] + self.logger( + "Configuring switch %s using P4Runtime with file %s" + % (sw_name, runtime_json) + ) + with open(runtime_json, "r") as sw_conf_file: + outfile = "%s/%s-p4runtime-requests.txt" % (self.log_dir, sw_name) p4runtime_lib.simple_controller.program_switch( - addr='127.0.0.1:%d' % grpc_port, + addr="127.0.0.1:%d" % grpc_port, device_id=device_id, sw_conf_file=sw_conf_file, workdir=os.getcwd(), proto_dump_fpath=outfile, - runtime_json=runtime_json + runtime_json=runtime_json, ) def program_switch_cli(self, sw_name, sw_dict): - """ This method will start up the CLI and use the contents of the - command files as input. + """This method will start up the CLI and use the contents of the + command files as input. """ - cli = 'simple_switch_CLI' + cli = "simple_switch_CLI" # get the port for this particular switch's thrift server sw_obj = self.net.get(sw_name) thrift_port = sw_obj.thrift_port - cli_input_commands = sw_dict['cli_input'] - self.logger('Configuring switch %s with file %s' % (sw_name, cli_input_commands)) - with open(cli_input_commands, 'r') as fin: - cli_outfile = '%s/%s_cli_output.log'%(self.log_dir, sw_name) - with open(cli_outfile, 'w') as fout: - subprocess.Popen([cli, '--thrift-port', str(thrift_port)], + cli_input_commands = sw_dict["cli_input"] + self.logger("Configuring switch %s with file %s" + "" % (sw_name, cli_input_commands)) + with open(cli_input_commands, "r") as fin: + cli_outfile = "%s/%s_cli_output.log" % (self.log_dir, sw_name) + with open(cli_outfile, "w") as fout: + subprocess.Popen([cli, "--thrift-port", str(thrift_port)], stdin=fin, stdout=fout) def program_switches(self): - """ This method will program each switch using the BMv2 CLI and/or - P4Runtime, depending if any command or runtime JSON files were - provided for the switches. + """This method will program each switch using the BMv2 CLI and/or + P4Runtime, depending if any command or runtime JSON files were + provided for the switches. """ for sw_name, sw_dict in self.switches.items(): - if 'cli_input' not in sw_dict and 'runtime_json' not in sw_dict: - self.logger('Warning: No control plane file provided for switch %s.' % sw_name) + if "cli_input" not in sw_dict and "runtime_json" not in sw_dict: + self.logger("Warning: No control plane file provided" + " for switch %s." % (sw_name)) continue - if 'cli_input' in sw_dict: + if "cli_input" in sw_dict: self.program_switch_cli(sw_name, sw_dict) - if 'runtime_json' in sw_dict: + if "runtime_json" in sw_dict: self.program_switch_p4runtime(sw_name, sw_dict) def program_hosts(self): - """ Execute any commands provided in the topology.json file on each Mininet host - """ + """Execute any commands provided in the topology.json file on + each Mininet host""" for host_name, host_info in list(self.hosts.items()): h = self.net.get(host_name) if "commands" in host_info: for cmd in host_info["commands"]: h.cmd(cmd) - def do_net_cli(self): - """ Starts up the mininet CLI and prints some helpful output. + """Starts up the mininet CLI and prints some helpful output. - Assumes: - - A mininet instance is stored as self.net and self.net.start() has - been called. + Assumes: + - A mininet instance is stored as self.net and self.net.start() has + been called. """ for s in self.net.switches: s.describe() @@ -335,57 +373,92 @@ def do_net_cli(self): self.logger("Starting mininet CLI") # Generate a message that will be printed by the Mininet CLI to make # interacting with the simple switch a little easier. - print('') - print('======================================================================') - print('Welcome to the BMV2 Mininet CLI!') - print('======================================================================') - print('Your P4 program is installed into the BMV2 software switch') - print('and your initial runtime configuration is loaded. You can interact') - print('with the network using the mininet CLI below.') - print('') + print("") + print("========================================" + "==============================") + print("Welcome to the BMV2 Mininet CLI!") + print("========================================" + "==============================") + print("Your P4 program is installed into the BMV2 software switch") + print("and your initial runtime configuration is loaded. You can") + print("interact with the network using the mininet CLI below.") + print("") if self.switch_json: - print('To inspect or change the switch configuration, connect to') - print('its CLI from your host operating system using this command:') - print(' simple_switch_CLI --thrift-port ') - print('') - print('To view a switch log, run this command from your host OS:') - print(' tail -f %s/.log' % self.log_dir) - print('') - print('To view the switch output pcap, check the pcap files in %s:' % self.pcap_dir) - print(' for example run: sudo tcpdump -xxx -r s1-eth1.pcap') - print('') - if 'grpc' in self.bmv2_exe: - print('To view the P4Runtime requests sent to the switch, check the') - print('corresponding txt file in %s:' % self.log_dir) - print(' for example run: cat %s/s1-p4runtime-requests.txt' % self.log_dir) - print('') + print("To inspect or change the switch configuration, connect to") + print("its CLI from your host operating system using this") + print("command:") + print(" simple_switch_CLI --thrift-port ") + print("") + print("To view a switch log, run this command from your host OS:") + print(" tail -f %s/.log" % self.log_dir) + print("") + print( + "To view the switch output pcap, check the pcap files in %s:" + % self.pcap_dir + ) + print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap") + print("") + if "grpc" in self.bmv2_exe: + print("To view the P4Runtime requests sent to the switch, check") + print("the corresponding txt file in %s:" % (self.log_dir)) + print(" for example run: cat %s/s1-p4runtime-requests.txt" + "" % (self.log_dir)) + print("") CLI(self.net) def get_args(): cwd = os.getcwd() - default_logs = os.path.join(cwd, 'logs') - default_pcaps = os.path.join(cwd, 'pcaps') + default_logs = os.path.join(cwd, "logs") + default_pcaps = os.path.join(cwd, "pcaps") parser = argparse.ArgumentParser() - parser.add_argument('-q', '--quiet', help='Suppress log messages.', - action='store_true', required=False, default=False) - parser.add_argument('-t', '--topo', help='Path to topology json', - type=str, required=False, default='./topology.json') - parser.add_argument('-l', '--log-dir', type=str, required=False, default=default_logs) - parser.add_argument('-p', '--pcap-dir', type=str, required=False, default=default_pcaps) - parser.add_argument('-j', '--switch_json', type=str, required=False) - parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable', - type=str, required=False, default='simple_switch') + parser.add_argument( + "-q", + "--quiet", + help="Suppress log messages.", + action="store_true", + required=False, + default=False, + ) + parser.add_argument( + "-t", + "--topo", + help="Path to topology json", + type=str, + required=False, + default="./topology.json", + ) + parser.add_argument( + "-l", "--log-dir", type=str, required=False, default=default_logs + ) + parser.add_argument( + "-p", "--pcap-dir", type=str, required=False, default=default_pcaps + ) + parser.add_argument("-j", "--switch_json", type=str, required=False) + parser.add_argument( + "-b", + "--behavioral-exe", + help="Path to behavioral executable", + type=str, + required=False, + default="simple_switch", + ) return parser.parse_args() -if __name__ == '__main__': +if __name__ == "__main__": # from mininet.log import setLogLevel # setLogLevel("info") args = get_args() - exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir, - args.switch_json, args.behavioral_exe, args.quiet) + exercise = ExerciseRunner( + args.topo, + args.log_dir, + args.pcap_dir, + args.switch_json, + args.behavioral_exe, + args.quiet, + ) exercise.run_exercise() diff --git a/vm-ubuntu-20.04/py3localpath.py b/vm-ubuntu-20.04/py3localpath.py index 0955b9ea..7d2bdfae 100755 --- a/vm-ubuntu-20.04/py3localpath.py +++ b/vm-ubuntu-20.04/py3localpath.py @@ -7,7 +7,8 @@ import re import sys -l1=[x for x in sys.path if re.match(r'/usr/local/lib/python3.[0-9]+/dist-packages', x)] +l1 = [x for x in sys.path + if re.match(r'/usr/local/lib/python3.[0-9]+/dist-packages', x)] if len(l1) == 1: py3distdir = l1[0] @@ -15,7 +16,8 @@ if m: print(m.group(1)) else: - print("Inconceivable! Somehow the second pattern did not match but the first did.") + print("Inconceivable! Somehow the second pattern did not match" + " but the first did.") sys.exit(1) else: print("Found %d matching entries in Python3 sys.path instead of 1: %s" From cf1b2931a3e417bc7ce4cda64d87cf1a7b1e8c26 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Thu, 8 Oct 2026 13:23:39 -0400 Subject: [PATCH 4/6] Add safe changes performed by `ruff check --fix` Signed-off-by: Andy Fingerhut --- exercises/basic/ptf/basic_fwd.py | 11 +- exercises/basic/receive.py | 7 +- exercises/basic/send.py | 4 +- exercises/basic_tunnel/myTunnel_header.py | 2 +- exercises/basic_tunnel/ptf/basic_tunnel.py | 17 +- exercises/basic_tunnel/receive.py | 2 +- exercises/basic_tunnel/send.py | 13 +- exercises/calc/calc.py | 5 +- exercises/ecn/receive.py | 2 +- exercises/ecn/send.py | 4 +- exercises/flowcache/mycontroller.py | 212 ++++++++++--------- exercises/flowcache/solution/mycontroller.py | 204 ++++++++++-------- exercises/link_monitor/probe_hdrs.py | 2 +- exercises/link_monitor/receive.py | 7 +- exercises/link_monitor/send.py | 2 +- exercises/load_balance/receive.py | 7 +- exercises/load_balance/send.py | 4 +- exercises/mri/receive.py | 13 +- exercises/mri/send.py | 15 +- exercises/p4runtime/mycontroller.py | 18 +- exercises/p4runtime/solution/mycontroller.py | 27 +-- exercises/qos/send.py | 6 +- exercises/source_routing/receive.py | 16 +- exercises/source_routing/send.py | 7 +- utils/README.md | 126 ++++++----- utils/mininet/appcontroller.py | 19 +- utils/mininet/apptopo.py | 9 +- utils/mininet/multi_switch_mininet.py | 33 ++- utils/mininet/p4_mininet.py | 21 +- utils/mininet/shortest_path.py | 2 - utils/mininet/single_switch_mininet.py | 11 +- utils/p4_mininet.py | 22 +- utils/p4apprunner.py | 107 +++++----- utils/p4runtime_lib/convert.py | 21 +- utils/p4runtime_lib/error_utils.py | 14 +- utils/p4runtime_lib/helper.py | 24 +-- utils/p4runtime_lib/simple_controller.py | 34 ++- utils/p4runtime_lib/switch.py | 26 +-- utils/p4runtime_switch.py | 33 +-- utils/run_exercise.py | 60 +++--- vm-ubuntu-20.04/py3localpath.py | 18 +- 41 files changed, 608 insertions(+), 579 deletions(-) mode change 100755 => 100644 exercises/link_monitor/probe_hdrs.py diff --git a/exercises/basic/ptf/basic_fwd.py b/exercises/basic/ptf/basic_fwd.py index 2e129c22..c8aaf84b 100644 --- a/exercises/basic/ptf/basic_fwd.py +++ b/exercises/basic/ptf/basic_fwd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # SPDX-FileCopyrightText: 2026 Andrew Nguyen # # SPDX-License-Identifier: Apache-2.0 @@ -24,8 +22,7 @@ logger = logging.getLogger(None) ch = logging.StreamHandler() ch.setLevel(logging.INFO) -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s" - " - %(message)s") +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") ch.setFormatter(formatter) logger.addHandler(ch) @@ -60,8 +57,7 @@ def setUp(self): # Load the P4 program onto the switch self.sw.SetForwardingPipelineConfig( - p4info=self.p4info_helper.p4info, - bmv2_json_file_path=p4prog_binary_fname + p4info=self.p4info_helper.p4info, bmv2_json_file_path=p4prog_binary_fname ) def tearDown(self): @@ -173,8 +169,7 @@ def runTest(self): ttl_in = 64 for e in entries: pkt = tu.simple_tcp_packet( - eth_src=in_smac, eth_dst=in_dmac, ip_dst=e["pkt_dst"], - ip_ttl=ttl_in + eth_src=in_smac, eth_dst=in_dmac, ip_dst=e["pkt_dst"], ip_ttl=ttl_in ) exp_pkt = tu.simple_tcp_packet( eth_src=in_dmac, diff --git a/exercises/basic/receive.py b/exercises/basic/receive.py index c202806a..9fc8a891 100755 --- a/exercises/basic/receive.py +++ b/exercises/basic/receive.py @@ -27,7 +27,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -36,8 +36,9 @@ class IPOption_MRI(IPOption): option = 31 fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", length_of="swids", - adjust=lambda pkt, x: x + 4), + FieldLenField( + "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 + ), ShortField("count", 0), FieldListField( "swids", [], IntField("", 0), length_from=lambda pkt: pkt.count * 4 diff --git a/exercises/basic/send.py b/exercises/basic/send.py index e1a77a67..23d41610 100755 --- a/exercises/basic/send.py +++ b/exercises/basic/send.py @@ -18,7 +18,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -26,7 +26,7 @@ def main(): if len(sys.argv) < 3: print('pass 2 arguments: ""') - exit(1) + sys.exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() diff --git a/exercises/basic_tunnel/myTunnel_header.py b/exercises/basic_tunnel/myTunnel_header.py index e61bf677..0eecd13a 100644 --- a/exercises/basic_tunnel/myTunnel_header.py +++ b/exercises/basic_tunnel/myTunnel_header.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-only -from scapy.all import Packet, Ether, IP, ShortField, bind_layers +from scapy.all import IP, Ether, Packet, ShortField, bind_layers TYPE_MYTUNNEL = 0x1212 TYPE_IPV4 = 0x0800 diff --git a/exercises/basic_tunnel/ptf/basic_tunnel.py b/exercises/basic_tunnel/ptf/basic_tunnel.py index 05e871e8..eb1fbb8f 100644 --- a/exercises/basic_tunnel/ptf/basic_tunnel.py +++ b/exercises/basic_tunnel/ptf/basic_tunnel.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # Copyright 2026 Andrew Nguyen # SPDX-License-Identifier: GPL-2.0-only # Reason-GPL: import-scapy @@ -73,8 +71,7 @@ def setUp(self): # Load the P4 Program onto the switch self.sw.SetForwardingPipelineConfig( - p4info=self.p4info_helper.p4info, - bmv2_json_file_path=p4prog_binary_fname + p4info=self.p4info_helper.p4info, bmv2_json_file_path=p4prog_binary_fname ) def tearDown(self): @@ -148,8 +145,7 @@ class TunnelForwardTest(BasicTunnelTest): def runTest(self): in_pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", - type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=2) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) @@ -166,8 +162,7 @@ class TunnelDropOnMissTest(BasicTunnelTest): def runTest(self): in_pkt = ( - Ether(src="00:11:22:33:44:66", dst="ff:ff:ff:ff:ff:ff", - type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:66", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=77) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) @@ -208,8 +203,7 @@ def runTest(self): self.add_tunnel_entry(dst_id=5, port=2) pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", - type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) / MyTunnel(proto_id=0x9999, dst_id=5) / "unknown-proto-payload" ) @@ -245,8 +239,7 @@ def runTest(self): # test tunnel packet which should hit myTunnel_exact table tunnel_pkt = ( - Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", - type=TYPE_MYTUNNEL) + Ether(src="00:11:22:33:44:55", dst="ff:ff:ff:ff:ff:ff", type=TYPE_MYTUNNEL) / MyTunnel(proto_id=TYPE_IPV4, dst_id=2) / IP(src="10.0.1.1", dst="10.0.3.3", ttl=64) / TCP(sport=12345, dport=1234) diff --git a/exercises/basic_tunnel/receive.py b/exercises/basic_tunnel/receive.py index 942c89ed..0abb4d81 100755 --- a/exercises/basic_tunnel/receive.py +++ b/exercises/basic_tunnel/receive.py @@ -18,7 +18,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface diff --git a/exercises/basic_tunnel/send.py b/exercises/basic_tunnel/send.py index 3c00fd84..709995f3 100755 --- a/exercises/basic_tunnel/send.py +++ b/exercises/basic_tunnel/send.py @@ -6,6 +6,7 @@ import argparse import random import socket +import sys from myTunnel_header import MyTunnel from scapy.all import IP, TCP, Ether, get_if_hwaddr, get_if_list, sendp @@ -19,16 +20,14 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface def main(): parser = argparse.ArgumentParser() - parser.add_argument("ip_addr", type=str, - help="The destination IP address to use") - parser.add_argument("message", type=str, - help="The message to include in packet") + parser.add_argument("ip_addr", type=str, help="The destination IP address to use") + parser.add_argument("message", type=str, help="The message to include in packet") parser.add_argument( "--dst_id", type=int, @@ -43,7 +42,7 @@ def main(): iface = get_if() if dst_id is not None: - print("sending on interface {} to dst_id {}".format(iface, str(dst_id))) + print(f"sending on interface {iface} to dst_id {dst_id!s}") pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") pkt = ( pkt @@ -53,7 +52,7 @@ def main(): / args.message ) else: - print("sending on interface {} to IP addr {}".format(iface, str(addr))) + print(f"sending on interface {iface} to IP addr {addr!s}") pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") pkt = ( pkt diff --git a/exercises/calc/calc.py b/exercises/calc/calc.py index 895a6bef..e27621c1 100644 --- a/exercises/calc/calc.py +++ b/exercises/calc/calc.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - # SPDX-FileCopyrightText: 2018 Nate Foster # # SPDX-License-Identifier: GPL-2.0-only @@ -98,8 +96,7 @@ def main(): i, ts = p(s, 0, []) # Construct packet using parsed tokens pkt = Ether(dst="00:04:00:00:00:00", type=0x1234) / P4calc( - op=ts[1].value, operand_a=int(ts[0].value), - operand_b=int(ts[2].value) + op=ts[1].value, operand_a=int(ts[0].value), operand_b=int(ts[2].value) ) pkt = pkt / " " diff --git a/exercises/ecn/receive.py b/exercises/ecn/receive.py index 282ebb35..4e55d86e 100755 --- a/exercises/ecn/receive.py +++ b/exercises/ecn/receive.py @@ -16,7 +16,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface diff --git a/exercises/ecn/send.py b/exercises/ecn/send.py index 866a3456..dda54d7f 100755 --- a/exercises/ecn/send.py +++ b/exercises/ecn/send.py @@ -19,7 +19,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -27,7 +27,7 @@ def main(): if len(sys.argv) < 4: print('pass 2 arguments: "" ') - exit(1) + sys.exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() diff --git a/exercises/flowcache/mycontroller.py b/exercises/flowcache/mycontroller.py index 0062a821..57a76d76 100755 --- a/exercises/flowcache/mycontroller.py +++ b/exercises/flowcache/mycontroller.py @@ -4,29 +4,28 @@ # # SPDX-License-Identifier: GPL-2.0-only import argparse -import os -import sys import asyncio -import traceback -import time import ipaddress +import os import pprint - +import sys +import time +import traceback from collections import Counter from datetime import datetime, timedelta -from scapy.all import Ether, IP import grpc +from scapy.all import IP, Ether # Import P4Runtime lib from parent utils dir # Probably there's a better way of doing this. sys.path.append( - os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../utils/")) + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../utils/") +) import p4runtime_lib.bmv2 import p4runtime_lib.helper -from p4runtime_lib.switch import ShutdownAllSwitchConnections import p4runtime_sh.p4runtime as shp4rt +from p4runtime_lib.switch import ShutdownAllSwitchConnections NSEC_PER_SEC = 1000 * 1000 * 1000 @@ -49,21 +48,9 @@ # to solve this problem more effectively. lookup_table = { - "s1": { - "10.0.1.1": 1, - "10.0.2.2": 2, - "10.0.3.3": 3 - }, - "s2": { - "10.0.2.2": 1, - "10.0.1.1": 2, - "10.0.3.3": 3 - }, - "s3": { - "10.0.3.3": 1, - "10.0.1.1": 2, - "10.0.2.2": 3 - } + "s1": {"10.0.1.1": 1, "10.0.2.2": 2, "10.0.3.3": 3}, + "s2": {"10.0.2.2": 1, "10.0.1.1": 2, "10.0.3.3": 3}, + "s3": {"10.0.3.3": 1, "10.0.1.1": 2, "10.0.2.2": 3}, } @@ -95,10 +82,12 @@ def flowCacheEntryToDebugStr(table_entry, include_action=False): # TODO: The match fields are hardcoded to specific indices to # retrieve specific parameters, such as hdr.ipv4.srcAddr and its # value. - src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, - byteorder="big")) - dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, - byteorder="big")) + src_ip = intToIpv4( + int.from_bytes(table_entry.match[1].exact.value, byteorder="big") + ) + dst_ip = intToIpv4( + int.from_bytes(table_entry.match[2].exact.value, byteorder="big") + ) proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) @@ -149,8 +138,14 @@ def controllerPacketMetadataDictKeyId(p4info_obj_map, name): def makeP4infoObjMap(p4info_data): p4info_obj_map = {} suffix_count = Counter() - for obj_type in ["tables", "action_profiles", "actions", "counters", - "direct_counters", "controller_packet_metadata"]: + for obj_type in [ + "tables", + "action_profiles", + "actions", + "counters", + "direct_counters", + "controller_packet_metadata", + ]: for obj in getattr(p4info_data, obj_type): pre = obj.preamble suffix = None @@ -174,8 +169,16 @@ def writeCloneSession(sw, clone_session_id, replicas): sw.WritePREEntry(clone_entry) -def addFlowRule(ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, - new_dscp, decrement_ttl_bool, dst_eth_addr): +def addFlowRule( + ingress_sw, + src_ip_addr, + dst_ip_addr, + protocol, + port, + new_dscp, + decrement_ttl_bool, + dst_eth_addr, +): """ Install flow rule in flow cache table @@ -203,13 +206,13 @@ def addFlowRule(ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, }, action_name="MyIngress.cached_action", action_params={ - "port": port, - "decrement_ttl": x, - "new_dscp": new_dscp, + "port": port, + "decrement_ttl": x, + "new_dscp": new_dscp, "dst_eth_addr": dst_eth_addr, }, # TODO: Add idle timeout - ) + ) ingress_sw.WriteTableEntry(table_entry) @@ -226,12 +229,8 @@ def createFlowRule(notif): "hdr.ipv4.protocol": int.from_bytes( te.match[0].exact.value, byteorder="big" ), - "hdr.ipv4.srcAddr": int( - ipaddress.IPv4Address(te.match[1].exact.value) - ), - "hdr.ipv4.dstAddr": int( - ipaddress.IPv4Address(te.match[2].exact.value) - ), + "hdr.ipv4.srcAddr": int(ipaddress.IPv4Address(te.match[1].exact.value)), + "hdr.ipv4.dstAddr": int(ipaddress.IPv4Address(te.match[2].exact.value)), }, ) return table_entry @@ -364,8 +363,10 @@ def printCounter(p4info_helper, sw, counter_name, index): def processPacket(message): payload = message["packet-in"].payload packet = message["packet-in"] - print("Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name)) + print( + "Received PacketIn message of length %d bytes from switch %s" + "" % (len(payload), message["sw"].name) + ) if len(payload) > 0: i = 0 pkt = Ether(payload) @@ -374,14 +375,13 @@ def processPacket(message): src_ip_addr = ipv4ToInt(ip_sa_str) ip_da_str = pkt[IP].dst dst_ip_addr = ipv4ToInt(ip_da_str) - pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], - packet) + pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], packet) debug_packetin = False if debug_packetin: i += 1 - print("") + print() print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" "" % (type(payload))) + print("type(pktin.packet.payload)=%s" % (type(payload))) print(payload) print(pktinfo) print("Scapy decode:") @@ -404,20 +404,31 @@ def processPacket(message): dest_port_int, ) sendPacketOut(message["sw"], payload, metadatas) - addFlowRule(message["sw"], - src_ip_addr, - dst_ip_addr, - ip_proto, - dest_port_int, - new_dscp_int, - decrement_ttl_bool, - dst_eth_addr) - - print("For switch %s flow (SA=%s, DA=%s, proto=%d)" - " added table entry to send packets" - " to port %d with new DSCP %d" - "" % (message["sw"].name, ip_sa_str, ip_da_str, - ip_proto, dest_port_int, new_dscp_int)) + addFlowRule( + message["sw"], + src_ip_addr, + dst_ip_addr, + ip_proto, + dest_port_int, + new_dscp_int, + decrement_ttl_bool, + dst_eth_addr, + ) + + print( + "For switch %s flow (SA=%s, DA=%s, proto=%d)" + " added table entry to send packets" + " to port %d with new DSCP %d" + "" + % ( + message["sw"].name, + ip_sa_str, + ip_da_str, + ip_proto, + dest_port_int, + new_dscp_int, + ) + ) async def processNotif(notif_queue): @@ -460,11 +471,12 @@ async def processNotif(notif_queue): addNotification(notif["sw"].name, table_entry) deleteFlowRule(notif["sw"], table_entry) else: - print("Received idle timeout notification for switch=%s %s" - " It is duplicate of recently processed notification," - " so ignoring it." - "" % (notif["sw"].name, - flowCacheEntryToDebugStr(table_entry))) + print( + "Received idle timeout notification for switch=%s %s" + " It is duplicate of recently processed notification," + " so ignoring it." + "" % (notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) + ) notif_queue.task_done() @@ -510,8 +522,7 @@ def printGrpcError(e): async def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file - global_data["p4info_helper"] = \ - p4runtime_lib.helper.P4InfoHelper(p4info_file_path) + global_data["p4info_helper"] = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) p4info_helper = global_data["p4info_helper"] try: @@ -522,17 +533,20 @@ async def main(p4info_file_path, bmv2_file_path): name="s1", address="127.0.0.1:50051", device_id=0, - proto_dump_file="logs/s1-p4runtime-requests.txt") + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( name="s2", address="127.0.0.1:50052", device_id=1, - proto_dump_file="logs/s2-p4runtime-requests.txt") + proto_dump_file="logs/s2-p4runtime-requests.txt", + ) s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection( name="s3", address="127.0.0.1:50053", device_id=2, - proto_dump_file="logs/s3-p4runtime-requests.txt") + proto_dump_file="logs/s3-p4runtime-requests.txt", + ) # Send master arbitration update message to establish this # controller as master (required by P4Runtime before @@ -542,14 +556,17 @@ async def main(p4info_file_path, bmv2_file_path): s3.MasterArbitrationUpdate() # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s1.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s1") - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s2.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s2") - s3.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s3.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s3") global_data["p4info_obj_map"] = makeP4infoObjMap(p4info_helper.p4info) @@ -566,20 +583,18 @@ async def main(p4info_file_path, bmv2_file_path): ) = serializableEnumDict(p4info_helper.p4info, "ControllerOpcode_t") try: - replicas = [{"egress_port": global_data["CPU_PORT"], - "instance": 1}] - writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) - writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) - writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) + replicas = [{"egress_port": global_data["CPU_PORT"], "instance": 1}] + writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) + writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) + writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) except shp4rt.P4RuntimeWriteException: - print("Got exception trying to configure clone session %d." - " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data["CPU_PORT_CLONE_SESSION_ID"])) + print( + "Got exception trying to configure clone session %d." + " Assuming it was initialized already in an earlier" + " run of the controller." + "" % (global_data["CPU_PORT_CLONE_SESSION_ID"]) + ) notif_queue = asyncio.Queue() @@ -593,8 +608,15 @@ async def main(p4info_file_path, bmv2_file_path): proc_notif = asyncio.create_task(processNotif(notif_queue)) - await asyncio.gather(pkt_s1, pkt_s2, pkt_s3, idle_notif_s1, - idle_notif_s2, idle_notif_s3, proc_notif) + await asyncio.gather( + pkt_s1, + pkt_s2, + pkt_s3, + idle_notif_s1, + idle_notif_s2, + idle_notif_s3, + proc_notif, + ) except KeyboardInterrupt: print(" Shutting down.") @@ -629,12 +651,10 @@ async def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" - "" % (args.p4info)) + print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" - "" % (args.bmv2_json)) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/flowcache/solution/mycontroller.py b/exercises/flowcache/solution/mycontroller.py index f182edf0..8d32ab62 100755 --- a/exercises/flowcache/solution/mycontroller.py +++ b/exercises/flowcache/solution/mycontroller.py @@ -4,29 +4,28 @@ # # SPDX-License-Identifier: GPL-2.0-only import argparse -import os -import sys import asyncio -import traceback -import time import ipaddress +import os import pprint - +import sys +import time +import traceback from collections import Counter from datetime import datetime, timedelta -from scapy.all import Ether, IP import grpc +from scapy.all import IP, Ether # Import P4Runtime lib from parent utils dir # Probably there's a better way of doing this. sys.path.append( - os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../utils/")) + os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../utils/") +) import p4runtime_lib.bmv2 import p4runtime_lib.helper -from p4runtime_lib.switch import ShutdownAllSwitchConnections import p4runtime_sh.p4runtime as shp4rt +from p4runtime_lib.switch import ShutdownAllSwitchConnections NSEC_PER_SEC = 1000 * 1000 * 1000 @@ -49,21 +48,9 @@ # to solve this problem more effectively. lookup_table = { - "s1": { - "10.0.1.1": 1, - "10.0.2.2": 2, - "10.0.3.3": 3 - }, - "s2": { - "10.0.2.2": 1, - "10.0.1.1": 2, - "10.0.3.3": 3 - }, - "s3": { - "10.0.3.3": 1, - "10.0.1.1": 2, - "10.0.2.2": 3 - } + "s1": {"10.0.1.1": 1, "10.0.2.2": 2, "10.0.3.3": 3}, + "s2": {"10.0.2.2": 1, "10.0.1.1": 2, "10.0.3.3": 3}, + "s3": {"10.0.3.3": 1, "10.0.1.1": 2, "10.0.2.2": 3}, } @@ -95,10 +82,12 @@ def flowCacheEntryToDebugStr(table_entry, include_action=False): # TODO: The match fields are hardcoded to specific indices to # retrieve specific parameters, such as hdr.ipv4.srcAddr and its # value. - src_ip = intToIpv4(int.from_bytes(table_entry.match[1].exact.value, - byteorder="big")) - dst_ip = intToIpv4(int.from_bytes(table_entry.match[2].exact.value, - byteorder="big")) + src_ip = intToIpv4( + int.from_bytes(table_entry.match[1].exact.value, byteorder="big") + ) + dst_ip = intToIpv4( + int.from_bytes(table_entry.match[2].exact.value, byteorder="big") + ) proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) @@ -149,8 +138,14 @@ def controllerPacketMetadataDictKeyId(p4info_obj_map, name): def makeP4infoObjMap(p4info_data): p4info_obj_map = {} suffix_count = Counter() - for obj_type in ["tables", "action_profiles", "actions", "counters", - "direct_counters", "controller_packet_metadata"]: + for obj_type in [ + "tables", + "action_profiles", + "actions", + "counters", + "direct_counters", + "controller_packet_metadata", + ]: for obj in getattr(p4info_data, obj_type): pre = obj.preamble suffix = None @@ -174,8 +169,16 @@ def writeCloneSession(sw, clone_session_id, replicas): sw.WritePREEntry(clone_entry) -def addFlowRule(ingress_sw, src_ip_addr, dst_ip_addr, protocol, port, - new_dscp, decrement_ttl_bool, dst_eth_addr): +def addFlowRule( + ingress_sw, + src_ip_addr, + dst_ip_addr, + protocol, + port, + new_dscp, + decrement_ttl_bool, + dst_eth_addr, +): """ Install flow rule in flow cache table @@ -227,12 +230,8 @@ def createFlowRule(notif): "hdr.ipv4.protocol": int.from_bytes( te.match[0].exact.value, byteorder="big" ), - "hdr.ipv4.srcAddr": int( - ipaddress.IPv4Address(te.match[1].exact.value) - ), - "hdr.ipv4.dstAddr": int( - ipaddress.IPv4Address(te.match[2].exact.value) - ), + "hdr.ipv4.srcAddr": int(ipaddress.IPv4Address(te.match[1].exact.value)), + "hdr.ipv4.dstAddr": int(ipaddress.IPv4Address(te.match[2].exact.value)), }, ) return table_entry @@ -365,8 +364,10 @@ def printCounter(p4info_helper, sw, counter_name, index): def processPacket(message): payload = message["packet-in"].payload packet = message["packet-in"] - print("Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name)) + print( + "Received PacketIn message of length %d bytes from switch %s" + "" % (len(payload), message["sw"].name) + ) if len(payload) > 0: i = 0 pkt = Ether(payload) @@ -375,14 +376,13 @@ def processPacket(message): src_ip_addr = ipv4ToInt(ip_sa_str) ip_da_str = pkt[IP].dst dst_ip_addr = ipv4ToInt(ip_da_str) - pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], - packet) + pktinfo = decodePacketInMetadata(global_data["cpm_packetin_id2data"], packet) debug_packetin = False if debug_packetin: i += 1 - print("") + print() print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" "" % (type(payload))) + print("type(pktin.packet.payload)=%s" % (type(payload))) print(payload) print(pktinfo) print("Scapy decode:") @@ -405,20 +405,31 @@ def processPacket(message): dest_port_int, ) sendPacketOut(message["sw"], payload, metadatas) - addFlowRule(message["sw"], - src_ip_addr, - dst_ip_addr, - ip_proto, - dest_port_int, - new_dscp_int, - decrement_ttl_bool, - dst_eth_addr) - - print("For switch %s flow (SA=%s, DA=%s, proto=%d)" - " added table entry to send packets" - " to port %d with new DSCP %d" - "" % (message["sw"].name, ip_sa_str, ip_da_str, - ip_proto, dest_port_int, new_dscp_int)) + addFlowRule( + message["sw"], + src_ip_addr, + dst_ip_addr, + ip_proto, + dest_port_int, + new_dscp_int, + decrement_ttl_bool, + dst_eth_addr, + ) + + print( + "For switch %s flow (SA=%s, DA=%s, proto=%d)" + " added table entry to send packets" + " to port %d with new DSCP %d" + "" + % ( + message["sw"].name, + ip_sa_str, + ip_da_str, + ip_proto, + dest_port_int, + new_dscp_int, + ) + ) async def processNotif(notif_queue): @@ -461,11 +472,12 @@ async def processNotif(notif_queue): addNotification(notif["sw"].name, table_entry) deleteFlowRule(notif["sw"], table_entry) else: - print("Received idle timeout notification for switch=%s %s" - " It is duplicate of recently processed notification," - " so ignoring it." - "" % (notif["sw"].name, - flowCacheEntryToDebugStr(table_entry))) + print( + "Received idle timeout notification for switch=%s %s" + " It is duplicate of recently processed notification," + " so ignoring it." + "" % (notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) + ) notif_queue.task_done() @@ -511,8 +523,7 @@ def printGrpcError(e): async def main(p4info_file_path, bmv2_file_path): # Instantiate a P4Runtime helper from the p4info file - global_data["p4info_helper"] = \ - p4runtime_lib.helper.P4InfoHelper(p4info_file_path) + global_data["p4info_helper"] = p4runtime_lib.helper.P4InfoHelper(p4info_file_path) p4info_helper = global_data["p4info_helper"] try: @@ -523,17 +534,20 @@ async def main(p4info_file_path, bmv2_file_path): name="s1", address="127.0.0.1:50051", device_id=0, - proto_dump_file="logs/s1-p4runtime-requests.txt") + proto_dump_file="logs/s1-p4runtime-requests.txt", + ) s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection( name="s2", address="127.0.0.1:50052", device_id=1, - proto_dump_file="logs/s2-p4runtime-requests.txt") + proto_dump_file="logs/s2-p4runtime-requests.txt", + ) s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection( name="s3", address="127.0.0.1:50053", device_id=2, - proto_dump_file="logs/s3-p4runtime-requests.txt") + proto_dump_file="logs/s3-p4runtime-requests.txt", + ) # Send master arbitration update message to establish this # controller as master (required by P4Runtime before @@ -543,14 +557,17 @@ async def main(p4info_file_path, bmv2_file_path): s3.MasterArbitrationUpdate() # Install the P4 program on the switches - s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s1.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s1") - s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s2.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s2") - s3.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_file_path) + s3.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_file_path + ) print("Installed P4 Program using SetForwardingPipelineConfig on s3") global_data["p4info_obj_map"] = makeP4infoObjMap(p4info_helper.p4info) @@ -567,20 +584,18 @@ async def main(p4info_file_path, bmv2_file_path): ) = serializableEnumDict(p4info_helper.p4info, "ControllerOpcode_t") try: - replicas = [{"egress_port": global_data["CPU_PORT"], - "instance": 1}] - writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) - writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) - writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], - replicas) + replicas = [{"egress_port": global_data["CPU_PORT"], "instance": 1}] + writeCloneSession(s1, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) + writeCloneSession(s2, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) + writeCloneSession(s3, global_data["CPU_PORT_CLONE_SESSION_ID"], replicas) except shp4rt.P4RuntimeWriteException: - print("Got exception trying to configure clone session %d." - " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data["CPU_PORT_CLONE_SESSION_ID"])) + print( + "Got exception trying to configure clone session %d." + " Assuming it was initialized already in an earlier" + " run of the controller." + "" % (global_data["CPU_PORT_CLONE_SESSION_ID"]) + ) notif_queue = asyncio.Queue() @@ -594,8 +609,15 @@ async def main(p4info_file_path, bmv2_file_path): proc_notif = asyncio.create_task(processNotif(notif_queue)) - await asyncio.gather(pkt_s1, pkt_s2, pkt_s3, idle_notif_s1, - idle_notif_s2, idle_notif_s3, proc_notif) + await asyncio.gather( + pkt_s1, + pkt_s2, + pkt_s3, + idle_notif_s1, + idle_notif_s2, + idle_notif_s3, + proc_notif, + ) except KeyboardInterrupt: print(" Shutting down.") @@ -630,12 +652,10 @@ async def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" - "" % (args.p4info)) + print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" - "" % (args.bmv2_json)) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/link_monitor/probe_hdrs.py b/exercises/link_monitor/probe_hdrs.py old mode 100755 new mode 100644 index abb9729c..9d74f529 --- a/exercises/link_monitor/probe_hdrs.py +++ b/exercises/link_monitor/probe_hdrs.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2019 Stephen Ibanez # # SPDX-License-Identifier: GPL-2.0-only -from scapy.all import Packet, Ether, ByteField, BitField, IntField, bind_layers +from scapy.all import BitField, ByteField, Ether, IntField, Packet, bind_layers TYPE_PROBE = 0x812 diff --git a/exercises/link_monitor/receive.py b/exercises/link_monitor/receive.py index eb01f03f..96b5700a 100755 --- a/exercises/link_monitor/receive.py +++ b/exercises/link_monitor/receive.py @@ -17,20 +17,19 @@ def expand(x): def handle_pkt(pkt): if ProbeData in pkt: data_layers = [x for x in expand(pkt) if x.name == "ProbeData"] - print("") + print() for sw in data_layers: utilization = ( 0 if sw.cur_time == sw.last_time else 8.0 * sw.byte_cnt / (sw.cur_time - sw.last_time) ) - print("Switch {} - Port {}: {} Mbps".format( - sw.swid, sw.port, utilization)) + print(f"Switch {sw.swid} - Port {sw.port}: {utilization} Mbps") def main(): iface = "eth0" - print("sniffing on {}".format(iface)) + print(f"sniffing on {iface}") sniff(iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/link_monitor/send.py b/exercises/link_monitor/send.py index b1eee7ab..17ee4c94 100755 --- a/exercises/link_monitor/send.py +++ b/exercises/link_monitor/send.py @@ -6,8 +6,8 @@ import sys import time -from scapy.all import Ether, sendp, get_if_hwaddr from probe_hdrs import Probe, ProbeFwd +from scapy.all import Ether, get_if_hwaddr, sendp def main(): diff --git a/exercises/load_balance/receive.py b/exercises/load_balance/receive.py index b4d14575..8d4c7a8f 100755 --- a/exercises/load_balance/receive.py +++ b/exercises/load_balance/receive.py @@ -26,7 +26,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -35,8 +35,9 @@ class IPOption_MRI(IPOption): option = 31 fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", length_of="swids", - adjust=lambda pkt, x: x + 4), + FieldLenField( + "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 + ), ShortField("count", 0), FieldListField( "swids", [], IntField("", 0), length_from=lambda pkt: pkt.count * 4 diff --git a/exercises/load_balance/send.py b/exercises/load_balance/send.py index e1a77a67..23d41610 100755 --- a/exercises/load_balance/send.py +++ b/exercises/load_balance/send.py @@ -18,7 +18,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -26,7 +26,7 @@ def main(): if len(sys.argv) < 3: print('pass 2 arguments: ""') - exit(1) + sys.exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() diff --git a/exercises/mri/receive.py b/exercises/mri/receive.py index 398d407a..a9f74bcf 100755 --- a/exercises/mri/receive.py +++ b/exercises/mri/receive.py @@ -26,7 +26,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -42,11 +42,16 @@ class IPOption_MRI(IPOption): option = 31 fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", length_of="swtraces", - adjust=lambda pkt, x: x * 2 + 4), + FieldLenField( + "length", + None, + fmt="B", + length_of="swtraces", + adjust=lambda pkt, x: x * 2 + 4, + ), ShortField("count", 0), PacketListField( - "swtraces", [], SwitchTrace, count_from=lambda pkt: (pkt.count * 1) + "swtraces", [], SwitchTrace, count_from=lambda pkt: pkt.count * 1 ), ] diff --git a/exercises/mri/send.py b/exercises/mri/send.py index 433f8803..0bf54d96 100755 --- a/exercises/mri/send.py +++ b/exercises/mri/send.py @@ -33,7 +33,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -49,11 +49,16 @@ class IPOption_MRI(IPOption): option = 31 fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", length_of="swtraces", - adjust=lambda pkt, x: x * 2 + 4), + FieldLenField( + "length", + None, + fmt="B", + length_of="swtraces", + adjust=lambda pkt, x: x * 2 + 4, + ), ShortField("count", 0), PacketListField( - "swtraces", [], SwitchTrace, count_from=lambda pkt: (pkt.count * 1) + "swtraces", [], SwitchTrace, count_from=lambda pkt: pkt.count * 1 ), ] @@ -62,7 +67,7 @@ def main(): if len(sys.argv) < 3: print('pass 2 arguments: ""') - exit(1) + sys.exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() diff --git a/exercises/p4runtime/mycontroller.py b/exercises/p4runtime/mycontroller.py index 113052cf..2f156437 100755 --- a/exercises/p4runtime/mycontroller.py +++ b/exercises/p4runtime/mycontroller.py @@ -202,14 +202,10 @@ def main(p4info_file_path, bmv2_file_path): while True: sleep(2) print("\n----- Reading tunnel counters -----") - printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", - 100) - printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", - 100) - printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", - 200) - printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", - 200) + printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 100) + printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100) + printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 200) + printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 200) except KeyboardInterrupt: print(" Shutting down.") @@ -241,12 +237,10 @@ def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" - "" % (args.p4info)) + print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" - "" % (args.bmv2_json)) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/p4runtime/solution/mycontroller.py b/exercises/p4runtime/solution/mycontroller.py index 86ffa666..abf374ab 100755 --- a/exercises/p4runtime/solution/mycontroller.py +++ b/exercises/p4runtime/solution/mycontroller.py @@ -116,8 +116,7 @@ def readTableRules(p4info_helper, sw): print("%s: " % table_name, end=" ") for m in entry.match: print( - p4info_helper.get_match_field_name(table_name, m.field_id), - end=" " + p4info_helper.get_match_field_name(table_name, m.field_id), end=" " ) print("%r" % (p4info_helper.get_match_field_value(m)), end=" ") action = entry.action.action @@ -125,8 +124,7 @@ def readTableRules(p4info_helper, sw): print("->", action_name, end=" ") for p in action.params: print( - p4info_helper.get_action_param_name(action_name, - p.param_id), + p4info_helper.get_action_param_name(action_name, p.param_id), end=" ", ) print("%r" % p.value, end=" ") @@ -164,8 +162,7 @@ def printGrpcError(e): status_code = e.code() print("(%s)" % status_code.name, end=" ") traceback = sys.exc_info()[2] - print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, - traceback.tb_lineno)) + print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) def main(p4info_file_path, bmv2_file_path): @@ -234,14 +231,10 @@ def main(p4info_file_path, bmv2_file_path): while True: sleep(2) print("\n----- Reading tunnel counters -----") - printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", - 100) - printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", - 100) - printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", - 200) - printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", - 200) + printCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 100) + printCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100) + printCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 200) + printCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 200) except KeyboardInterrupt: print(" Shutting down.") @@ -273,12 +266,10 @@ def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" - "" % (args.p4info)) + print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" - "" % (args.bmv2_json)) + print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/qos/send.py b/exercises/qos/send.py index 8818442b..5c0c06cb 100755 --- a/exercises/qos/send.py +++ b/exercises/qos/send.py @@ -6,6 +6,7 @@ import argparse import socket +import sys from time import sleep from scapy.all import IP, TCP, UDP, Ether, get_if_hwaddr, get_if_list, sendp @@ -19,7 +20,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -28,8 +29,7 @@ def main(): parser.add_argument( "--p", help="Protocol name To send TCP/UDP etc packets", type=str ) - parser.add_argument("--des", help="IP address of the destination", - type=str) + parser.add_argument("--des", help="IP address of the destination", type=str) parser.add_argument("--m", help="Raw Message", type=str) parser.add_argument("--dur", help="in seconds", type=str) args = parser.parse_args() diff --git a/exercises/source_routing/receive.py b/exercises/source_routing/receive.py index 249f6a10..aee3fb4f 100755 --- a/exercises/source_routing/receive.py +++ b/exercises/source_routing/receive.py @@ -15,9 +15,9 @@ ) from scapy.fields import ( BitField, - IntField, - FieldListField, FieldLenField, + FieldListField, + IntField, ShortField, XShortField, ) @@ -32,7 +32,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -41,11 +41,13 @@ class IPOption_MRI(IPOption): option = 31 fields_desc = [ _IPOption_HDR, - FieldLenField("length", None, fmt="B", length_of="swids", - adjust=lambda pkt, x: x + 4), + FieldLenField( + "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 + ), ShortField("count", 0), - FieldListField("swids", [], IntField("", 0), - length_from=lambda pkt: pkt.count * 4), + FieldListField( + "swids", [], IntField("", 0), length_from=lambda pkt: pkt.count * 4 + ), ] diff --git a/exercises/source_routing/send.py b/exercises/source_routing/send.py index e02b636d..05c37db9 100755 --- a/exercises/source_routing/send.py +++ b/exercises/source_routing/send.py @@ -27,7 +27,7 @@ def get_if(): break if not iface: print("Cannot find eth0 interface") - exit(1) + sys.exit(1) return iface @@ -44,7 +44,7 @@ def main(): if len(sys.argv) < 2: print("pass 2 arguments: ") - exit(1) + sys.exit(1) addr = socket.gethostbyname(sys.argv[1]) iface = get_if() @@ -54,8 +54,7 @@ def main(): print() s = str( input( - "Type space separated port nums " - '(example: "2 3 2 2 1") or "q" to quit: ' + 'Type space separated port nums (example: "2 3 2 2 1") or "q" to quit: ' ) ) if s == "q": diff --git a/utils/README.md b/utils/README.md index 950ecdc1..ec9b800e 100644 --- a/utils/README.md +++ b/utils/README.md @@ -204,9 +204,9 @@ link_dict = {'node1':s, The class *Exercise* is created to help manage all data and manage the execution flow. The code below describes the *run_exercise()* method. Lines 4 and 5 create the network. Lines 9 and 10 program the network elements. Line 15 launches the user interface. ```python -#(... Omitted ...)# +# (... Omitted ...)# def run_exercise(self): -# Initialize mininet with the topology specified by the config + # Initialize mininet with the topology specified by the config self.create_network() self.net.start() sleep(1) @@ -221,7 +221,9 @@ def run_exercise(self): self.do_net_cli() # stop right after the CLI is exited self.net.stop() -#(... Omitted ...)# + + +# (... Omitted ...)# ``` #### Creating the Mininet Network @@ -229,17 +231,22 @@ def run_exercise(self): It is in this stage that switches, links, and hosts are added to the network. To instantiate the network object, a class, *ExerciseTopo*, is used. This class inherits from the *Topo Class* native to Mininet. The code below shows the initialization process of the *ExerciseTopo* class. ```python -#(... Omitted ...)# -self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir) +# (... Omitted ...)# +self.topo = ExerciseTopo( + self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir +) + class ExerciseTopo(Topo): - """ The mininet topology class for the P4 tutorial exercises. - """ + """The mininet topology class for the P4 tutorial exercises.""" + def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): Topo.__init__(self, **opts) host_links = [] switch_links = [] - #(... Omitted ...)# + + +# (... Omitted ...)# ``` Below is shown how the switches are configured. Using the method *configureP4Switch* ensures the switch is created using the correct architecture, (*simple_switch* or *simple_switch_grpc*). @@ -247,36 +254,37 @@ Below is shown how the switches are configured. Using the method *configureP4Swi If no program is specified, the switch follows the default implementation. ```python -#(... Omitted ...)# +# (... Omitted ...)# for sw, params in switches.items(): if "program" in params: switchClass = configureP4Switch( - sw_path=bmv2_exe, - json_path=params["program"], - log_console=True, - pcap_dump=pcap_dir) + sw_path=bmv2_exe, + json_path=params["program"], + log_console=True, + pcap_dump=pcap_dir, + ) else: # add default switch switchClass = None - self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw), cls=switchClass) -#(... Omitted ...) + self.addSwitch(sw, log_file="%s/%s.log" % (log_dir, sw), cls=switchClass) +# (... Omitted ...) ``` The penultimate step is to generate the hosts and the host-to-switch links. Using the information provided in the [topology file](#topology-file) and the methods *addHost* and *addLink*, configurations are directly translated to the Mininet Network. ```python -#(... Omitted ...) +# (... Omitted ...) for link in host_links: - host_name = link['node1'] - sw_name, sw_port = self.parse_switch_node(link['node2']) - host_ip = hosts[host_name]['ip'] - host_mac = hosts[host_name]['mac'] + host_name = link["node1"] + sw_name, sw_port = self.parse_switch_node(link["node2"]) + host_ip = hosts[host_name]["ip"] + host_mac = hosts[host_name]["mac"] self.addHost(host_name, ip=host_ip, mac=host_mac) - self.addLink(host_name, sw_name, - delay=link['latency'], bw=link['bandwidth'], - port2=sw_port) -#(... Omitted ...) + self.addLink( + host_name, sw_name, delay=link["latency"], bw=link["bandwidth"], port2=sw_port + ) +# (... Omitted ...) ``` @@ -284,14 +292,19 @@ Finally, the links between the switches are added. Using the data structure dict ```python -#(... Omitted ...) +# (... Omitted ...) for link in switch_links: - sw1_name, sw1_port = self.parse_switch_node(link['node1']) - sw2_name, sw2_port = self.parse_switch_node(link['node2']) - self.addLink(sw1_name, sw2_name, - port1=sw1_port, port2=sw2_port, - delay=link['latency'], bw=link['bandwidth']) -#(... Omitted ...) + sw1_name, sw1_port = self.parse_switch_node(link["node1"]) + sw2_name, sw2_port = self.parse_switch_node(link["node2"]) + self.addLink( + sw1_name, + sw2_name, + port1=sw1_port, + port2=sw2_port, + delay=link["latency"], + bw=link["bandwidth"], + ) +# (... Omitted ...) ``` #### Starting the Mininet Network @@ -301,14 +314,16 @@ Starting the Mininet network can be achieved by using the *start* method of the After starting the network, runtime commands are executed. Below it is demonstrated how console commands are applied to the hosts created in the network. First, the host is retrieved from the network (using its name as key) and then, using the method ```.cmd()```, commands are executed. ```python -#(... Omitted ...)# +# (... Omitted ...)# def program_hosts(self): for host_name, host_info in list(self.hosts.items()): h = self.net.get(host_name) if "commands" in host_info: for cmd in host_info["commands"]: h.cmd(cmd) -#(... Omitted ...)# + + +# (... Omitted ...)# ``` #### Programming the Switches @@ -319,19 +334,23 @@ def program_switch_p4runtime(self, sw_name, sw_dict): sw_obj = self.net.get(sw_name) grpc_port = sw_obj.grpc_port device_id = sw_obj.device_id - runtime_json = sw_dict['runtime_json'] - self.logger('Configuring switch %s using P4Runtime with file %s' % (sw_name, runtime_json)) - with open(runtime_json, 'r') as sw_conf_file: - outfile = '%s/%s-p4runtime-requests.txt' %(self.log_dir, sw_name) + runtime_json = sw_dict["runtime_json"] + self.logger( + "Configuring switch %s using P4Runtime with file %s" % (sw_name, runtime_json) + ) + with open(runtime_json, "r") as sw_conf_file: + outfile = "%s/%s-p4runtime-requests.txt" % (self.log_dir, sw_name) p4runtime_lib.simple_controller.program_switch( - addr='127.0.0.1:%d' % grpc_port, + addr="127.0.0.1:%d" % grpc_port, device_id=device_id, sw_conf_file=sw_conf_file, workdir=os.getcwd(), proto_dump_fpath=outfile, - runtime_json=runtime_json + runtime_json=runtime_json, ) -#(... Omitted ...)# + + +# (... Omitted ...)# ``` @@ -340,15 +359,15 @@ The final step of execution is presenting the user with a interface. This tool i ```python def do_net_cli(self): - #(... Omitted ...)# - print('===============================================') - print('Welcome to the BMV2 Mininet CLI!') - print('===============================================') - print('Your P4 program is installed into the BMV2 software switch') - print('and your initial runtime configuration is loaded. You can interact') - print('with the network using the mininet CLI below.') - print('') - #(... Omitted ...)# + # (... Omitted ...)# + print("===============================================") + print("Welcome to the BMV2 Mininet CLI!") + print("===============================================") + print("Your P4 program is installed into the BMV2 software switch") + print("and your initial runtime configuration is loaded. You can interact") + print("with the network using the mininet CLI below.") + print("") + # (... Omitted ...)# CLI(self.net) ``` @@ -372,10 +391,11 @@ As mentioned previously, if no program is provided, the default switch is used i ```python defaultSwitchClass = configureP4Switch( - sw_path=self.bmv2_exe, - json_path=self.switch_json, - log_console=True, - pcap_dump=self.pcap_dir) + sw_path=self.bmv2_exe, + json_path=self.switch_json, + log_console=True, + pcap_dump=self.pcap_dir, +) ``` ### Makefile diff --git a/utils/mininet/appcontroller.py b/utils/mininet/appcontroller.py index 2eb62c5d..29b6e313 100644 --- a/utils/mininet/appcontroller.py +++ b/utils/mininet/appcontroller.py @@ -7,9 +7,7 @@ class AppController: - - def __init__(self, manifest=None, target=None, topo=None, net=None, - links=None): + def __init__(self, manifest=None, target=None, topo=None, net=None, links=None): self.manifest = manifest self.target = target self.conf = manifest["targets"][target] @@ -48,8 +46,7 @@ def read_register(self, register, idx, thrift_port=9090, sw=None): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - stdout, stderr = p.communicate(input="register_read %s %d" - "" % (register, idx)) + stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) reg_val = [x for x in stdout.split("\n") if " %s[%d]" % (register, idx) in x][ 0 ].split("= ", 1)[1] @@ -87,8 +84,7 @@ def start(self): h.setIP(link["host_ip"], 24) h.setMAC(link["host_mac"]) # h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd("arp -i %s -s %s %s" % (iface, link["sw_ip"], - link["sw_mac"])) + h.cmd("arp -i %s -s %s %s" % (iface, link["sw_ip"], link["sw_mac"])) h.cmd("ethtool --offload %s rx off tx off" % iface) h.cmd("ip route add %s dev %s" % (link["sw_ip"], iface)) h.setDefaultRoute("via %s" % link["sw_ip"]) @@ -96,8 +92,7 @@ def start(self): for h in self.net.hosts: h_link = list(self.topo._host_links[h.name].values())[0] for sw in self.net.switches: - path = shortestpath.get(sw.name, h.name, - exclude=lambda n: n[0] == "h") + path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0] == "h") if not path: continue if not path[1][0] == "s": @@ -110,14 +105,12 @@ def start(self): for h2 in self.net.hosts: if h == h2: continue - path = shortestpath.get(h.name, h2.name, - exclude=lambda n: n[0] == "h") + path = shortestpath.get(h.name, h2.name, exclude=lambda n: n[0] == "h") if not path: continue h_link = self.topo._host_links[h.name][path[1]] h2_link = list(self.topo._host_links[h2.name].values())[0] - h.cmd("ip route add %s via %s" - "" % (h2_link["host_ip"], h_link["sw_ip"])) + h.cmd("ip route add %s via %s" % (h2_link["host_ip"], h_link["sw_ip"])) print("**********") print("Configuring entries in p4 tables") diff --git a/utils/mininet/apptopo.py b/utils/mininet/apptopo.py index eb0b6d33..bb2ba50c 100644 --- a/utils/mininet/apptopo.py +++ b/utils/mininet/apptopo.py @@ -5,7 +5,6 @@ class AppTopo(Topo): - def __init__( self, links, @@ -14,7 +13,7 @@ def __init__( target=None, log_dir="/tmp", bws={}, - **opts + **opts, ): Topo.__init__(self, **opts) @@ -41,9 +40,9 @@ def __init__( for link in host_links: sw = link[0] if link[0] != host_name else link[1] sw_num = int(sw[1:]) - assert ( - sw[0] == "s" - ), "Hosts should be connected to switches, not " + str(sw) + assert sw[0] == "s", ( + "Hosts should be connected to switches, not " + str(sw) + ) host_ip = "10.0.%d.%d" % (sw_num, host_num) host_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num) delay_key = "".join([host_name, sw]) diff --git a/utils/mininet/multi_switch_mininet.py b/utils/mininet/multi_switch_mininet.py index 4ba14f66..e3f3f748 100755 --- a/utils/mininet/multi_switch_mininet.py +++ b/utils/mininet/multi_switch_mininet.py @@ -44,14 +44,10 @@ action="store_true", ) parser.add_argument( - "--json", - help="Path to JSON config file", - type=str, action="store", required=True + "--json", help="Path to JSON config file", type=str, action="store", required=True ) parser.add_argument( - "--pcap-dump", - help="Dump packets on interfaces to pcap files", - action="store_true" + "--pcap-dump", help="Dump packets on interfaces to pcap files", action="store_true" ) parser.add_argument( "--manifest", @@ -117,12 +113,11 @@ def main(): conf = manifest["targets"][args.target] params = conf["parameters"] if "parameters" in conf else {} - os.environ.update(dict([(k_v[0], str(k_v[1])) - for k_v in iter(params.items())])) + os.environ.update(dict([(k_v[0], str(k_v[1])) for k_v in iter(params.items())])) def formatParams(s): for param in params: - s = re.sub("\$" + param + "(\W|$)", str(params[param]) + r"\1", s) + s = re.sub(r"\$" + param + r"(\W|$)", str(params[param]) + r"\1", s) s = s.replace("${" + param + "}", str(params[param])) return s @@ -149,8 +144,7 @@ def formatParams(s): latencies = dict( [("".join(sorted(x[:2])), x[2]) for x in conf["links"] if len(x) >= 3] ) - bws = dict([("".join(sorted(x[:2])), x[3]) - for x in conf["links"] if len(x) >= 4]) + bws = dict([("".join(sorted(x[:2])), x[3]) for x in conf["links"] if len(x) >= 4]) for host_name in sorted(conf["hosts"].keys()): host = conf["hosts"][host_name] @@ -185,16 +179,18 @@ def formatParams(s): log_console=bmv2_log, pcap_dump=pcap_dump, ) - net = Mininet(topo=topo, link=TCLink, host=P4Host, switch=switchClass, - controller=None) + net = Mininet( + topo=topo, link=TCLink, host=P4Host, switch=switchClass, controller=None + ) net.start() sleep(1) controller = None if args.auto_control_plane or "controller_module" in conf: - controller = AppController(manifest=manifest, target=args.target, - topo=topo, net=net, links=links) + controller = AppController( + manifest=manifest, target=args.target, topo=topo, net=net, links=links + ) controller.start() for h in net.hosts: @@ -204,7 +200,7 @@ def formatParams(s): with open(args.cli_message, "r") as message_file: print(message_file.read()) - if args.cli or ("cli" in conf and conf["cli"]): + if args.cli or (conf.get("cli")): CLI(net) stdout_files = dict() @@ -241,12 +237,11 @@ def _wait_for_exit(p, host): stdout_files[h.name] = open(stdout_filename, "w") cmd = formatCmd(host["cmd"]) print(h.name, cmd) - p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, - preexec_fn=os.setpgrp) + p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) if "startup_sleep" in host: sleep(host["startup_sleep"]) - if "wait" in host and host["wait"]: + if host.get("wait"): _wait_for_exit(p, host_name) else: host_procs.append((p, host_name)) diff --git a/utils/mininet/p4_mininet.py b/utils/mininet/p4_mininet.py index 020c2b5e..0459ec7a 100644 --- a/utils/mininet/p4_mininet.py +++ b/utils/mininet/p4_mininet.py @@ -15,11 +15,10 @@ class P4Host(Host): def config(self, **params): - r = super(P4Host, self).config(**params) + r = super().config(**params) for off in ["rx", "tx", "sg"]: - cmd = ("/sbin/ethtool --offload %s %s off" - "" % (self.defaultIntf().name, off)) + cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) self.cmd(cmd) # disable IPv6 @@ -62,7 +61,7 @@ def __init__( verbose=False, device_id=None, enable_debugger=False, - **kwargs + **kwargs, ): Switch.__init__(self, name, **kwargs) assert sw_path @@ -78,7 +77,7 @@ def __init__( self.verbose = verbose self.log_file = log_file if self.log_file is None: - self.log_file = "/tmp/p4s.{}.log".format(self.name) + self.log_file = f"/tmp/p4s.{self.name}.log" self.output = open(self.log_file, "w") self.thrift_port = thrift_port self.pcap_dump = pcap_dump @@ -90,7 +89,7 @@ def __init__( else: self.device_id = P4Switch.device_id P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) + self.nanomsg = f"ipc:///tmp/bm-{self.device_id}-log.ipc" @classmethod def setup(cls): @@ -113,7 +112,7 @@ def check_switch_started(self, pid): def start(self, controllers): "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) + info(f"Starting P4 switch {self.name}.\n") args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): @@ -141,15 +140,15 @@ def start(self, controllers): " ".join(args) + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name ) pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) + debug(f"P4 switch {self.name} PID is {pid}.\n") sleep(1) if not self.check_switch_started(pid): error( - "P4 switch {} did not start correctly." - "Check the switch log file.\n".format(self.name) + f"P4 switch {self.name} did not start correctly." + "Check the switch log file.\n" ) exit(1) - info("P4 switch {} has been started.\n".format(self.name)) + info(f"P4 switch {self.name} has been started.\n") def stop(self): "Terminate P4 switch." diff --git a/utils/mininet/shortest_path.py b/utils/mininet/shortest_path.py index 50d1cd42..81f9d3df 100644 --- a/utils/mininet/shortest_path.py +++ b/utils/mininet/shortest_path.py @@ -2,7 +2,6 @@ # # SPDX-License-Identifier: Apache-2.0 class ShortestPath: - def __init__(self, edges=[]): self.neighbors = {} for edge in edges: @@ -35,7 +34,6 @@ def _recPath(self, a, b, visited, exclude): if __name__ == "__main__": - edges = [ (1, 2), (1, 3), diff --git a/utils/mininet/single_switch_mininet.py b/utils/mininet/single_switch_mininet.py index f10eb5c2..f886390c 100755 --- a/utils/mininet/single_switch_mininet.py +++ b/utils/mininet/single_switch_mininet.py @@ -81,8 +81,7 @@ class SingleSwitchTopo(Topo): "Single switch connected to n (< 256) hosts." - def __init__(self, sw_path, json_path, log_file, thrift_port, pcap_dump, - n, **opts): + def __init__(self, sw_path, json_path, log_file, thrift_port, pcap_dump, n, **opts): # Initialize topology and default options Topo.__init__(self, **opts) @@ -98,8 +97,9 @@ def __init__(self, sw_path, json_path, log_file, thrift_port, pcap_dump, ) for h in range(n): - host = self.addHost("h%d" % (h + 1), ip="10.0.%d.10/24" % h, - mac="00:04:00:00:00:%02x" % h) + host = self.addHost( + "h%d" % (h + 1), ip="10.0.%d.10/24" % h, mac="00:04:00:00:00:%02x" % h + ) print("Adding host", str(host)) self.addLink(host, switch) @@ -129,8 +129,7 @@ def main(): h.setDefaultRoute("dev %s" % h.defaultIntf().name) else: h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" - "" % (h.defaultIntf().name, sw_addr[n])) + h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) for n in range(num_hosts): h = net.get("h%d" % (n + 1)) diff --git a/utils/p4_mininet.py b/utils/p4_mininet.py index d88dba5f..609a90ea 100644 --- a/utils/p4_mininet.py +++ b/utils/p4_mininet.py @@ -63,7 +63,7 @@ def __init__( verbose=False, device_id=None, enable_debugger=False, - **kwargs + **kwargs, ): Switch.__init__(self, name, **kwargs) assert sw_path @@ -77,12 +77,14 @@ def __init__( self.sw_path = sw_path self.json_path = json_path self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) + logfile = f"/tmp/p4s.{self.name}.log" self.output = open(logfile, "w") self.thrift_port = thrift_port if check_listening_on_port(self.thrift_port): - error("%s cannot bind port %d because it is bound by" - " another process\n" % (self.name, self.grpc_port)) + error( + "%s cannot bind port %d because it is bound by" + " another process\n" % (self.name, self.grpc_port) + ) exit(1) self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger @@ -90,14 +92,14 @@ def __init__( if log_file is not None: self.log_file = log_file else: - self.log_file = "/tmp/p4s.{}.log".format(self.name) + self.log_file = f"/tmp/p4s.{self.name}.log" if device_id is not None: self.device_id = device_id P4Switch.device_id = max(P4Switch.device_id, device_id) else: self.device_id = P4Switch.device_id P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) + self.nanomsg = f"ipc:///tmp/bm-{self.device_id}-log.ipc" @classmethod def setup(cls): @@ -118,7 +120,7 @@ def check_switch_started(self, pid): def start(self, controllers): "Start up a new P4 switch" - info("Starting P4 switch {}.\n".format(self.name)) + info(f"Starting P4 switch {self.name}.\n") args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): @@ -145,11 +147,11 @@ def start(self, controllers): " ".join(args) + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name ) pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) + debug(f"P4 switch {self.name} PID is {pid}.\n") if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) + error(f"P4 switch {self.name} did not start correctly.\n") exit(1) - info("P4 switch {} has been started.\n".format(self.name)) + info(f"P4 switch {self.name} has been started.\n") def stop(self): "Terminate P4 switch." diff --git a/utils/p4apprunner.py b/utils/p4apprunner.py index dac176fd..823044fa 100755 --- a/utils/p4apprunner.py +++ b/utils/p4apprunner.py @@ -39,7 +39,7 @@ parser.add_argument("app", help=".p4app package to run.", type=str) parser.add_argument( "target", - help=("Target to run. Defaults to the first target " "in the package."), + help=("Target to run. Defaults to the first target in the package."), nargs="?", type=str, ) @@ -97,8 +97,9 @@ def read_manifest(manifest_file): log_error("Target not found in manifest:", chosen_target) sys.exit(1) - return Manifest(program_file, language, chosen_target, - manifest["targets"][chosen_target]) + return Manifest( + program_file, language, chosen_target, manifest["targets"][chosen_target] + ) def run_compile_bmv2(manifest): @@ -168,33 +169,40 @@ def run_mininet(manifest): # interacting with the simple switch a little easier. message_file = "mininet_message.txt" with open(message_file, "w") as message: - print(file=message) - print("========================================" - "==============================", file=message) + print( + "======================================================================", + file=message, + ) print("Welcome to the BMV2 Mininet CLI!", file=message) - print("========================================" - "==============================", file=message) - print("Your P4 program is installed into the BMV2 software switch", - file=message) - print("and your initial configuration is loaded. You can interact", - file=message) + print( + "======================================================================", + file=message, + ) + print( + "Your P4 program is installed into the BMV2 software switch", file=message + ) + print( + "and your initial configuration is loaded. You can interact", file=message + ) print("with the network using the mininet CLI below.", file=message) print(file=message) - print("To inspect or change the switch configuration, connect to", - file=message) - print("its CLI from your host operating system using this command:", - file=message) + print("To inspect or change the switch configuration, connect to", file=message) + print( + "its CLI from your host operating system using this command:", file=message + ) print(" simple_switch_CLI", file=message) print(file=message) - print("To view the switch log, run this command from your host OS:", - file=message) + print( + "To view the switch log, run this command from your host OS:", file=message + ) print(" tail -f %s" % log_file, file=message) print(file=message) - print("To view the switch output pcap, check the pcap files in %s:" - "" % (pcap_dir), file=message) - print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", - file=message) + print( + "To view the switch output pcap, check the pcap files in %s:" % (pcap_dir), + file=message, + ) + print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", file=message) print(file=message) # print('To run the switch debugger, run this command from your host OS:', file=message) # print(' bm_p4dbg' , file=message) @@ -203,8 +211,7 @@ def run_mininet(manifest): switch_args.append('--cli-message "%s"' % message_file) if "num-hosts" in manifest.target_config: - switch_args.append("--num-hosts %s" - "" % (manifest.target_config["num-hosts"])) + switch_args.append("--num-hosts %s" % (manifest.target_config["num-hosts"])) if "switch-config" in manifest.target_config: switch_args.append( @@ -231,8 +238,7 @@ def run_multiswitch(manifest): script_args.append('--manifest "%s"' % args.manifest) script_args.append('--target "%s"' % manifest.target) if ( - "auto-control-plane" in manifest.target_config - and manifest.target_config["auto-control-plane"] + manifest.target_config.get("auto-control-plane") ): script_args.append("--auto-control-plane") script_args.append('--behavioral-exe "%s"' % "simple_switch") @@ -243,34 +249,38 @@ def run_multiswitch(manifest): # interacting with the simple switch a little easier. message_file = "mininet_message.txt" with open(message_file, "w") as message: - print(file=message) - print("========================================" - "==============================", file=message) + print( + "======================================================================", + file=message, + ) print("Welcome to the BMV2 Mininet CLI!", file=message) - print("========================================" - "==============================", file=message) - print("Your P4 program is installed into the BMV2 software switch", - file=message) - print("and your initial configuration is loaded. You can interact", - file=message) + print( + "======================================================================", + file=message, + ) + print( + "Your P4 program is installed into the BMV2 software switch", file=message + ) + print( + "and your initial configuration is loaded. You can interact", file=message + ) print("with the network using the mininet CLI below.", file=message) print(file=message) - print("To inspect or change the switch configuration, connect to", - file=message) - print("its CLI from your host operating system using this command:", - file=message) - print(" simple_switch_CLI --thrift-port ", - file=message) + print("To inspect or change the switch configuration, connect to", file=message) + print( + "its CLI from your host operating system using this command:", file=message + ) + print(" simple_switch_CLI --thrift-port ", file=message) print(file=message) - print("To view a switch log, run this command from your host OS:", - file=message) + print("To view a switch log, run this command from your host OS:", file=message) print(" tail -f %s/.log" % log_dir, file=message) print(file=message) - print("To view the switch output pcap, check the pcap files in %s:" - "" % (pcap_dir), file=message) - print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", - file=message) + print( + "To view the switch output pcap, check the pcap files in %s:" % (pcap_dir), + file=message, + ) + print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", file=message) print(file=message) # print('To run the switch debugger, run this command from your host OS:', file=message) # print(' bm_p4dbg' , file=message) @@ -314,8 +324,7 @@ def run_custom(manifest): log_error("No mininet program file provided.") sys.exit(1) program = manifest.target_config["program"] - rv = run_command("%s python3 %s %s" - "" % (python_path, program, " ".join(script_args))) + rv = run_command("%s python3 %s %s" % (python_path, program, " ".join(script_args))) if rv != 0: sys.exit(1) diff --git a/utils/p4runtime_lib/convert.py b/utils/p4runtime_lib/convert.py index cef450e2..54d2a68d 100644 --- a/utils/p4runtime_lib/convert.py +++ b/utils/p4runtime_lib/convert.py @@ -47,7 +47,7 @@ def matchesIPv6(ip_addr_string): try: socket.inet_pton(socket.AF_INET6, ip_addr_string) return True - except socket.error: + except OSError: return False @@ -70,18 +70,21 @@ def encodeNum(number, bitwidth): orig_number = number if number < 0: if number < -(2 ** (bitwidth - 1)): - raise Exception("Negative number, %d, has 2's complement" - " representation that does not fit in %d bits" - "" % (number, bitwidth)) + raise Exception( + "Negative number, %d, has 2's complement" + " representation that does not fit in %d bits" + "" % (number, bitwidth) + ) number = (2**bitwidth) + number num_str = "%x" % number if orig_number < 0: - print("CONVERT_NEGATIVE_NUMBER debug: orig_number=%s number=%s" - " bitwidth=%d num_str='%s'" - "" % (orig_number, number, bitwidth, num_str)) + print( + "CONVERT_NEGATIVE_NUMBER debug: orig_number=%s number=%s" + " bitwidth=%d num_str='%s'" + "" % (orig_number, number, bitwidth, num_str) + ) if number >= 2**bitwidth: - raise Exception("Number, %d, does not fit in %d bits" - "" % (number, bitwidth)) + raise Exception("Number, %d, does not fit in %d bits" % (number, bitwidth)) return bytes.fromhex("0" * (byte_len * 2 - len(num_str)) + num_str) diff --git a/utils/p4runtime_lib/error_utils.py b/utils/p4runtime_lib/error_utils.py index 85707b72..c2651c5f 100644 --- a/utils/p4runtime_lib/error_utils.py +++ b/utils/p4runtime_lib/error_utils.py @@ -13,7 +13,7 @@ # an incorrect format. class P4RuntimeErrorFormatException(Exception): def __init__(self, message): - super(P4RuntimeErrorFormatException, self).__init__(message) + super().__init__(message) # Parse the binary details of the gRPC error. This is required to @@ -26,9 +26,7 @@ def __init__(self, message): def parseGrpcErrorBinaryDetails(grpc_error): # Check if grpc_error is None or not an instance of grpc.RpcError if not grpc_error or not isinstance(grpc_error, grpc.RpcError): - raise P4RuntimeErrorFormatException( - f"Invalid gRPC error object: {grpc_error}" - ) + raise P4RuntimeErrorFormatException(f"Invalid gRPC error object: {grpc_error}") if grpc_error.code() != grpc.StatusCode.UNKNOWN: return None @@ -71,10 +69,9 @@ def parseGrpcErrorBinaryDetails(grpc_error): def printGrpcError(grpc_error): print("gRPC Error", grpc_error.details(), end=" ") status_code = grpc_error.code() - print("({})".format(status_code.name), end=" ") + print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("[{}:{}]".format(traceback.tb_frame.f_code.co_filename, - traceback.tb_lineno)) + print(f"[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") if status_code != grpc.StatusCode.UNKNOWN: return p4_errors = parseGrpcErrorBinaryDetails(grpc_error) @@ -83,5 +80,4 @@ def printGrpcError(grpc_error): print("Errors in batch:") for idx, p4_error in p4_errors: code_name = code_pb2._CODE.values_by_number[p4_error.canonical_code].name - print("\t* At index {}: {}, '{}'\n".format(idx, code_name, - p4_error.message)) + print(f"\t* At index {idx}: {code_name}, '{p4_error.message}'\n") diff --git a/utils/p4runtime_lib/helper.py b/utils/p4runtime_lib/helper.py index daea2b73..7d05f340 100644 --- a/utils/p4runtime_lib/helper.py +++ b/utils/p4runtime_lib/helper.py @@ -10,7 +10,7 @@ from .convert import encode -class P4InfoHelper(object): +class P4InfoHelper: def __init__(self, p4_info_filepath): p4info = p4info_pb2.P4Info() # Load the p4info file into a skeleton P4Info object @@ -34,11 +34,9 @@ def get(self, entity_type, name=None, id=None): return o if name: - raise AttributeError("Could not find %r of type %s" - "" % (name, entity_type)) + raise AttributeError("Could not find %r of type %s" % (name, entity_type)) else: - raise AttributeError("Could not find id %r of type %s" - "" % (id, entity_type)) + raise AttributeError("Could not find id %r of type %s" % (id, entity_type)) def get_id(self, entity_type, name): return self.get(entity_type, name=name).preamble.id @@ -65,8 +63,7 @@ def __getattr__(self, attr): primitive = m.group(1) return lambda id: self.get_name(primitive, id) - raise AttributeError("%r object has no attribute %r" - "" % (self.__class__, attr)) + raise AttributeError("%r object has no attribute %r" % (self.__class__, attr)) def get_match_field(self, table_name, name=None, id=None): for t in self.p4info.tables: @@ -79,9 +76,9 @@ def get_match_field(self, table_name, name=None, id=None): elif id is not None: if mf.id == id: return mf - raise AttributeError("%r has no attribute %r" - "" % (table_name, - name if name is not None else id)) + raise AttributeError( + "%r has no attribute %r" % (table_name, name if name is not None else id) + ) def get_match_field_id(self, table_name, match_field_name): return self.get_match_field(table_name, name=match_field_name).id @@ -210,13 +207,10 @@ def buildMulticastGroupEntry(self, multicast_group_id, replicas): mc_entry.multicast_group_entry.replicas.extend([r]) return mc_entry - def buildCloneSessionEntry(self, clone_session_id, replicas, - packet_length_bytes=0): + def buildCloneSessionEntry(self, clone_session_id, replicas, packet_length_bytes=0): clone_entry = p4runtime_pb2.PacketReplicationEngineEntry() clone_entry.clone_session_entry.session_id = clone_session_id - clone_entry.clone_session_entry.packet_length_bytes = ( - packet_length_bytes - ) + clone_entry.clone_session_entry.packet_length_bytes = packet_length_bytes clone_entry.clone_session_entry.class_of_service = ( 0 # PI currently supports only CoS=0 for clone session entry ) diff --git a/utils/p4runtime_lib/simple_controller.py b/utils/p4runtime_lib/simple_controller.py index 291dbaf1..2a00dc3e 100755 --- a/utils/p4runtime_lib/simple_controller.py +++ b/utils/p4runtime_lib/simple_controller.py @@ -35,8 +35,9 @@ def main(): parser.add_argument( "-a", "--p4runtime-server-addr", - help=("address and port of the switch's P4Runtime server" - " (e.g. 192.168.0.1:50051)"), + help=( + "address and port of the switch's P4Runtime server (e.g. 192.168.0.1:50051)" + ), type=str, action="store", required=True, @@ -146,8 +147,9 @@ def program_switch( if target == "bmv2": info("Setting pipeline config (%s)..." % sw_conf["bmv2_json"]) bmv2_json_fpath = os.path.join(workdir, sw_conf["bmv2_json"]) - sw.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, - bmv2_json_file_path=bmv2_json_fpath) + sw.SetForwardingPipelineConfig( + p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json_fpath + ) else: raise Exception("Should not be here") @@ -188,13 +190,13 @@ def validateTableEntry(flow, p4info_helper, runtime_json): ] if match_fields is not None and (priority is None or priority == 0): for match_field_name, _ in match_fields.items(): - p4info_match = p4info_helper.get_match_field(table_name, - match_field_name) + p4info_match = p4info_helper.get_match_field(table_name, match_field_name) match_type = p4info_match.match_type if match_type in match_types_with_priority: - raise AssertionError("non-zero 'priority' field is required" - " for all entries for table {} in {}" - "".format(table_name, runtime_json)) + raise AssertionError( + "non-zero 'priority' field is required" + f" for all entries for table {table_name} in {runtime_json}" + ) def insertTableEntry(sw, flow, p4info_helper): @@ -232,8 +234,7 @@ def _byteify(data, ignore_dicts=False): # but only if we haven't already byteified it if isinstance(data, dict) and not ignore_dicts: return { - _byteify(key, ignore_dicts=True): _byteify(value, - ignore_dicts=True) + _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.items() } # if it's anything else, return it in its original form @@ -247,7 +248,7 @@ def tableEntryToString(flow): for match_name in flow["match"] ] match_str = ", ".join(match_str) - elif "default_action" in flow and flow["default_action"]: + elif flow.get("default_action"): match_str = "(default action)" else: match_str = "(any)" @@ -256,15 +257,14 @@ def tableEntryToString(flow): for param_name in flow["action_params"] ] params = ", ".join(params) - return ("%s: %s => %s(%s)" - "" % (flow["table"], match_str, flow["action_name"], params)) + return "%s: %s => %s(%s)" % (flow["table"], match_str, flow["action_name"], params) def groupEntryToString(rule): group_id = rule["multicast_group_id"] replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] ports_str = ", ".join(replicas) - return "Group {0} => ({1})".format(group_id, ports_str) + return f"Group {group_id} => ({ports_str})" def cloneEntryToString(rule): @@ -275,9 +275,7 @@ def cloneEntryToString(rule): packet_length_bytes = "NO_TRUNCATION" replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] ports_str = ", ".join(replicas) - return "Clone Session {0} => ({1}) ({2})".format( - clone_id, ports_str, packet_length_bytes - ) + return f"Clone Session {clone_id} => ({ports_str}) ({packet_length_bytes})" def insertMulticastGroupEntry(sw, rule, p4info_helper): diff --git a/utils/p4runtime_lib/switch.py b/utils/p4runtime_lib/switch.py index e7b12e1a..d73fa3be 100644 --- a/utils/p4runtime_lib/switch.py +++ b/utils/p4runtime_lib/switch.py @@ -1,10 +1,10 @@ # SPDX-FileCopyrightText: 2017 Open Networking Foundation # # SPDX-License-Identifier: Apache-2.0 +import threading from abc import abstractmethod from datetime import datetime from queue import Queue -import threading import grpc from p4.v1 import p4runtime_pb2, p4runtime_pb2_grpc @@ -52,10 +52,10 @@ def stop(self): self.running = False -class SwitchConnection(object): - - def __init__(self, name=None, address="127.0.0.1:50051", device_id=0, - proto_dump_file=None): +class SwitchConnection: + def __init__( + self, name=None, address="127.0.0.1:50051", device_id=0, proto_dump_file=None + ): self.name = name self.address = address self.device_id = device_id @@ -75,8 +75,11 @@ def __init__(self, name=None, address="127.0.0.1:50051", device_id=0, @abstractmethod def buildDeviceConfig(self, **kwargs): - print("switch.py:SwitchConnection:buildDeviceConfig() should be" - " overridden, but never called (?)", flush=True,) + print( + "switch.py:SwitchConnection:buildDeviceConfig() should be" + " overridden, but never called (?)", + flush=True, + ) assert False def shutdown(self): @@ -236,17 +239,14 @@ def log_message(self, method_name, body): if len(msg) < MSG_LOG_MAX_LEN: f.write(str(body)) else: - f.write("Message too long (%d bytes)! Skipping log...\n" - "" % (len(msg))) + f.write("Message too long (%d bytes)! Skipping log...\n" % (len(msg))) f.write("---\n") - def intercept_unary_unary(self, continuation, client_call_details, - request): + def intercept_unary_unary(self, continuation, client_call_details, request): self.log_message(client_call_details.method, request) return continuation(client_call_details, request) - def intercept_unary_stream(self, continuation, client_call_details, - request): + def intercept_unary_stream(self, continuation, client_call_details, request): self.log_message(client_call_details.method, request) return continuation(client_call_details, request) diff --git a/utils/p4runtime_switch.py b/utils/p4runtime_switch.py index 11c73b11..ea39f265 100644 --- a/utils/p4runtime_switch.py +++ b/utils/p4runtime_switch.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 import os +import sys import tempfile from time import sleep @@ -33,7 +34,7 @@ def __init__( device_id=None, enable_debugger=False, log_file=None, - **kwargs + **kwargs, ): Switch.__init__(self, name, **kwargs) assert sw_path @@ -44,8 +45,8 @@ def __init__( if json_path is not None: # make sure that the provided JSON file exists if not os.path.isfile(json_path): - error("Invalid JSON file: {}\n".format(json_path)) - exit(1) + error(f"Invalid JSON file: {json_path}\n") + sys.exit(1) self.json_path = json_path else: self.json_path = None @@ -63,13 +64,15 @@ def __init__( P4RuntimeSwitch.next_thrift_port += 1 if check_listening_on_port(self.grpc_port): - error("%s cannot bind port %d because it is bound" - " by another process\n" - "" % (self.name, self.grpc_port)) - exit(1) + error( + "%s cannot bind port %d because it is bound" + " by another process\n" + "" % (self.name, self.grpc_port) + ) + sys.exit(1) self.verbose = verbose - logfile = "/tmp/p4s.{}.log".format(self.name) + logfile = f"/tmp/p4s.{self.name}.log" self.output = open(logfile, "w") self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger @@ -77,14 +80,14 @@ def __init__( if log_file is not None: self.log_file = log_file else: - self.log_file = "/tmp/p4s.{}.log".format(self.name) + self.log_file = f"/tmp/p4s.{self.name}.log" if device_id is not None: self.device_id = device_id P4Switch.device_id = max(P4Switch.device_id, device_id) else: self.device_id = P4Switch.device_id P4Switch.device_id += 1 - self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id) + self.nanomsg = f"ipc:///tmp/bm-{self.device_id}-log.ipc" self.cpu_port = None if "cpu_port" in kwargs: @@ -103,7 +106,7 @@ def check_switch_started(self, pid): sleep(0.5) def start(self, controllers): - info("Starting P4 switch {}.\n".format(self.name)) + info(f"Starting P4 switch {self.name}.\n") args = [self.sw_path] for port, intf in list(self.intfs.items()): if not intf.IP(): @@ -138,8 +141,8 @@ def start(self, controllers): with tempfile.NamedTemporaryFile() as f: self.cmd(cmd + " >" + self.log_file + " 2>&1 & echo $! >> " + f.name) pid = int(f.read()) - debug("P4 switch {} PID is {}.\n".format(self.name, pid)) + debug(f"P4 switch {self.name} PID is {pid}.\n") if not self.check_switch_started(pid): - error("P4 switch {} did not start correctly.\n".format(self.name)) - exit(1) - info("P4 switch {} has been started.\n".format(self.name)) + error(f"P4 switch {self.name} did not start correctly.\n") + sys.exit(1) + info(f"P4 switch {self.name} has been started.\n") diff --git a/utils/run_exercise.py b/utils/run_exercise.py index 0026be54..503cefb6 100755 --- a/utils/run_exercise.py +++ b/utils/run_exercise.py @@ -62,8 +62,7 @@ def describe(self): class ExerciseTopo(Topo): """The mininet topology class for the P4 tutorial exercises.""" - def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, - **opts): + def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): Topo.__init__(self, **opts) host_links = [] switch_links = [] @@ -87,8 +86,7 @@ def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, # add default switch switchClass = None - switch_opts = {"log_file": "%s/%s.log" % (log_dir, sw), - "cls": switchClass} + switch_opts = {"log_file": "%s/%s.log" % (log_dir, sw), "cls": switchClass} for key in ("cpu_port", "priority_queues"): if key in params: @@ -128,8 +126,7 @@ def parse_switch_node(self, node): try: sw_port = int(sw_port[1:]) except: - raise Exception("Invalid switch node in" - " topology file: {}".format(node)) + raise Exception(f"Invalid switch node in topology file: {node}") return sw_name, sw_port @@ -197,8 +194,7 @@ def __init__( for dir_name in [log_dir, pcap_dir]: if not os.path.isdir(dir_name): if os.path.exists(dir_name): - raise Exception("'%s' exists and is not a directory!" - "" % (dir_name)) + raise Exception("'%s' exists and is not a directory!" % (dir_name)) os.mkdir(dir_name) self.log_dir = log_dir self.pcap_dir = pcap_dir @@ -245,18 +241,16 @@ def parse_links(self, unparsed_links): if s > t: s, t = t, s - link_dict = {"node1": s, "node2": t, "latency": "0ms", - "bandwidth": None} + link_dict = {"node1": s, "node2": t, "latency": "0ms", "bandwidth": None} if len(link) > 2: link_dict["latency"] = self.format_latency(link[2]) if len(link) > 3: link_dict["bandwidth"] = link[3] if link_dict["node1"][0] == "h": - assert ( - link_dict["node2"][0] == "s" - ), "Hosts should be connected to switches, not " + str( - link_dict["node2"] + assert link_dict["node2"][0] == "s", ( + "Hosts should be connected to switches, not " + + str(link_dict["node2"]) ) links.append(link_dict) return links @@ -327,13 +321,15 @@ def program_switch_cli(self, sw_name, sw_dict): thrift_port = sw_obj.thrift_port cli_input_commands = sw_dict["cli_input"] - self.logger("Configuring switch %s with file %s" - "" % (sw_name, cli_input_commands)) + self.logger( + "Configuring switch %s with file %s" % (sw_name, cli_input_commands) + ) with open(cli_input_commands, "r") as fin: cli_outfile = "%s/%s_cli_output.log" % (self.log_dir, sw_name) with open(cli_outfile, "w") as fout: - subprocess.Popen([cli, "--thrift-port", str(thrift_port)], - stdin=fin, stdout=fout) + subprocess.Popen( + [cli, "--thrift-port", str(thrift_port)], stdin=fin, stdout=fout + ) def program_switches(self): """This method will program each switch using the BMv2 CLI and/or @@ -342,8 +338,9 @@ def program_switches(self): """ for sw_name, sw_dict in self.switches.items(): if "cli_input" not in sw_dict and "runtime_json" not in sw_dict: - self.logger("Warning: No control plane file provided" - " for switch %s." % (sw_name)) + self.logger( + "Warning: No control plane file provided for switch %s." % (sw_name) + ) continue if "cli_input" in sw_dict: self.program_switch_cli(sw_name, sw_dict) @@ -373,37 +370,36 @@ def do_net_cli(self): self.logger("Starting mininet CLI") # Generate a message that will be printed by the Mininet CLI to make # interacting with the simple switch a little easier. - print("") - print("========================================" - "==============================") + print() + print("======================================================================") print("Welcome to the BMV2 Mininet CLI!") - print("========================================" - "==============================") + print("======================================================================") print("Your P4 program is installed into the BMV2 software switch") print("and your initial runtime configuration is loaded. You can") print("interact with the network using the mininet CLI below.") - print("") + print() if self.switch_json: print("To inspect or change the switch configuration, connect to") print("its CLI from your host operating system using this") print("command:") print(" simple_switch_CLI --thrift-port ") - print("") + print() print("To view a switch log, run this command from your host OS:") print(" tail -f %s/.log" % self.log_dir) - print("") + print() print( "To view the switch output pcap, check the pcap files in %s:" % self.pcap_dir ) print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap") - print("") + print() if "grpc" in self.bmv2_exe: print("To view the P4Runtime requests sent to the switch, check") print("the corresponding txt file in %s:" % (self.log_dir)) - print(" for example run: cat %s/s1-p4runtime-requests.txt" - "" % (self.log_dir)) - print("") + print( + " for example run: cat %s/s1-p4runtime-requests.txt" % (self.log_dir) + ) + print() CLI(self.net) diff --git a/vm-ubuntu-20.04/py3localpath.py b/vm-ubuntu-20.04/py3localpath.py index 7d2bdfae..9f623133 100755 --- a/vm-ubuntu-20.04/py3localpath.py +++ b/vm-ubuntu-20.04/py3localpath.py @@ -7,21 +7,25 @@ import re import sys -l1 = [x for x in sys.path - if re.match(r'/usr/local/lib/python3.[0-9]+/dist-packages', x)] +l1 = [ + x for x in sys.path if re.match(r"/usr/local/lib/python3.[0-9]+/dist-packages", x) +] if len(l1) == 1: py3distdir = l1[0] - m = re.match(r'(/usr/local/lib/python3.[0-9]+)/dist-packages', l1[0]) + m = re.match(r"(/usr/local/lib/python3.[0-9]+)/dist-packages", l1[0]) if m: print(m.group(1)) else: - print("Inconceivable! Somehow the second pattern did not match" - " but the first did.") + print( + "Inconceivable! Somehow the second pattern did not match" + " but the first did." + ) sys.exit(1) else: - print("Found %d matching entries in Python3 sys.path instead of 1: %s" - % (len(l1), l1)) + print( + "Found %d matching entries in Python3 sys.path instead of 1: %s" % (len(l1), l1) + ) sys.exit(1) sys.exit(0) From 3253f22efaf82402f43bb6f11c6b9c4fbb20bd23 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Thu, 8 Oct 2026 14:55:07 -0400 Subject: [PATCH 5/6] Fix many more warnings from ruff linter Signed-off-by: Andy Fingerhut --- exercises/basic/receive.py | 2 +- exercises/basic/send.py | 2 +- exercises/basic_tunnel/receive.py | 2 +- exercises/calc/calc.py | 2 +- exercises/ecn/receive.py | 2 +- exercises/ecn/send.py | 9 +-- exercises/flowcache/mycontroller.py | 64 +++++++---------- exercises/flowcache/solution/mycontroller.py | 64 +++++++---------- exercises/load_balance/receive.py | 2 +- exercises/load_balance/send.py | 2 +- exercises/mri/receive.py | 2 +- exercises/mri/send.py | 9 +-- exercises/p4runtime/mycontroller.py | 23 +++--- exercises/p4runtime/solution/mycontroller.py | 35 ++++----- exercises/qos/receive.py | 2 +- exercises/qos/send.py | 18 ++--- exercises/source_routing/receive.py | 2 +- exercises/source_routing/send.py | 2 +- utils/mininet/appcontroller.py | 30 ++++---- utils/mininet/apptopo.py | 75 +++++++++++--------- utils/mininet/multi_switch_mininet.py | 34 ++++----- utils/mininet/p4_mininet.py | 15 ++-- utils/mininet/shortest_path.py | 4 +- utils/mininet/single_switch_mininet.py | 14 ++-- utils/p4_mininet.py | 18 ++--- utils/p4apprunner.py | 75 ++++++++++---------- utils/p4runtime_lib/convert.py | 26 ++++--- utils/p4runtime_lib/helper.py | 25 +++---- utils/p4runtime_lib/simple_controller.py | 38 +++++----- utils/p4runtime_lib/switch.py | 14 ++-- utils/p4runtime_switch.py | 7 +- utils/run_exercise.py | 34 +++++---- vm-ubuntu-20.04/py3localpath.py | 2 +- 33 files changed, 293 insertions(+), 362 deletions(-) diff --git a/exercises/basic/receive.py b/exercises/basic/receive.py index 9fc8a891..dea7ad1e 100755 --- a/exercises/basic/receive.py +++ b/exercises/basic/receive.py @@ -57,7 +57,7 @@ def handle_pkt(pkt): def main(): ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/basic/send.py b/exercises/basic/send.py index 23d41610..4646de38 100755 --- a/exercises/basic/send.py +++ b/exercises/basic/send.py @@ -31,7 +31,7 @@ def main(): addr = socket.gethostbyname(sys.argv[1]) iface = get_if() - print("sending on interface %s to %s" % (iface, str(addr))) + print(f"sending on interface {iface} to {addr!s}") pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") pkt = ( pkt diff --git a/exercises/basic_tunnel/receive.py b/exercises/basic_tunnel/receive.py index 0abb4d81..ae13704f 100755 --- a/exercises/basic_tunnel/receive.py +++ b/exercises/basic_tunnel/receive.py @@ -34,7 +34,7 @@ def handle_pkt(pkt): def main(): ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/calc/calc.py b/exercises/calc/calc.py index e27621c1..cfde9574 100644 --- a/exercises/calc/calc.py +++ b/exercises/calc/calc.py @@ -93,7 +93,7 @@ def main(): break print(s) try: - i, ts = p(s, 0, []) + _i, ts = p(s, 0, []) # Construct packet using parsed tokens pkt = Ether(dst="00:04:00:00:00:00", type=0x1234) / P4calc( op=ts[1].value, operand_a=int(ts[0].value), operand_b=int(ts[2].value) diff --git a/exercises/ecn/receive.py b/exercises/ecn/receive.py index 4e55d86e..ddae25d5 100755 --- a/exercises/ecn/receive.py +++ b/exercises/ecn/receive.py @@ -29,7 +29,7 @@ def handle_pkt(pkt): def main(): iface = "eth0" - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/ecn/send.py b/exercises/ecn/send.py index dda54d7f..9c6e8067 100755 --- a/exercises/ecn/send.py +++ b/exercises/ecn/send.py @@ -40,12 +40,9 @@ def main(): ) pkt.show2() # hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise + for i in range(int(sys.argv[3])): + sendp(pkt, iface=iface) + sleep(1) if __name__ == "__main__": diff --git a/exercises/flowcache/mycontroller.py b/exercises/flowcache/mycontroller.py index 57a76d76..5df082dd 100755 --- a/exercises/flowcache/mycontroller.py +++ b/exercises/flowcache/mycontroller.py @@ -70,7 +70,7 @@ def intToIpv4(n): """Take an argument 'n' containing a 32-bit IPv4 address as an integer in the range [0, 2^32-1], and return a string in dotted decimal notation.""" - return "%d.%d.%d.%d" % ( + return "{}.{}.{}.{}".format( (n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, @@ -89,7 +89,7 @@ def flowCacheEntryToDebugStr(table_entry, include_action=False): int.from_bytes(table_entry.match[2].exact.value, byteorder="big") ) proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") - return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) + return f"(SA={src_ip}, DA={dst_ip}, proto={proto})" def decodePacketInMetadata(pktin_info, packet): @@ -101,7 +101,7 @@ def decodePacketInMetadata(pktin_info, packet): md_field_info = pktin_info[md_id_int] pktin_field_to_val[md_field_info["name"]] = md_val_int ret = {"metadata": pktin_field_to_val, "payload": packet.payload} - print("decodePacketInMetadata: ret=%s" % (ret)) + print(f"decodePacketInMetadata: ret={ret}") return ret @@ -115,8 +115,7 @@ def serializableEnumDict(p4info_data, name): name_to_int[name] = int_val int_to_name[int_val] = name print( - "serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" - "" % (name, name_to_int, int_to_name) + f"serializableEnumDict: name='{name}' name_to_int={name_to_int} int_to_name={int_to_name}" ) return name_to_int, int_to_name @@ -239,8 +238,7 @@ def createFlowRule(notif): def deleteFlowRule(sw, table_entry): sw.DeleteTableEntry(table_entry) print( - "Deleted flow_cache entry on %s. %s" - "" % (sw.name, flowCacheEntryToDebugStr(table_entry)) + f"Deleted flow_cache entry on {sw.name}. {flowCacheEntryToDebugStr(table_entry)}" ) @@ -307,7 +305,7 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print("\n----- Reading tables rules for %s -----" % (sw.name)) + print(f"\n----- Reading tables rules for {sw.name} -----") for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry @@ -337,14 +335,7 @@ def printCounter(p4info_helper, sw, counter_name, index): for entity in response.entities: counter = entity.counter_entry print( - "%s %s %d: %d packets (%d bytes)" - % ( - sw.name, - counter_name, - index, - counter.data.packet_count, - counter.data.byte_count, - ) + f"{sw.name} {counter_name} {index}: {counter.data.packet_count} packets ({counter.data.byte_count} bytes)" ) except grpc.RpcError as e: print(f"[gRPC Error in printCounter for {sw.name}]") @@ -363,10 +354,9 @@ def printCounter(p4info_helper, sw, counter_name, index): def processPacket(message): payload = message["packet-in"].payload packet = message["packet-in"] - print( - "Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name) - ) + print("Received PacketIn message of length {} bytes from switch {}".format( + len(payload), message["sw"].name + )) if len(payload) > 0: i = 0 pkt = Ether(payload) @@ -380,15 +370,15 @@ def processPacket(message): if debug_packetin: i += 1 print() - print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" % (type(payload))) + print(f"pktin {i} of {len(payload)}") + print(f"type(pktin.packet.payload)={type(payload)}") print(payload) print(pktinfo) print("Scapy decode:") print(pkt) - print("IPv4 proto %d (type %s)" % (ip_proto, type(ip_proto))) - print("IPv4 SA %08x (type %s)" % (src_ip_addr, type(src_ip_addr))) - print("IPv4 DA %08x (type %s)" % (dst_ip_addr, type(dst_ip_addr))) + print(f"IPv4 proto {ip_proto} (type {type(ip_proto)})") + print(f"IPv4 SA {src_ip_addr:08x} (type {type(src_ip_addr)})") + print(f"IPv4 DA {dst_ip_addr:08x} (type {type(dst_ip_addr)})") if ( pktinfo["metadata"]["punt_reason"] == global_data["punt_reason_name2int"]["FLOW_UNKNOWN"] @@ -416,11 +406,9 @@ def processPacket(message): ) print( - "For switch %s flow (SA=%s, DA=%s, proto=%d)" + "For switch {} flow (SA={}, DA={}, proto={})" " added table entry to send packets" - " to port %d with new DSCP %d" - "" - % ( + " to port {} with new DSCP {}".format( message["sw"].name, ip_sa_str, ip_da_str, @@ -472,10 +460,10 @@ async def processNotif(notif_queue): deleteFlowRule(notif["sw"], table_entry) else: print( - "Received idle timeout notification for switch=%s %s" + "Received idle timeout notification for switch={} {}" " It is duplicate of recently processed notification," " so ignoring it." - "" % (notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) + "".format(notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) ) notif_queue.task_done() @@ -515,9 +503,9 @@ async def idleTimeHandler(notif_queue, sw): def printGrpcError(e): print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=" ") + print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + print("f[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") async def main(p4info_file_path, bmv2_file_path): @@ -590,10 +578,10 @@ async def main(p4info_file_path, bmv2_file_path): except shp4rt.P4RuntimeWriteException: print( - "Got exception trying to configure clone session %d." + "Got exception trying to configure clone session {}." " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data["CPU_PORT_CLONE_SESSION_ID"]) + " run of the controller.".format( + global_data["CPU_PORT_CLONE_SESSION_ID"]) ) notif_queue = asyncio.Queue() @@ -651,10 +639,10 @@ async def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) + print(f"\np4info file not found: {args.p4info}\nHave you run 'make'?") parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) + print(f"\nBMv2 JSON file not found: {args.bmv2_json}\nHave you run 'make'?") parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/flowcache/solution/mycontroller.py b/exercises/flowcache/solution/mycontroller.py index 8d32ab62..9d024334 100755 --- a/exercises/flowcache/solution/mycontroller.py +++ b/exercises/flowcache/solution/mycontroller.py @@ -70,7 +70,7 @@ def intToIpv4(n): """Take an argument 'n' containing a 32-bit IPv4 address as an integer in the range [0, 2^32-1], and return a string in dotted decimal notation.""" - return "%d.%d.%d.%d" % ( + return "{}.{}.{}.{}".format( (n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, @@ -89,7 +89,7 @@ def flowCacheEntryToDebugStr(table_entry, include_action=False): int.from_bytes(table_entry.match[2].exact.value, byteorder="big") ) proto = int.from_bytes(table_entry.match[0].exact.value, byteorder="big") - return "(SA=%s, DA=%s, proto=%d)" % (src_ip, dst_ip, proto) + return f"(SA={src_ip}, DA={dst_ip}, proto={proto})" def decodePacketInMetadata(pktin_info, packet): @@ -101,7 +101,7 @@ def decodePacketInMetadata(pktin_info, packet): md_field_info = pktin_info[md_id_int] pktin_field_to_val[md_field_info["name"]] = md_val_int ret = {"metadata": pktin_field_to_val, "payload": packet.payload} - print("decodePacketInMetadata: ret=%s" % (ret)) + print(f"decodePacketInMetadata: ret={ret}") return ret @@ -115,8 +115,7 @@ def serializableEnumDict(p4info_data, name): name_to_int[name] = int_val int_to_name[int_val] = name print( - "serializableEnumDict: name='%s' name_to_int=%s int_to_name=%s" - "" % (name, name_to_int, int_to_name) + f"serializableEnumDict: name='{name}' name_to_int={name_to_int} int_to_name={int_to_name}" ) return name_to_int, int_to_name @@ -240,8 +239,7 @@ def createFlowRule(notif): def deleteFlowRule(sw, table_entry): sw.DeleteTableEntry(table_entry) print( - "Deleted flow_cache entry on %s. %s" - "" % (sw.name, flowCacheEntryToDebugStr(table_entry)) + f"Deleted flow_cache entry on {sw.name}. {flowCacheEntryToDebugStr(table_entry)}" ) @@ -308,7 +306,7 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print("\n----- Reading tables rules for %s -----" % (sw.name)) + print(f"\n----- Reading tables rules for {sw.name} -----") for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry @@ -338,14 +336,7 @@ def printCounter(p4info_helper, sw, counter_name, index): for entity in response.entities: counter = entity.counter_entry print( - "%s %s %d: %d packets (%d bytes)" - % ( - sw.name, - counter_name, - index, - counter.data.packet_count, - counter.data.byte_count, - ) + f"{sw.name} {counter_name} {index}: {counter.data.packet_count} packets ({counter.data.byte_count} bytes)" ) except grpc.RpcError as e: print(f"[gRPC Error in printCounter for {sw.name}]") @@ -364,10 +355,9 @@ def printCounter(p4info_helper, sw, counter_name, index): def processPacket(message): payload = message["packet-in"].payload packet = message["packet-in"] - print( - "Received PacketIn message of length %d bytes from switch %s" - "" % (len(payload), message["sw"].name) - ) + print("Received PacketIn message of length {} bytes from switch {}".format( + len(payload), message["sw"].name + )) if len(payload) > 0: i = 0 pkt = Ether(payload) @@ -381,15 +371,15 @@ def processPacket(message): if debug_packetin: i += 1 print() - print("pktin %d of %d" % (i, len(payload))) - print("type(pktin.packet.payload)=%s" % (type(payload))) + print(f"pktin {i} of {len(payload)}") + print(f"type(pktin.packet.payload)={type(payload)}") print(payload) print(pktinfo) print("Scapy decode:") print(pkt) - print("IPv4 proto %d (type %s)" % (ip_proto, type(ip_proto))) - print("IPv4 SA %08x (type %s)" % (src_ip_addr, type(src_ip_addr))) - print("IPv4 DA %08x (type %s)" % (dst_ip_addr, type(dst_ip_addr))) + print(f"IPv4 proto {ip_proto} (type {type(ip_proto)})") + print(f"IPv4 SA {src_ip_addr:08x} (type {type(src_ip_addr)})") + print(f"IPv4 DA {dst_ip_addr:08x} (type {type(dst_ip_addr)})") if ( pktinfo["metadata"]["punt_reason"] == global_data["punt_reason_name2int"]["FLOW_UNKNOWN"] @@ -417,11 +407,9 @@ def processPacket(message): ) print( - "For switch %s flow (SA=%s, DA=%s, proto=%d)" + "For switch {} flow (SA={}, DA={}, proto={})" " added table entry to send packets" - " to port %d with new DSCP %d" - "" - % ( + " to port {} with new DSCP {}".format( message["sw"].name, ip_sa_str, ip_da_str, @@ -473,10 +461,10 @@ async def processNotif(notif_queue): deleteFlowRule(notif["sw"], table_entry) else: print( - "Received idle timeout notification for switch=%s %s" + "Received idle timeout notification for switch={} {}" " It is duplicate of recently processed notification," " so ignoring it." - "" % (notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) + "".format(notif["sw"].name, flowCacheEntryToDebugStr(table_entry)) ) notif_queue.task_done() @@ -516,9 +504,9 @@ async def idleTimeHandler(notif_queue, sw): def printGrpcError(e): print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=" ") + print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + print("f[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") async def main(p4info_file_path, bmv2_file_path): @@ -591,10 +579,10 @@ async def main(p4info_file_path, bmv2_file_path): except shp4rt.P4RuntimeWriteException: print( - "Got exception trying to configure clone session %d." + "Got exception trying to configure clone session {}." " Assuming it was initialized already in an earlier" - " run of the controller." - "" % (global_data["CPU_PORT_CLONE_SESSION_ID"]) + " run of the controller.".format( + global_data["CPU_PORT_CLONE_SESSION_ID"]) ) notif_queue = asyncio.Queue() @@ -652,10 +640,10 @@ async def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) + print(f"\np4info file not found: {args.p4info}\nHave you run 'make'?") parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) + print(f"\nBMv2 JSON file not found: {args.bmv2_json}\nHave you run 'make'?") parser.exit(1) asyncio.run(main(args.p4info, args.bmv2_json)) diff --git a/exercises/load_balance/receive.py b/exercises/load_balance/receive.py index 8d4c7a8f..9ba032b6 100755 --- a/exercises/load_balance/receive.py +++ b/exercises/load_balance/receive.py @@ -55,7 +55,7 @@ def handle_pkt(pkt): def main(): ifaces = [i for i in os.listdir("/sys/class/net/") if "eth" in i] iface = ifaces[0] - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(filter="tcp", iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/load_balance/send.py b/exercises/load_balance/send.py index 23d41610..4646de38 100755 --- a/exercises/load_balance/send.py +++ b/exercises/load_balance/send.py @@ -31,7 +31,7 @@ def main(): addr = socket.gethostbyname(sys.argv[1]) iface = get_if() - print("sending on interface %s to %s" % (iface, str(addr))) + print(f"sending on interface {iface} to {addr!s}") pkt = Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") pkt = ( pkt diff --git a/exercises/mri/receive.py b/exercises/mri/receive.py index a9f74bcf..d722d307 100755 --- a/exercises/mri/receive.py +++ b/exercises/mri/receive.py @@ -65,7 +65,7 @@ def handle_pkt(pkt): def main(): iface = "eth0" - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/mri/send.py b/exercises/mri/send.py index 0bf54d96..8a201dc0 100755 --- a/exercises/mri/send.py +++ b/exercises/mri/send.py @@ -85,12 +85,9 @@ def main(): # dport=4321, sport=1234) / sys.argv[2] pkt.show2() # hexdump(pkt) - try: - for i in range(int(sys.argv[3])): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise + for i in range(int(sys.argv[3])): + sendp(pkt, iface=iface) + sleep(1) if __name__ == "__main__": diff --git a/exercises/p4runtime/mycontroller.py b/exercises/p4runtime/mycontroller.py index 2f156437..5d3bb45c 100755 --- a/exercises/p4runtime/mycontroller.py +++ b/exercises/p4runtime/mycontroller.py @@ -54,7 +54,7 @@ def writeTunnelRules( }, ) ingress_sw.WriteTableEntry(table_entry) - print("Installed ingress tunnel rule on %s" % ingress_sw.name) + print(f"Installed ingress tunnel rule on {ingress_sw.name}") # 2) Tunnel Transit Rule # The rule will need to be added to the myTunnel_exact table and @@ -90,7 +90,7 @@ def writeTunnelRules( action_params={"dstAddr": dst_eth_addr, "port": SWITCH_TO_HOST_PORT}, ) egress_sw.WriteTableEntry(table_entry) - print("Installed egress tunnel rule on %s" % egress_sw.name) + print(f"Installed egress tunnel rule on {egress_sw.name}") def readTableRules(p4info_helper, sw): @@ -100,7 +100,7 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print("\n----- Reading tables rules for %s -----" % sw.name) + print(f"\n----- Reading tables rules for {sw.name} -----") for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry @@ -124,16 +124,9 @@ def printCounter(p4info_helper, sw, counter_name, index): for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry - print( - "%s %s %d: %d packets (%d bytes)" - % ( - sw.name, - counter_name, - index, - counter.data.packet_count, - counter.data.byte_count, - ) - ) + print(f"{sw.name} {counter_name} {index}:" + " {counter.data.packet_count} packets" + " ({counter.data.byte_count} bytes)") def main(p4info_file_path, bmv2_file_path): @@ -237,10 +230,10 @@ def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) + print(f"\np4info file not found: {args.p4info}\nHave you run 'make'?") parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) + print(f"\nBMv2 JSON file not found: {args.bmv2_json}\nHave you run 'make'?") parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/p4runtime/solution/mycontroller.py b/exercises/p4runtime/solution/mycontroller.py index abf374ab..9d7e4a41 100755 --- a/exercises/p4runtime/solution/mycontroller.py +++ b/exercises/p4runtime/solution/mycontroller.py @@ -53,7 +53,7 @@ def writeTunnelRules( }, ) ingress_sw.WriteTableEntry(table_entry) - print("Installed ingress tunnel rule on %s" % ingress_sw.name) + print(f"Installed ingress tunnel rule on {ingress_sw.name}") # 2) Tunnel Transit Rule # The rule will need to be added to the myTunnel_exact table and @@ -82,7 +82,7 @@ def writeTunnelRules( action_params={"port": SWITCH_TO_SWITCH_PORT}, ) ingress_sw.WriteTableEntry(table_entry) - print("Installed transit tunnel rule on %s" % ingress_sw.name) + print(f"Installed transit tunnel rule on {ingress_sw.name}") # 3) Tunnel Egress Rule # For our simple topology, the host will always be located on the @@ -96,7 +96,7 @@ def writeTunnelRules( action_params={"dstAddr": dst_eth_addr, "port": SWITCH_TO_HOST_PORT}, ) egress_sw.WriteTableEntry(table_entry) - print("Installed egress tunnel rule on %s" % egress_sw.name) + print(f"Installed egress tunnel rule on {egress_sw.name}") def readTableRules(p4info_helper, sw): @@ -106,19 +106,19 @@ def readTableRules(p4info_helper, sw): :param p4info_helper: the P4Info helper :param sw: the switch connection """ - print("\n----- Reading tables rules for %s -----" % sw.name) + print(f"\n----- Reading tables rules for {sw.name} -----") for response in sw.ReadTableEntries(): for entity in response.entities: entry = entity.table_entry # TODO For extra credit, you can use the p4info_helper to translate # the IDs in the entry to names table_name = p4info_helper.get_tables_name(entry.table_id) - print("%s: " % table_name, end=" ") + print(f"{table_name}: ", end=" ") for m in entry.match: print( p4info_helper.get_match_field_name(table_name, m.field_id), end=" " ) - print("%r" % (p4info_helper.get_match_field_value(m)), end=" ") + print(f"{p4info_helper.get_match_field_value(m)!r}", end=" ") action = entry.action.action action_name = p4info_helper.get_actions_name(action.action_id) print("->", action_name, end=" ") @@ -127,7 +127,7 @@ def readTableRules(p4info_helper, sw): p4info_helper.get_action_param_name(action_name, p.param_id), end=" ", ) - print("%r" % p.value, end=" ") + print(f"{p.value!r}", end=" ") print() @@ -145,24 +145,17 @@ def printCounter(p4info_helper, sw, counter_name, index): for response in sw.ReadCounters(p4info_helper.get_counters_id(counter_name), index): for entity in response.entities: counter = entity.counter_entry - print( - "%s %s %d: %d packets (%d bytes)" - % ( - sw.name, - counter_name, - index, - counter.data.packet_count, - counter.data.byte_count, - ) - ) + print(f"{sw.name} {counter_name} {index}:" + " {counter.data.packet_count} packets" + " ({counter.data.byte_count} bytes)") def printGrpcError(e): print("gRPC Error:", e.details(), end=" ") status_code = e.code() - print("(%s)" % status_code.name, end=" ") + print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("[%s:%d]" % (traceback.tb_frame.f_code.co_filename, traceback.tb_lineno)) + print(f"[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") def main(p4info_file_path, bmv2_file_path): @@ -266,10 +259,10 @@ def main(p4info_file_path, bmv2_file_path): if not os.path.exists(args.p4info): parser.print_help() - print("\np4info file not found: %s\nHave you run 'make'?" % (args.p4info)) + print(f"\np4info file not found: {args.p4info}\nHave you run 'make'?") parser.exit(1) if not os.path.exists(args.bmv2_json): parser.print_help() - print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % (args.bmv2_json)) + print(f"\nBMv2 JSON file not found: {args.bmv2_json}\nHave you run 'make'?") parser.exit(1) main(args.p4info, args.bmv2_json) diff --git a/exercises/qos/receive.py b/exercises/qos/receive.py index cbee9c16..57527fe5 100755 --- a/exercises/qos/receive.py +++ b/exercises/qos/receive.py @@ -17,7 +17,7 @@ def handle_pkt(pkt): def main(): iface = "eth0" - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/qos/send.py b/exercises/qos/send.py index 5c0c06cb..afc4d3dd 100755 --- a/exercises/qos/send.py +++ b/exercises/qos/send.py @@ -45,12 +45,9 @@ def main(): / args.m ) pkt.show2() - try: - for i in range(int(args.dur)): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise + for i in range(int(args.dur)): + sendp(pkt, iface=iface) + sleep(1) elif args.p == "TCP": pkt = ( Ether(src=get_if_hwaddr(iface), dst="ff:ff:ff:ff:ff:ff") @@ -59,12 +56,9 @@ def main(): / args.m ) pkt.show2() - try: - for i in range(int(args.dur)): - sendp(pkt, iface=iface) - sleep(1) - except KeyboardInterrupt: - raise + for i in range(int(args.dur)): + sendp(pkt, iface=iface) + sleep(1) if __name__ == "__main__": diff --git a/exercises/source_routing/receive.py b/exercises/source_routing/receive.py index aee3fb4f..2f32ed95 100755 --- a/exercises/source_routing/receive.py +++ b/exercises/source_routing/receive.py @@ -73,7 +73,7 @@ class SourceRoutingTail(Packet): def main(): iface = "eth0" - print("sniffing on %s" % iface) + print(f"sniffing on {iface}") sys.stdout.flush() sniff(filter="udp and port 4321", iface=iface, prn=lambda x: handle_pkt(x)) diff --git a/exercises/source_routing/send.py b/exercises/source_routing/send.py index 05c37db9..e4608b9e 100755 --- a/exercises/source_routing/send.py +++ b/exercises/source_routing/send.py @@ -48,7 +48,7 @@ def main(): addr = socket.gethostbyname(sys.argv[1]) iface = get_if() - print("sending on interface %s to %s" % (iface, str(addr))) + print(f"sending on interface {iface} to {addr!s}") while True: print() diff --git a/utils/mininet/appcontroller.py b/utils/mininet/appcontroller.py index 29b6e313..222663e1 100644 --- a/utils/mininet/appcontroller.py +++ b/utils/mininet/appcontroller.py @@ -46,10 +46,8 @@ def read_register(self, register, idx, thrift_port=9090, sw=None): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - stdout, stderr = p.communicate(input="register_read %s %d" % (register, idx)) - reg_val = [x for x in stdout.split("\n") if " %s[%d]" % (register, idx) in x][ - 0 - ].split("= ", 1)[1] + stdout, _stderr = p.communicate(input=f"register_read {register} {idx}") + reg_val = next(x for x in stdout.split("\n") if f" {register}[{idx}]" in x).split("= ", 1)[1] return int(reg_val) def start(self): @@ -84,18 +82,18 @@ def start(self): h.setIP(link["host_ip"], 24) h.setMAC(link["host_mac"]) # h.cmd('ifconfig %s %s hw ether %s' % (iface, link['host_ip'], link['host_mac'])) - h.cmd("arp -i %s -s %s %s" % (iface, link["sw_ip"], link["sw_mac"])) - h.cmd("ethtool --offload %s rx off tx off" % iface) - h.cmd("ip route add %s dev %s" % (link["sw_ip"], iface)) - h.setDefaultRoute("via %s" % link["sw_ip"]) + h.cmd("arp -i {} -s {} {}".format(iface, link["sw_ip"], link["sw_mac"])) + h.cmd(f"ethtool --offload {iface} rx off tx off") + h.cmd("ip route add {} dev {}".format(link["sw_ip"], iface)) + h.setDefaultRoute("via {}".format(link["sw_ip"])) for h in self.net.hosts: - h_link = list(self.topo._host_links[h.name].values())[0] + h_link = next(iter(self.topo._host_links[h.name].values())) for sw in self.net.switches: path = shortestpath.get(sw.name, h.name, exclude=lambda n: n[0] == "h") if not path: continue - if not path[1][0] == "s": + if path[1][0] != "s": continue # next hop is a switch # sw_link = self.topo._sw_links[sw.name][path[1]] # entries[sw.name].append('table_add send_frame rewrite_mac %d => %s' % (sw_link[0]['port'], sw_link[0]['mac'])) @@ -109,17 +107,17 @@ def start(self): if not path: continue h_link = self.topo._host_links[h.name][path[1]] - h2_link = list(self.topo._host_links[h2.name].values())[0] - h.cmd("ip route add %s via %s" % (h2_link["host_ip"], h_link["sw_ip"])) + h2_link = next(iter(self.topo._host_links[h2.name].values())) + h.cmd("ip route add {} via {}".format(h2_link["host_ip"], h_link["sw_ip"])) print("**********") print("Configuring entries in p4 tables") - for sw_name in entries: + for sw_name, value in entries.items(): print() - print("Configuring switch... %s" % sw_name) + print(f"Configuring switch... {sw_name}") sw = self.net.get(sw_name) - if entries[sw_name]: - self.add_entries(sw=sw, entries=entries[sw_name]) + if value: + self.add_entries(sw=sw, entries=value) print("Configuration complete.") print("**********") diff --git a/utils/mininet/apptopo.py b/utils/mininet/apptopo.py index bb2ba50c..da59c29d 100644 --- a/utils/mininet/apptopo.py +++ b/utils/mininet/apptopo.py @@ -1,6 +1,9 @@ # SPDX-FileCopyrightText: 2018 Nate Foster # # SPDX-License-Identifier: Apache-2.0 +import functools +import operator + from mininet.topo import Topo @@ -8,25 +11,29 @@ class AppTopo(Topo): def __init__( self, links, - latencies={}, + latencies=None, manifest=None, target=None, log_dir="/tmp", - bws={}, + bws=None, **opts, ): + if bws is None: + bws = {} + if latencies is None: + latencies = {} Topo.__init__(self, **opts) - nodes = sum(list(map(list, list(zip(*links)))), []) - host_names = sorted(list(set([n for n in nodes if n[0] == "h"]))) - sw_names = sorted(list(set([n for n in nodes if n[0] == "s"]))) - sw_ports = dict([(sw, []) for sw in sw_names]) + nodes = functools.reduce(operator.iadd, list(map(list, list(zip(*links)))), []) + host_names = sorted({n for n in nodes if n[0] == "h"}) + sw_names = sorted({n for n in nodes if n[0] == "s"}) + sw_ports = {sw: [] for sw in sw_names} self._host_links = {} - self._sw_links = dict([(sw, {}) for sw in sw_names]) + self._sw_links = {sw: {} for sw in sw_names} for sw_name in sw_names: - self.addSwitch(sw_name, log_file="%s/%s.log" % (log_dir, sw_name)) + self.addSwitch(sw_name, log_file=f"{log_dir}/{sw_name}.log") for host_name in host_names: host_num = int(host_name[1:]) @@ -36,28 +43,27 @@ def __init__( self._host_links[host_name] = {} host_links = [x for x in links if x[0] == host_name or x[1] == host_name] - sw_idx = 0 - for link in host_links: + for sw_idx, link in enumerate(host_links): sw = link[0] if link[0] != host_name else link[1] sw_num = int(sw[1:]) assert sw[0] == "s", ( "Hosts should be connected to switches, not " + str(sw) ) - host_ip = "10.0.%d.%d" % (sw_num, host_num) - host_mac = "00:00:00:00:%02x:%02x" % (sw_num, host_num) - delay_key = "".join([host_name, sw]) - delay = latencies[delay_key] if delay_key in latencies else "0ms" - bw = bws[delay_key] if delay_key in bws else None + host_ip = f"10.0.{sw_num}.{host_num}" + host_mac = f"00:00:00:00:{sw_num:02x}:{host_num:02x}" + delay_key = f"{host_name}{sw}" + delay = latencies.get(delay_key, "0ms") + bw = bws.get(delay_key, None) sw_ports[sw].append(host_name) - self._host_links[host_name][sw] = dict( - idx=sw_idx, - host_mac=host_mac, - host_ip=host_ip, - sw=sw, - sw_mac="00:00:00:00:%02x:%02x" % (sw_num, host_num), - sw_ip="10.0.%d.%d" % (sw_num, 254), - sw_port=sw_ports[sw].index(host_name) + 1, - ) + self._host_links[host_name][sw] = { + "idx": sw_idx, + "host_mac": host_mac, + "host_ip": host_ip, + "sw": sw, + "sw_mac": f"00:00:00:00:{sw_num:02x}:{host_num:02x}", + "sw_ip": f"10.0.{sw_num}.254", + "sw_port": sw_ports[sw].index(host_name) + 1, + } self.addLink( host_name, sw, @@ -66,7 +72,6 @@ def __init__( addr1=host_mac, addr2=self._host_links[host_name][sw]["sw_mac"], ) - sw_idx += 1 for link in links: # only check switch-switch links sw1, sw2 = link @@ -74,22 +79,22 @@ def __init__( continue delay_key = "".join(sorted([sw1, sw2])) - delay = latencies[delay_key] if delay_key in latencies else "0ms" - bw = bws[delay_key] if delay_key in bws else None + delay = latencies.get(delay_key, "0ms") + bw = bws.get(delay_key, None) self.addLink(sw1, sw2, delay=delay, bw=bw) # , max_queue_size=10) sw_ports[sw1].append(sw2) sw_ports[sw2].append(sw1) sw1_num, sw2_num = int(sw1[1:]), int(sw2[1:]) - sw1_port = dict( - mac="00:00:00:%02x:%02x:00" % (sw1_num, sw2_num), - port=sw_ports[sw1].index(sw2) + 1, - ) - sw2_port = dict( - mac="00:00:00:%02x:%02x:00" % (sw2_num, sw1_num), - port=sw_ports[sw2].index(sw1) + 1, - ) + sw1_port = { + "mac": f"00:00:00:{sw1_num:02x}:{sw2_num:02x}:00", + "port": sw_ports[sw1].index(sw2) + 1, + } + sw2_port = { + "mac": f"00:00:00:{sw2_num:02x}:{sw1_num:02x}:00", + "port": sw_ports[sw2].index(sw1) + 1, + } self._sw_links[sw1][sw2] = [sw1_port, sw2_port] self._sw_links[sw2][sw1] = [sw2_port, sw1_port] diff --git a/utils/mininet/multi_switch_mininet.py b/utils/mininet/multi_switch_mininet.py index e3f3f748..e4f0c138 100755 --- a/utils/mininet/multi_switch_mininet.py +++ b/utils/mininet/multi_switch_mininet.py @@ -111,9 +111,9 @@ def main(): manifest = json.load(f) conf = manifest["targets"][args.target] - params = conf["parameters"] if "parameters" in conf else {} + params = conf.get("parameters", {}) - os.environ.update(dict([(k_v[0], str(k_v[1])) for k_v in iter(params.items())])) + os.environ.update({k_v[0]: str(k_v[1]) for k_v in iter(params.items())}) def formatParams(s): for param in params: @@ -136,15 +136,15 @@ def formatParams(s): if not os.path.isdir(args.log_dir): if os.path.exists(args.log_dir): - raise Exception("Log dir exists and is not a dir") + raise ValueError("Log dir exists and is not a dir") os.mkdir(args.log_dir) os.environ["P4APP_LOGDIR"] = args.log_dir links = [x[:2] for x in conf["links"]] - latencies = dict( - [("".join(sorted(x[:2])), x[2]) for x in conf["links"] if len(x) >= 3] - ) - bws = dict([("".join(sorted(x[:2])), x[3]) for x in conf["links"] if len(x) >= 4]) + latencies = { + "".join(sorted(x[:2])): x[2] for x in conf["links"] if len(x) >= 3 + } + bws = {"".join(sorted(x[:2])): x[3] for x in conf["links"] if len(x) >= 4} for host_name in sorted(conf["hosts"].keys()): host = conf["hosts"][host_name] @@ -156,11 +156,11 @@ def formatParams(s): other = a if a != host_name else b latencies[host_name + other] = host["latency"] - for x in latencies: - if isinstance(latencies[x], str): - latencies[x] = formatParams(latencies[x]) + for x, latency in latencies.items(): + if isinstance(latency, str): + latencies[x] = formatParams(latency) else: - latencies[x] = str(latencies[x]) + "ms" + latencies[x] = str(latency) + "ms" bmv2_log = args.bmv2_log or ("bmv2_log" in conf and conf["bmv2_log"]) pcap_dump = args.pcap_dump or ("pcap_dump" in conf and conf["pcap_dump"]) @@ -203,7 +203,7 @@ def formatParams(s): if args.cli or (conf.get("cli")): CLI(net) - stdout_files = dict() + stdout_files = {} return_codes = [] host_procs = [] @@ -223,7 +223,7 @@ def _wait_for_exit(p, host): stdout_files[host_name].close() print( - "\n".join(["%s: %s" % (k_v1[0], k_v1[1]) for k_v1 in iter(params.items())]) + "\n".join([f"{k_v1[0]}: {k_v1[1]}" for k_v1 in iter(params.items())]) + "\n" ) @@ -234,7 +234,7 @@ def _wait_for_exit(p, host): h = net.get(host_name) stdout_filename = os.path.join(args.log_dir, h.name + ".stdout") - stdout_files[h.name] = open(stdout_filename, "w") + stdout_files[h.name] = open(stdout_filename, "w") # noqa: SIM115 cmd = formatCmd(host["cmd"]) print(h.name, cmd) p = h.popen(cmd, stdout=stdout_files[h.name], shell=True, preexec_fn=os.setpgrp) @@ -254,13 +254,13 @@ def _wait_for_exit(p, host): if "wait" in conf["hosts"][host_name] and conf["hosts"][host_name]["wait"]: continue if p.returncode is None: - run_command("pkill -INT -P %d" % p.pid) + run_command(f"pkill -INT -P {p.pid}") sleep(0.2) # check if it's still running - rc = run_command("pkill -0 -P %d" % p.pid) + rc = run_command(f"pkill -0 -P {p.pid}") if rc == 0: # the process group is still running, send TERM sleep(1) # give it a little more time to exit gracefully - run_command("pkill -TERM -P %d" % p.pid) + run_command(f"pkill -TERM -P {p.pid}") _wait_for_exit(p, host_name) if "after" in conf and "cmd" in conf["after"]: diff --git a/utils/mininet/p4_mininet.py b/utils/mininet/p4_mininet.py index 0459ec7a..d3ca2bbc 100644 --- a/utils/mininet/p4_mininet.py +++ b/utils/mininet/p4_mininet.py @@ -18,7 +18,7 @@ def config(self, **params): r = super().config(**params) for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf().name, off) + cmd = f"/sbin/ethtool --offload {self.defaultIntf().name} {off} off" self.cmd(cmd) # disable IPv6 @@ -30,17 +30,12 @@ def config(self, **params): def describe(self, sw_addr=None, sw_mac=None): print("**********") - print("Network configuration for: %s" % self.name) + print(f"Network configuration for: {self.name}") print( - "Default interface: %s\t%s\t%s" - % ( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC(), - ) + f"Default interface: {self.defaultIntf().name}\t{self.defaultIntf().IP()}\t{self.defaultIntf().MAC()}" ) if sw_addr is not None or sw_mac is not None: - print("Default route to switch: %s (%s)" % (sw_addr, sw_mac)) + print(f"Default route to switch: {sw_addr} ({sw_mac})") print("**********") @@ -78,7 +73,7 @@ def __init__( self.log_file = log_file if self.log_file is None: self.log_file = f"/tmp/p4s.{self.name}.log" - self.output = open(self.log_file, "w") + self.output = open(self.log_file, "w") # noqa: SIM115 self.thrift_port = thrift_port self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger diff --git a/utils/mininet/shortest_path.py b/utils/mininet/shortest_path.py index 81f9d3df..04dd5542 100644 --- a/utils/mininet/shortest_path.py +++ b/utils/mininet/shortest_path.py @@ -2,7 +2,9 @@ # # SPDX-License-Identifier: Apache-2.0 class ShortestPath: - def __init__(self, edges=[]): + def __init__(self, edges=None): + if edges is None: + edges = [] self.neighbors = {} for edge in edges: self.addEdge(*edge) diff --git a/utils/mininet/single_switch_mininet.py b/utils/mininet/single_switch_mininet.py index f886390c..88f0a72d 100755 --- a/utils/mininet/single_switch_mininet.py +++ b/utils/mininet/single_switch_mininet.py @@ -98,7 +98,7 @@ def __init__(self, sw_path, json_path, log_file, thrift_port, pcap_dump, n, **op for h in range(n): host = self.addHost( - "h%d" % (h + 1), ip="10.0.%d.10/24" % h, mac="00:04:00:00:00:%02x" % h + f"h{h+1}", ip=f"10.0.{h}.10/24", mac=f"00:04:00:00:00:{h:02x}" ) print("Adding host", str(host)) self.addLink(host, switch) @@ -119,20 +119,20 @@ def main(): net = Mininet(topo=topo, host=P4Host, switch=P4Switch, controller=None) net.start() - sw_mac = ["00:aa:bb:00:00:%02x" % n for n in range(num_hosts)] + sw_mac = [f"00:aa:bb:00:00:{n:02x}" for n in range(num_hosts)] - sw_addr = ["10.0.%d.1" % n for n in range(num_hosts)] + sw_addr = [f"10.0.{n}.1" for n in range(num_hosts)] for n in range(num_hosts): - h = net.get("h%d" % (n + 1)) + h = net.get(f"h{n+1}") if mode == "l2": - h.setDefaultRoute("dev %s" % h.defaultIntf().name) + h.setDefaultRoute(f"dev {h.defaultIntf().name}") else: h.setARP(sw_addr[n], sw_mac[n]) - h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n])) + h.setDefaultRoute(f"dev {h.defaultIntf().name} via {sw_addr[n]}") for n in range(num_hosts): - h = net.get("h%d" % (n + 1)) + h = net.get(f"h{n+1}") h.describe(sw_addr[n], sw_mac[n]) sleep(1) diff --git a/utils/p4_mininet.py b/utils/p4_mininet.py index 609a90ea..703c42b1 100644 --- a/utils/p4_mininet.py +++ b/utils/p4_mininet.py @@ -22,7 +22,7 @@ def config(self, **params): self.defaultIntf().rename("eth0") for off in ["rx", "tx", "sg"]: - cmd = "/sbin/ethtool --offload eth0 %s off" % off + cmd = f"/sbin/ethtool --offload eth0 {off} off" self.cmd(cmd) # disable IPv6 @@ -36,12 +36,7 @@ def describe(self): print("**********") print(self.name) print( - "default interface: %s\t%s\t%s" - % ( - self.defaultIntf().name, - self.defaultIntf().IP(), - self.defaultIntf().MAC(), - ) + f"default interface: {self.defaultIntf().name}\t{self.defaultIntf().IP()}\t{self.defaultIntf().MAC()}" ) print("**********") @@ -78,13 +73,10 @@ def __init__( self.json_path = json_path self.verbose = verbose logfile = f"/tmp/p4s.{self.name}.log" - self.output = open(logfile, "w") + self.output = open(logfile, "w") # noqa: SIM115 self.thrift_port = thrift_port if check_listening_on_port(self.thrift_port): - error( - "%s cannot bind port %d because it is bound by" - " another process\n" % (self.name, self.grpc_port) - ) + error(f"{self.name} cannot bind port {self.grpc_port} because it is bound by another process\n") exit(1) self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger @@ -126,7 +118,7 @@ def start(self, controllers): if not intf.IP(): args.extend(["-i", str(port) + "@" + intf.name]) if self.pcap_dump: - args.append("--pcap %s" % self.pcap_dump) + args.append(f"--pcap {self.pcap_dump}") if self.thrift_port: args.extend(["--thrift-port", str(self.thrift_port)]) if self.nanomsg: diff --git a/utils/p4apprunner.py b/utils/p4apprunner.py index 823044fa..14c24118 100755 --- a/utils/p4apprunner.py +++ b/utils/p4apprunner.py @@ -91,7 +91,7 @@ def read_manifest(manifest_file): elif "default-target" in manifest: chosen_target = manifest["default-target"] else: - chosen_target = list(manifest["targets"].keys())[0] + chosen_target = next(iter(manifest["targets"].keys())) if chosen_target not in manifest["targets"]: log_error("Target not found in manifest:", chosen_target) @@ -130,9 +130,9 @@ def run_compile_bmv2(manifest): # Compile the program. output_file = manifest.program_file + ".json" - compiler_args.append('"%s"' % manifest.program_file) - compiler_args.append('-o "%s"' % output_file) - rv = run_command("p4c-bm2-ss %s" % " ".join(compiler_args)) + compiler_args.append(f'"{manifest.program_file}"') + compiler_args.append(f'-o "{output_file}"') + rv = run_command("p4c-bm2-ss {}".format(" ".join(compiler_args))) if "run-after-compile" in manifest.target_config: commands = manifest.target_config["run-after-compile"] @@ -158,12 +158,12 @@ def run_mininet(manifest): # We'll place the switch's log file in current (build) folder. cwd = os.getcwd() log_file = os.path.join(cwd, manifest.program_file + ".log") - print("*** Log file %s" % log_file) - switch_args.append('--log-file "%s"' % log_file) + print(f"*** Log file {log_file}") + switch_args.append(f'--log-file "{log_file}"') pcap_dir = os.path.join(cwd) - print("*** Pcap folder %s" % pcap_dir) - switch_args.append('--pcap-dump "%s" ' % pcap_dir) + print(f"*** Pcap folder {pcap_dir}") + switch_args.append(f'--pcap-dump "{pcap_dir}" ') # Generate a message that will be printed by the Mininet CLI to make # interacting with the simple switch a little easier. @@ -196,10 +196,10 @@ def run_mininet(manifest): print( "To view the switch log, run this command from your host OS:", file=message ) - print(" tail -f %s" % log_file, file=message) + print(f" tail -f {log_file}", file=message) print(file=message) print( - "To view the switch output pcap, check the pcap files in %s:" % (pcap_dir), + f"To view the switch output pcap, check the pcap files in {pcap_dir}:", file=message, ) print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", file=message) @@ -208,21 +208,21 @@ def run_mininet(manifest): # print(' bm_p4dbg' , file=message) # print(file=message) - switch_args.append('--cli-message "%s"' % message_file) + switch_args.append(f'--cli-message "{message_file}"') if "num-hosts" in manifest.target_config: - switch_args.append("--num-hosts %s" % (manifest.target_config["num-hosts"])) + switch_args.append("--num-hosts {}".format(manifest.target_config["num-hosts"])) if "switch-config" in manifest.target_config: switch_args.append( - '--switch-config "%s"' % manifest.target_config["switch-config"] + '--switch-config "{}"'.format(manifest.target_config["switch-config"]) ) - switch_args.append('--behavioral-exe "%s"' % "simple_switch") - switch_args.append('--json "%s"' % output_file) + switch_args.append('--behavioral-exe "{}"'.format("simple_switch")) + switch_args.append(f'--json "{output_file}"') - program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] - return run_command("python3 %s %s" % (program, " ".join(switch_args))) + program = f'"{sys.path[0]}/mininet/single_switch_mininet.py"' + return run_command("python3 {} {}".format(program, " ".join(switch_args))) def run_multiswitch(manifest): @@ -231,18 +231,18 @@ def run_multiswitch(manifest): script_args = [] cwd = os.getcwd() log_dir = os.path.join(cwd, cwd + "/logs") - print("*** Log directory %s" % log_dir) - script_args.append('--log-dir "%s"' % log_dir) + print(f"*** Log directory {log_dir}") + script_args.append(f'--log-dir "{log_dir}"') pcap_dir = os.path.join(cwd) - print("*** Pcap directory %s" % cwd) - script_args.append('--manifest "%s"' % args.manifest) - script_args.append('--target "%s"' % manifest.target) + print(f"*** Pcap directory {cwd}") + script_args.append(f'--manifest "{args.manifest}"') + script_args.append(f'--target "{manifest.target}"') if ( manifest.target_config.get("auto-control-plane") ): script_args.append("--auto-control-plane") - script_args.append('--behavioral-exe "%s"' % "simple_switch") - script_args.append('--json "%s"' % output_file) + script_args.append('--behavioral-exe "{}"'.format("simple_switch")) + script_args.append(f'--json "{output_file}"') # script_args.append('--cli') # Generate a message that will be printed by the Mininet CLI to make @@ -274,10 +274,10 @@ def run_multiswitch(manifest): print(" simple_switch_CLI --thrift-port ", file=message) print(file=message) print("To view a switch log, run this command from your host OS:", file=message) - print(" tail -f %s/.log" % log_dir, file=message) + print(f" tail -f {log_dir}/.log", file=message) print(file=message) print( - "To view the switch output pcap, check the pcap files in %s:" % (pcap_dir), + f"To view the switch output pcap, check the pcap files in {pcap_dir}:", file=message, ) print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap", file=message) @@ -286,10 +286,10 @@ def run_multiswitch(manifest): # print(' bm_p4dbg' , file=message) # print(file=message) - script_args.append('--cli-message "%s"' % message_file) + script_args.append(f'--cli-message "{message_file}"') - program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] - return run_command("python3 %s %s" % (program, " ".join(script_args))) + program = f'"{sys.path[0]}/mininet/multi_switch_mininet.py"' + return run_command("python3 {} {}".format(program, " ".join(script_args))) def run_stf(manifest): @@ -306,8 +306,8 @@ def run_stf(manifest): stf_args.append(os.path.join(args.build_dir, output_file)) stf_args.append(os.path.join(args.build_dir, stf_file)) - program = '"%s/stf/bmv2stf.py"' % sys.path[0] - rv = run_command("python3 %s %s" % (program, " ".join(stf_args))) + program = f'"{sys.path[0]}/stf/bmv2stf.py"' + rv = run_command("python3 {} {}".format(program, " ".join(stf_args))) if rv != 0: sys.exit(1) return rv @@ -317,14 +317,14 @@ def run_custom(manifest): output_file = run_compile_bmv2(manifest) python_path = "PYTHONPATH=$PYTHONPATH:/scripts/mininet/" script_args = [] - script_args.append('--behavioral-exe "%s"' % "simple_switch") - script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % "simple_switch_CLI") + script_args.append('--behavioral-exe "{}"'.format("simple_switch")) + script_args.append(f'--json "{output_file}"') + script_args.append('--cli "{}"'.format("simple_switch_CLI")) if "program" not in manifest.target_config: log_error("No mininet program file provided.") sys.exit(1) program = manifest.target_config["program"] - rv = run_command("%s python3 %s %s" % (python_path, program, " ".join(script_args))) + rv = run_command("{} python3 {} {}".format(python_path, program, " ".join(script_args))) if rv != 0: sys.exit(1) @@ -338,9 +338,8 @@ def main(): # A '.p4app' package is really just a '.tar.gz' archive. Extract it so we # can process its contents. log("Extracting package.") - tar = tarfile.open(args.app) - tar.extractall() - tar.close() + with tarfile.open(args.app) as tar: + tar.extractall() log("Reading package manifest.") with open(args.manifest, "r") as manifest_file: diff --git a/utils/p4runtime_lib/convert.py b/utils/p4runtime_lib/convert.py index 54d2a68d..38f38586 100644 --- a/utils/p4runtime_lib/convert.py +++ b/utils/p4runtime_lib/convert.py @@ -60,7 +60,7 @@ def decodeIPv6(encoded_ip_addr): def bitwidthToBytes(bitwidth): - return int(math.ceil(bitwidth / 8.0)) + return math.ceil(bitwidth / 8.0) def encodeNum(number, bitwidth): @@ -70,21 +70,19 @@ def encodeNum(number, bitwidth): orig_number = number if number < 0: if number < -(2 ** (bitwidth - 1)): - raise Exception( - "Negative number, %d, has 2's complement" - " representation that does not fit in %d bits" - "" % (number, bitwidth) + raise ValueError( + f"Negative number, {number}, has 2's complement" + " representation that does not fit in {bitwidth} bits" ) number = (2**bitwidth) + number - num_str = "%x" % number + num_str = f"{number:x}" if orig_number < 0: print( - "CONVERT_NEGATIVE_NUMBER debug: orig_number=%s number=%s" - " bitwidth=%d num_str='%s'" - "" % (orig_number, number, bitwidth, num_str) + f"CONVERT_NEGATIVE_NUMBER debug: orig_number={orig_number} number={number}" + " bitwidth={bitwidth} num_str='{num_str}'" ) if number >= 2**bitwidth: - raise Exception("Number, %d, does not fit in %d bits" % (number, bitwidth)) + raise ValueError(f"Number, {number}, does not fit in {bitwidth} bits") return bytes.fromhex("0" * (byte_len * 2 - len(num_str)) + num_str) @@ -95,7 +93,7 @@ def decodeNum(encoded_number): def encode(x, bitwidth): "Tries to infer the type of `x` and encode it" byte_len = bitwidthToBytes(bitwidth) - if (isinstance(x, list) or isinstance(x, tuple)) and len(x) == 1: + if (isinstance(x, (list, tuple))) and len(x) == 1: x = x[0] encoded_bytes = None if isinstance(x, str): @@ -111,7 +109,7 @@ def encode(x, bitwidth): elif isinstance(x, int): encoded_bytes = encodeNum(x, bitwidth) else: - raise Exception("Encoding objects of %r is not supported" % type(x)) + raise TypeError(f"Encoding objects of {type(x)!r} is not supported") assert len(encoded_bytes) == byte_len return encoded_bytes @@ -170,6 +168,6 @@ def encode(x, bitwidth): byte_len = 2 try: enc_num = encodeNum(num, 8) - raise Exception("expected exception") - except Exception as e: + raise ValueError("expected exception") + except ValueError as e: print(e) diff --git a/utils/p4runtime_lib/helper.py b/utils/p4runtime_lib/helper.py index 7d05f340..89fdbe3a 100644 --- a/utils/p4runtime_lib/helper.py +++ b/utils/p4runtime_lib/helper.py @@ -34,9 +34,9 @@ def get(self, entity_type, name=None, id=None): return o if name: - raise AttributeError("Could not find %r of type %s" % (name, entity_type)) + raise AttributeError(f"Could not find {name!r} of type {entity_type}") else: - raise AttributeError("Could not find id %r of type %s" % (id, entity_type)) + raise AttributeError(f"Could not find id {id!r} of type {entity_type}") def get_id(self, entity_type, name): return self.get(entity_type, name=name).preamble.id @@ -63,7 +63,7 @@ def __getattr__(self, attr): primitive = m.group(1) return lambda id: self.get_name(primitive, id) - raise AttributeError("%r object has no attribute %r" % (self.__class__, attr)) + raise AttributeError(f"{self.__class__!r} object has no attribute {attr!r}") def get_match_field(self, table_name, name=None, id=None): for t in self.p4info.tables: @@ -73,11 +73,10 @@ def get_match_field(self, table_name, name=None, id=None): if name is not None: if mf.name == name: return mf - elif id is not None: - if mf.id == id: - return mf + elif id is not None and mf.id == id: + return mf raise AttributeError( - "%r has no attribute %r" % (table_name, name if name is not None else id) + f"{table_name!r} has no attribute {name if name is not None else id!r}" ) def get_match_field_id(self, table_name, match_field_name): @@ -108,7 +107,7 @@ def get_match_field_pb(self, table_name, match_field_name, value): range_entry.low = encode(value[0], bitwidth) range_entry.high = encode(value[1], bitwidth) else: - raise Exception("Unsupported match type with type %r" % match_type) + raise ValueError(f"Unsupported match type with type {match_type!r}") return p4runtime_match def get_match_field_value(self, match_field): @@ -124,7 +123,7 @@ def get_match_field_value(self, match_field): elif match_type == "range": return (match_field.range.low, match_field.range.high) else: - raise Exception("Unsupported match type with type %r" % match_type) + raise ValueError(f"Unsupported match type with type {match_type!r}") def get_action_param(self, action_name, name=None, id=None): for a in self.p4info.actions: @@ -134,12 +133,10 @@ def get_action_param(self, action_name, name=None, id=None): if name is not None: if p.name == name: return p - elif id is not None: - if p.id == id: - return p + elif id is not None and p.id == id: + return p raise AttributeError( - "action %r has no param %r, (has: %r)" - % (action_name, name if name is not None else id, a.params) + f"action {action_name!r} has no param {name if name is not None else id!r}, (has: {a.params!r})" ) def get_action_param_id(self, action_name, param_name): diff --git a/utils/p4runtime_lib/simple_controller.py b/utils/p4runtime_lib/simple_controller.py index 2a00dc3e..f5d81b0b 100755 --- a/utils/p4runtime_lib/simple_controller.py +++ b/utils/p4runtime_lib/simple_controller.py @@ -70,7 +70,7 @@ def main(): args = parser.parse_args() if not os.path.exists(args.runtime_conf_file): - parser.error("File %s does not exist!" % args.runtime_conf_file) + parser.error(f"File {args.runtime_conf_file} does not exist!") workdir = os.path.dirname(os.path.abspath(args.runtime_conf_file)) with open(args.runtime_conf_file, "r") as sw_conf_file: program_switch( @@ -91,7 +91,7 @@ def check_switch_conf(sw_conf, workdir): raise ConfException("missing key 'target'") target = sw_conf["target"] if target not in target_choices: - raise ConfException("unknown target '%s'" % target) + raise ConfException(f"unknown target '{target}'") if target == "bmv2": required_keys.append("bmv2_json") @@ -99,12 +99,12 @@ def check_switch_conf(sw_conf, workdir): for conf_key in required_keys: if conf_key not in sw_conf or len(sw_conf[conf_key]) == 0: - raise ConfException("missing key '%s' or empty value" % conf_key) + raise ConfException(f"missing key '{conf_key}' or empty value") for conf_key in files_to_check: real_path = os.path.join(workdir, sw_conf[conf_key]) if not os.path.exists(real_path): - raise ConfException("file does not exist %s" % real_path) + raise ConfException(f"file does not exist {real_path}") # check for file content (e.g. JSON format for bmv2_json) if conf_key == "bmv2_json": with open(real_path, "r") as f: @@ -123,39 +123,39 @@ def program_switch( try: check_switch_conf(sw_conf=sw_conf, workdir=workdir) except ConfException as e: - error("While parsing input runtime configuration: %s" % str(e)) + error(f"While parsing input runtime configuration: {e!s}") return - info("Using P4Info file %s..." % sw_conf["p4info"]) + info("Using P4Info file {}...".format(sw_conf["p4info"])) p4info_fpath = os.path.join(workdir, sw_conf["p4info"]) p4info_helper = helper.P4InfoHelper(p4info_fpath) target = sw_conf["target"] - info("Connecting to P4Runtime server on %s (%s)..." % (addr, target)) + info(f"Connecting to P4Runtime server on {addr} ({target})...") if target == "bmv2": sw = bmv2.Bmv2SwitchConnection( address=addr, device_id=device_id, proto_dump_file=proto_dump_fpath ) else: - raise Exception("Don't know how to connect to target %s" % target) + raise ValueError(f"Don't know how to connect to target {target}") try: sw.MasterArbitrationUpdate() if target == "bmv2": - info("Setting pipeline config (%s)..." % sw_conf["bmv2_json"]) + info("Setting pipeline config ({})...".format(sw_conf["bmv2_json"])) bmv2_json_fpath = os.path.join(workdir, sw_conf["bmv2_json"]) sw.SetForwardingPipelineConfig( p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json_fpath ) else: - raise Exception("Should not be here") + raise ValueError("Should not be here") if "table_entries" in sw_conf: table_entries = sw_conf["table_entries"] - info("Inserting %d table entries..." % len(table_entries)) + info(f"Inserting {len(table_entries)} table entries...") for entry in table_entries: info(tableEntryToString(entry)) validateTableEntry(entry, p4info_helper, runtime_json) @@ -163,14 +163,14 @@ def program_switch( if "multicast_group_entries" in sw_conf: group_entries = sw_conf["multicast_group_entries"] - info("Inserting %d group entries..." % len(group_entries)) + info(f"Inserting {len(group_entries)} group entries...") for entry in group_entries: info(groupEntryToString(entry)) insertMulticastGroupEntry(sw, entry, p4info_helper) if "clone_session_entries" in sw_conf: clone_entries = sw_conf["clone_session_entries"] - info("Inserting %d clone entries..." % len(clone_entries)) + info(f"Inserting {len(clone_entries)} clone entries...") for entry in clone_entries: info(cloneEntryToString(entry)) insertCloneGroupEntry(sw, entry, p4info_helper) @@ -189,7 +189,7 @@ def validateTableEntry(flow, p4info_helper, runtime_json): p4info_pb2.MatchField.OPTIONAL, ] if match_fields is not None and (priority is None or priority == 0): - for match_field_name, _ in match_fields.items(): + for match_field_name in match_fields: p4info_match = p4info_helper.get_match_field(table_name, match_field_name) match_type = p4info_match.match_type if match_type in match_types_with_priority: @@ -244,7 +244,7 @@ def _byteify(data, ignore_dicts=False): def tableEntryToString(flow): if "match" in flow: match_str = [ - "%s=%s" % (match_name, str(flow["match"][match_name])) + "{}={}".format(match_name, str(flow["match"][match_name])) for match_name in flow["match"] ] match_str = ", ".join(match_str) @@ -253,16 +253,16 @@ def tableEntryToString(flow): else: match_str = "(any)" params = [ - "%s=%s" % (param_name, str(flow["action_params"][param_name])) + "{}={}".format(param_name, str(flow["action_params"][param_name])) for param_name in flow["action_params"] ] params = ", ".join(params) - return "%s: %s => %s(%s)" % (flow["table"], match_str, flow["action_name"], params) + return "{}: {} => {}({})".format(flow["table"], match_str, flow["action_name"], params) def groupEntryToString(rule): group_id = rule["multicast_group_id"] - replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] + replicas = [str(replica["egress_port"]) for replica in rule["replicas"]] ports_str = ", ".join(replicas) return f"Group {group_id} => ({ports_str})" @@ -273,7 +273,7 @@ def cloneEntryToString(rule): packet_length_bytes = str(rule["packet_length_bytes"]) + "B" else: packet_length_bytes = "NO_TRUNCATION" - replicas = ["%d" % replica["egress_port"] for replica in rule["replicas"]] + replicas = [str(replica["egress_port"]) for replica in rule["replicas"]] ports_str = ", ".join(replicas) return f"Clone Session {clone_id} => ({ports_str}) ({packet_length_bytes})" diff --git a/utils/p4runtime_lib/switch.py b/utils/p4runtime_lib/switch.py index d73fa3be..12a0eed7 100644 --- a/utils/p4runtime_lib/switch.py +++ b/utils/p4runtime_lib/switch.py @@ -1,9 +1,9 @@ # SPDX-FileCopyrightText: 2017 Open Networking Foundation # # SPDX-License-Identifier: Apache-2.0 +import datetime import threading from abc import abstractmethod -from datetime import datetime from queue import Queue import grpc @@ -155,8 +155,7 @@ def ReadTableEntries(self, table_id=None, dry_run=False): if dry_run: print("P4Runtime Read:", request) else: - for response in self.client_stub.Read(request): - yield response + yield from self.client_stub.Read(request) def ReadCounters(self, counter_id=None, index=None, dry_run=False): request = p4runtime_pb2.ReadRequest() @@ -172,8 +171,7 @@ def ReadCounters(self, counter_id=None, index=None, dry_run=False): if dry_run: print("P4Runtime Read:", request) else: - for response in self.client_stub.Read(request): - yield response + yield from self.client_stub.Read(request) def WritePREEntry(self, pre_entry, dry_run=False): request = p4runtime_pb2.WriteRequest() @@ -233,13 +231,13 @@ def __init__(self, log_file): def log_message(self, method_name, body): with open(self.log_file, "a") as f: - ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] msg = str(body) - f.write("\n[%s] %s\n---\n" % (ts, method_name)) + f.write(f"\n[{ts}] {method_name}\n---\n") if len(msg) < MSG_LOG_MAX_LEN: f.write(str(body)) else: - f.write("Message too long (%d bytes)! Skipping log...\n" % (len(msg))) + f.write(f"Message too long ({len(msg)} bytes)! Skipping log...\n") f.write("---\n") def intercept_unary_unary(self, continuation, client_call_details, request): diff --git a/utils/p4runtime_switch.py b/utils/p4runtime_switch.py index ea39f265..ff7c0174 100644 --- a/utils/p4runtime_switch.py +++ b/utils/p4runtime_switch.py @@ -65,15 +65,14 @@ def __init__( if check_listening_on_port(self.grpc_port): error( - "%s cannot bind port %d because it is bound" + f"{self.name} cannot bind port {self.grpc_port} because it is bound" " by another process\n" - "" % (self.name, self.grpc_port) ) sys.exit(1) self.verbose = verbose logfile = f"/tmp/p4s.{self.name}.log" - self.output = open(logfile, "w") + self.output = open(logfile, "w") # noqa: SIM115 self.pcap_dump = pcap_dump self.enable_debugger = enable_debugger self.log_console = log_console @@ -112,7 +111,7 @@ def start(self, controllers): if not intf.IP(): args.extend(["-i", str(port) + "@" + intf.name]) if self.pcap_dump: - args.append("--pcap %s" % self.pcap_dump) + args.append(f"--pcap {self.pcap_dump}") if self.nanomsg: args.extend(["--nanolog", self.nanomsg]) args.extend(["--device-id", str(self.device_id)]) diff --git a/utils/run_exercise.py b/utils/run_exercise.py index 503cefb6..f0301223 100755 --- a/utils/run_exercise.py +++ b/utils/run_exercise.py @@ -39,7 +39,7 @@ def __init__(self, *opts, **kwargs): P4RuntimeSwitch.__init__(self, *opts, **kwargs) def describe(self): - print("%s -> gRPC port: %d" % (self.name, self.grpc_port)) + print(f"{self.name} -> gRPC port: {self.grpc_port}") return ConfiguredP4RuntimeSwitch else: @@ -54,7 +54,7 @@ def __init__(self, *opts, **kwargs): P4Switch.__init__(self, *opts, **kwargs) def describe(self): - print("%s -> Thrift port: %d" % (self.name, self.thrift_port)) + print(f"{self.name} -> Thrift port: {self.thrift_port}") return ConfiguredP4Switch @@ -86,7 +86,7 @@ def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts): # add default switch switchClass = None - switch_opts = {"log_file": "%s/%s.log" % (log_dir, sw), "cls": switchClass} + switch_opts = {"log_file": f"{log_dir}/{sw}.log", "cls": switchClass} for key in ("cpu_port", "priority_queues"): if key in params: @@ -125,8 +125,8 @@ def parse_switch_node(self, node): sw_name, sw_port = node.split("-") try: sw_port = int(sw_port[1:]) - except: - raise Exception(f"Invalid switch node in topology file: {node}") + except ValueError: + raise ValueError(f"Invalid switch node in topology file: {node}") return sw_name, sw_port @@ -194,7 +194,7 @@ def __init__( for dir_name in [log_dir, pcap_dir]: if not os.path.isdir(dir_name): if os.path.exists(dir_name): - raise Exception("'%s' exists and is not a directory!" % (dir_name)) + raise ValueError(f"'{dir_name}' exists and is not a directory!") os.mkdir(dir_name) self.log_dir = log_dir self.pcap_dir = pcap_dir @@ -297,13 +297,12 @@ def program_switch_p4runtime(self, sw_name, sw_dict): device_id = sw_obj.device_id runtime_json = sw_dict["runtime_json"] self.logger( - "Configuring switch %s using P4Runtime with file %s" - % (sw_name, runtime_json) + f"Configuring switch {sw_name} using P4Runtime with file {runtime_json}" ) with open(runtime_json, "r") as sw_conf_file: - outfile = "%s/%s-p4runtime-requests.txt" % (self.log_dir, sw_name) + outfile = f"{self.log_dir}/{sw_name}-p4runtime-requests.txt" p4runtime_lib.simple_controller.program_switch( - addr="127.0.0.1:%d" % grpc_port, + addr=f"127.0.0.1:{grpc_port}", device_id=device_id, sw_conf_file=sw_conf_file, workdir=os.getcwd(), @@ -322,10 +321,10 @@ def program_switch_cli(self, sw_name, sw_dict): cli_input_commands = sw_dict["cli_input"] self.logger( - "Configuring switch %s with file %s" % (sw_name, cli_input_commands) + f"Configuring switch {sw_name} with file {cli_input_commands}" ) with open(cli_input_commands, "r") as fin: - cli_outfile = "%s/%s_cli_output.log" % (self.log_dir, sw_name) + cli_outfile = f"{self.log_dir}/{sw_name}_cli_output.log" with open(cli_outfile, "w") as fout: subprocess.Popen( [cli, "--thrift-port", str(thrift_port)], stdin=fin, stdout=fout @@ -339,7 +338,7 @@ def program_switches(self): for sw_name, sw_dict in self.switches.items(): if "cli_input" not in sw_dict and "runtime_json" not in sw_dict: self.logger( - "Warning: No control plane file provided for switch %s." % (sw_name) + f"Warning: No control plane file provided for switch {sw_name}." ) continue if "cli_input" in sw_dict: @@ -385,19 +384,18 @@ def do_net_cli(self): print(" simple_switch_CLI --thrift-port ") print() print("To view a switch log, run this command from your host OS:") - print(" tail -f %s/.log" % self.log_dir) + print(f" tail -f {self.log_dir}/.log") print() print( - "To view the switch output pcap, check the pcap files in %s:" - % self.pcap_dir + f"To view the switch output pcap, check the pcap files in {self.pcap_dir}:" ) print(" for example run: sudo tcpdump -xxx -r s1-eth1.pcap") print() if "grpc" in self.bmv2_exe: print("To view the P4Runtime requests sent to the switch, check") - print("the corresponding txt file in %s:" % (self.log_dir)) + print(f"the corresponding txt file in {self.log_dir}:") print( - " for example run: cat %s/s1-p4runtime-requests.txt" % (self.log_dir) + f" for example run: cat {self.log_dir}/s1-p4runtime-requests.txt" ) print() diff --git a/vm-ubuntu-20.04/py3localpath.py b/vm-ubuntu-20.04/py3localpath.py index 9f623133..96443930 100755 --- a/vm-ubuntu-20.04/py3localpath.py +++ b/vm-ubuntu-20.04/py3localpath.py @@ -24,7 +24,7 @@ sys.exit(1) else: print( - "Found %d matching entries in Python3 sys.path instead of 1: %s" % (len(l1), l1) + f"Found {len(l1)} matching entries in Python3 sys.path instead of 1: {l1}" ) sys.exit(1) From f17e94c54abac8ec59ea45b71e889dbb51ee52d6 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Fri, 9 Oct 2026 12:27:46 -0400 Subject: [PATCH 6/6] Fix some more warnings in Python code from ruff linter Signed-off-by: Andy Fingerhut --- exercises/basic/ptf/basic_fwd.py | 4 ++-- exercises/basic/receive.py | 3 ++- exercises/basic_tunnel/myTunnel_header.py | 4 +++- exercises/basic_tunnel/ptf/basic_tunnel.py | 7 ++++--- exercises/calc/calc.py | 3 ++- exercises/flowcache/mycontroller.py | 8 ++++---- exercises/flowcache/solution/mycontroller.py | 8 ++++---- exercises/link_monitor/probe_hdrs.py | 8 +++++--- exercises/load_balance/receive.py | 3 ++- exercises/mri/receive.py | 5 +++-- exercises/mri/send.py | 5 +++-- exercises/p4runtime/mycontroller.py | 4 ++-- exercises/p4runtime/solution/mycontroller.py | 4 ++-- exercises/source_routing/receive.py | 7 ++++--- exercises/source_routing/send.py | 3 ++- 15 files changed, 44 insertions(+), 32 deletions(-) diff --git a/exercises/basic/ptf/basic_fwd.py b/exercises/basic/ptf/basic_fwd.py index c8aaf84b..c35fb488 100644 --- a/exercises/basic/ptf/basic_fwd.py +++ b/exercises/basic/ptf/basic_fwd.py @@ -32,7 +32,7 @@ def setUp(self): self.dataplane = ptf.dataplane_instance self.dataplane.flush() - logging.debug("BasicFwdTest.setUp()") + logger.debug("BasicFwdTest.setUp()") # Get test parameters grpc_addr = tu.test_param_get("grpcaddr") @@ -61,7 +61,7 @@ def setUp(self): ) def tearDown(self): - logging.debug("BasicFwdTest.tearDown()") + logger.debug("BasicFwdTest.tearDown()") ShutdownAllSwitchConnections() ###################################################################### diff --git a/exercises/basic/receive.py b/exercises/basic/receive.py index dea7ad1e..77ef4433 100755 --- a/exercises/basic/receive.py +++ b/exercises/basic/receive.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-only import os import sys +from typing import Any, ClassVar from scapy.all import ( TCP, @@ -34,7 +35,7 @@ def get_if(): class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ _IPOption_HDR, FieldLenField( "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 diff --git a/exercises/basic_tunnel/myTunnel_header.py b/exercises/basic_tunnel/myTunnel_header.py index 0eecd13a..01e9b7b4 100644 --- a/exercises/basic_tunnel/myTunnel_header.py +++ b/exercises/basic_tunnel/myTunnel_header.py @@ -3,6 +3,8 @@ # SPDX-License-Identifier: GPL-2.0-only +from typing import Any, ClassVar + from scapy.all import IP, Ether, Packet, ShortField, bind_layers TYPE_MYTUNNEL = 0x1212 @@ -11,7 +13,7 @@ class MyTunnel(Packet): name = "MyTunnel" - fields_desc = [ShortField("pid", 0), ShortField("dst_id", 0)] + fields_desc: ClassVar[list[Any]] = [ShortField("pid", 0), ShortField("dst_id", 0)] def mysummary(self): return self.sprintf("pid=%pid%, dst_id=%dst_id%") diff --git a/exercises/basic_tunnel/ptf/basic_tunnel.py b/exercises/basic_tunnel/ptf/basic_tunnel.py index eb1fbb8f..48bfb235 100644 --- a/exercises/basic_tunnel/ptf/basic_tunnel.py +++ b/exercises/basic_tunnel/ptf/basic_tunnel.py @@ -5,6 +5,7 @@ import logging import os import sys +from typing import Any, ClassVar import ptf import ptf.testutils as tu @@ -18,7 +19,7 @@ class MyTunnel(Packet): name = "MyTunnel" - fields_desc = [ShortField("proto_id", TYPE_IPV4), ShortField("dst_id", 0)] + fields_desc: ClassVar[list[Any]] = [ShortField("proto_id", TYPE_IPV4), ShortField("dst_id", 0)] bind_layers(Ether, MyTunnel, type=TYPE_MYTUNNEL) @@ -48,7 +49,7 @@ def setUp(self): self.dataplane = ptf.dataplane_instance self.dataplane.flush() - logging.debug("BasicTunnelTest.setUp()") + logger.debug("BasicTunnelTest.setUp()") # Get test parameters grpc_addr = tu.test_param_get("grpcaddr") or "localhost:9559" @@ -75,7 +76,7 @@ def setUp(self): ) def tearDown(self): - logging.debug("BasicTunnelTest.tearDown()") + logger.debug("BasicTunnelTest.tearDown()") ShutdownAllSwitchConnections() ###################################################################### diff --git a/exercises/calc/calc.py b/exercises/calc/calc.py index cfde9574..1b8193a2 100644 --- a/exercises/calc/calc.py +++ b/exercises/calc/calc.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-only import re +from typing import Any, ClassVar from scapy.all import ( Ether, @@ -19,7 +20,7 @@ class P4calc(Packet): name = "P4calc" # Define fields for the P4calc packet - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ StrFixedLenField("P", "P", length=1), StrFixedLenField("Four", "4", length=1), XByteField("version", 0x01), diff --git a/exercises/flowcache/mycontroller.py b/exercises/flowcache/mycontroller.py index 5df082dd..b0d4d39b 100755 --- a/exercises/flowcache/mycontroller.py +++ b/exercises/flowcache/mycontroller.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-only import argparse import asyncio +import datetime import ipaddress import os import pprint @@ -12,7 +13,6 @@ import time import traceback from collections import Counter -from datetime import datetime, timedelta import grpc from scapy.all import IP, Ether @@ -245,7 +245,7 @@ def deleteFlowRule(sw, table_entry): def addNotification(sw_name, flow_rule): # Add notification to notification DB notification = { - "timestamp": datetime.now(), + "timestamp": datetime.datetime.now(datetime.timezone.utc), "flow_rule": flow_rule, } notif_db[sw_name].append(notification) @@ -265,7 +265,7 @@ def checkFlowRule(sw_name, flow_rule): def isExpired(timestamp, timeout): - return datetime.now() - timestamp > timedelta(seconds=timeout) + return datetime.datetime.now(datetime.timezone.utc) - timestamp > datetime.timedelta(seconds=timeout) def cleanExpiredNotifiction(sw_name, timeout=5): @@ -505,7 +505,7 @@ def printGrpcError(e): status_code = e.code() print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("f[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") + print(f"[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") async def main(p4info_file_path, bmv2_file_path): diff --git a/exercises/flowcache/solution/mycontroller.py b/exercises/flowcache/solution/mycontroller.py index 9d024334..f98e6ddd 100755 --- a/exercises/flowcache/solution/mycontroller.py +++ b/exercises/flowcache/solution/mycontroller.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-only import argparse import asyncio +import datetime import ipaddress import os import pprint @@ -12,7 +13,6 @@ import time import traceback from collections import Counter -from datetime import datetime, timedelta import grpc from scapy.all import IP, Ether @@ -246,7 +246,7 @@ def deleteFlowRule(sw, table_entry): def addNotification(sw_name, flow_rule): # Add notification to notification DB notification = { - "timestamp": datetime.now(), + "timestamp": datetime.datetime.now(datetime.timezone.utc), "flow_rule": flow_rule, } notif_db[sw_name].append(notification) @@ -266,7 +266,7 @@ def checkFlowRule(sw_name, flow_rule): def isExpired(timestamp, timeout): - return datetime.now() - timestamp > timedelta(seconds=timeout) + return datetime.datetime.now(datetime.timezone.utc) - timestamp > datetime.timedelta(seconds=timeout) def cleanExpiredNotifiction(sw_name, timeout=5): @@ -506,7 +506,7 @@ def printGrpcError(e): status_code = e.code() print(f"({status_code.name})", end=" ") traceback = sys.exc_info()[2] - print("f[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") + print(f"[{traceback.tb_frame.f_code.co_filename}:{traceback.tb_lineno}]") async def main(p4info_file_path, bmv2_file_path): diff --git a/exercises/link_monitor/probe_hdrs.py b/exercises/link_monitor/probe_hdrs.py index 9d74f529..68ce1b34 100644 --- a/exercises/link_monitor/probe_hdrs.py +++ b/exercises/link_monitor/probe_hdrs.py @@ -1,17 +1,19 @@ # SPDX-FileCopyrightText: 2019 Stephen Ibanez # # SPDX-License-Identifier: GPL-2.0-only +from typing import Any, ClassVar + from scapy.all import BitField, ByteField, Ether, IntField, Packet, bind_layers TYPE_PROBE = 0x812 class Probe(Packet): - fields_desc = [ByteField("hop_cnt", 0)] + fields_desc: ClassVar[list[Any]] = [ByteField("hop_cnt", 0)] class ProbeData(Packet): - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ BitField("bos", 0, 1), BitField("swid", 0, 7), ByteField("port", 0), @@ -22,7 +24,7 @@ class ProbeData(Packet): class ProbeFwd(Packet): - fields_desc = [ByteField("egress_spec", 0)] + fields_desc: ClassVar[list[Any]] = [ByteField("egress_spec", 0)] bind_layers(Ether, Probe, type=TYPE_PROBE) diff --git a/exercises/load_balance/receive.py b/exercises/load_balance/receive.py index 9ba032b6..ad752ad6 100755 --- a/exercises/load_balance/receive.py +++ b/exercises/load_balance/receive.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-only import os import sys +from typing import Any, ClassVar from scapy.all import ( FieldLenField, @@ -33,7 +34,7 @@ def get_if(): class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ _IPOption_HDR, FieldLenField( "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 diff --git a/exercises/mri/receive.py b/exercises/mri/receive.py index d722d307..a5d4d7f8 100755 --- a/exercises/mri/receive.py +++ b/exercises/mri/receive.py @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-only import sys +from typing import Any, ClassVar from scapy.all import ( FieldLenField, @@ -31,7 +32,7 @@ def get_if(): class SwitchTrace(Packet): - fields_desc = [IntField("swid", 0), IntField("qdepth", 0)] + fields_desc: ClassVar[list[Any]] = [IntField("swid", 0), IntField("qdepth", 0)] def extract_padding(self, p): return "", p @@ -40,7 +41,7 @@ def extract_padding(self, p): class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ _IPOption_HDR, FieldLenField( "length", diff --git a/exercises/mri/send.py b/exercises/mri/send.py index 8a201dc0..f42502f7 100755 --- a/exercises/mri/send.py +++ b/exercises/mri/send.py @@ -7,6 +7,7 @@ import socket import sys from time import sleep +from typing import Any, ClassVar from scapy.all import ( IP, @@ -38,7 +39,7 @@ def get_if(): class SwitchTrace(Packet): - fields_desc = [IntField("swid", 0), IntField("qdepth", 0)] + fields_desc: ClassVar[list[Any]] = [IntField("swid", 0), IntField("qdepth", 0)] def extract_padding(self, p): return "", p @@ -47,7 +48,7 @@ def extract_padding(self, p): class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ _IPOption_HDR, FieldLenField( "length", diff --git a/exercises/p4runtime/mycontroller.py b/exercises/p4runtime/mycontroller.py index 5d3bb45c..2b4c238b 100755 --- a/exercises/p4runtime/mycontroller.py +++ b/exercises/p4runtime/mycontroller.py @@ -125,8 +125,8 @@ def printCounter(p4info_helper, sw, counter_name, index): for entity in response.entities: counter = entity.counter_entry print(f"{sw.name} {counter_name} {index}:" - " {counter.data.packet_count} packets" - " ({counter.data.byte_count} bytes)") + f" {counter.data.packet_count} packets" + f" ({counter.data.byte_count} bytes)") def main(p4info_file_path, bmv2_file_path): diff --git a/exercises/p4runtime/solution/mycontroller.py b/exercises/p4runtime/solution/mycontroller.py index 9d7e4a41..92b049f3 100755 --- a/exercises/p4runtime/solution/mycontroller.py +++ b/exercises/p4runtime/solution/mycontroller.py @@ -146,8 +146,8 @@ def printCounter(p4info_helper, sw, counter_name, index): for entity in response.entities: counter = entity.counter_entry print(f"{sw.name} {counter_name} {index}:" - " {counter.data.packet_count} packets" - " ({counter.data.byte_count} bytes)") + f" {counter.data.packet_count} packets" + f" ({counter.data.byte_count} bytes)") def printGrpcError(e): diff --git a/exercises/source_routing/receive.py b/exercises/source_routing/receive.py index 2f32ed95..ad1ea5b8 100755 --- a/exercises/source_routing/receive.py +++ b/exercises/source_routing/receive.py @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-only import sys +from typing import Any, ClassVar from scapy.all import ( Ether, @@ -39,7 +40,7 @@ def get_if(): class IPOption_MRI(IPOption): name = "MRI" option = 31 - fields_desc = [ + fields_desc: ClassVar[list[Any]] = [ _IPOption_HDR, FieldLenField( "length", None, fmt="B", length_of="swids", adjust=lambda pkt, x: x + 4 @@ -59,11 +60,11 @@ def handle_pkt(pkt): class SourceRoute(Packet): - fields_desc = [BitField("bos", 0, 1), BitField("port", 0, 15)] + fields_desc: ClassVar[list[Any]] = [BitField("bos", 0, 1), BitField("port", 0, 15)] class SourceRoutingTail(Packet): - fields_desc = [XShortField("etherType", 0x800)] + fields_desc: ClassVar[list[Any]] = [XShortField("etherType", 0x800)] bind_layers(Ether, SourceRoute, type=0x1234) diff --git a/exercises/source_routing/send.py b/exercises/source_routing/send.py index e4608b9e..66bc5ccc 100755 --- a/exercises/source_routing/send.py +++ b/exercises/source_routing/send.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-only import socket import sys +from typing import Any, ClassVar from scapy.all import ( IP, @@ -32,7 +33,7 @@ def get_if(): class SourceRoute(Packet): - fields_desc = [BitField("bos", 0, 1), BitField("port", 0, 15)] + fields_desc: ClassVar[list[Any]] = [BitField("bos", 0, 1), BitField("port", 0, 15)] bind_layers(Ether, SourceRoute, type=0x1234)