Sync: md-sal augmentation re-model with broker. Traffic profile: Best Effort hooked up.
[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):
231         ws.set_port(8181)       
232
233     def topology(self):
234         ws.set_path('/restconf/operational/opendaylight-inventory:nodes')
235         content = ws.get()
236         j=json.loads(content[2])
237         ws.show(j)
238
239     def cableflow_list(self):
240         ws.set_path('/config/opendaylight-inventory:nodes/node/%d/flow-node-inventory:table/0/flow')
241         content = ws.get()
242         j=json.loads(content[2])
243         ws.show(j)
244         #ws.show(content[2])
245         return(j)
246
247     def cableflow_update(self, flow):
248         ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
249         content = ws.post(flow)
250         j=json.loads(content[2])
251
252     def cableflow_list(self):
253         ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d')
254         content = ws.get()
255         j=json.loads(content[2])
256         ws.show(j)
257         #ws.show(content[2])
258
259     def cableflow_add(self, flow):
260 # PUT http://localhost:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"
261         ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
262         ws.show(flow)
263         content = ws.put(flow)
264         #print content
265         flowadd_response_codes = {
266         201:"Flow Config processed successfully",
267         400:"Failed to create Static Flow entry due to invalid flow configuration",
268         401:"User not authorized to perform this operation",
269         404:"The Container Name or nodeId is not found",
270         406:"Cannot operate on Default Container when other Containers are active",
271         409:"Failed to create Static Flow entry due to Conflicting Name or configuration",
272         500:"Failed to create Static Flow entry. Failure Reason included in HTTP Error response",
273         503:"One or more of Controller services are unavailable",
274         } 
275         msg=flowadd_response_codes.get(content[0])
276         print content[0], content[1], msg
277
278     def cableflow_remove(self, flow):
279         ws.set_path('/restconf/config/opendaylight-inventory:nodes/node/openflow:%d/table/0/flow/%d"' % flow['node']['id'],  flow['id'] )
280         content = ws.remove("", flow)
281
282         flowdelete_reponse_codes = {
283         204:"Flow Config deleted successfully",
284         401:"User not authorized to perform this operation",
285         404:"The Container Name or Node-id or Flow Name passed is not found",
286         406:"Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response",
287         500:"Failed to delete Flow config. Failure Reason included in HTTP Error response",
288         503:"One or more of Controller service is unavailable",
289         }
290         msg=flowdelete_reponse_codes.get(content[0])
291         print content[0], content[1], msg
292
293     def cableflow_remove_all(self):
294         allFlowConfigs = self.cableflow_list()
295         flowConfigs = allFlowConfigs['flowConfig']
296         for fl in flowConfigs:
297             print "Removing ", fl['name']
298             self.cableflow_remove(fl)
299                 
300
301
302     def statistics_flows(self):
303         ws.set_path('/controller/nb/v2/statistics/default/flow')
304         content = ws.get()
305         allFlowStats = json.loads(content[2])
306
307         flowStats = allFlowStats['flowStatistics']
308         # These JSON dumps were handy when trying to parse the responses 
309         #print json.dumps(flowStats[0]['flowStat'][1], indent = 2)
310         #print json.dumps(flowStats[4], indent = 2)
311         for fs in flowStats:
312             print "\nSwitch ID : " + fs['node']['id']
313             print '{0:8} {1:8} {2:5} {3:15}'.format('Count', 'Action', 'Port', 'DestIP')
314             if not 'flowStatistic' in fs.values(): 
315                 print '              none'
316                 continue
317             for aFlow in fs['flowStatistic']:
318                 #print "*", aFlow, "*", " ", len(aFlow), " ", not aFlow
319                 count = aFlow['packetCount']
320                 actions = aFlow['flow']['actions'] 
321                 actionType = ''
322                 actionPort = ''
323                 #print actions
324                 if(type(actions) == type(list())):
325                     actionType = actions[1]['type']
326                     actionPort = actions[1]['port']['id']
327                 else:
328                     actionType = actions['type']
329                     actionPort = actions['port']['id']
330                 dst = aFlow['flow']['match']['matchField'][0]['value']
331                 print '{0:8} {1:8} {2:5} {3:15}'.format(count, actionType, actionPort, dst)
332
333
334     def cableflow_remove_all(self):
335         allFlowConfigs = self.cableflow_list()
336         flowConfigs = allFlowConfigs['flowConfig']
337         for fl in flowConfigs:
338             print "Removing ", fl['name']
339             self.cableflow_remove(fl)
340                 
341
342
343 class CableflowTests(object):
344     def __init__(self):
345         self.flows = {}
346     def cmts_add_1():
347         print "Add cmts 1     "
348         odl.cmts_add(cmts1)
349
350     def cmts_add_2():
351         print "Add cmts 2     "
352         odl.cmts_add(cmts2)
353
354     def cmts_remove_1():
355         print "Add cmts 1     "
356         odl.cmts_remove(cmts1)
357
358     def cmts_remove_2():
359         print "Add cmts 2     "
360         odl.cmts_remove(cmts2)
361
362     def flow_add_1():
363         print "Add Flow 1     "
364         odl.cableflow_add(flow1)
365
366
367     def flow_add_2():
368         print "Add Flow 2     "
369         odl.cableflow_add(flow2)
370
371     def flow_add_several():
372         print "Add Flow Several     "
373         odl.cableflow_add(flow1)
374         odl.cableflow_add(flow2)
375         odl.cableflow_add(flow3)
376         odl.cableflow_add(flow4)
377         odl.cableflow_add(flow5)
378
379
380     def flow_remove_1():
381         print "Remove Flow 1  "
382         odl.cableflow_remove(flow1)
383
384     def flow_remove_2():
385         print "Remove Flow 2  "
386         odl.cableflow_remove(flow2)
387
388     def flow_remove_all():
389         print "Remove All Flows "
390         odl.cableflow_remove_all()
391
392     def flow_list_stats():
393         print "List Flow Stats"
394         odl.statistics_flows()
395
396     def topology_list():
397         print "List Topology  "
398         odl.topology()
399
400     def flow_list():
401         print "List Flows  "
402         odl.cableflow_list()
403
404
405     def flows_read(self, content_type='json'):
406         #print "content_type = %s" % content_type
407         for path, dirs, files in os.walk('.'):
408             for filename in files:
409                 if filename.endswith(".%s" % content_type):
410                     base_filename = basename(filename)
411                     print base_filename
412                     #print filename
413                     with open(filename) as fp:
414                         data = fp.read()
415                         print(data)
416                         # jdata = yaml.load ( data  )
417                         # print(jdata)
418                         self.flows[filename]=data #equivalent to: self.varname= 'something'
419                         if content_type == "xml":
420                             pprint (self.flows[filename], width=4)
421                         else:
422                             # pprint (self.flows[filename], width=4)
423                             json.dumps(json.loads(data), indent=4)
424
425     def exit_app():
426         print "Quit           "
427         exit(0)
428
429 ws = RestfulAPI('127.0.0.1')
430
431 if __name__ == "__main__":
432     ws.credentials('admin', 'admin')
433     odl = ODLCableflowRestconf()
434     tests = CableflowTests()
435     tests.flows_read()
436
437     exit(0)
438     menu=Menu()
439     menu.run()
440     exit(0)
441
442
443