def __init__(self, grpc_addr, device_id, device, election_id, role_id, config_path, p4info_path, ctx_json): self.grpc_addr = grpc_addr if self.grpc_addr is None: self.grpc_addr = 'localhost:50051' self.device_id = int(device_id) if self.device_id is None: print("Device ID is not set") self.device = device self.config_path = config_path self.p4info_path = p4info_path self.ctx_json_path = ctx_json self.channel = grpc.insecure_channel(self.grpc_addr) self.stub = p4runtime_pb2.P4RuntimeStub(self.channel) print "Importing p4info proto from", p4info_path self.p4info = p4info_pb2.P4Info() with open(p4info_path, "rb") as fin: google.protobuf.text_format.Merge(fin.read(), self.p4info) self.p4info_helper = helper.P4InfoHelper(p4info_path) self.import_p4info_names() # used to store write requests sent to the P4Runtime server, useful for # autocleanup of tests (see definition of autocleanup decorator below) self.reqs = [] self.election_id = election_id self.role_id = role_id self.set_up_stream()
def ConfigureNetwork(p4info_file="build/data_plane.p4info", bmv2_json="build/data_plane.json"): p4info_helper = helper.P4InfoHelper(p4info_file) threads = [] print "Connecting to P4Runtime server on s1..." sw1 = bmv2.Bmv2SwitchConnection('s1', "127.0.0.1:50051", 0) sw1.MasterArbitrationUpdate() sw1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json) t = threading.Thread(target=RunControlPlane, args=(sw1, 1, p4info_helper)) t.start() threads.append(t) print "Connecting to P4Runtime server on s2..." sw2 = bmv2.Bmv2SwitchConnection('s2', "127.0.0.1:50052", 1) sw2.MasterArbitrationUpdate() sw2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json) t = threading.Thread(target=RunControlPlane, args=(sw2, 2, p4info_helper)) t.start() threads.append(t) print "Connecting to P4Runtime server on s3..." sw3 = bmv2.Bmv2SwitchConnection('s3', "127.0.0.1:50053", 2) sw3.MasterArbitrationUpdate() sw3.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json) t = threading.Thread(target=RunControlPlane, args=(sw3, 3, p4info_helper)) t.start() threads.append(t) for t in threads: t.join()
def ConfigureNetwork(p4info_file="build/data_plane.p4info", bmv2_json="build/data_plane.json", topology_json="topology2.json"): p4info_helper = helper.P4InfoHelper(p4info_file) with open(topology_json, 'r') as f: routers = json.load(f, object_pairs_hook=OrderedDict)['routers'] threads = [] port = 50051 id_num = 0 for name, config in routers.items(): config = byteify(config) print "Connecting to P4Runtime server on {}...".format(name) r = bmv2.Bmv2SwitchConnection(name, "127.0.0.1:" + str(port), id_num) r.MasterArbitrationUpdate() r.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path=bmv2_json) t = threading.Thread(target=RunControlPlane, args=(r, config, p4info_helper)) t.start() threads.append(t) port += 1 id_num += 1 for t in threads: t.join()
def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath): sw_conf = json_load_byteified(sw_conf_file) try: check_switch_conf(sw_conf=sw_conf, workdir=workdir) except ConfException as e: 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']) p4info_helper = helper.P4InfoHelper(p4info_fpath) 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) else: raise Exception("Don't know how to connect to target %s" % target) try: 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']) 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'] info("Inserting %d table entries..." % len(table_entries)) for entry in table_entries: info(tableEntryToString(entry)) insertTableEntry(sw, entry, p4info_helper) 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'] info("Inserting %d clone entries..." % len(clone_entries)) for entry in clone_entries: info(cloneEntryToString(entry)) insertCloneGroupEntry(sw, entry, p4info_helper) finally: sw.shutdown()
def install_s1__entry_rule(): # DO NOT USE # ALREADY DONE IN s1-runtime.json # INTENDED AS AN EXAMPLE FOR FUTURE DEVELOPMENT #!/usr/bin/env python2 import grpc import os from time import sleep # 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/p4runtime_lib')) import bmv2 from error_utils import printGrpcError from switch import ShutdownAllSwitchConnections import helper p4info_file_path = "build/Raft.p4.p4info.txt" p4info_helper = helper.P4InfoHelper(p4info_file_path) s1 = bmv2.Bmv2SwitchConnection( name='s1', address='127.0.0.1:50051', device_id=0, proto_dump_file='logs/s1-p4runtime-requests_my_py.txt') s1.MasterArbitrationUpdate() table_entry = p4info_helper.buildTableEntry( table_name="MyIngress.leader", match_fields={ "meta.raft_metadata.role": 2, "hdr.raft.messageType": 2 }, action_name="MyIngress.spread_new_request", action_params={}) table_entry2 = p4info_helper.buildTableEntry( table_name="MyIngress.follower", match_fields={ "meta.raft_metadata.role": 0, "hdr.raft.messageType": 10, "hdr.ipv4.dstAddr": ["10.0.1.254", 32] }, action_name="MyIngress.follower_timeout", action_params={}) s1.WriteTableEntry(table_entry) s1.WriteTableEntry(table_entry2) print("Installed leader table entry rule on {}".format(s1.name))
def program_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath): sw_conf = json_load_byteified(sw_conf_file) try: check_switch_conf(sw_conf=sw_conf, workdir=workdir) except ConfException as e: error("While parsing input runtime configuration: %s" % str(e)) return info('Using P4Info file %s...' % sw_conf['p4info']) # basic.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)) if target == "bmv2": sw = bmv2.Bmv2SwitchConnection( address=addr, device_id=device_id, # Bmv2SwitchConnection extends SwitchConnection proto_dump_file=proto_dump_fpath) # p4info is not set else: raise Exception("Don't know how to connect to target %s" % target) try: sw.MasterArbitrationUpdate() if target == "bmv2": info("Setting pipeline config (%s)..." % sw_conf['bmv2_json']) # p4c compiled json file bmv2_json_fpath = os.path.join(workdir, sw_conf['bmv2_json']) print("bmv2_json_fpath---", bmv2_json_fpath) # set p4info and p4c compiled json file to switch sw.SetForwardingPipelineConfig( p4info=p4info_helper.p4info, # p4info is in bmv2_json_file_path=bmv2_json_fpath ) # set p4c compiled json file else: raise Exception("Should not be here") 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)) insertTableEntry(sw, entry, p4info_helper) finally: #print("continue") sw.shutdown()
def reset_switch(addr, device_id, sw_conf_file, workdir, proto_dump_fpath): sw_conf = json_load_byteified(sw_conf_file) try: check_switch_conf(sw_conf=sw_conf, workdir=workdir) except ConfException as e: error("While parsing input runtime configuration: %s" % str(e)) return p4info_fpath = os.path.join(workdir, sw_conf['p4info']) p4info_helper = helper.P4InfoHelper(p4info_fpath) target = sw_conf['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) try: sw.MasterArbitrationUpdate() if target == "bmv2": 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'] for entry in table_entries: insertTableEntry(sw, entry, p4info_helper) deleteTableEntry(sw, entry, p4info_helper) finally: sw.shutdown()
parser = argparse.ArgumentParser(description='CIS553 P4Runtime Controller') parser.add_argument("-b", '--bmv2-json', help="path to BMv2 switch description (json)", type=str, action="store", default="build/basic.json") parser.add_argument("-c", '--p4info-file', help="path to P4Runtime protobuf description (text)", type=str, action="store", default="build/basic.p4info") args = parser.parse_args() if not os.path.exists(args.p4info_file): parser.error("File %s does not exist!" % args.p4info_file) if not os.path.exists(args.bmv2_json): parser.error("File %s does not exist!" % args.bmv2_json) p4info_helper = helper.P4InfoHelper(args.p4info_file) threads = [] print "Connecting to P4Runtime server on s1..." sw1 = bmv2.Bmv2SwitchConnection('s1', "127.0.0.1:50051", 0) sw1.MasterArbitrationUpdate() sw1.SetForwardingPipelineConfig(p4info = p4info_helper.p4info, bmv2_json_file_path = args.bmv2_json) t = threading.Thread(target=ProgramSwitch, args=(sw1, 1, p4info_helper)) t.start() threads.append(t) print "Connecting to P4Runtime server on s2..." sw2 = bmv2.Bmv2SwitchConnection('s2', "127.0.0.1:50052", 1)
import grpc import os, sys from time import sleep # 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/p4runtime_lib')) import bmv2 from error_utils import printGrpcError from switch import ShutdownAllSwitchConnections import helper p4info_file_path = "build/Raft.p4.p4info.txt" p4info_helper = helper.P4InfoHelper(p4info_file_path) s1 = bmv2.Bmv2SwitchConnection( name='s1', address='127.0.0.1:50051', device_id=0, proto_dump_file='logs/s1-p4runtime-requests_my_py.txt') s1.MasterArbitrationUpdate() table_entry2 = p4info_helper.buildTableEntry( table_name="MyIngress.follower", match_fields={ "meta.raft_metadata.role": 0, "hdr.raft.messageType": 10, "hdr.ipv4.dstAddr": [0x0, 0xa, 1]
def __init__(self, name=None, address="127.0.0.1:50051", device_id=0, p4info_file_path=None, proto_dump_file=None, p4configjson=None, toFlowID=0xffff0000, queueDepth=1000, queueRate=1000, thriftPort=9090, controlPlaneDelay=0): self.name = name self.toFlowID = toFlowID self.address = address self.device_id = device_id self.swNum = device_id + 1 self.p4info = None self.bmv2_json_file_path = NotImplemented # set this at programming time self.p4info_file_path = p4info_file_path self.p4info_helper = p4runtime_lib_helper.P4InfoHelper( p4info_file_path) self.channel = grpc.insecure_channel(self.address) if proto_dump_file is not None: interceptor = GrpcRequestLogger(proto_dump_file) self.channel = grpc.intercept_channel(self.channel, interceptor) self.client_stub = p4runtime_pb2_grpc.P4RuntimeStub(self.channel) self.proto_dump_file = proto_dump_file self.p4configjson = p4configjson ## below is from me: self.stop = False self.defaultRules = [] self.activePorts = {} self.activePorts[1] = 0 self.inactivePorts = [] self.activeServices = {} self.master = False self.stream_out_q = Queue() self.qlogfile = open('logs/' + str(self.device_id) + '.log', "w+") self.qlogfileFirst = False self.numdigest = 0 self.vnfList = [] self.services = [] self.flowRates = {} self.queueDepth = queueDepth self.thriftPort = thriftPort self.queueRate = queueRate self.overloadDetected = False self.migThreads = [] self.controlPlaneDelay = controlPlaneDelay self.overloading = 0 def stream_req_iterator(): while True: p = self.stream_out_q.get() if p is None: break yield p def stream_recv(self): i = 0 #pr = cProfile.Profile() for msg in self.stream: tStart = time() if msg.HasField('packet'): if msg.packet.payload.encode( 'hex')[0:4] == '3341' or msg.packet.payload.encode( 'hex')[0:4] == '3143': #print msg.packet.payload.encode('hex') + ' recevied from ' + str(self.name) if (msg.packet.payload.encode('hex')[20] == str( self.swNum) and (msg.packet.payload.encode('hex')[0:4] == '3341')): # this packet is a sync thing received from a migration source: #print msg.packet.payload.encode('hex') migThread = threading.Thread(target=self.doCPSYnc, args=(msg, )) migThread.start() else: migThread = threading.Thread( target=self.processMigPkt, args=(msg, )) migThread.start() #print "processing migpacket took %f seconds"% float(time() - tStart) else: pkt = controlPacket.controlPacket(msg, self.name, ts=float(tStart)) # # break if no valid ethtype can be derived if hasattr(pkt.eth, 'type') and pkt.eth != None: discList = [0xffff, 0xffee, 0xeffe] ignoreList = [0x86dd, 0x0806] if pkt.eth.type in discList: thread = threading.Thread( target=self.processDiscPacket, args=(pkt, )) thread.start() continue elif hex(pkt.eth.type ) not in ignoreList and pkt.ip != None: thread = threading.Thread( target=self.processpacketIn, args=(pkt, )) thread.start() elif msg.HasField('arbitration'): print('arbitration packet recieved!') if not msg.arbitration.status.message == "Is master": print('Error setting controller as master for device') else: self.master = True elif msg.HasField('digest'): # Digest message received. Send digestACK back: self.digestMessageACK(msg.digest.digest_id, msg.digest.list_id) self.numdigest += 1 digFields = self.p4info_helper.get_digest_fields_by_id( msg.digest.digest_id) # convert message to json format: dictMsg = google.protobuf.json_format.MessageToDict(msg) # read information to a dictionary: infoDict = {} printVals = [] printHdrs = [] enq_qdepth = None deq_qdepth = None deq_timedelta = None threshold = None timestamp = None flowID = None numPackets = None enq_timestamp = None for x in range(0, len(digFields)): for y in digFields[x]: infoDict[y] = {} infoDict[y]['value'] = int( bytes( base64.decodestring( dictMsg['digest']['data'][0]['struct'] ['members'][x]['bitstring'])).encode( 'hex'), 16) infoDict[y]['bitlength'] = digFields[x][y] if self.qlogfileFirst == False: printHdrs.append(str(y)) printVals.append(infoDict[y]['value']) if False == True: print '%s, (%i bit): %i' % ( y, infoDict[y]['bitlength'], infoDict[y]['value']) # save values if str(y) == str('enq_qdepth'): enq_qdepth = infoDict[y]['value'] if str(y) == str('deq_qdepth'): deq_qdepth = infoDict[y]['value'] if str(y) == str('deq_timedelta'): deq_timedelta = float( infoDict[y]['value']) / float( 1000000) # convert to ms if str(y) == str('threshold'): threshold = infoDict[y]['value'] if str(y) == str('timestamp'): timestamp = infoDict[y]['value'] if str(y) == str('flowID'): flowID = infoDict[y]['value'] if str(y) == str('numPackets'): numPackets = infoDict[y]['value'] if str(y) == str('enq_timestamp'): enq_timestamp = infoDict[y]['value'] if enq_qdepth != None and deq_qdepth != None and deq_timedelta != None and enq_timestamp != None: # print 'SWITCH OVERLOADING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' # print "enq_qdepth %s" % enq_qdepth # print "deq_qdepth %s" % deq_qdepth # print "deq_timedelta %s" % deq_timedelta # figure out what to do # create a thread that runs the checks for migration and initializes the migration. # runMigration = False # if self.overloadDetected == False: # runMigration = True # # try: # # if (time() - float(self.overloadDetected)) > float(10): # # runMigration = True # # except: # # pass t = threading.Thread(target=pktMon.addpktDigestReading, args=(self.name, enq_qdepth, deq_qdepth, deq_timedelta, enq_timestamp)) t.start() #print 'enqdepth: %i, deqdepth:%i' % (enq_qdepth, deq_qdepth) if (enq_qdepth < deq_qdepth) and (deq_qdepth > 20): #print "enqQdepth: %f deqQdepth: %f" %(float(enq_qdepth), float(deq_qdepth)) # NO SCALING IF CONTINUE BELOW HERE! #continue if self.overloadDetected == False: # and self.overloading > 5: self.overloadDetected = tStart migrateThread = threading.Thread( target=flowHandler.migrationHandler, args=(self, enq_timestamp)) migrateThread.start() print('Switch %s overloading!!!!' % self.name) # Start the procedure to redirect the load to an other switch (if possible). # if no solution exists, a new hardware switch should be added. # This can be a problem.... elif threshold != None and timestamp != None and flowID != None and numPackets != None: # this is a packet for rates.... t = threading.Thread(target=flowMonitor.addReading, args=(flowID, timestamp, numPackets, self)) t.start() #flowMonitor.addReading(flowID, timestamp, numPackets, self) #print "%i/%i at timestamp %i for flowID %s from switch %s" % (numPackets, threshold, timestamp, flowID, self.name) else: print ' begin hellup' if enq_qdepth == None: print 'enq_qdepth is none' if deq_qdepth == None: print 'deq_qdepth is none' if deq_timedelta == None: print 'deq_timedelta is none' print ' eind hellup' # # writ things to file # if self.qlogfileFirst == False: # self.qlogfile.write(str(printHdrs) + '\n') # self.qlogfileFirst = True # self.qlogfile.write(str(printVals) + '\n') else: print('unknown packet received') # has completed successfully tElapsed = time() - tStart #print("Elapsed time for packetin/out: %.20f ms" %(tElapsed * 1000)) self.stream = self.client_stub.StreamChannel(stream_req_iterator()) self.stream_recv_thread = threading.Thread(target=stream_recv, args=(self, )) self.stream_recv_thread.start() # send masterarbitrationupdate: req = p4runtime_pb2.StreamMessageRequest() arbitration = req.arbitration arbitration.device_id = self.device_id arbitration.election_id.high = 0 arbitration.election_id.low = 1 self.stream_out_q.put(req) switchFinder.addSwitch(self)