#!/usr/bin/env python __author__ = 'xsited' import os import httplib import json import yaml import base64 import string from urlparse import urlparse from pprint import pprint from os.path import basename toggle = 1 # consider refectoring with request http://docs.python-requests.org/en/latest/index.html class Error: # indicates an HTTP error def __init__(self, url, errcode, errmsg, headers): self.url = url self.errcode = errcode self.errmsg = errmsg self.headers = headers def __repr__(self): return ( "" % (self.url, self.errcode, self.errmsg) ) class RestfulAPI(object): def __init__(self, server): self.server = server self.path = '/wm/staticflowentrypusher/json' self.auth = '' self.port = 8080 def get_server(self): return self.server def set_server(self, server): self.server = server def set_path(self, path): #print path self.path = path # def set_path(self, path, port): # self.path = path # self.port = port def set_port(self, port): #print port self.port = port def use_creds(self): u = self.auth is not None and len(self.auth) > 0 # p = self.password is not None and len(self.password) > 0 return u def credentials(self, username, password): self.auth = base64.encodestring('%s:%s' % (username, password)).replace('\n', '') def get(self, data=''): ret = self.rest_call({}, 'GET') #return json.loads(ret[2]) return ret def set(self, data): #ret = self.rest_call(data, 'PUT') ret = self.rest_call2(data, 'PUT') print ret[0], ret[1] # return ret[0] == 200 return ret def post(self, data): ret = self.rest_call(data, 'POST') #ret = self.rest_call2(data, 'POST') print ret[0], ret[1] return ret def put(self, data): ret = self.rest_call(data, 'PUT') return ret #return ret[0] == 200 def remove(self, objtype, data): ret = self.rest_call(data, 'DELETE') #return ret[0] == 200 return ret def show(self, data): print "" print json.dumps(data, indent=4, sort_keys=True) # print 'DATA:', repr(data) # # print "" # data_string = json.dumps(data) # print 'JSON:', data_string # # print "" # data_string = json.dumps(data) # print 'ENCODED:', data_string # # print "" # decoded = json.loads(data_string) # print 'DECODED:', decoded def rest_call2(self, data, action, content_type='json'): #conn = httplib.HTTPConnection(self.server, self.port) conn = httplib.HTTP(self.server, self.port) conn.putrequest(action, self.path) conn.putheader("Host", self.server+':%s'%self.port) conn.putheader("User-Agent", "Python HTTP Auth") conn.putheader('Content-type', 'application/%s' % content_type) body = json.dumps(data) #conn.putheader("Content-length", "%d" % len(data)) conn.putheader("Content-length", "%d" % len(body)) if self.use_creds(): # print "using creds" conn.putheader("Authorization", "Basic %s" % self.auth) conn.endheaders() conn.send(body) errcode, errmsg, headers = conn.getreply() ret = (errcode, errmsg, headers) #if errcode != 201: # raise Error(self.path, errcode, errmsg, headers) # get response #response = conn.getresponse() #headers = response.read() #ret = (response.status, response.reason, headers) #if response.status != 200: # raise Error(self.path, response.status, response.reason, headers) return ret def rest_call(self, data, action, content_type='json'): # this? putheaders = {'content-type': 'application/json'} getheaders = {'Accept': 'application/json'} body = json.dumps(data) if self.use_creds(): # print "using creds" headers = { 'Content-type': 'application/%s' % content_type, 'Accept': 'application/%s' % content_type, 'Content-length': "%d" % len(body), 'Authorization': "Basic %s" % self.auth, } else: headers = { 'Content-type': 'application/%s' % content_type, 'Accept': 'application/%s' % content_type, 'Content-length': "%d" % len(body), } print self.server+':',self.port, self.path conn = httplib.HTTPConnection(self.server, self.port) conn.request(action, self.path, body, headers) response = conn.getresponse() data = response.read() ret = (response.status, response.reason, data) #print "status %d %s" % (response.status,response.reason) conn.close() return ret class Menu(object): def __init__(self): pass def print_menu(self): print (30 * '-') print (" CABLEFLOW ") print (30 * '-') print ("1. Add CMTS 1 ") print ("2. Add CMTS 2 ") print ("3. Add Flow 1 CMTS 1 ") print ("4. Add Flow 2 CMTS 2 ") print ("5. Remove Flow 1 CMTS 1") print ("6. Remove Flow 2 CMTS 2") print ("7. Remove All Flows ") print ("8. List Flow Stats ") print ("9. List Topology ") print ("10. List Flows ") print ("11. Remove CMTS 1 ") print ("12. Remove CMTS 2 ") print ("q. Quit ") # print (30 * '-') def no_such_action(self): print "Invalid option!" def run(self): #self.print_menu() actions = { "1": tests.flow_add_1, "2": tests.flow_add_2, "3": tests.flow_add_several, "4": tests.flow_remove_1, "5": tests.flow_remove_2, "6": tests.flow_remove_all, "8": tests.flow_list_stats, "9": tests.topology_list, "10":tests.flow_list, "q": tests.exit_app, } while True: self.print_menu() selection = raw_input("Enter selection: ") if "quit" == selection: return toDo = actions.get(selection, self.no_such_action) toDo() class ODLCableflowRestconf(object): def __init__(self, ws): self.ws = ws self.ws.set_port(8181) def topology(self): self.ws.set_path('/restconf/operational/opendaylight-inventory:nodes') content = self.ws.get() j=json.loads(content[2]) ws.show(j) def cableflow_list(self): self.ws.set_path('/config/opendaylight-inventory:nodes/node/%d/flow-node-inventory:table/0/flow') content = self.ws.get() j=json.loads(content[2]) self.ws.show(j) #ws.show(content[2]) return(j) def cableflow_update(self, flow): self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'], flow['id'] ) content = self.ws.post(flow) j=json.loads(content[2]) def cableflow_list(self): self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d') content = self.ws.get() j=json.loads(content[2]) ws.show(j) #ws.show(content[2]) def cableflow_add(self, flow): # PUT http://localhost:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d" self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'], flow['id'] ) self.ws.show(flow) content = self.ws.put(flow) #print content flowadd_response_codes = { 201:"Flow Config processed successfully", 400:"Failed to create Static Flow entry due to invalid flow configuration", 401:"User not authorized to perform this operation", 404:"The Container Name or nodeId is not found", 406:"Cannot operate on Default Container when other Containers are active", 409:"Failed to create Static Flow entry due to Conflicting Name or configuration", 500:"Failed to create Static Flow entry. Failure Reason included in HTTP Error response", 503:"One or more of Controller services are unavailable", } msg=flowadd_response_codes.get(content[0]) print content[0], content[1], msg def cableflow_remove(self, flow): self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'], flow['id'] ) content = self.ws.remove("", flow) flowdelete_reponse_codes = { 204:"Flow Config deleted successfully", 401:"User not authorized to perform this operation", 404:"The Container Name or Node-id or Flow Name passed is not found", 406:"Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response", 500:"Failed to delete Flow config. Failure Reason included in HTTP Error response", 503:"One or more of Controller service is unavailable", } msg=flowdelete_reponse_codes.get(content[0]) print content[0], content[1], msg def cableflow_remove_all(self): allFlowConfigs = self.cableflow_list() flowConfigs = allFlowConfigs['flowConfig'] for fl in flowConfigs: print "Removing ", fl['name'] self.cableflow_remove(fl) def statistics_flows(self): self.ws.set_path('/controller/nb/v2/statistics/default/flow') content = self.ws.get() allFlowStats = json.loads(content[2]) flowStats = allFlowStats['flowStatistics'] # These JSON dumps were handy when trying to parse the responses #print json.dumps(flowStats[0]['flowStat'][1], indent = 2) #print json.dumps(flowStats[4], indent = 2) for fs in flowStats: print "\nSwitch ID : " + fs['node']['id'] print '{0:8} {1:8} {2:5} {3:15}'.format('Count', 'Action', 'Port', 'DestIP') if not 'flowStatistic' in fs.values(): print ' none' continue for aFlow in fs['flowStatistic']: #print "*", aFlow, "*", " ", len(aFlow), " ", not aFlow count = aFlow['packetCount'] actions = aFlow['flow']['actions'] actionType = '' actionPort = '' #print actions if(type(actions) == type(list())): actionType = actions[1]['type'] actionPort = actions[1]['port']['id'] else: actionType = actions['type'] actionPort = actions['port']['id'] dst = aFlow['flow']['match']['matchField'][0]['value'] print '{0:8} {1:8} {2:5} {3:15}'.format(count, actionType, actionPort, dst) def cableflow_remove_all(self): allFlowConfigs = self.cableflow_list() flowConfigs = allFlowConfigs['flowConfig'] for fl in flowConfigs: print "Removing ", fl['name'] self.cableflow_remove(fl) class CableflowTests(object): def __init__(self, odl): self.flows = {} self.odl = odl def cmts_add_1(): print "Add cmts 1 " self.odl.cmts_add(cmts1) def cmts_add_2(): print "Add cmts 2 " self.odl.cmts_add(cmts2) def cmts_remove_1(): print "Add cmts 1 " self.odl.cmts_remove(cmts1) def cmts_remove_2(): print "Add cmts 2 " self.odl.cmts_remove(cmts2) def flow_add_1(): print "Add Flow 1 " self.odl.cableflow_add(flow1) def flow_add_2(): print "Add Flow 2 " self.odl.cableflow_add(flow2) def flow_add_several(): print "Add Flow Several " self.odl.cableflow_add(flow1) self.odl.cableflow_add(flow2) self.odl.cableflow_add(flow3) self.odl.cableflow_add(flow4) self.odl.cableflow_add(flow5) def flow_remove_1(): print "Remove Flow 1 " self.odl.cableflow_remove(flow1) def flow_remove_2(): print "Remove Flow 2 " self.odl.cableflow_remove(flow2) def flow_remove_all(): print "Remove All Flows " self.odl.cableflow_remove_all() def flow_list_stats(): print "List Flow Stats" self.odl.statistics_flows() def topology_list(): print "List Topology " self.odl.topology() def flow_list(): print "List Flows " self.odl.cableflow_list() def flows_read(self, content_type='json'): #print "content_type = %s" % content_type for path, dirs, files in os.walk('.'): for filename in files: if filename.endswith(".%s" % content_type): base_filename = basename(filename) # print base_filename fn = os.path.splitext(os.path.basename(filename))[0] #print filename with open(filename) as fp: data = fp.read() # print(data) # jdata = yaml.load ( data ) # print(jdata) self.flows[fn]=data #equivalent to: self.varname= 'something' if content_type == "xml": pprint (self.flows[fn], width=4) else: #print fn #pprint (self.flows[fn], width=4) json.dumps(json.loads(data), indent=4) def flows_print(self): # print flow dictionary l = self.flows.items() l.sort() #for k,v in self.flows.items(): for k,v in l: print k def exit_app(self): print "Quit " exit(0) if __name__ == "__main__": ws = RestfulAPI('127.0.0.1') ws.credentials('admin', 'admin') odl = ODLCableflowRestconf(ws) tests = CableflowTests(odl) tests.flows_read() tests.flows_print() menu=Menu() menu.run() exit(0)