Updates for p4runtime example (#71)

* Created p4runtime exercise directory with draft P4 program

* Updating VM

- Adding p4 to vboxsf group for VirtualBox Shared Folders
- Adding gRPC Python package for p4 runtime
- Setting up VM to use 2 CPUs

* Updating .gitignore for PyCharms and Mac OS

* Adding P4RuntimeSwitch type and support in run_exercises

If the grpc switch target is used, we will instantiate a P4RuntimeSwitch.
Ideally, this will get merged with BMv2's P4Switch and can be removed

* Adding p4 runtime and p4info browser libraries

Also, adding a Makefile for this project
This commit is contained in:
Brian O'Connor 2017-11-03 10:52:04 -07:00 committed by Robert Soule
parent c9151e767a
commit aa4298859f
12 changed files with 716 additions and 18 deletions

7
.gitignore vendored
View File

@ -1,5 +1,6 @@
# Python byte code # Python byte code
*.pyc *.pyc
*.pyo
# Emacs # Emacs
*~ *~
@ -18,3 +19,9 @@ build*/
# Vagrant # Vagrant
.vagrant/ .vagrant/
# Mac OS
*.DS_Store
# IntelliJ / PyCharm
.idea/

View File

@ -0,0 +1,10 @@
include ../../utils/Makefile
# Override build method to use simple_switch_grpc target
run: build
sudo python $(RUN_SCRIPT) -t $(TOPO) -b simple_switch_grpc
# Override p4c step to also produce p4info file
P4INFO_ARGS = --p4runtime-file $(basename $@).p4info --p4runtime-format text
$(BUILD_DIR)/%.json: %.p4
$(P4C) --p4v 16 $(P4INFO_ARGS) -o $@ $<

View File

@ -0,0 +1,236 @@
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
const bit<16> TYPE_MYTUNNEL = 0x1212;
const bit<16> TYPE_IPV4 = 0x800;
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
typedef bit<9> egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}
header myTunnel_t {
bit<16> proto_id;
bit<16> dst_id;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}
struct metadata {
/* empty */
}
struct headers {
ethernet_t ethernet;
myTunnel_t myTunnel;
ipv4_t ipv4;
}
/*************************************************************************
*********************** P A R S E R ***********************************
*************************************************************************/
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start {
transition parse_ethernet;
}
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
TYPE_MYTUNNEL: parse_myTunnel;
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_myTunnel {
packet.extract(hdr.myTunnel);
transition select(hdr.myTunnel.proto_id) {
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition accept;
}
}
/*************************************************************************
************ C H E C K S U M V E R I F I C A T I O N *************
*************************************************************************/
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
/*************************************************************************
************** I N G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action drop() {
mark_to_drop();
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
standard_metadata.egress_spec = port;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}
action myTunnel_ingress(bit<16> dst_id) {
hdr.myTunnel.setValid();
hdr.myTunnel.dst_id = dst_id;
hdr.myTunnel.proto_id = hdr.ethernet.etherType;
hdr.ethernet.etherType = TYPE_MYTUNNEL;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
ipv4_forward;
myTunnel_ingress;
drop;
NoAction;
}
size = 1024;
default_action = NoAction();
}
direct_counter(CounterType.packets_and_bytes) tunnelCount;
action myTunnel_forward(egressSpec_t port) {
standard_metadata.egress_spec = port;
tunnelCount.count();
}
action myTunnel_egress(egressSpec_t port) {
standard_metadata.egress_spec = port;
hdr.myTunnel.setInvalid();
tunnelCount.count();
}
table myTunnel_exact {
key = {
hdr.myTunnel.dst_id: exact;
}
actions = {
myTunnel_forward;
myTunnel_egress;
drop;
}
size = 1024;
counters = tunnelCount;
default_action = drop();
}
apply {
if (hdr.ipv4.isValid() && !hdr.myTunnel.isValid()) {
// Process only non-tunneled IPv4 packets.
ipv4_lpm.apply();
}
if (hdr.myTunnel.isValid()) {
// Process all tunneled packets.
myTunnel_exact.apply();
}
}
}
/*************************************************************************
**************** E G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply { }
}
/*************************************************************************
************* C H E C K S U M C O M P U T A T I O N **************
*************************************************************************/
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
apply {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.diffserv,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.myTunnel);
packet.emit(hdr.ipv4);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

View File

