fe41c4a501fd49cd0c317a2ed47663a7ad2b2c80
[packetcable.git] / packetcable-client / restconftest.py
1 #!/usr/bin/env python
2
3 __author__ = 'xsited'
4 import os
5 import httplib
6 import json
7 import yaml
8 import base64
9 import string
10 from urlparse import urlparse
11 from pprint import pprint
12 from os.path import basename
13
14 toggle = 1
15
16 # consider refectoring with request http://docs.python-requests.org/en/latest/index.html
17
18 class Error:
19     # indicates an HTTP error
20     def __init__(self, url, errcode, errmsg, headers):
21         self.url = url
22         self.errcode = errcode
23         self.errmsg = errmsg
24         self.headers = headers
25     def __repr__(self):
26         return (
27             "<Error for %s: %s %s>" %
28             (self.url, self.errcode, self.errmsg)
29             )
30
31
32 class RestfulAPI(object):
33     def __init__(self, server):
34         self.server = server
35         self.path = '/wm/staticflowentrypusher/json'
36         self.auth = ''
37         self.port = 8080
38
39     def get_server(self):
40         return self.server
41
42     def set_server(self, server):
43         self.server = server
44
45
46     def set_path(self, path):
47         #print path
48         self.path = path
49
50 #    def set_path(self, path, port):
51 #        self.path = path
52 #        self.port = port
53
54     def set_port(self, port):
55         #print port
56         self.port = port
57
58     def use_creds(self):
59         u = self.auth is not None and len(self.auth) > 0
60 #       p = self.password is not None and len(self.password) > 0
61         return u
62
63     def credentials(self, username, password):
64         self.auth = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
65
66     def get(self, data=''):
67         ret = self.rest_call({}, 'GET')
68         #return json.loads(ret[2])
69         return ret
70
71     def set(self, data):
72         #ret = self.rest_call(data, 'PUT')
73         ret = self.rest_call2(data, 'PUT')
74         print ret[0], ret[1]
75         # return ret[0] == 200
76         return ret
77
78     def post(self, data):
79         ret = self.rest_call(data, 'POST')
80         #ret = self.rest_call2(data, 'POST')
81         print ret[0], ret[1]
82         return ret
83
84     def put(self, data):
85         ret = self.rest_call(data, 'PUT')
86         return ret
87         #return ret[0] == 200
88
89
90     def remove(self, objtype, data):
91         ret = self.rest_call(data, 'DELETE')
92         #return ret[0] == 200
93         return ret
94
95     def show(self, data):
96         print ""
97         print json.dumps(data, indent=4, sort_keys=True)
98 #       print 'DATA:', repr(data)
99 #
100 #       print ""
101 #       data_string = json.dumps(data)
102 #       print 'JSON:', data_string
103 #
104 #       print ""
105 #       data_string = json.dumps(data)
106 #       print 'ENCODED:', data_string
107 #
108 #       print ""
109 #       decoded = json.loads(data_string)
110 #       print 'DECODED:', decoded
111
112
113     def rest_call2(self, data, action, content_type='json'):
114
115         #conn = httplib.HTTPConnection(self.server, self.port)
116         conn = httplib.HTTP(self.server, self.port)
117         conn.putrequest(action, self.path)
118         conn.putheader("Host", self.server+':%s'%self.port)
119         conn.putheader("User-Agent", "Python HTTP Auth")
120         conn.putheader('Content-type', 'application/%s' % content_type)
121         body = json.dumps(data)
122         #conn.putheader("Content-length", "%d" % len(data))
123         conn.putheader("Content-length", "%d" % len(body))
124         if self.use_creds():
125         #    print "using creds"
126             conn.putheader("Authorization", "Basic %s" % self.auth)
127         conn.endheaders()
128         
129         conn.send(body)
130         errcode, errmsg, headers = conn.getreply()
131         ret = (errcode, errmsg, headers)
132
133         #if errcode != 201:
134         #   raise Error(self.path, errcode, errmsg, headers)
135
136         # get response
137         #response = conn.getresponse()
138         #headers = response.read()
139         #ret = (response.status, response.reason, headers)
140         #if response.status != 200:
141         #    raise Error(self.path, response.status, response.reason, headers)
142         return ret
143
144
145     def rest_call(self, data, action, content_type='json'):
146         # this? 
147         putheaders = {'content-type': 'application/json'}
148         getheaders = {'Accept': 'application/json'}
149         body = json.dumps(data)
150         if self.use_creds():
151         #    print "using creds"
152             headers = {
153                 'Content-type': 'application/%s' % content_type,
154                 'Accept': 'application/%s' % content_type,
155                 'Content-length': "%d" % len(body),
156                 'Authorization': "Basic %s" % self.auth,
157             }
158         else:
159             headers = {
160                 'Content-type': 'application/%s' % content_type,
161                 'Accept': 'application/%s' % content_type,
162                 'Content-length': "%d" % len(body),
163             }
164                 
165         print self.server+':',self.port, self.path
166         conn = httplib.HTTPConnection(self.server, self.port)
167         conn.request(action, self.path, body, headers)
168         response = conn.getresponse()
169         data = response.read()
170         ret = (response.status, response.reason, data)
171         #print "status %d %s" % (response.status,response.reason)
172         conn.close()
173         return ret
174
175
176 class Menu(object):
177     def __init__(self):
178         pass
179
180     def print_menu(self):
181         print (30 * '-')
182         print ("   CABLEFLOW          ")
183         print (30 * '-')
184         print ("1.  Add CMTS 1        ")
185         print ("2.  Add CMTS 2        ")
186         print ("3.  Add Flow 1 CMTS 1 ")
187         print ("4.  Add Flow 2 CMTS 2 ")
188         print ("5.  Remove Flow 1 CMTS 1")
189         print ("6.  Remove Flow 2 CMTS 2")
190         print ("7.  Remove All Flows  ")
191         print ("8.  List Flow Stats   ")
192         print ("9.  List Topology     ")
193         print ("10. List Flows        ")
194         print ("11. Remove CMTS 1     ")
195         print ("12. Remove CMTS 2     ")
196         print ("q. Quit               ")
197 #        print (30 * '-')
198
199
200     def no_such_action(self):
201         print "Invalid option!"
202
203     def run(self):
204         #self.print_menu()
205         actions = {
206         "1": tests.flow_add_1, 
207         "2": tests.flow_add_2, 
208         "3": tests.flow_add_several, 
209         "4": tests.flow_remove_1,
210         "5": tests.flow_remove_2,
211         "6": tests.flow_remove_all,
212         "8": tests.flow_list_stats,
213         "9": tests.topology_list,
214         "10":tests.flow_list,
215         "q": tests.exit_app,
216         }
217
218         while True:
219             self.print_menu()
220             selection = raw_input("Enter selection: ")
221             if "quit" == selection:
222                 return
223             toDo = actions.get(selection, self.no_such_action)
224             toDo()
225
226
227
228
229 class ODLCableflowRestconf(object):
230     def __init__(self, ws):
231         self.ws = ws
232         self.ws.set_port(8181)  
233         
234
235     def topology(self):
236         self.ws.set_path('/restconf/operational/opendaylight-inventory:nodes')
237         content = self.ws.get()
238         j=json.loads(content[2])
239         ws.show(j)
240
241     def cableflow_list(self):
242         self.ws.set_path('/config/opendaylight-inventory:nodes/node/%d/flow-node-inventory:table/0/flow')
243         content = self.ws.get()
244         j=json.loads(content[2])
245         self.ws.show(j)
246         #ws.show(content[2])
247         return(j)
248
249     def cableflow_update(self, flow):
250         self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
251         content = self.ws.post(flow)
252         j=json.loads(content[2])
253
254     def cableflow_list(self):
255         self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d')
256         content = self.ws.get()
257         j=json.loads(content[2])
258         ws.show(j)
259         #ws.show(content[2])
260
261     def cableflow_add(self, flow):
262 # PUT http://localhost:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"
263         self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
264         self.ws.show(flow)
265         content = self.ws.put(flow)
266         #print content
267         flowadd_response_codes = {
268         201:"Flow Config processed successfully",
269         400:"Failed to create Static Flow entry due to invalid flow configuration",
270         401:"User not authorized to perform this operation",
271         404:"The Container Name or nodeId is not found",
272         406:"Cannot operate on Default Container when other Containers are active",
273         409:"Failed to create Static Flow entry due to Conflicting Name or configuration",
274         500:"Failed to create Static Flow entry. Failure Reason included in HTTP Error response",
275         503:"One or more of Controller services are unavailable",
276         } 
277         msg=flowadd_response_codes.get(content[0])
278         print content[0], content[1], msg
279
280     def cableflow_remove(self, flow):
281         self.ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
282         content = self.ws.remove("", flow)
283
284         flowdelete_reponse_codes = {
285         204:"Flow Config deleted successfully",
286         401:"User not authorized to perform this operation",
287         404:"The Container Name or Node-id or Flow Name passed is not found",
288         406:"Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response",
289         500:"Failed to delete Flow config. Failure Reason included in HTTP Error response",
290         503:"One or more of Controller service is unavailable",
291         }
292         msg=flowdelete_reponse_codes.get(content[0])
293         print content[0], content[1], msg
294
295     def cableflow_remove_all(self):
296         allFlowConfigs = self.cableflow_list()
297         flowConfigs = allFlowConfigs['flowConfig']
298         for fl in flowConfigs:
299             print "Removing ", fl['name']
300             self.cableflow_remove(fl)
301                 
302
303
304     def statistics_flows(self):
305         self.ws.set_path('/controller/nb/v2/statistics/default/flow')
306         content = self.ws.get()
307         allFlowStats = json.loads(content[2])
308
309         flowStats = allFlowStats['flowStatistics']
310         # These JSON dumps were handy when trying to parse the responses 
311         #print json.dumps(flowStats[0]['flowStat'][1], indent = 2)
312         #print json.dumps(flowStats[4], indent = 2)
313         for fs in flowStats:
314             print "\nSwitch ID : " + fs['node']['id']
315             print '{0:8} {1:8} {2:5} {3:15}'.format('Count', 'Action', 'Port', 'DestIP')
316             if not 'flowStatistic' in fs.values(): 
317                 print '              none'
318                 continue
319             for aFlow in fs['flowStatistic']:
320                 #print "*", aFlow, "*", " ", len(aFlow), " ", not aFlow
321                 count = aFlow['packetCount']
322                 actions = aFlow['flow']['actions'] 
323                 actionType = ''
324                 actionPort = ''
325                 #print actions
326                 if(type(actions) == type(list())):
327                     actionType = actions[1]['type']
328                     actionPort = actions[1]['port']['id']
329                 else:
330                     actionType = actions['type']
331                     actionPort = actions['port']['id']
332                 dst = aFlow['flow']['match']['matchField'][0]['value']
333                 print '{0:8} {1:8} {2:5} {3:15}'.format(count, actionType, actionPort, dst)
334
335
336     def cableflow_remove_all(self):
337         allFlowConfigs = self.cableflow_list()
338         flowConfigs = allFlowConfigs['flowConfig']
339         for fl in flowConfigs:
340             print "Removing ", fl['name']
341             self.cableflow_remove(fl)
342                 
343
344
345 class CableflowTests(object):
346     def __init__(self, odl):
347         self.flows = {}
348         self.odl = odl
349     def cmts_add_1():
350         print "Add cmts 1     "
351         self.odl.cmts_add(cmts1)
352
353     def cmts_add_2():
354         print "Add cmts 2     "
355         self.odl.cmts_add(cmts2)
356
357     def cmts_remove_1():
358         print "Add cmts 1     "
359         self.odl.cmts_remove(cmts1)
360
361     def cmts_remove_2():
362         print "Add cmts 2     "
363         self.odl.cmts_remove(cmts2)
364
365     def flow_add_1():
366         print "Add Flow 1     "
367         self.odl.cableflow_add(flow1)
368
369
370     def flow_add_2():
371         print "Add Flow 2     "
372         self.odl.cableflow_add(flow2)
373
374     def flow_add_several():
375         print "Add Flow Several     "
376         self.odl.cableflow_add(flow1)
377         self.odl.cableflow_add(flow2)
378         self.odl.cableflow_add(flow3)
379         self.odl.cableflow_add(flow4)
380         self.odl.cableflow_add(flow5)
381
382
383     def flow_remove_1():
384         print "Remove Flow 1  "
385         self.odl.cableflow_remove(flow1)
386
387     def flow_remove_2():
388         print "Remove Flow 2  "
389         self.odl.cableflow_remove(flow2)
390
391     def flow_remove_all():
392         print "Remove All Flows "
393         self.odl.cableflow_remove_all()
394
395     def flow_list_stats():
396         print "List Flow Stats"
397         self.odl.statistics_flows()
398
399     def topology_list():
400         print "List Topology  "
401         self.odl.topology()
402
403     def flow_list():
404         print "List Flows  "
405         self.odl.cableflow_list()
406
407
408     def flows_read(self, content_type='json'):
409         #print "content_type = %s" % content_type
410         for path, dirs, files in os.walk('.'):
411             for filename in files:
412                 if filename.endswith(".%s" % content_type):
413                     base_filename = basename(filename)
414                     # print base_filename
415                     fn = os.path.splitext(os.path.basename(filename))[0]  
416                     #print filename
417                     with open(filename) as fp:
418                         data = fp.read()
419                         # print(data)
420                         # jdata = yaml.load ( data  )
421                         # print(jdata)
422                         self.flows[fn]=data #equivalent to: self.varname= 'something'
423                         if content_type == "xml":
424                             pprint (self.flows[fn], width=4)
425                         else:
426                             #print fn
427                             #pprint (self.flows[fn], width=4)
428                             json.dumps(json.loads(data), indent=4)
429
430     def flows_print(self):
431         # print flow dictionary
432         l = self.flows.items()
433         l.sort()
434         #for k,v in self.flows.items():
435         for k,v in l:
436             print k
437
438     def exit_app(self):
439         print "Quit           "
440         exit(0)
441
442
443 if __name__ == "__main__":
444     ws = RestfulAPI('127.0.0.1')
445     ws.credentials('admin', 'admin')
446     odl = ODLCableflowRestconf(ws)
447     tests = CableflowTests(odl)
448     tests.flows_read()
449     tests.flows_print()
450
451     menu=Menu()
452     menu.run()
453     exit(0)
454
455
456