Skip to content
An interactive Python shell for P4Runtime
Python Dockerfile Shell
Branch: master
Clone or download
antoninbas Add APIVersion shell command
To query the P4Runtime API version implemented by the server. The
version string is retrieved through the Capabilities RPC (refer to
P4Runtime specification >= 1.1.0).
Latest commit d193bc1 Feb 14, 2020
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
config_builders Do not spawn new processes in unit tests Aug 10, 2019
p4runtime @ c09cd0b Add APIVersion shell command Feb 15, 2020
p4runtime_sh Add APIVersion shell command Feb 15, 2020
usage Support for MulticastGroupEntry and CloneSessionEntry Jun 11, 2019
.coveragerc Integrate with codecov Aug 8, 2019
.dockerignore Initial commit! Apr 17, 2019
.flake8 Initial commit! Apr 17, 2019
.gitignore Integrate with codecov Aug 8, 2019
.gitmodules Initial commit! Apr 17, 2019
.travis.yml Integrate with codecov Aug 8, 2019
Dockerfile
Dockerfile.dev Integrate with codecov Aug 8, 2019
LICENSE Initial commit! Apr 17, 2019
README.md
docker_entry_point.sh Initial commit! Apr 17, 2019
p4runtime-sh Initial commit! Apr 17, 2019
p4runtime-sh-docker Add wrapper script to run p4runtime-sh docker image Apr 17, 2019
requirements-dev.txt Integrate with codecov Aug 8, 2019
requirements.txt Initial commit! Apr 17, 2019
unittest.cfg Integrate with codecov Aug 8, 2019

README.md

A shell for P4Runtime

Build Status

This is still a work in progress. Feedback is welcome.

p4runtime-sh is an interactive Python shell for P4Runtime based on IPython.

Using the shell

We recommend that you download the Docker image (~200MB) and use it, but you can also build the image directly with:

git clone --recursive https://github.com/p4lang/p4runtime-shell
cd p4runtime-shell
docker build -t p4lang/p4runtime-sh .

Run the shell as follows:

[sudo] docker run -ti p4lang/p4runtime-sh \
  --grpc-addr <server IP>:<server port> \
  --device-id 0 --election-id 0,1

The above command will retrieve the forwarding pipeline configuration from the P4Runtime server. You can also push a forwarding pipeline configuration with the shell (you will need to mount the directory containing the P4Info and binary device config in the docker):

[sudo] docker run -ti -v /tmp/:/tmp/ p4lang/p4runtime-sh \
  --grpc-addr <server IP>:<server port> \
  --device-id 0 --election-id 0,1 --config /tmp/p4info.txt,/tmp/bmv2.json

The above command assumes that the P4Info (p4info.txt) and the binary device config (bmv2.json) are under /tmp/.

To make the process more convenient, we provide a wrapper script, which takes care of running the docker (including mounting the P4Info and binary device config files in the docker if needed):

[sudo] ./p4runtime-sh-docker --grpc-addr <server IP>:<server port> \
  --device-id 0 --election-id 0,1 \
  --config <path to p4info>,<path to binary config>

If you are a Linux user, you can follow this guide to run Docker commands without sudo. You will be able to use p4runtime-sh-docker as a non-privileged user.

If you are using the Docker image to run p4runtime-shell and you are trying to connect to a P4Runtime server running natively on the same system and listening on the localhost interface, you will not be able to connect to the server using --grpc-addr localhost:<port> or --grpc-addr 127.0.0.1:<port>. Instead, you should have your P4Runtime server listen on all interfaces (0.0.0.0) and you will need to use the IP address assigned to the Docker bridge (docker0 by default) or the IP address assigned to the local network management interface (e.g. eth0).

Available commands

tables, actions, action_profiles, counters, direct_counters, meters, direct_meters (named after the P4Info message fields) to query information about P4Info objects.

table_entry, action_profile_member, action_profile_group, counter_entry, direct_counter_entry, meter_entry, direct_meter_entry (named after the P4Runtime Entity fields), along with multicast_group_entry and clone_session_entry, for runtime entity programming.

The Write command can be used to read a WriteRequest message from a file (for now, Protobuf text format only) and send it to a server:

Write <path to file encoding WriteRequest message in text format>

Type the command name followed by ? for information on each command, e.g. table_entry?.

Example usage

Here is some of what you can do when using p4runtime-sh with ONF's fabric.p4.

*** Welcome to the IPython shell for P4Runtime ***
P4Runtime sh >>> tables
FabricEgress.egress_next.egress_vlan
FabricIngress.acl.acl
FabricIngress.filtering.fwd_classifier
FabricIngress.filtering.ingress_port_vlan
FabricIngress.forwarding.bridging
FabricIngress.forwarding.mpls
FabricIngress.forwarding.routing_v4
FabricIngress.next.hashed
FabricIngress.next.multicast
FabricIngress.next.next_vlan
FabricIngress.next.xconnect

P4Runtime sh >>> tables["FabricIngress.forwarding.routing_v4"]
Out[2]:
preamble {
  id: 33562650
  name: "FabricIngress.forwarding.routing_v4"
  alias: "routing_v4"
}
match_fields {
  id: 1
  name: "ipv4_dst"
  bitwidth: 32
  match_type: LPM
}
action_refs {
  id: 16777434 ("FabricIngress.forwarding.set_next_id_routing_v4")
}
action_refs {
  id: 16804187 ("FabricIngress.forwarding.nop_routing_v4")
}
action_refs {
  id: 16819938 ("nop")
  annotations: "@defaultonly"
  scope: DEFAULT_ONLY
}
const_default_action_id: 16819938 ("nop")
direct_resource_ids: 318811107 ("FabricIngress.forwarding.routing_v4_counter")
size: 1024