@ -0,0 +1,135 @@
# 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 re
import google.protobuf.text_format
from p4 import p4runtime_pb2
from p4.config import p4info_pb2
class P4InfoBrowser(object):
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)
self.p4info = p4info
def get(self, entity_type, name=None, id=None):
if name is not None and id is not None:
raise AssertionError("name or id must be None")
for o in getattr(self.p4info, entity_type):
pre = o.preamble
if 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))
else:
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
def get_name(self, entity_type, id):
return self.get(entity_type, id=id).preamble.name
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_table_id() or get_action_id()
m = re.search("^get_(\w+)_id$", attr)
if m:
primitive = m.group(1)
return lambda name: self.get_id(primitive, name)
# Synthesize convenience functions for id to name lookups
m = re.search("^get_(\w+)_name$", attr)
if m:
primitive = m.group(1)
return lambda id: self.get_name(primitive, id)
raise AttributeError("%r object has no attribute %r" % (self.__class__, attr))
# TODO remove
def get_table_entry(self, table_name):
t = self.get(table_name, "table")
entry = p4runtime_pb2.TableEntry()
entry.table_id = t.preamble.id
entry
pass
def get_match_field(self, table_name, match_field_name):
for t in self.p4info.tables:
pre = t.preamble
if pre.name == table_name:
for mf in t.match_fields:
if mf.name == match_field_name:
return mf
def get_match_field_id(self, table_name, match_field_name):
return self.get_match_field(table_name,match_field_name).id
def get_match_field_pb(self, table_name, match_field_name, value):
p4info_match = self.get_match_field(table_name, match_field_name)
bw = p4info_match.bitwidth
p4runtime_match = p4runtime_pb2.FieldMatch()
p4runtime_match.field_id = p4info_match.id
# TODO switch on match type and map the value into the appropriate message type
match_type = p4info_pb2._MATCHFIELD_MATCHTYPE.values_by_number[
p4info_match.match_type].name
if match_type == 'EXACT':
exact = p4runtime_match.exact
exact.value = value
elif match_type == 'LPM':
lpm = p4runtime_match.lpm
lpm.value = value[0]
lpm.prefix_len = value[1]
# TODO finish cases and validate types and bitwidth
# VALID = 1;
# EXACT = 2;
# LPM = 3;
# TERNARY = 4;
# RANGE = 5;
# and raise exception
return p4runtime_match
def get_action_param(self, action_name, param_name):
for a in self.p4info.actions:
pre = a.preamble
if pre.name == action_name:
for p in a.params:
if p.name == param_name:
return p
raise AttributeError("%r has no attribute %r" % (action_name, param_name))
def get_action_param_id(self, action_name, param_name):
return self.get_action_param(action_name, param_name).id
def get_action_param_pb(self, action_name, param_name, value):
p4info_param = self.get_action_param(action_name, param_name)
#bw = p4info_param.bitwidth
p4runtime_param = p4runtime_pb2.Action.Param()
p4runtime_param.param_id = p4info_param.id
p4runtime_param.value = value # TODO make sure it's the correct bitwidth
return p4runtime_param

View File

@ -0,0 +1,31 @@
# 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.
#
from switch import SwitchConnection
from p4.tmp import p4config_pb2
def buildDeviceConfig(bmv2_json_file_path=None):
"Builds the device config for BMv2"
device_config = p4config_pb2.P4DeviceConfig()
device_config.reassign = True
# set device_config.extra to default instance
with open(bmv2_json_file_path) as f:
device_config.device_data = f.read()
return device_config
class Bmv2SwitchConnection(SwitchConnection):
def buildDeviceConfig(self, **kwargs):
return buildDeviceConfig(**kwargs)

View File

