blob: 1b7c8fd3476421e71e318761c418b3289079225b [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
4
5 TestON is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 ( at your option ) any later version.
9
10 TestON is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TestON. If not, see <http://www.gnu.org/licenses/>.
17
18"""
19import json
20import os
21import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070022import types
Jon Halle401b092015-09-23 13:34:24 -070023import sys
Jon Hallfc915882015-07-14 13:33:17 -070024
Jon Hallfc915882015-07-14 13:33:17 -070025from drivers.common.api.controllerdriver import Controller
26
27
28class OnosRestDriver( Controller ):
29
30 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070031 self.pwd = None
32 self.user_name = "user"
Jon Hallfc915882015-07-14 13:33:17 -070033 super( Controller, self ).__init__()
34 self.ip_address = "localhost"
35 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070036 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070037
38 def connect( self, **connectargs ):
39 try:
40 for key in connectargs:
41 vars( self )[ key ] = connectargs[ key ]
42 self.name = self.options[ 'name' ]
43 except Exception as e:
44 main.log.exception( e )
45 try:
46 if os.getenv( str( self.ip_address ) ) != None:
47 self.ip_address = os.getenv( str( self.ip_address ) )
48 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070049 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070050 except KeyError:
51 main.log.info( "Invalid host name," +
52 "defaulting to 'localhost' instead" )
53 self.ip_address = 'localhost'
54 except Exception as inst:
55 main.log.error( "Uncaught exception: " + str( inst ) )
56
57 self.handle = super( OnosRestDriver, self ).connect()
58 return self.handle
59
Jon Halle401b092015-09-23 13:34:24 -070060 def pprint( self, jsonObject ):
61 """
62 Pretty Prints a json object
63
64 arguments:
65 jsonObject - a parsed json object
66 returns:
67 A formatted string for printing or None on error
68 """
69 try:
70 if isinstance( jsonObject, str ):
71 jsonObject = json.loads( jsonObject )
72 return json.dumps( jsonObject, sort_keys=True,
73 indent=4, separators=(',', ': '))
74 except ( TypeError, ValueError ):
75 main.log.exception( "Error parsing jsonObject" )
76 return None
77
suibin zhangd5b6fe42016-05-12 08:48:58 -070078 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070079 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070080 """
81 Arguments:
82 str ip: ONOS IP Address
83 str port: ONOS REST Port
84 str url: ONOS REST url path.
85 NOTE that this is is only the relative path. IE "/devices"
86 str base: The base url for the given REST api. Applications could
87 potentially have their own base url
88 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070089 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070090 the request
91 dict data: Dictionary to be sent in the body of the request
92 """
93 # TODO: Authentication - simple http (user,pass) tuple
94 # TODO: should we maybe just pass kwargs straight to response?
95 # TODO: Do we need to allow for other protocols besides http?
96 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -070097 if ip == "DEFAULT":
98 main.log.warn( "No ip given, reverting to ip from topo file" )
99 ip = self.ip_address
100 if port == "DEFAULT":
101 main.log.warn( "No port given, reverting to port " +
102 "from topo file" )
103 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700104
Jon Hallfc915882015-07-14 13:33:17 -0700105 try:
106 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700107 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800108 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700109 auth = (self.user_name, self.pwd)
110 else:
111 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700112 main.log.info( "Sending request " + path + " using " +
113 method.upper() + " method." )
114 response = requests.request( method.upper(),
115 path,
116 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700117 data=data,
118 auth=auth )
119 if debug:
120 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700121 return ( response.status_code, response.text.encode( 'utf8' ) )
122 except requests.exceptions:
123 main.log.exception( "Error sending request." )
124 return None
Jon Halle401b092015-09-23 13:34:24 -0700125 except Exception:
126 main.log.exception( self.name + ": Uncaught exception!" )
127 main.cleanup()
128 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700129
130 def intents( self, ip="DEFAULT", port="DEFAULT" ):
131 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700132 Description:
133 Gets a list of dictionary of all intents in the system
134 Returns:
135 A list of dictionary of intents in string type to match the cli
136 version for now; Returns main.FALSE if error on request;
137 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700138 """
139 try:
140 output = None
141 if ip == "DEFAULT":
142 main.log.warn( "No ip given, reverting to ip from topo file" )
143 ip = self.ip_address
144 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700145 main.log.warn( "No port given, reverting to port " +
146 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700147 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700148 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700149 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700150 if 200 <= response[ 0 ] <= 299:
151 output = response[ 1 ]
152 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700153 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700154 b = json.dumps( a )
155 return b
Jon Hallfc915882015-07-14 13:33:17 -0700156 else:
157 main.log.error( "Error with REST request, response was: " +
158 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700159 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700160 except ( AttributeError, AssertionError, TypeError ):
161 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700162 return None
Jon Halle401b092015-09-23 13:34:24 -0700163 except Exception:
164 main.log.exception( self.name + ": Uncaught exception!" )
165 main.cleanup()
166 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700167
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700168 def intent( self, intentId, appId="org.onosproject.cli",
169 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700170 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700171 Description:
172 Get the specific intent information of the given application ID and
173 intent ID
174 Required:
175 str intentId - Intent id in hexadecimal form
176 Optional:
177 str appId - application id of intent
178 Returns:
179 Returns an information dictionary of the given intent;
180 Returns main.FALSE if error on requests; Returns None for exception
181 NOTE:
182 The GET /intents REST api command accepts application id but the
183 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700184 """
185 try:
186 output = None
187 if ip == "DEFAULT":
188 main.log.warn( "No ip given, reverting to ip from topo file" )
189 ip = self.ip_address
190 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700191 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700192 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700193 port = self.port
194 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700195 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700196 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700197 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700198 if 200 <= response[ 0 ] <= 299:
199 output = response[ 1 ]
200 a = json.loads( output )
201 return a
Jon Hallfc915882015-07-14 13:33:17 -0700202 else:
203 main.log.error( "Error with REST request, response was: " +
204 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700205 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700206 except ( AttributeError, TypeError ):
207 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700208 return None
Jon Halle401b092015-09-23 13:34:24 -0700209 except Exception:
210 main.log.exception( self.name + ": Uncaught exception!" )
211 main.cleanup()
212 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700213
214 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
215 """
216 Description:
217 Gets all intents ID using intents function
218 Returns:
219 List of intents ID;Returns None for exception; Returns None for
220 exception; Returns None for exception
221 """
222 try:
223 intentsDict = {}
224 intentsIdList = []
225 intentsDict = json.loads( self.intents( ip=ip, port=port ) )
226 for intent in intentsDict:
227 intentsIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700228 if not intentsIdList:
229 main.log.debug( "Cannot find any intents" )
230 return main.FALSE
231 else:
232 main.log.info( "Found intents: " + str( intentsIdList ) )
233 return main.TRUE
Jon Halle401b092015-09-23 13:34:24 -0700234 except ( AttributeError, TypeError ):
235 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700236 return None
Jon Halle401b092015-09-23 13:34:24 -0700237 except Exception:
238 main.log.exception( self.name + ": Uncaught exception!" )
239 main.cleanup()
240 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700241
242 def apps( self, ip="DEFAULT", port="DEFAULT" ):
243 """
244 Description:
245 Returns all the current application installed in the system
246 Returns:
247 List of dictionary of installed application; Returns main.FALSE for
248 error on request; Returns None for exception
249 """
250 try:
251 output = None
252 if ip == "DEFAULT":
253 main.log.warn( "No ip given, reverting to ip from topo file" )
254 ip = self.ip_address
255 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700256 main.log.warn( "No port given, reverting to port " +
257 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700258 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700259 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700260 if response:
261 if 200 <= response[ 0 ] <= 299:
262 output = response[ 1 ]
263 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700264 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700265 b = json.dumps( a )
266 return b
267 else:
268 main.log.error( "Error with REST request, response was: " +
269 str( response ) )
270 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700271 except ( AttributeError, AssertionError, TypeError ):
272 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700273 return None
Jon Halle401b092015-09-23 13:34:24 -0700274 except Exception:
275 main.log.exception( self.name + ": Uncaught exception!" )
276 main.cleanup()
277 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700278
279 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
280 """
281 Decription:
282 Activate an app that is already installed in ONOS
283 Optional:
284 bool check - If check is True, method will check the status
285 of the app after the command is issued
286 Returns:
287 Returns main.TRUE if the command was successfully or main.FALSE
288 if the REST responded with an error or given incorrect input;
289 Returns None for exception
290
291 """
292 try:
293 output = None
294 if ip == "DEFAULT":
295 main.log.warn( "No ip given, reverting to ip from topo file" )
296 ip = self.ip_address
297 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700298 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700299 "from topo file" )
300 port = self.port
301 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700302 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700303 url="/applications" + query,
304 ip = ip, port = port)
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700305 if response:
306 output = response[ 1 ]
307 app = json.loads( output )
308 if 200 <= response[ 0 ] <= 299:
309 if check:
310 if app.get( 'state' ) == 'ACTIVE':
311 main.log.info( self.name + ": " + appName +
312 " application" +
313 " is in ACTIVE state" )
314 return main.TRUE
315 else:
316 main.log.error( self.name + ": " + appName +
317 " application" + " is in " +
318 app.get( 'state' ) + " state" )
319 return main.FALSE
320 else:
321 main.log.warn( "Skipping " + appName +
322 "application check" )
323 return main.TRUE
324 else:
325 main.log.error( "Error with REST request, response was: " +
326 str( response ) )
327 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700328 except ( AttributeError, TypeError ):
329 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700330 return None
Jon Halle401b092015-09-23 13:34:24 -0700331 except Exception:
332 main.log.exception( self.name + ": Uncaught exception!" )
333 main.cleanup()
334 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700335
336 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
337 check=True ):
338 """
339 Required:
340 Deactivate an app that is already activated in ONOS
341 Optional:
342 bool check - If check is True, method will check the status of the
343 app after the command is issued
344 Returns:
345 Returns main.TRUE if the command was successfully sent
346 main.FALSE if the REST responded with an error or given
347 incorrect input; Returns None for exception
348 """
349 try:
350 output = None
351 if ip == "DEFAULT":
352 main.log.warn( "No ip given, reverting to ip from topo file" )
353 ip = self.ip_address
354 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700355 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700356 "from topo file" )
357 port = self.port
358 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700359 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700360 url="/applications" + query,
361 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700362 if response:
363 output = response[ 1 ]
364 app = json.loads( output )
365 if 200 <= response[ 0 ] <= 299:
366 if check:
367 if app.get( 'state' ) == 'INSTALLED':
368 main.log.info( self.name + ": " + appName +
369 " application" +
370 " is in INSTALLED state" )
371 return main.TRUE
372 else:
373 main.log.error( self.name + ": " + appName +
374 " application" + " is in " +
375 app.get( 'state' ) + " state" )
376 return main.FALSE
377 else:
378 main.log.warn( "Skipping " + appName +
379 "application check" )
380 return main.TRUE
381 else:
382 main.log.error( "Error with REST request, response was: " +
383 str( response ) )
384 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700385 except ( AttributeError, TypeError ):
386 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700387 return None
Jon Halle401b092015-09-23 13:34:24 -0700388 except Exception:
389 main.log.exception( self.name + ": Uncaught exception!" )
390 main.cleanup()
391 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700392
393 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
394 port="DEFAULT" ):
395 """
396 Decription:
397 Gets the informaion of the given application
398 Required:
399 str name - Name of onos application
400 Returns:
401 Returns a dictionary of information ONOS application in string type;
402 Returns main.FALSE if error on requests; Returns None for exception
403 """
404 try:
405 output = None
406 if ip == "DEFAULT":
407 main.log.warn( "No ip given, reverting to ip from topo file" )
408 ip = self.ip_address
409 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700410 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700411 "from topo file" )
412 port = self.port
413 query = "/" + project + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700414 response = self.send( url="/applications" + query,
415 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700416 if response:
417 if 200 <= response[ 0 ] <= 299:
418 output = response[ 1 ]
419 a = json.loads( output )
420 return a
421 else:
422 main.log.error( "Error with REST request, response was: " +
423 str( response ) )
424 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700425 except ( AttributeError, TypeError ):
426 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700427 return None
Jon Halle401b092015-09-23 13:34:24 -0700428 except Exception:
429 main.log.exception( self.name + ": Uncaught exception!" )
430 main.cleanup()
431 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700432
433 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
434 ip="DEFAULT", port="DEFAULT" ):
435 """
436 Description:
437 Adds a host-to-host intent ( bidirectional ) by
438 specifying the two hosts.
439 Required:
440 * hostIdOne: ONOS host id for host1
441 * hostIdTwo: ONOS host id for host2
442 Optional:
443 str appId - Application name of intent identifier
444 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700445 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700446 error on requests; Returns None for exceptions
447 """
448 try:
449 intentJson = {"two": str( hostIdTwo ),
450 "selector": {"criteria": []}, "priority": 7,
451 "treatment": {"deferred": [], "instructions": []},
452 "appId": appId, "one": str( hostIdOne ),
453 "type": "HostToHostIntent",
454 "constraints": [{"type": "LinkTypeConstraint",
455 "types": ["OPTICAL"],
456 "inclusive": 'false' }]}
457 output = None
458 if ip == "DEFAULT":
459 main.log.warn( "No ip given, reverting to ip from topo file" )
460 ip = self.ip_address
461 if port == "DEFAULT":
462 main.log.warn( "No port given, reverting to port " +
463 "from topo file" )
464 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700465 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700466 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700467 data=json.dumps( intentJson ) )
468 if response:
469 if 201:
470 main.log.info( self.name + ": Successfully POST host" +
471 " intent between host: " + hostIdOne +
472 " and host: " + hostIdTwo )
473 return main.TRUE
474 else:
475 main.log.error( "Error with REST request, response was: " +
476 str( response ) )
477 return main.FALSE
478
Jon Halle401b092015-09-23 13:34:24 -0700479 except ( AttributeError, TypeError ):
480 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700481 return None
Jon Halle401b092015-09-23 13:34:24 -0700482 except Exception:
483 main.log.exception( self.name + ": Uncaught exception!" )
484 main.cleanup()
485 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700486
kelvin-onlabb50074f2015-07-27 16:18:32 -0700487 def addPointIntent( self,
488 ingressDevice,
489 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700490 appId='org.onosproject.cli',
491 ingressPort="",
492 egressPort="",
493 ethType="",
494 ethSrc="",
495 ethDst="",
496 bandwidth="",
497 lambdaAlloc=False,
498 ipProto="",
499 ipSrc="",
500 ipDst="",
501 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700502 tcpDst="",
503 ip="DEFAULT",
504 port="DEFAULT" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700505 """
506 Description:
507 Adds a point-to-point intent ( uni-directional ) by
508 specifying device id's and optional fields
509 Required:
510 * ingressDevice: device id of ingress device
511 * egressDevice: device id of egress device
512 Optional:
513 * ethType: specify ethType
514 * ethSrc: specify ethSrc ( i.e. src mac addr )
515 * ethDst: specify ethDst ( i.e. dst mac addr )
516 * bandwidth: specify bandwidth capacity of link (TODO)
517 * lambdaAlloc: if True, intent will allocate lambda
518 for the specified intent (TODO)
519 * ipProto: specify ip protocol
520 * ipSrc: specify ip source address with mask eg. ip#/24
521 * ipDst: specify ip destination address eg. ip#/24
522 * tcpSrc: specify tcp source port
523 * tcpDst: specify tcp destination port
524 Returns:
525 Returns main.TRUE for successful requests; Returns main.FALSE if
526 no ingress|egress port found and if error on requests;
527 Returns None for exceptions
528 NOTE:
529 The ip and port option are for the requests input's ip and port
530 of the ONOS node
531 """
532 try:
533 if "/" in ingressDevice:
534 if not ingressPort:
535 ingressPort = ingressDevice.split( "/" )[ 1 ]
536 ingressDevice = ingressDevice.split( "/" )[ 0 ]
537 else:
538 if not ingressPort:
539 main.log.debug( self.name + ": Ingress port not specified" )
540 return main.FALSE
541
542 if "/" in egressDevice:
543 if not egressPort:
544 egressPort = egressDevice.split( "/" )[ 1 ]
545 egressDevice = egressDevice.split( "/" )[ 0 ]
546 else:
547 if not egressPort:
548 main.log.debug( self.name + ": Egress port not specified" )
549 return main.FALSE
550
551 intentJson ={ "ingressPoint": { "device": ingressDevice,
552 "port": ingressPort },
553 "selector": { "criteria": [] },
554 "priority": 55,
555 "treatment": { "deferred": [],
556 "instructions": [] },
557 "egressPoint": { "device": egressDevice,
558 "port": egressPort },
559 "appId": appId,
560 "type": "PointToPointIntent",
561 "constraints": [ { "type": "LinkTypeConstraint",
562 "types": [ "OPTICAL" ],
563 "inclusive": "false" } ] }
564
565 if ethType == "IPV4":
566 intentJson[ 'selector' ][ 'criteria' ].append( {
567 "type":"ETH_TYPE",
568 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700569 elif ethType:
570 intentJson[ 'selector' ][ 'criteria' ].append( {
571 "type":"ETH_TYPE",
572 "ethType":ethType } )
573
kelvin-onlabb50074f2015-07-27 16:18:32 -0700574 if ethSrc:
575 intentJson[ 'selector' ][ 'criteria' ].append(
576 { "type":"ETH_SRC",
577 "mac":ethSrc } )
578 if ethDst:
579 intentJson[ 'selector' ][ 'criteria' ].append(
580 { "type":"ETH_DST",
581 "mac":ethDst } )
582 if ipSrc:
583 intentJson[ 'selector' ][ 'criteria' ].append(
584 { "type":"IPV4_SRC",
585 "ip":ipSrc } )
586 if ipDst:
587 intentJson[ 'selector' ][ 'criteria' ].append(
588 { "type":"IPV4_DST",
589 "ip":ipDst } )
590 if tcpSrc:
591 intentJson[ 'selector' ][ 'criteria' ].append(
592 { "type":"TCP_SRC",
593 "tcpPort": tcpSrc } )
594 if tcpDst:
595 intentJson[ 'selector' ][ 'criteria' ].append(
596 { "type":"TCP_DST",
597 "tcpPort": tcpDst } )
598 if ipProto:
599 intentJson[ 'selector' ][ 'criteria' ].append(
600 { "type":"IP_PROTO",
601 "protocol": ipProto } )
602
603 # TODO: Bandwidth and Lambda will be implemented if needed
604
605 main.log.debug( intentJson )
606
607 output = None
608 if ip == "DEFAULT":
609 main.log.warn( "No ip given, reverting to ip from topo file" )
610 ip = self.ip_address
611 if port == "DEFAULT":
612 main.log.warn( "No port given, reverting to port " +
613 "from topo file" )
614 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700615 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700616 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700617 data=json.dumps( intentJson ) )
618 if response:
619 if 201:
620 main.log.info( self.name + ": Successfully POST point" +
621 " intent between ingress: " + ingressDevice +
622 " and egress: " + egressDevice + " devices" )
623 return main.TRUE
624 else:
625 main.log.error( "Error with REST request, response was: " +
626 str( response ) )
627 return main.FALSE
628
Jon Halle401b092015-09-23 13:34:24 -0700629 except ( AttributeError, TypeError ):
630 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700631 return None
Jon Halle401b092015-09-23 13:34:24 -0700632 except Exception:
633 main.log.exception( self.name + ": Uncaught exception!" )
634 main.cleanup()
635 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700636
637 def removeIntent( self, intentId, appId='org.onosproject.cli',
638 ip="DEFAULT", port="DEFAULT" ):
639 """
640 Remove intent for specified application id and intent id;
641 Returns None for exception
642 """
643 try:
644 output = None
645 if ip == "DEFAULT":
646 main.log.warn( "No ip given, reverting to ip from topo file" )
647 ip = self.ip_address
648 if port == "DEFAULT":
649 main.log.warn( "No port given, reverting to port " +
650 "from topo file" )
651 port = self.port
652 # NOTE: REST url requires the intent id to be in decimal form
653 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700654 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700655 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700656 if response:
657 if 200 <= response[ 0 ] <= 299:
658 return main.TRUE
659 else:
660 main.log.error( "Error with REST request, response was: " +
661 str( response ) )
662 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700663 except ( AttributeError, TypeError ):
664 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700665 return None
Jon Halle401b092015-09-23 13:34:24 -0700666 except Exception:
667 main.log.exception( self.name + ": Uncaught exception!" )
668 main.cleanup()
669 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700670
671 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
672 """
673 Returns a list of intents id; Returns None for exception
674 """
675 try:
676 intentIdList = []
677 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700678 for intent in intentsJson:
679 intentIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700680 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700681 except ( AttributeError, TypeError ):
682 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700683 return None
Jon Halle401b092015-09-23 13:34:24 -0700684 except Exception:
685 main.log.exception( self.name + ": Uncaught exception!" )
686 main.cleanup()
687 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700688
689 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
690 ip="DEFAULT", port="DEFAULT", delay=5 ):
691 """
692 Description:
693 Remove all the intents
694 Returns:
695 Returns main.TRUE if all intents are removed, otherwise returns
696 main.FALSE; Returns None for exception
697 """
698 try:
699 results = []
700 if intentIdList == 'ALL':
701 intentIdList = self.getIntentsId( ip=ip, port=port )
702
703 main.log.info( self.name + ": Removing intents " +
704 str( intentIdList ) )
705
706 if isinstance( intentIdList, types.ListType ):
707 for intent in intentIdList:
708 results.append( self.removeIntent( intentId=intent,
709 appId=appId,
710 ip=ip,
711 port=port ) )
712 # Check for remaining intents
713 # NOTE: Noticing some delay on Deleting the intents so i put
714 # this time out
715 import time
716 time.sleep( delay )
717 intentRemain = len( json.loads( self.intents() ) )
718 if all( result==main.TRUE for result in results ) and \
719 intentRemain == 0:
720 main.log.info( self.name + ": All intents are removed " )
721 return main.TRUE
722 else:
723 main.log.error( self.name + ": Did not removed all intents,"
724 + " there are " + str( intentRemain )
725 + " intents remaining" )
726 return main.FALSE
727 else:
728 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700729 except ( AttributeError, TypeError ):
730 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700731 return None
Jon Halle401b092015-09-23 13:34:24 -0700732 except Exception:
733 main.log.exception( self.name + ": Uncaught exception!" )
734 main.cleanup()
735 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700736
737 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
738 """
739 Description:
740 Get a list of dictionary of all discovered hosts
741 Returns:
742 Returns a list of dictionary of information of the hosts currently
743 discovered by ONOS; Returns main.FALSE if error on requests;
744 Returns None for exception
745 """
746 try:
747 output = None
748 if ip == "DEFAULT":
749 main.log.warn( "No ip given, reverting to ip from topo file" )
750 ip = self.ip_address
751 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700752 main.log.warn( "No port given, reverting to port " +
753 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700754 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700755 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700756 if response:
757 if 200 <= response[ 0 ] <= 299:
758 output = response[ 1 ]
759 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700760 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700761 b = json.dumps( a )
762 return b
763 else:
764 main.log.error( "Error with REST request, response was: " +
765 str( response ) )
766 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700767 except ( AttributeError, AssertionError, TypeError ):
768 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700769 return None
Jon Halle401b092015-09-23 13:34:24 -0700770 except Exception:
771 main.log.exception( self.name + ": Uncaught exception!" )
772 main.cleanup()
773 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700774
775 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
776 """
777 Description:
778 Gets the information from the given host
779 Required:
780 str mac - MAC address of the host
781 Optional:
782 str vlan - VLAN tag of the host, defaults to -1
783 Returns:
784 Return the host id from the hosts/mac/vlan output in REST api
785 whose 'id' contains mac/vlan; Returns None for exception;
786 Returns main.FALSE if error on requests
787
788 NOTE:
789 Not sure what this function should do, any suggestion?
790 """
791 try:
792 output = None
793 if ip == "DEFAULT":
794 main.log.warn( "No ip given, reverting to ip from topo file" )
795 ip = self.ip_address
796 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700797 main.log.warn( "No port given, reverting to port " +
798 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700799 port = self.port
800 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700801 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700802 if response:
803 # NOTE: What if the person wants other values? would it be better
804 # to have a function that gets a key and return a value instead?
805 # This function requires mac and vlan and returns an ID which
806 # makes this current function useless
807 if 200 <= response[ 0 ] <= 299:
808 output = response[ 1 ]
809 hostId = json.loads( output ).get( 'id' )
810 return hostId
811 else:
812 main.log.error( "Error with REST request, response was: " +
813 str( response ) )
814 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700815 except ( AttributeError, TypeError ):
816 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700817 return None
Jon Halle401b092015-09-23 13:34:24 -0700818 except Exception:
819 main.log.exception( self.name + ": Uncaught exception!" )
820 main.cleanup()
821 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700822
823 def topology( self, ip="DEFAULT", port="DEFAULT" ):
824 """
825 Description:
826 Gets the overview of network topology
827 Returns:
828 Returns a dictionary containing information about network topology;
829 Returns None for exception
830 """
831 try:
832 output = None
833 if ip == "DEFAULT":
834 main.log.warn( "No ip given, reverting to ip from topo file" )
835 ip = self.ip_address
836 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700837 main.log.warn( "No port given, reverting to port " +
838 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700839 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700840 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700841 if response:
842 if 200 <= response[ 0 ] <= 299:
843 output = response[ 1 ]
844 a = json.loads( output )
845 b = json.dumps( a )
846 return b
847 else:
848 main.log.error( "Error with REST request, response was: " +
849 str( response ) )
850 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700851 except ( AttributeError, TypeError ):
852 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700853 return None
Jon Halle401b092015-09-23 13:34:24 -0700854 except Exception:
855 main.log.exception( self.name + ": Uncaught exception!" )
856 main.cleanup()
857 main.exit()
858
859 def devices( self, ip="DEFAULT", port="DEFAULT" ):
860 """
861 Description:
862 Get the devices discovered by ONOS is json string format
863 Returns:
864 a json string of the devices currently discovered by ONOS OR
865 main.FALSE if there is an error in the request OR
866 Returns None for exception
867 """
868 try:
869 output = None
870 if ip == "DEFAULT":
871 main.log.warn( "No ip given, reverting to ip from topo file" )
872 ip = self.ip_address
873 if port == "DEFAULT":
874 main.log.warn( "No port given, reverting to port " +
875 "from topo file" )
876 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700877 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -0700878 if response:
879 if 200 <= response[ 0 ] <= 299:
880 output = response[ 1 ]
881 a = json.loads( output ).get( 'devices' )
882 assert a is not None, "Error parsing json object"
883 b = json.dumps( a )
884 return b
885 else:
886 main.log.error( "Error with REST request, response was: " +
887 str( response ) )
888 return main.FALSE
889 except ( AttributeError, AssertionError, TypeError ):
890 main.log.exception( self.name + ": Object not as expected" )
891 return None
892 except Exception:
893 main.log.exception( self.name + ": Uncaught exception!" )
894 main.cleanup()
895 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700896
897 def getIntentState( self, intentsId, intentsJson=None,
898 ip="DEFAULT", port="DEFAULT" ):
899 """
900 Description:
901 Get intent state.
902 Accepts a single intent ID (string type) or a list of intent IDs.
903 Returns the state(string type) of the id if a single intent ID is
904 accepted.
905 Required:
906 intentId: intent ID (string type)
907 intentsJson: parsed json object from the onos:intents api
908 Returns:
909 Returns a dictionary with intent IDs as the key and its
910 corresponding states as the values; Returns None for invalid IDs or
911 Type error and any exceptions
912 NOTE:
913 An intent's state consist of INSTALLED,WITHDRAWN etc.
914 """
915 try:
916 state = "State is Undefined"
917 if not intentsJson:
918 intentsJsonTemp = json.loads( self.intents() )
919 else:
920 intentsJsonTemp = json.loads( intentsJson )
921 if isinstance( intentsId, types.StringType ):
922 for intent in intentsJsonTemp:
923 if intentsId == intent[ 'id' ]:
924 state = intent[ 'state' ]
925 return state
926 main.log.info( "Cannot find intent ID" + str( intentsId ) +
927 " on the list" )
928 return state
929 elif isinstance( intentsId, types.ListType ):
930 dictList = []
931 for i in xrange( len( intentsId ) ):
932 stateDict = {}
933 for intents in intentsJsonTemp:
934 if intentsId[ i ] == intents[ 'id' ]:
935 stateDict[ 'state' ] = intents[ 'state' ]
936 stateDict[ 'id' ] = intentsId[ i ]
937 dictList.append( stateDict )
938 break
939 if len( intentsId ) != len( dictList ):
940 main.log.info( "Cannot find some of the intent ID state" )
941 return dictList
942 else:
943 main.log.info( "Invalid intents ID entry" )
944 return None
945
Jon Halle401b092015-09-23 13:34:24 -0700946 except ( AttributeError, TypeError ):
947 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700948 return None
Jon Halle401b092015-09-23 13:34:24 -0700949 except Exception:
950 main.log.exception( self.name + ": Uncaught exception!" )
951 main.cleanup()
952 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700953
954 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
955 ip="DEFAULT", port="DEFAULT"):
956 """
957 Description:
958 Check intents state based on expected state which defaults to
959 INSTALLED state
960 Required:
961 intentsId - List of intents ID to be checked
962 Optional:
963 expectedState - Check the expected state(s) of each intents
964 state in the list.
965 *NOTE: You can pass in a list of expected state,
966 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
967 Return:
968 Returns main.TRUE only if all intent are the same as expected states
969 , otherwise, returns main.FALSE; Returns None for general exception
970 """
971 try:
972 # Generating a dictionary: intent id as a key and state as value
973 returnValue = main.TRUE
974 if intentsId == "ALL":
975 intentsId = self.getIntentsId( ip=ip, port=port )
976 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
977
978 #print "len of intentsDict ", str( len( intentsDict ) )
979 if len( intentsId ) != len( intentsDict ):
980 main.log.error( self.name + ": There is something wrong " +
981 "getting intents state" )
982 return main.FALSE
983
984 if isinstance( expectedState, types.StringType ):
985 for intents in intentsDict:
986 if intents.get( 'state' ) != expectedState:
987 main.log.debug( self.name + " : Intent ID - " +
988 intents.get( 'id' ) +
989 " actual state = " +
990 intents.get( 'state' )
991 + " does not equal expected state = "
992 + expectedState )
993 returnValue = main.FALSE
994
995 elif isinstance( expectedState, types.ListType ):
996 for intents in intentsDict:
997 if not any( state == intents.get( 'state' ) for state in
998 expectedState ):
999 main.log.debug( self.name + " : Intent ID - " +
1000 intents.get( 'id' ) +
1001 " actual state = " +
1002 intents.get( 'state' ) +
1003 " does not equal expected states = "
1004 + str( expectedState ) )
1005 returnValue = main.FALSE
1006
1007 if returnValue == main.TRUE:
1008 main.log.info( self.name + ": All " +
1009 str( len( intentsDict ) ) +
1010 " intents are in " + str( expectedState ) +
1011 " state" )
1012 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001013 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001014 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001015 return None
Jon Halle401b092015-09-23 13:34:24 -07001016 except Exception:
1017 main.log.exception( self.name + ": Uncaught exception!" )
1018 main.cleanup()
1019 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001020
1021 def flows( self, ip="DEFAULT", port="DEFAULT" ):
1022 """
1023 Description:
1024 Get flows currently added to the system
1025 NOTE:
1026 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001027 the REST output
1028
1029 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001030 """
1031 try:
1032 output = None
1033 if ip == "DEFAULT":
1034 main.log.warn( "No ip given, reverting to ip from topo file" )
1035 ip = self.ip_address
1036 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001037 main.log.warn( "No port given, reverting to port " +
1038 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001039 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001040 response = self.send( url="/flows", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001041 if response:
1042 if 200 <= response[ 0 ] <= 299:
1043 output = response[ 1 ]
1044 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001045 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001046 b = json.dumps( a )
1047 return b
1048 else:
1049 main.log.error( "Error with REST request, response was: " +
1050 str( response ) )
1051 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001052 except ( AttributeError, AssertionError, TypeError ):
1053 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001054 return None
Jon Halle401b092015-09-23 13:34:24 -07001055 except Exception:
1056 main.log.exception( self.name + ": Uncaught exception!" )
1057 main.cleanup()
1058 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001059
Jon Halle401b092015-09-23 13:34:24 -07001060 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001061 """
1062 Description:
1063 Gets all the flows of the device or get a specific flow in the
1064 device by giving its flow ID
1065 Required:
Jon Halle401b092015-09-23 13:34:24 -07001066 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001067 Optional:
1068 int/hex flowId - ID of the flow
1069 """
1070 try:
1071 output = None
1072 if ip == "DEFAULT":
1073 main.log.warn( "No ip given, reverting to ip from topo file" )
1074 ip = self.ip_address
1075 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001076 main.log.warn( "No port given, reverting to port " +
1077 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001078 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001079 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001080 if flowId:
1081 url += "/" + str( int( flowId ) )
1082 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001083 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001084 if response:
1085 if 200 <= response[ 0 ] <= 299:
1086 output = response[ 1 ]
1087 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001088 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001089 b = json.dumps( a )
1090 return b
1091 else:
1092 main.log.error( "Error with REST request, response was: " +
1093 str( response ) )
1094 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001095 except ( AttributeError, AssertionError, TypeError ):
1096 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001097 return None
Jon Halle401b092015-09-23 13:34:24 -07001098 except Exception:
1099 main.log.exception( self.name + ": Uncaught exception!" )
1100 main.cleanup()
1101 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001102
GlennRC073e8bc2015-10-27 17:11:28 -07001103 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001104 """
1105 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001106 Sends a single flow to the specified device. This function exists
1107 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001108 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001109 * The flow in json
1110 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001111 Returns:
1112 Returns main.TRUE for successful requests; Returns main.FALSE
1113 if error on requests;
1114 Returns None for exceptions
1115 NOTE:
1116 The ip and port option are for the requests input's ip and port
1117 of the ONOS node
1118 """
GlennRC073e8bc2015-10-27 17:11:28 -07001119
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001120 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001121 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001122 output = None
1123 if ip == "DEFAULT":
1124 main.log.warn( "No ip given, reverting to ip from topo file" )
1125 ip = self.ip_address
1126 if port == "DEFAULT":
1127 main.log.warn( "No port given, reverting to port " +
1128 "from topo file" )
1129 port = self.port
1130 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001131 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001132 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001133 data=json.dumps( flowJson ) )
1134 if response:
1135 if 201:
1136 main.log.info( self.name + ": Successfully POST flow" +
1137 "in device: " + str( deviceId ) )
1138 return main.TRUE
1139 else:
1140 main.log.error( "Error with REST request, response was: " +
1141 str( response ) )
1142 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001143 except NotImplementedError as e:
1144 raise e # Inform the caller
1145 except ( AttributeError, TypeError ):
1146 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001147 return None
Jon Halle401b092015-09-23 13:34:24 -07001148 except Exception:
1149 main.log.exception( self.name + ": Uncaught exception!" )
1150 main.cleanup()
1151 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001152
GlennRC073e8bc2015-10-27 17:11:28 -07001153 def addFlow( self,
1154 deviceId,
1155 appId=0,
1156 ingressPort="",
1157 egressPort="",
1158 ethType="",
1159 ethSrc="",
1160 ethDst="",
1161 vlan="",
1162 ipProto="",
1163 ipSrc=(),
1164 ipDst=(),
1165 tcpSrc="",
1166 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001167 udpDst="",
1168 udpSrc="",
1169 mpls="",
GlennRC073e8bc2015-10-27 17:11:28 -07001170 ip="DEFAULT",
1171 port="DEFAULT",
1172 debug=False ):
1173 """
1174 Description:
1175 Creates a single flow in the specified device
1176 Required:
1177 * deviceId: id of the device
1178 Optional:
1179 * ingressPort: port ingress device
1180 * egressPort: port of egress device
1181 * ethType: specify ethType
1182 * ethSrc: specify ethSrc ( i.e. src mac addr )
1183 * ethDst: specify ethDst ( i.e. dst mac addr )
1184 * ipProto: specify ip protocol
1185 * ipSrc: specify ip source address with mask eg. ip#/24
1186 as a tuple (type, ip#)
1187 * ipDst: specify ip destination address eg. ip#/24
1188 as a tuple (type, ip#)
1189 * tcpSrc: specify tcp source port
1190 * tcpDst: specify tcp destination port
1191 Returns:
1192 Returns main.TRUE for successful requests; Returns main.FALSE
1193 if error on requests;
1194 Returns None for exceptions
1195 NOTE:
1196 The ip and port option are for the requests input's ip and port
1197 of the ONOS node
1198 """
1199 try:
1200 flowJson = { "priority":100,
1201 "isPermanent":"true",
1202 "timeout":0,
1203 "deviceId":deviceId,
1204 "treatment":{"instructions":[]},
1205 "selector": {"criteria":[]}}
1206 if appId:
1207 flowJson[ "appId" ] = appId
1208 if egressPort:
1209 flowJson[ 'treatment' ][ 'instructions' ].append( {
1210 "type":"OUTPUT",
1211 "port":egressPort } )
1212 if ingressPort:
1213 flowJson[ 'selector' ][ 'criteria' ].append( {
1214 "type":"IN_PORT",
1215 "port":ingressPort } )
1216 if ethType:
1217 flowJson[ 'selector' ][ 'criteria' ].append( {
1218 "type":"ETH_TYPE",
1219 "ethType":ethType } )
1220 if ethSrc:
1221 flowJson[ 'selector' ][ 'criteria' ].append( {
1222 "type":"ETH_SRC",
1223 "mac":ethSrc } )
1224 if ethDst:
1225 flowJson[ 'selector' ][ 'criteria' ].append( {
1226 "type":"ETH_DST",
1227 "mac":ethDst } )
1228 if vlan:
1229 flowJson[ 'selector' ][ 'criteria' ].append( {
1230 "type":"VLAN_VID",
1231 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001232 if mpls:
1233 flowJson[ 'selector' ][ 'criteria' ].append( {
1234 "type":"MPLS_LABEL",
1235 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001236 if ipSrc:
1237 flowJson[ 'selector' ][ 'criteria' ].append( {
1238 "type":ipSrc[0],
1239 "ip":ipSrc[1] } )
1240 if ipDst:
1241 flowJson[ 'selector' ][ 'criteria' ].append( {
1242 "type":ipDst[0],
1243 "ip":ipDst[1] } )
1244 if tcpSrc:
1245 flowJson[ 'selector' ][ 'criteria' ].append( {
1246 "type":"TCP_SRC",
1247 "tcpPort": tcpSrc } )
1248 if tcpDst:
1249 flowJson[ 'selector' ][ 'criteria' ].append( {
1250 "type":"TCP_DST",
1251 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001252 if udpSrc:
1253 flowJson[ 'selector' ][ 'criteria' ].append( {
1254 "type":"UDP_SRC",
1255 "udpPort": udpSrc } )
1256 if udpDst:
1257 flowJson[ 'selector' ][ 'criteria' ].append( {
1258 "type":"UDP_DST",
1259 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001260 if ipProto:
1261 flowJson[ 'selector' ][ 'criteria' ].append( {
1262 "type":"IP_PROTO",
1263 "protocol": ipProto } )
1264
1265 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1266
1267 except ( AttributeError, TypeError ):
1268 main.log.exception( self.name + ": Object not as expected" )
1269 return None
1270 except Exception:
1271 main.log.exception( self.name + ": Uncaught exception!" )
1272 main.cleanup()
1273 main.exit()
1274
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001275 def removeFlow( self, deviceId, flowId,
1276 ip="DEFAULT", port="DEFAULT" ):
1277 """
1278 Description:
1279 Remove specific device flow
1280 Required:
1281 str deviceId - id of the device
1282 str flowId - id of the flow
1283 Return:
1284 Returns main.TRUE if successfully deletes flows, otherwise
1285 Returns main.FALSE, Returns None on error
1286 """
1287 try:
1288 output = None
1289 if ip == "DEFAULT":
1290 main.log.warn( "No ip given, reverting to ip from topo file" )
1291 ip = self.ip_address
1292 if port == "DEFAULT":
1293 main.log.warn( "No port given, reverting to port " +
1294 "from topo file" )
1295 port = self.port
1296 # NOTE: REST url requires the intent id to be in decimal form
1297 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001298 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001299 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001300 if response:
1301 if 200 <= response[ 0 ] <= 299:
1302 return main.TRUE
1303 else:
1304 main.log.error( "Error with REST request, response was: " +
1305 str( response ) )
1306 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001307 except ( AttributeError, TypeError ):
1308 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001309 return None
Jon Halle401b092015-09-23 13:34:24 -07001310 except Exception:
1311 main.log.exception( self.name + ": Uncaught exception!" )
1312 main.cleanup()
1313 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001314
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001315 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1316 """
1317 Description:
1318 Check if all the current flows are in ADDED state
1319 Return:
1320 returnValue - Returns main.TRUE only if all flows are in
1321 return main.FALSE otherwise;
1322 Returns None for exception
1323 """
1324 try:
1325 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1326 returnValue = main.TRUE
1327 for flow in tempFlows:
1328 if flow.get( 'state' ) != 'ADDED':
1329 main.log.info( self.name + ": flow Id: " +
1330 str( flow.get( 'groupId' ) ) +
1331 " | state:" +
1332 str( flow.get( 'state' ) ) )
1333 returnValue = main.FALSE
1334 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001335 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001336 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001337 return None
1338 except Exception:
1339 main.log.exception( self.name + ": Uncaught exception!" )
1340 main.cleanup()
1341 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001342
1343 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1344 subjectClass=None, subjectKey=None, configKey=None ):
1345 """
1346 Description:
1347 Get a json object with the ONOS network configurations
1348 Returns:
1349 A json object containing the network configuration in
1350 ONOS; Returns main.FALSE if error on requests;
1351 Returns None for exception
1352 """
1353 try:
1354 output = None
1355 if ip == "DEFAULT":
1356 main.log.warn( "No ip given, reverting to ip from topo file" )
1357 ip = self.ip_address
1358 if port == "DEFAULT":
1359 main.log.warn( "No port given, reverting to port " +
1360 "from topo file" )
1361 port = self.port
1362 url = "/network/configuration"
1363 if subjectClass:
1364 url += "/" + subjectClass
1365 if subjectKey:
1366 url += "/" + subjectKey
1367 if configKey:
1368 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001369 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001370 if response:
1371 if 200 <= response[ 0 ] <= 299:
1372 output = response[ 1 ]
1373 a = json.loads( output )
1374 b = json.dumps( a )
1375 return b
1376 elif response[ 0 ] == 404:
1377 main.log.error( "Requested configuration doesn't exist: " +
1378 str( response ) )
1379 return {}
1380 else:
1381 main.log.error( "Error with REST request, response was: " +
1382 str( response ) )
1383 return main.FALSE
1384 except ( AttributeError, TypeError ):
1385 main.log.exception( self.name + ": Object not as expected" )
1386 return None
1387 except Exception:
1388 main.log.exception( self.name + ": Uncaught exception!" )
1389 main.cleanup()
1390 main.exit()
1391
1392 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1393 subjectClass=None, subjectKey=None, configKey=None ):
1394 """
1395 Description:
1396 Set a json object with the ONOS network configurations
1397 Returns:
1398 Returns main.TRUE for successful requests; Returns main.FALSE
1399 if error on requests;
1400 Returns None for exceptions
1401
1402 """
1403 try:
1404 output = None
1405 if ip == "DEFAULT":
1406 main.log.warn( "No ip given, reverting to ip from topo file" )
1407 ip = self.ip_address
1408 if port == "DEFAULT":
1409 main.log.warn( "No port given, reverting to port " +
1410 "from topo file" )
1411 port = self.port
1412 url = "/network/configuration"
1413 if subjectClass:
1414 url += "/" + subjectClass
1415 if subjectKey:
1416 url += "/" + subjectKey
1417 if configKey:
1418 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001419 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001420 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001421 data=json.dumps( cfgJson ) )
1422 if response:
1423 if 200 <= response[ 0 ] <= 299:
1424 main.log.info( self.name + ": Successfully POST cfg" )
1425 return main.TRUE
1426 else:
1427 main.log.error( "Error with REST request, response was: " +
1428 str( response ) )
1429 return main.FALSE
1430 except ( AttributeError, TypeError ):
1431 main.log.exception( self.name + ": Object not as expected" )
1432 return None
1433 except Exception:
1434 main.log.exception( self.name + ": Uncaught exception!" )
1435 main.cleanup()
1436 main.exit()
1437
1438 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1439 subjectClass=None, subjectKey=None, configKey=None ):
1440 """
1441 Description:
1442 Remove a json object from the ONOS network configurations
1443 Returns:
1444 Returns main.TRUE for successful requests; Returns main.FALSE
1445 if error on requests;
1446 Returns None for exceptions
1447
1448 """
1449 try:
1450 output = None
1451 if ip == "DEFAULT":
1452 main.log.warn( "No ip given, reverting to ip from topo file" )
1453 ip = self.ip_address
1454 if port == "DEFAULT":
1455 main.log.warn( "No port given, reverting to port " +
1456 "from topo file" )
1457 port = self.port
1458 url = "/network/configuration"
1459 if subjectClass:
1460 url += "/" + subjectClass
1461 if subjectKey:
1462 url += "/" + subjectKey
1463 if configKey:
1464 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001465 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001466 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001467 if response:
1468 if 200 <= response[ 0 ] <= 299:
1469 main.log.info( self.name + ": Successfully delete cfg" )
1470 return main.TRUE
1471 else:
1472 main.log.error( "Error with REST request, response was: " +
1473 str( response ) )
1474 return main.FALSE
1475 except ( AttributeError, TypeError ):
1476 main.log.exception( self.name + ": Object not as expected" )
1477 return None
1478 except Exception:
1479 main.log.exception( self.name + ": Uncaught exception!" )
1480 main.cleanup()
1481 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001482
1483 def createFlowBatch( self,
1484 numSw = 1,
1485 swIndex = 1,
1486 batchSize = 1,
1487 batchIndex = 1,
1488 deviceIdpreFix = "of:",
1489 appId=0,
1490 deviceID="",
1491 ingressPort="",
1492 egressPort="",
1493 ethType="",
1494 ethSrc="",
1495 ethDst="",
1496 vlan="",
1497 ipProto="",
1498 ipSrc=(),
1499 ipDst=(),
1500 tcpSrc="",
1501 tcpDst="",
1502 udpDst="",
1503 udpSrc="",
1504 mpls="",
1505 ip="DEFAULT",
1506 port="DEFAULT",
1507 debug=False ):
1508 """
1509 Description:
1510 Creates batches of MAC-rule flows for POST.
1511 Predefined MAC: 2 MS Hex digit for iterating devices
1512 Next 5 Hex digit for iterating batch numbers
1513 Next 5 Hex digit for iterating flows within a batch
1514 Required:
1515 * deviceId: id of the device
1516 Optional:
1517 * ingressPort: port ingress device
1518 * egressPort: port of egress device
1519 * ethType: specify ethType
1520 * ethSrc: specify ethSrc ( i.e. src mac addr )
1521 * ethDst: specify ethDst ( i.e. dst mac addr )
1522 * ipProto: specify ip protocol
1523 * ipSrc: specify ip source address with mask eg. ip#/24
1524 as a tuple (type, ip#)
1525 * ipDst: specify ip destination address eg. ip#/24
1526 as a tuple (type, ip#)
1527 * tcpSrc: specify tcp source port
1528 * tcpDst: specify tcp destination port
1529 Returns:
1530 Returns main.TRUE for successful requests; Returns main.FALSE
1531 if error on requests;
1532 Returns None for exceptions
1533 NOTE:
1534 The ip and port option are for the requests input's ip and port
1535 of the ONOS node
1536 """
1537 #from pprint import pprint
1538
1539 flowJsonList = []
1540 flowJsonBatch = {"flows":flowJsonList}
1541 dev = swIndex
1542
1543 for fl in range(1, batchSize + 1):
1544 flowJson = { "priority":100,
1545 "deviceId":"",
1546 "isPermanent":"true",
1547 "timeout":0,
1548 "treatment":{"instructions":[]},
1549 "selector": {"criteria":[]}}
1550
1551 #main.log.info("fl: " + str(fl))
1552 if dev <= numSw:
1553 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1554 #print deviceId
1555 flowJson['deviceId'] = deviceId
1556 dev += 1
1557 else:
1558 dev = 1
1559 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1560 #print deviceId
1561 flowJson['deviceId'] = deviceId
1562 dev += 1
1563
1564 # ethSrc starts with "0"; ethDst starts with "1"
1565 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1566 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1567 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1568 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1569 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1570 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1571 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1572
1573 if appId:
1574 flowJson[ "appId" ] = appId
1575
1576 if egressPort:
1577 flowJson[ 'treatment' ][ 'instructions' ].append( {
1578 "type":"OUTPUT",
1579 "port":egressPort } )
1580 if ingressPort:
1581 flowJson[ 'selector' ][ 'criteria' ].append( {
1582 "type":"IN_PORT",
1583 "port":ingressPort } )
1584 if ethType:
1585 flowJson[ 'selector' ][ 'criteria' ].append( {
1586 "type":"ETH_TYPE",
1587 "ethType":ethType } )
1588 if ethSrc:
1589 flowJson[ 'selector' ][ 'criteria' ].append( {
1590 "type":"ETH_SRC",
1591 "mac":ethSrc } )
1592 if ethDst:
1593 flowJson[ 'selector' ][ 'criteria' ].append( {
1594 "type":"ETH_DST",
1595 "mac":ethDst } )
1596 if vlan:
1597 flowJson[ 'selector' ][ 'criteria' ].append( {
1598 "type":"VLAN_VID",
1599 "vlanId":vlan } )
1600 if mpls:
1601 flowJson[ 'selector' ][ 'criteria' ].append( {
1602 "type":"MPLS_LABEL",
1603 "label":mpls } )
1604 if ipSrc:
1605 flowJson[ 'selector' ][ 'criteria' ].append( {
1606 "type":ipSrc[0],
1607 "ip":ipSrc[1] } )
1608 if ipDst:
1609 flowJson[ 'selector' ][ 'criteria' ].append( {
1610 "type":ipDst[0],
1611 "ip":ipDst[1] } )
1612 if tcpSrc:
1613 flowJson[ 'selector' ][ 'criteria' ].append( {
1614 "type":"TCP_SRC",
1615 "tcpPort": tcpSrc } )
1616 if tcpDst:
1617 flowJson[ 'selector' ][ 'criteria' ].append( {
1618 "type":"TCP_DST",
1619 "tcpPort": tcpDst } )
1620 if udpSrc:
1621 flowJson[ 'selector' ][ 'criteria' ].append( {
1622 "type":"UDP_SRC",
1623 "udpPort": udpSrc } )
1624 if udpDst:
1625 flowJson[ 'selector' ][ 'criteria' ].append( {
1626 "type":"UDP_DST",
1627 "udpPort": udpDst } )
1628 if ipProto:
1629 flowJson[ 'selector' ][ 'criteria' ].append( {
1630 "type":"IP_PROTO",
1631 "protocol": ipProto } )
1632 #pprint(flowJson)
1633 flowJsonList.append(flowJson)
1634
1635 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1636 flowJsonBatch['flows'] = flowJsonList
1637 #pprint(flowJsonBatch)
1638
1639 return flowJsonBatch
1640
1641
1642 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1643 """
1644 Description:
1645 Sends a single flow batch through /flows REST API.
1646 Required:
1647 * The batch of flows
1648 Returns:
1649 Returns main.TRUE for successful requests; Returns main.FALSE
1650 if error on requests;
1651 Returns None for exceptions
1652 NOTE:
1653 The ip and port option are for the requests input's ip and port
1654 of the ONOS node
1655 """
1656 import time
1657
1658 try:
1659 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1660 output = None
1661 if ip == "DEFAULT":
1662 main.log.warn( "No ip given, reverting to ip from topo file" )
1663 ip = self.ip_address
1664 if port == "DEFAULT":
1665 main.log.warn( "No port given, reverting to port " +
1666 "from topo file" )
1667 port = self.port
1668 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001669 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001670 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001671 data=json.dumps( batch ) )
1672 #main.log.info("Post response is: ", str(response[0]))
1673 if response[0] == 200:
1674 main.log.info( self.name + ": Successfully POST flow batch" )
1675 return main.TRUE, response
1676 else:
1677 main.log.error( "Error with REST request, response was: " +
1678 str( response ) )
1679 return main.FALSE
1680 except NotImplementedError as e:
1681 raise e # Inform the caller
1682 except ( AttributeError, TypeError ):
1683 main.log.exception( self.name + ": Object not as expected" )
1684 return None
1685 except Exception:
1686 main.log.exception( self.name + ": Uncaught exception!" )
1687 main.cleanup()
1688 main.exit()
1689
1690 def removeFlowBatch( self, batch={},
1691 ip="DEFAULT", port="DEFAULT" ):
1692 """
1693 Description:
1694 Remove a batch of flows
1695 Required:
1696 flow batch
1697 Return:
1698 Returns main.TRUE if successfully deletes flows, otherwise
1699 Returns main.FALSE, Returns None on error
1700 """
1701 try:
1702 output = None
1703 if ip == "DEFAULT":
1704 main.log.warn( "No ip given, reverting to ip from topo file" )
1705 ip = self.ip_address
1706 if port == "DEFAULT":
1707 main.log.warn( "No port given, reverting to port " +
1708 "from topo file" )
1709 port = self.port
1710 # NOTE: REST url requires the intent id to be in decimal form
1711
suibin zhang116647a2016-05-06 16:30:09 -07001712 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001713 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001714 data = json.dumps(batch) )
1715 if response:
1716 if 200 <= response[ 0 ] <= 299:
1717 return main.TRUE
1718 else:
1719 main.log.error( "Error with REST request, response was: " +
1720 str( response ) )
1721 return main.FALSE
1722 except ( AttributeError, TypeError ):
1723 main.log.exception( self.name + ": Object not as expected" )
1724 return None
1725 except Exception:
1726 main.log.exception( self.name + ": Uncaught exception!" )
1727 main.cleanup()
1728 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001729
1730 def getTopology( self, topologyOutput ):
1731 """
1732 Definition:
1733 Loads a json topology output
1734 Return:
1735 topology = current ONOS topology
1736 """
1737 import json
1738 try:
1739 # either onos:topology or 'topology' will work in CLI
1740 topology = json.loads(topologyOutput)
1741 main.log.debug( topology )
1742 return topology
1743 except pexpect.EOF:
1744 main.log.error( self.name + ": EOF exception found" )
1745 main.log.error( self.name + ": " + self.handle.before )
1746 main.cleanup()
1747 main.exit()
1748 except Exception:
1749 main.log.exception( self.name + ": Uncaught exception!" )
1750 main.cleanup()
1751 main.exit()
1752
1753 def checkStatus(
1754 self,
1755 topologyResult,
1756 numoswitch,
1757 numolink,
1758 logLevel="info" ):
1759 """
1760 Checks the number of switches & links that ONOS sees against the
1761 supplied values. By default this will report to main.log, but the
1762 log level can be specific.
1763
1764 Params: topologyResult = the output of topology command
1765 numoswitch = expected number of switches
1766 numolink = expected number of links
1767 logLevel = level to log to.
1768 Currently accepts 'info', 'warn' and 'report'
1769
1770 Returns: main.TRUE if the number of switches and links are correct,
1771 main.FALSE if the number of switches and links is incorrect,
1772 and main.ERROR otherwise
1773 """
1774 try:
1775 topology = self.getTopology( topologyResult )
1776 if topology == {}:
1777 return main.ERROR
1778 output = ""
1779 # Is the number of switches is what we expected
1780 devices = topology.get( 'devices', False )
1781 links = topology.get( 'links', False )
1782 if devices is False or links is False:
1783 return main.ERROR
1784 switchCheck = ( int( devices ) == int( numoswitch ) )
1785 # Is the number of links is what we expected
1786 linkCheck = ( int( links ) == int( numolink ) )
1787 if switchCheck and linkCheck:
1788 # We expected the correct numbers
1789 output = output + "The number of links and switches match "\
1790 + "what was expected"
1791 result = main.TRUE
1792 else:
1793 output = output + \
1794 "The number of links and switches does not match " + \
1795 "what was expected"
1796 result = main.FALSE
1797 output = output + "\n ONOS sees %i devices" % int( devices )
1798 output = output + " (%i expected) " % int( numoswitch )
1799 output = output + "and %i links " % int( links )
1800 output = output + "(%i expected)" % int( numolink )
1801 if logLevel == "report":
1802 main.log.report( output )
1803 elif logLevel == "warn":
1804 main.log.warn( output )
1805 else:
1806 main.log.info( output )
1807 return result
1808 except pexpect.EOF:
1809 main.log.error( self.name + ": EOF exception found" )
1810 main.log.error( self.name + ": " + self.handle.before )
1811 main.cleanup()
1812 main.exit()
1813 except Exception:
1814 main.log.exception( self.name + ": Uncaught exception!" )
1815 main.cleanup()
1816 main.exit()