p4tutorials-turkce/utils/p4runtime_switch.py
Nate Foster dc08948a34
P4 Developer Day 2018 Spring (#159)
* Repository reorganization for 2018 Spring P4 Developer Day.

* Port tutorial exercises to P4Runtime with static controller (#156)

* Switch VM to a minimal Ubuntu 16.04 desktop image

* Add commands to install Protobuf Python bindings to user_bootstrap.sh

* Implement P4Runtime static controller for use in exercises

From the exercise perspective, the main difference is that control plane
rules are now specified using JSON files instead of CLI commands. Such
JSON files define rules that use the same name for tables, keys, etc. as
in the P4Info file.

All P4Runtime requests generated as part of the make run process are
logged in the exercise's “logs” directory, making it easier for students
to see the actual P4Runtime messages sent to the switch.

Only the "basic" exercise has been ported to use P4Runtime.
The "p4runtime" exercise has been updated to work with P4Runtime
protocol changes.

Known issues:
- make run hangs in case of errors when running the P4Runtime controller
    (probably due to gRPC stream channel threads not terminated properly)
- missing support for inserting table entries with default action
    (can specify in P4 program as a workaround)

* Force install protobuf python module

* Fixing Ctrl-C hang by shutdown switches

* Moving gRPC error print to function for readability

Unforuntately, if this gets moved out of the file, the process hangs.
We'll need to figure out how why later.

* Renaming ShutdownAllSwitches -> ShutdownAllSwitchConnections

* Reverting counter index change

* Porting the ECN exercise to use P4 Runtime Static Controller

* updating the README in the ecn exercise to reflect the change in rule files

* Allow set table default action in P4Runtime static controller

* Fixed undefined match string when printing P4Runtime table entry

* Updated basic_tunnel exercise to use P4Runtime controller.

* Changed default action in the basic exercise's ipv4_lpm table to drop

* Porting the MRI exercise to use P4runtime with static controller

* Updating readme to reflect the change of controller for mri

* Update calc exercise for P4Runtime static controller

* Port source_routing to P4 Runtime static controller (#157)

* Port Load Balance to P4 Runtime Static Controller (#158)
2018-06-01 02:54:33 -04:00

133 lines
4.7 KiB
Python

# Copyright 2017-present Barefoot Networks, Inc.
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys, os, tempfile, socket
from time import sleep
from mininet.node import Switch
from mininet.moduledeps import pathCheck
from mininet.log import info, error, debug
from p4_mininet import P4Switch, SWITCH_START_TIMEOUT
from netstat import check_listening_on_port
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,
**kwargs):
Switch.__init__(self, name, **kwargs)
assert (sw_path)
self.sw_path = sw_path
# make sure that the provided sw_path is valid
pathCheck(sw_path)
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")
exit(1)
self.json_path = json_path
else:
self.json_path = None
if grpc_port is not None:
self.grpc_port = grpc_port
else:
self.grpc_port = P4RuntimeSwitch.next_grpc_port
P4RuntimeSwitch.next_grpc_port += 1
if thrift_port is not None:
self.thrift_port = thrift_port
else:
self.thrift_port = P4RuntimeSwitch.next_thrift_port
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)
self.verbose = verbose
logfile = "/tmp/p4s.{}.log".format(self.name)
self.output = open(logfile, 'w')
self.pcap_dump = pcap_dump
self.enable_debugger = enable_debugger
self.log_console = log_console
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)
def check_switch_started(self, pid):
for _ in range(SWITCH_START_TIMEOUT * 2):
if not os.path.exists(os.path.join("/proc", str(pid))):
return False
if check_listening_on_port(self.grpc_port):
return True
sleep(0.5)
def start(self, controllers):
info("Starting P4 switch {}.\n".format(self.name))
args = [self.sw_path]
for port, intf in self.intfs.items():
if not intf.IP():
args.extend(['-i', str(port) + "@" + intf.name])
if self.pcap_dump:
args.append("--pcap")
if self.nanomsg:
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)
else:
args.append("--no-p4")
if self.enable_debugger:
args.append("--debugger")
if self.log_console:
args.append("--log-console")
if 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))
cmd = ' '.join(args)
info(cmd + "\n")
logfile = "/tmp/p4s.{}.log".format(self.name)
pid = None
with tempfile.NamedTemporaryFile() as f:
self.cmd(cmd + ' >' + logfile + ' 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):
error("P4 switch {} did not start correctly.\n".format(self.name))
exit(1)
info("P4 switch {} has been started.\n".format(self.name))