@ -0,0 +1,130 @@
# 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.
#
from abc import abstractmethod
import grpc
from p4 import p4runtime_pb2
from p4.tmp import p4config_pb2
from p4info import p4browser
def buildSetPipelineRequest(p4info, device_config, device_id):
request = p4runtime_pb2.SetForwardingPipelineConfigRequest()
config = request.configs.add()
config.device_id = device_id
config.p4info.CopyFrom(p4info)
config.p4_device_config = device_config.SerializeToString()
request.action = p4runtime_pb2.SetForwardingPipelineConfigRequest.VERIFY_AND_COMMIT
return request
def buildTableEntry(p4info_browser,
table_name,
match_fields={},
action_name=None,
action_params={}):
table_entry = p4runtime_pb2.TableEntry()
table_entry.table_id = p4info_browser.get_tables_id(table_name)
if match_fields:
table_entry.match.extend([
p4info_browser.get_match_field_pb(table_name, match_field_name, value)
for match_field_name, value in match_fields.iteritems()
])
if action_name:
action = table_entry.action.action
action.action_id = p4info_browser.get_actions_id(action_name)
if action_params:
action.params.extend([
p4info_browser.get_action_param_pb(action_name, field_name, value)
for field_name, value in action_params.iteritems()
])
return table_entry
class SwitchConnection(object):
def __init__(self, name, address='127.0.0.1:50051', device_id=0):
self.name = name
self.address = address
self.device_id = device_id
self.p4info = None
self.channel = grpc.insecure_channel(self.address)
# TODO Do want to do a better job managing stub?
self.client_stub = p4runtime_pb2.P4RuntimeStub(self.channel)
@abstractmethod
def buildDeviceConfig(self, **kwargs):
return p4config_pb2.P4DeviceConfig()
def SetForwardingPipelineConfig(self, p4info_file_path, dry_run=False, **kwargs):
p4info_broswer = p4browser.P4InfoBrowser(p4info_file_path)
device_config = self.buildDeviceConfig(**kwargs)
request = buildSetPipelineRequest(p4info_broswer.p4info, device_config, self.device_id)
if dry_run:
print "P4 Runtime SetForwardingPipelineConfig:", request
else:
self.client_stub.SetForwardingPipelineConfig(request)
# Update the local P4 Info reference
self.p4info_broswer = p4info_broswer
def buildTableEntry(self,
table_name,
match_fields={},
action_name=None,
action_params={}):
return buildTableEntry(self.p4info_broswer, table_name, match_fields, action_name, action_params)
def WriteTableEntry(self, table_entry, dry_run=False):
request = p4runtime_pb2.WriteRequest()
request.device_id = self.device_id
update = request.updates.add()
update.type = p4runtime_pb2.Update.INSERT
update.entity.table_entry.CopyFrom(table_entry)
if dry_run:
print "P4 Runtime Write:", request
else:
print self.client_stub.Write(request)
def ReadTableEntries(self, table_name, dry_run=False):
request = p4runtime_pb2.ReadRequest()
request.device_id = self.device_id
entity = request.entities.add()
table_entry = entity.table_entry
table_entry.table_id = self.p4info_broswer.get_tables_id(table_name)
if dry_run:
print "P4 Runtime Read:", request
else:
for response in self.client_stub.Read(request):
yield response
def ReadDirectCounters(self, table_name=None, counter_name=None, table_entry=None, dry_run=False):
request = p4runtime_pb2.ReadRequest()
request.device_id = self.device_id
entity = request.entities.add()
counter_entry = entity.direct_counter_entry
if counter_name:
counter_entry.counter_id = self.p4info_broswer.get_direct_counters_id(counter_name)
else:
counter_entry.counter_id = 0
# TODO we may not need this table entry
if table_name:
table_entry.table_id = self.p4info_broswer.get_tables_id(table_name)
counter_entry.table_entry.CopyFrom(table_entry)
counter_entry.data.packet_count = 0
if dry_run:
print "P4 Runtime Read:", request
else:
for response in self.client_stub.Read(request):
print response

View File

@ -0,0 +1,125 @@
# 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
# this path is needed to import p4_mininet.py from the bmv2 repo
sys.path.append('/home/vagrant/behavioral-model/mininet')
from p4_mininet import P4Switch
class P4RuntimeSwitch(P4Switch):
"BMv2 switch with gRPC support"
next_grpc_port = 50051
def __init__(self, name, sw_path = None, json_path = None,
grpc_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
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):
while True:
if not os.path.exists(os.path.join("/proc", str(pid))):
return False
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0.5)
result = sock.connect_ex(("localhost", self.grpc_port))
if result == 0:
# TODO It seems like sometimes BMv2 can hang if multiple instances start up too quickly;
# this sleep might also do nothing...
sleep(0.5)
return True
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.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))

View File