P4Runtime sh >>> te = table_entry["FabricIngress.forwarding.routing_v4"](action="set_next_id_routing_v4")

P4Runtime sh >>> te?
Signature:   te(**kwargs)
Type:        TableEntry
String form:
table_id: 33562650 ("FabricIngress.forwarding.routing_v4")
action {
  action {
    action_id: 16777434 ("FabricIngress.forwarding.set_next_id_routing_v4")
  }
}
File:        /p4runtime-sh/p4runtime_sh/shell.py
Docstring:
An entry for table 'FabricIngress.forwarding.routing_v4'

Use <self>.info to display the P4Info entry for this table.

To set the match key, use <self>.match['<field name>'] = <expr>.
Type <self>.match? for more details.

To set the action specification <self>.action = <instance of type Action>.
To set the value of action parameters, use <self>.action['<param name>'] = <expr>.
Type <self>.action? for more details.

To set the priority, use <self>.priority = <expr>.

To mark the entry as default, use <self>.is_default = True.

Typical usage to insert a table entry:
t = table_entry['<table_name>'](action='<action_name>')
t.match['<f1>'] = ...
...
t.match['<fN>'] = ...
# OR t.match.set(f1=..., ..., fN=...)
t.action['<p1>'] = ...
...
t.action['<pM>'] = ...
# OR t.action.set(p1=..., ..., pM=...)
t.insert

Typical usage to set the default entry:
t = table_entry['<table_name>'](is_default=True)
t.action['<p1>'] = ...
...
t.action['<pM>'] = ...
# OR t.action.set(p1=..., ..., pM=...)
t.modify

For information about how to read table entries, use <self>.read?

P4Runtime sh >>> te.match?
Type:      MatchKey
File:      /p4runtime-sh/p4runtime_sh/shell.py
Docstring:
Match key fields for table 'FabricIngress.forwarding.routing_v4':

id: 1
name: "ipv4_dst"
bitwidth: 32
match_type: LPM

Set a field value with <self>['<field_name>'] = '...'
  * For exact match: <self>['<f>'] = '<value>'
  * For ternary match: <self>['<f>'] = '<value>&&&<mask>'
  * For LPM match: <self>['<f>'] = '<value>/<mask>'
  * For range match: <self>['<f>'] = '<value>..<mask>'

If it's inconvenient to use the whole field name, you can use a unique suffix.

You may also use <self>.set(<f>='<value>')
        (<f> must not include a '.' in this case, but remember that you can use a unique suffix)

P4Runtime sh >>> te.match["ipv4_dst"] = "10.0.0.0/16"
field_id: 1
lpm {
  value: "\n\000\000\000"
  prefix_len: 16
}


P4Runtime sh >>> te.action?
Type:      Action
File:      /p4runtime-sh/p4runtime_sh/shell.py
Docstring:
Action parameters for action 'set_next_id_routing_v4':

id: 1
name: "next_id"
bitwidth: 32


Set a param value with <self>['<param_name>'] = '<value>'
You may also use <self>.set(<param_name>='<value>')

P4Runtime sh >>> te.action["next_id"] = "10"
param_id: 1
value: "\000\000\000\n"


P4Runtime sh >>> te.insert

P4Runtime sh >>> for te in table_entry["FabricIngress.forwarding.routing_v4"].read():
            ...:     print(te)
            ...:
table_id: 33562650 ("FabricIngress.forwarding.routing_v4")
match {
  field_id: 1 ("ipv4_dst")
  lpm {
    value: "\\x0a\\x00\\x00\\x00"
    prefix_len: 16
  }
}
action {
  action {
    action_id: 16777434 ("FabricIngress.forwarding.set_next_id_routing_v4")
    params {
      param_id: 1 ("next_id")
      value: "\\x00\\x00\\x00\\x0a"
    }
  }
}


P4Runtime sh >>> table_entry["FabricIngress.forwarding.routing_v4"].read(lambda te: te.delete())

P4Runtime sh >>> table_entry["FabricIngress.forwarding.routing_v4"].read(lambda te: print(te))

P4Runtime sh >>>

Using p4runtime-shell in scripts

You can also leverage this project as a convenient P4Runtime wrapper to programmatically program switches using Pyhton scripts:

import p4runtime_sh.shell as sh

# you can omit the config argument if the switch is already configured with the
# correct P4 dataplane.
sh.setup(
    device_id=1,
    grpc_addr='localhost:50051',
    election_id=(0, 1), # (high, low)
    config=sh.FwdPipeConfig('config/p4info.pb.txt', 'config/device_config.bin')
)

# see p4runtime_sh/test.py for more examples
te = sh.TableEntry('<table_name>')(action='<action_name>')
te.match['<name>'] = '<value>'
te.action['<name>'] = '<value>'
te.insert()

# ...

sh.teardown()

Note that at the moment the P4Runtime client object is a global variable, which means that we only support one P4Runtime connection to a single switch.

Target-specific support

P4.org Bmv2

Just use the bmv2 JSON file generated by the compiler as the binary device config.

Barefoot Tofino

We provide a script which can be used to "pack" the Barefoot p4c compiler output (part of the Barefoot SDE) into one binary file, to be used as the binary device config.

./config_builders/tofino.py --ctx-json <path to context JSON> \
  --tofino-bin <path to tofino.bin> -p <program name> -o out.bin

You can then use out.bin when invoking p4runtime-sh-docker:

[sudo] ./p4runtime-sh-docker --grpc-addr <server IP>:<server port> \
  --device-id 0 --election-id 0,1 \
  --config <path to p4info>,out.bin
You can’t perform that action at this time.