diff --git a/.gitignore b/.gitignore index b1a6ea5..d80c61f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Python byte code *.pyc +*.pyo # Emacs *~ @@ -18,3 +19,9 @@ build*/ # Vagrant .vagrant/ + +# Mac OS +*.DS_Store + +# IntelliJ / PyCharm +.idea/ diff --git a/P4D2_2017_Fall/exercises/p4runtime/Makefile b/P4D2_2017_Fall/exercises/p4runtime/Makefile new file mode 100644 index 0000000..2971ff0 --- /dev/null +++ b/P4D2_2017_Fall/exercises/p4runtime/Makefile @@ -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 $@ $< diff --git a/P4D2_2017_Fall/exercises/p4runtime/advanced_tunnel.p4 b/P4D2_2017_Fall/exercises/p4runtime/advanced_tunnel.p4 new file mode 100644 index 0000000..11e2ae1 --- /dev/null +++ b/P4D2_2017_Fall/exercises/p4runtime/advanced_tunnel.p4 @@ -0,0 +1,236 @@ +/* -*- P4_16 -*- */ +#include +#include + +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; diff --git a/P4D2_2017_Fall/exercises/p4runtime/p4info/p4browser.py b/P4D2_2017_Fall/exercises/p4runtime/p4info/p4browser.py new file mode 100644 index 0000000..1065917 --- /dev/null +++ b/P4D2_2017_Fall/exercises/p4runtime/p4info/p4browser.py @@ -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 \ No newline at end of file diff --git a/P4D2_2017_Fall/exercises/p4runtime/switches/__init__.py b/P4D2_2017_Fall/exercises/p4runtime/switches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/P4D2_2017_Fall/exercises/p4runtime/switches/bmv2.py b/P4D2_2017_Fall/exercises/p4runtime/switches/bmv2.py new file mode 100644 index 0000000..5d9f53c --- /dev/null +++ b/P4D2_2017_Fall/exercises/p4runtime/switches/bmv2.py @@ -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) diff --git a/P4D2_2017_Fall/exercises/p4runtime/switches/switch.py b/P4D2_2017_Fall/exercises/p4runtime/switches/switch.py new file mode 100644 index 0000000..18880ab --- /dev/null +++ b/P4D2_2017_Fall/exercises/p4runtime/switches/switch.py @@ -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 diff --git a/P4D2_2017_Fall/utils/p4runtime_switch.py b/P4D2_2017_Fall/utils/p4runtime_switch.py new file mode 100644 index 0000000..71c589d --- /dev/null +++ b/P4D2_2017_Fall/utils/p4runtime_switch.py @@ -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)) + diff --git a/P4D2_2017_Fall/utils/run_exercise.py b/P4D2_2017_Fall/utils/run_exercise.py index 09611ae..e0ff3db 100755 --- a/P4D2_2017_Fall/utils/run_exercise.py +++ b/P4D2_2017_Fall/utils/run_exercise.py @@ -31,24 +31,38 @@ from mininet.topo import Topo from mininet.link import TCLink from mininet.cli import CLI - - -first_thrift_port = 9090 -next_thrift_port = first_thrift_port +from p4runtime_switch import P4RuntimeSwitch 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. """ - class ConfiguredP4Switch(P4Switch): - def __init__(self, *opts, **kwargs): - global next_thrift_port - kwargs.update(switch_args) - kwargs['thrift_port'] = next_thrift_port - next_thrift_port += 1 - P4Switch.__init__(self, *opts, **kwargs) - return ConfiguredP4Switch + 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 P4 Runtime + class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch): + def __init__(self, *opts, **kwargs): + kwargs.update(switch_args) + P4RuntimeSwitch.__init__(self, *opts, **kwargs) + + 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): @@ -312,6 +326,8 @@ class ExerciseRunner: been called. """ self.logger("Starting mininet CLI") + for s in self.net.switches: + s.describe() for h in self.net.hosts: h.describe() # 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('with the network using the mininet CLI below.') print('') - 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('') + 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('') @@ -349,13 +366,16 @@ def get_args(): 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=True) + 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__': + # 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) diff --git a/P4D2_2017_Fall/vm/Vagrantfile b/P4D2_2017_Fall/vm/Vagrantfile index c8d4ec9..f5bb2f9 100644 --- a/P4D2_2017_Fall/vm/Vagrantfile +++ b/P4D2_2017_Fall/vm/Vagrantfile @@ -5,7 +5,8 @@ Vagrant.configure(2) do |config| config.vm.box = "bento/ubuntu-16.04" config.vm.provider "virtualbox" do |vb| vb.gui = true - vb.memory = "2048" + vb.memory = 2048 + vb.cpus = 2 vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end config.vm.synced_folder '.', '/vagrant', disabled: true diff --git a/P4D2_2017_Fall/vm/root-bootstrap.sh b/P4D2_2017_Fall/vm/root-bootstrap.sh index 21f71a7..398c34b 100755 --- a/P4D2_2017_Fall/vm/root-bootstrap.sh +++ b/P4D2_2017_Fall/vm/root-bootstrap.sh @@ -67,6 +67,7 @@ useradd -m -d /home/p4 -s /bin/bash p4 echo "p4:p4" | chpasswd echo "p4 ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_p4 chmod 440 /etc/sudoers.d/99_p4 +usermod -aG vboxsf p4 cd /usr/share/lubuntu/wallpapers/ cp /home/vagrant/p4-logo.png . diff --git a/P4D2_2017_Fall/vm/user-bootstrap.sh b/P4D2_2017_Fall/vm/user-bootstrap.sh index 63b00ff..3a2b14e 100644 --- a/P4D2_2017_Fall/vm/user-bootstrap.sh +++ b/P4D2_2017_Fall/vm/user-bootstrap.sh @@ -45,6 +45,8 @@ sudo make install sudo ldconfig unset LDFLAGS cd .. +# Install gRPC Python Package +sudo pip install grpcio # BMv2 deps (needed by PI) git clone https://github.com/p4lang/behavioral-model.git