@ -31,24 +31,38 @@ from mininet.topo import Topo
from mininet.link import TCLink from mininet.link import TCLink
from mininet.cli import CLI from mininet.cli import CLI
from p4runtime_switch import P4RuntimeSwitch
first_thrift_port = 9090
next_thrift_port = first_thrift_port
def configureP4Switch(**switch_args): def configureP4Switch(**switch_args):
""" Helper class that is called by mininet to initialize """ Helper class that is called by mininet to initialize
the virtual P4 switches. The purpose is to ensure each the virtual P4 switches. The purpose is to ensure each
switch's thrift server is using a unique port. switch's thrift server is using a unique port.
""" """
class ConfiguredP4Switch(P4Switch): if "sw_path" in switch_args and 'grpc' in switch_args['sw_path']:
def __init__(self, *opts, **kwargs): # If grpc appears in the BMv2 switch target, we assume will start P4 Runtime
global next_thrift_port class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch):
kwargs.update(switch_args) def __init__(self, *opts, **kwargs):
kwargs['thrift_port'] = next_thrift_port kwargs.update(switch_args)
next_thrift_port += 1 P4RuntimeSwitch.__init__(self, *opts, **kwargs)
P4Switch.__init__(self, *opts, **kwargs)
return ConfiguredP4Switch def describe(self):
print "%s -> gRPC port: %d" % (self.name, self.grpc_port)
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
ConfiguredP4Switch.next_thrift_port += 1
P4Switch.__init__(self, *opts, **kwargs)
def describe(self):
print "%s -> Thrift port: %d" % (self.name, self.thrift_port)
return ConfiguredP4Switch
class ExerciseTopo(Topo): class ExerciseTopo(Topo):
@ -312,6 +326,8 @@ class ExerciseRunner:
been called. been called.
""" """
self.logger("Starting mininet CLI") self.logger("Starting mininet CLI")
for s in self.net.switches:
s.describe()
for h in self.net.hosts: for h in self.net.hosts:
h.describe() h.describe()
# Generate a message that will be printed by the Mininet CLI to make # Generate a message that will be printed by the Mininet CLI to make
@ -324,10 +340,11 @@ class ExerciseRunner:
print('and your initial configuration is loaded. You can interact') print('and your initial configuration is loaded. You can interact')
print('with the network using the mininet CLI below.') print('with the network using the mininet CLI below.')
print('') print('')
print('To inspect or change the switch configuration, connect to') if self.switch_json:
print('its CLI from your host operating system using this command:') print('To inspect or change the switch configuration, connect to')
print(' simple_switch_CLI --thrift-port <switch thrift port>') print('its CLI from your host operating system using this command:')
print('') print(' simple_switch_CLI --thrift-port <switch thrift port>')
print('')
print('To view a switch log, run this command from your host OS:') print('To view a switch log, run this command from your host OS:')
print(' tail -f %s/<switchname>.log' % self.log_dir) print(' tail -f %s/<switchname>.log' % self.log_dir)
print('') print('')
@ -349,13 +366,16 @@ def get_args():
type=str, required=False, default='./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('-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('-p', '--pcap-dir', type=str, required=False, default=default_pcaps)
parser.add_argument('-j', '--switch_json', type=str, required=True) parser.add_argument('-j', '--switch_json', type=str, required=False)
parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable', parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable',
type=str, required=False, default='simple_switch') type=str, required=False, default='simple_switch')
return parser.parse_args() return parser.parse_args()
if __name__ == '__main__': if __name__ == '__main__':
# from mininet.log import setLogLevel
# setLogLevel("info")
args = get_args() args = get_args()
exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir, exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir,
args.switch_json, args.behavioral_exe, args.quiet) args.switch_json, args.behavioral_exe, args.quiet)

View File

@ -5,7 +5,8 @@ Vagrant.configure(2) do |config|
config.vm.box = "bento/ubuntu-16.04" config.vm.box = "bento/ubuntu-16.04"
config.vm.provider "virtualbox" do |vb| config.vm.provider "virtualbox" do |vb|
vb.gui = true vb.gui = true
vb.memory = "2048" vb.memory = 2048
vb.cpus = 2
vb.customize ["modifyvm", :id, "--cableconnected1", "on"] vb.customize ["modifyvm", :id, "--cableconnected1", "on"]
end end
config.vm.synced_folder '.', '/vagrant', disabled: true config.vm.synced_folder '.', '/vagrant', disabled: true

View File

@ -67,6 +67,7 @@ useradd -m -d /home/p4 -s /bin/bash p4
echo "p4:p4" | chpasswd echo "p4:p4" | chpasswd
echo "p4 ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_p4 echo "p4 ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_p4
chmod 440 /etc/sudoers.d/99_p4 chmod 440 /etc/sudoers.d/99_p4
usermod -aG vboxsf p4
cd /usr/share/lubuntu/wallpapers/ cd /usr/share/lubuntu/wallpapers/
cp /home/vagrant/p4-logo.png . cp /home/vagrant/p4-logo.png .

View File

@ -45,6 +45,8 @@ sudo make install
sudo ldconfig sudo ldconfig
unset LDFLAGS unset LDFLAGS
cd .. cd ..
# Install gRPC Python Package
sudo pip install grpcio
# BMv2 deps (needed by PI) # BMv2 deps (needed by PI)
git clone https://github.com/p4lang/behavioral-model.git git clone https://github.com/p4lang/behavioral-model.git