blob: 08f541e1bd818ccb55156e1a5fa17d003f756e9f [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
5
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jon Hallfc915882015-07-14 13:33:17 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 ( at your option ) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23"""
24import json
25import os
26import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070027import types
Jon Halle401b092015-09-23 13:34:24 -070028import sys
Jon Hallfc915882015-07-14 13:33:17 -070029
Jon Hallfc915882015-07-14 13:33:17 -070030from drivers.common.api.controllerdriver import Controller
31
32
33class OnosRestDriver( Controller ):
34
35 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070036 self.pwd = None
37 self.user_name = "user"
Jon Hallfc915882015-07-14 13:33:17 -070038 super( Controller, self ).__init__()
39 self.ip_address = "localhost"
40 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070041 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070042
43 def connect( self, **connectargs ):
44 try:
45 for key in connectargs:
46 vars( self )[ key ] = connectargs[ key ]
47 self.name = self.options[ 'name' ]
48 except Exception as e:
49 main.log.exception( e )
50 try:
51 if os.getenv( str( self.ip_address ) ) != None:
52 self.ip_address = os.getenv( str( self.ip_address ) )
53 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070054 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070055 except KeyError:
56 main.log.info( "Invalid host name," +
57 "defaulting to 'localhost' instead" )
58 self.ip_address = 'localhost'
59 except Exception as inst:
60 main.log.error( "Uncaught exception: " + str( inst ) )
61
62 self.handle = super( OnosRestDriver, self ).connect()
63 return self.handle
64
Jon Halle401b092015-09-23 13:34:24 -070065 def pprint( self, jsonObject ):
66 """
67 Pretty Prints a json object
68
69 arguments:
70 jsonObject - a parsed json object
71 returns:
72 A formatted string for printing or None on error
73 """
74 try:
75 if isinstance( jsonObject, str ):
76 jsonObject = json.loads( jsonObject )
77 return json.dumps( jsonObject, sort_keys=True,
78 indent=4, separators=(',', ': '))
79 except ( TypeError, ValueError ):
80 main.log.exception( "Error parsing jsonObject" )
81 return None
82
suibin zhangd5b6fe42016-05-12 08:48:58 -070083 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070084 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070085 """
86 Arguments:
87 str ip: ONOS IP Address
88 str port: ONOS REST Port
89 str url: ONOS REST url path.
90 NOTE that this is is only the relative path. IE "/devices"
91 str base: The base url for the given REST api. Applications could
92 potentially have their own base url
93 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070094 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070095 the request
96 dict data: Dictionary to be sent in the body of the request
97 """
98 # TODO: Authentication - simple http (user,pass) tuple
99 # TODO: should we maybe just pass kwargs straight to response?
100 # TODO: Do we need to allow for other protocols besides http?
101 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700102 if ip == "DEFAULT":
103 main.log.warn( "No ip given, reverting to ip from topo file" )
104 ip = self.ip_address
105 if port == "DEFAULT":
106 main.log.warn( "No port given, reverting to port " +
107 "from topo file" )
108 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700109
Jon Hallfc915882015-07-14 13:33:17 -0700110 try:
111 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700112 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800113 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700114 auth = (self.user_name, self.pwd)
115 else:
116 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700117 main.log.info( "Sending request " + path + " using " +
118 method.upper() + " method." )
119 response = requests.request( method.upper(),
120 path,
121 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700122 data=data,
123 auth=auth )
124 if debug:
125 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700126 return ( response.status_code, response.text.encode( 'utf8' ) )
127 except requests.exceptions:
128 main.log.exception( "Error sending request." )
129 return None
Jon Halle401b092015-09-23 13:34:24 -0700130 except Exception:
131 main.log.exception( self.name + ": Uncaught exception!" )
132 main.cleanup()
133 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700134
135 def intents( self, ip="DEFAULT", port="DEFAULT" ):
136 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700137 Description:
138 Gets a list of dictionary of all intents in the system
139 Returns:
140 A list of dictionary of intents in string type to match the cli
141 version for now; Returns main.FALSE if error on request;
142 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700143 """
144 try:
145 output = None
146 if ip == "DEFAULT":
147 main.log.warn( "No ip given, reverting to ip from topo file" )
148 ip = self.ip_address
149 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700150 main.log.warn( "No port given, reverting to port " +
151 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700152 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700153 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700154 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700155 if 200 <= response[ 0 ] <= 299:
156 output = response[ 1 ]
157 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700158 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700159 b = json.dumps( a )
160 return b
Jon Hallfc915882015-07-14 13:33:17 -0700161 else:
162 main.log.error( "Error with REST request, response was: " +
163 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700164 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700165 except ( AttributeError, AssertionError, TypeError ):
166 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700167 return None
Jon Halle401b092015-09-23 13:34:24 -0700168 except Exception:
169 main.log.exception( self.name + ": Uncaught exception!" )
170 main.cleanup()
171 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700172
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700173 def intent( self, intentId, appId="org.onosproject.cli",
174 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700175 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700176 Description:
177 Get the specific intent information of the given application ID and
178 intent ID
179 Required:
180 str intentId - Intent id in hexadecimal form
181 Optional:
182 str appId - application id of intent
183 Returns:
184 Returns an information dictionary of the given intent;
185 Returns main.FALSE if error on requests; Returns None for exception
186 NOTE:
187 The GET /intents REST api command accepts application id but the
188 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700189 """
190 try:
191 output = None
192 if ip == "DEFAULT":
193 main.log.warn( "No ip given, reverting to ip from topo file" )
194 ip = self.ip_address
195 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700196 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700197 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700198 port = self.port
199 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700200 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700201 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700202 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700203 if 200 <= response[ 0 ] <= 299:
204 output = response[ 1 ]
205 a = json.loads( output )
206 return a
Jon Hallfc915882015-07-14 13:33:17 -0700207 else:
208 main.log.error( "Error with REST request, response was: " +
209 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700210 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700211 except ( AttributeError, TypeError ):
212 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700213 return None
Jon Halle401b092015-09-23 13:34:24 -0700214 except Exception:
215 main.log.exception( self.name + ": Uncaught exception!" )
216 main.cleanup()
217 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700218
219 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
220 """
221 Description:
222 Gets all intents ID using intents function
223 Returns:
224 List of intents ID;Returns None for exception; Returns None for
225 exception; Returns None for exception
226 """
227 try:
228 intentsDict = {}
229 intentsIdList = []
230 intentsDict = json.loads( self.intents( ip=ip, port=port ) )
231 for intent in intentsDict:
232 intentsIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700233 if not intentsIdList:
234 main.log.debug( "Cannot find any intents" )
235 return main.FALSE
236 else:
237 main.log.info( "Found intents: " + str( intentsIdList ) )
238 return main.TRUE
Jon Halle401b092015-09-23 13:34:24 -0700239 except ( AttributeError, TypeError ):
240 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700241 return None
Jon Halle401b092015-09-23 13:34:24 -0700242 except Exception:
243 main.log.exception( self.name + ": Uncaught exception!" )
244 main.cleanup()
245 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700246
247 def apps( self, ip="DEFAULT", port="DEFAULT" ):
248 """
249 Description:
250 Returns all the current application installed in the system
251 Returns:
252 List of dictionary of installed application; Returns main.FALSE for
253 error on request; Returns None for exception
254 """
255 try:
256 output = None
257 if ip == "DEFAULT":
258 main.log.warn( "No ip given, reverting to ip from topo file" )
259 ip = self.ip_address
260 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700261 main.log.warn( "No port given, reverting to port " +
262 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700263 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700264 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700265 if response:
266 if 200 <= response[ 0 ] <= 299:
267 output = response[ 1 ]
268 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700269 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700270 b = json.dumps( a )
271 return b
272 else:
273 main.log.error( "Error with REST request, response was: " +
274 str( response ) )
275 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700276 except ( AttributeError, AssertionError, TypeError ):
277 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700278 return None
Jon Halle401b092015-09-23 13:34:24 -0700279 except Exception:
280 main.log.exception( self.name + ": Uncaught exception!" )
281 main.cleanup()
282 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700283
284 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
285 """
286 Decription:
287 Activate an app that is already installed in ONOS
288 Optional:
289 bool check - If check is True, method will check the status
290 of the app after the command is issued
291 Returns:
292 Returns main.TRUE if the command was successfully or main.FALSE
293 if the REST responded with an error or given incorrect input;
294 Returns None for exception
295
296 """
297 try:
298 output = None
299 if ip == "DEFAULT":
300 main.log.warn( "No ip given, reverting to ip from topo file" )
301 ip = self.ip_address
302 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700303 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700304 "from topo file" )
305 port = self.port
306 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700307 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700308 url="/applications" + query,
309 ip = ip, port = port)
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700310 if response:
311 output = response[ 1 ]
312 app = json.loads( output )
313 if 200 <= response[ 0 ] <= 299:
314 if check:
315 if app.get( 'state' ) == 'ACTIVE':
316 main.log.info( self.name + ": " + appName +
317 " application" +
318 " is in ACTIVE state" )
319 return main.TRUE
320 else:
321 main.log.error( self.name + ": " + appName +
322 " application" + " is in " +
323 app.get( 'state' ) + " state" )
324 return main.FALSE
325 else:
326 main.log.warn( "Skipping " + appName +
327 "application check" )
328 return main.TRUE
329 else:
330 main.log.error( "Error with REST request, response was: " +
331 str( response ) )
332 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700333 except ( AttributeError, TypeError ):
334 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700335 return None
Jon Halle401b092015-09-23 13:34:24 -0700336 except Exception:
337 main.log.exception( self.name + ": Uncaught exception!" )
338 main.cleanup()
339 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700340
341 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
342 check=True ):
343 """
344 Required:
345 Deactivate an app that is already activated in ONOS
346 Optional:
347 bool check - If check is True, method will check the status of the
348 app after the command is issued
349 Returns:
350 Returns main.TRUE if the command was successfully sent
351 main.FALSE if the REST responded with an error or given
352 incorrect input; Returns None for exception
353 """
354 try:
355 output = None
356 if ip == "DEFAULT":
357 main.log.warn( "No ip given, reverting to ip from topo file" )
358 ip = self.ip_address
359 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700360 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700361 "from topo file" )
362 port = self.port
363 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700364 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700365 url="/applications" + query,
366 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700367 if response:
368 output = response[ 1 ]
369 app = json.loads( output )
370 if 200 <= response[ 0 ] <= 299:
371 if check:
372 if app.get( 'state' ) == 'INSTALLED':
373 main.log.info( self.name + ": " + appName +
374 " application" +
375 " is in INSTALLED state" )
376 return main.TRUE
377 else:
378 main.log.error( self.name + ": " + appName +
379 " application" + " is in " +
380 app.get( 'state' ) + " state" )
381 return main.FALSE
382 else:
383 main.log.warn( "Skipping " + appName +
384 "application check" )
385 return main.TRUE
386 else:
387 main.log.error( "Error with REST request, response was: " +
388 str( response ) )
389 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700390 except ( AttributeError, TypeError ):
391 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700392 return None
Jon Halle401b092015-09-23 13:34:24 -0700393 except Exception:
394 main.log.exception( self.name + ": Uncaught exception!" )
395 main.cleanup()
396 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700397
398 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
399 port="DEFAULT" ):
400 """
401 Decription:
402 Gets the informaion of the given application
403 Required:
404 str name - Name of onos application
405 Returns:
406 Returns a dictionary of information ONOS application in string type;
407 Returns main.FALSE if error on requests; Returns None for exception
408 """
409 try:
410 output = None
411 if ip == "DEFAULT":
412 main.log.warn( "No ip given, reverting to ip from topo file" )
413 ip = self.ip_address
414 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700415 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700416 "from topo file" )
417 port = self.port
418 query = "/" + project + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700419 response = self.send( url="/applications" + query,
420 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700421 if response:
422 if 200 <= response[ 0 ] <= 299:
423 output = response[ 1 ]
424 a = json.loads( output )
425 return a
426 else:
427 main.log.error( "Error with REST request, response was: " +
428 str( response ) )
429 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700430 except ( AttributeError, TypeError ):
431 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700432 return None
Jon Halle401b092015-09-23 13:34:24 -0700433 except Exception:
434 main.log.exception( self.name + ": Uncaught exception!" )
435 main.cleanup()
436 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700437
438 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700439 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700440 """
441 Description:
442 Adds a host-to-host intent ( bidirectional ) by
443 specifying the two hosts.
444 Required:
445 * hostIdOne: ONOS host id for host1
446 * hostIdTwo: ONOS host id for host2
447 Optional:
448 str appId - Application name of intent identifier
449 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700450 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700451 error on requests; Returns None for exceptions
452 """
453 try:
454 intentJson = {"two": str( hostIdTwo ),
455 "selector": {"criteria": []}, "priority": 7,
456 "treatment": {"deferred": [], "instructions": []},
457 "appId": appId, "one": str( hostIdOne ),
458 "type": "HostToHostIntent",
459 "constraints": [{"type": "LinkTypeConstraint",
460 "types": ["OPTICAL"],
461 "inclusive": 'false' }]}
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700462 if vlanId:
463 intentJson[ 'selector' ][ 'criteria' ].append( { "type":"VLAN_VID",
464 "vlanId":vlanId } )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700465 output = None
466 if ip == "DEFAULT":
467 main.log.warn( "No ip given, reverting to ip from topo file" )
468 ip = self.ip_address
469 if port == "DEFAULT":
470 main.log.warn( "No port given, reverting to port " +
471 "from topo file" )
472 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700473 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700474 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700475 data=json.dumps( intentJson ) )
476 if response:
477 if 201:
478 main.log.info( self.name + ": Successfully POST host" +
479 " intent between host: " + hostIdOne +
480 " and host: " + hostIdTwo )
481 return main.TRUE
482 else:
483 main.log.error( "Error with REST request, response was: " +
484 str( response ) )
485 return main.FALSE
486
Jon Halle401b092015-09-23 13:34:24 -0700487 except ( AttributeError, TypeError ):
488 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700489 return None
Jon Halle401b092015-09-23 13:34:24 -0700490 except Exception:
491 main.log.exception( self.name + ": Uncaught exception!" )
492 main.cleanup()
493 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700494
kelvin-onlabb50074f2015-07-27 16:18:32 -0700495 def addPointIntent( self,
496 ingressDevice,
497 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700498 appId='org.onosproject.cli',
499 ingressPort="",
500 egressPort="",
501 ethType="",
502 ethSrc="",
503 ethDst="",
504 bandwidth="",
505 lambdaAlloc=False,
506 ipProto="",
507 ipSrc="",
508 ipDst="",
509 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700510 tcpDst="",
511 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700512 port="DEFAULT",
513 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700514 """
515 Description:
516 Adds a point-to-point intent ( uni-directional ) by
517 specifying device id's and optional fields
518 Required:
519 * ingressDevice: device id of ingress device
520 * egressDevice: device id of egress device
521 Optional:
522 * ethType: specify ethType
523 * ethSrc: specify ethSrc ( i.e. src mac addr )
524 * ethDst: specify ethDst ( i.e. dst mac addr )
525 * bandwidth: specify bandwidth capacity of link (TODO)
526 * lambdaAlloc: if True, intent will allocate lambda
527 for the specified intent (TODO)
528 * ipProto: specify ip protocol
529 * ipSrc: specify ip source address with mask eg. ip#/24
530 * ipDst: specify ip destination address eg. ip#/24
531 * tcpSrc: specify tcp source port
532 * tcpDst: specify tcp destination port
533 Returns:
534 Returns main.TRUE for successful requests; Returns main.FALSE if
535 no ingress|egress port found and if error on requests;
536 Returns None for exceptions
537 NOTE:
538 The ip and port option are for the requests input's ip and port
539 of the ONOS node
540 """
541 try:
542 if "/" in ingressDevice:
543 if not ingressPort:
544 ingressPort = ingressDevice.split( "/" )[ 1 ]
545 ingressDevice = ingressDevice.split( "/" )[ 0 ]
546 else:
547 if not ingressPort:
548 main.log.debug( self.name + ": Ingress port not specified" )
549 return main.FALSE
550
551 if "/" in egressDevice:
552 if not egressPort:
553 egressPort = egressDevice.split( "/" )[ 1 ]
554 egressDevice = egressDevice.split( "/" )[ 0 ]
555 else:
556 if not egressPort:
557 main.log.debug( self.name + ": Egress port not specified" )
558 return main.FALSE
559
560 intentJson ={ "ingressPoint": { "device": ingressDevice,
561 "port": ingressPort },
562 "selector": { "criteria": [] },
563 "priority": 55,
564 "treatment": { "deferred": [],
565 "instructions": [] },
566 "egressPoint": { "device": egressDevice,
567 "port": egressPort },
568 "appId": appId,
569 "type": "PointToPointIntent",
570 "constraints": [ { "type": "LinkTypeConstraint",
571 "types": [ "OPTICAL" ],
572 "inclusive": "false" } ] }
573
574 if ethType == "IPV4":
575 intentJson[ 'selector' ][ 'criteria' ].append( {
576 "type":"ETH_TYPE",
577 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700578 elif ethType:
579 intentJson[ 'selector' ][ 'criteria' ].append( {
580 "type":"ETH_TYPE",
581 "ethType":ethType } )
582
kelvin-onlabb50074f2015-07-27 16:18:32 -0700583 if ethSrc:
584 intentJson[ 'selector' ][ 'criteria' ].append(
585 { "type":"ETH_SRC",
586 "mac":ethSrc } )
587 if ethDst:
588 intentJson[ 'selector' ][ 'criteria' ].append(
589 { "type":"ETH_DST",
590 "mac":ethDst } )
591 if ipSrc:
592 intentJson[ 'selector' ][ 'criteria' ].append(
593 { "type":"IPV4_SRC",
594 "ip":ipSrc } )
595 if ipDst:
596 intentJson[ 'selector' ][ 'criteria' ].append(
597 { "type":"IPV4_DST",
598 "ip":ipDst } )
599 if tcpSrc:
600 intentJson[ 'selector' ][ 'criteria' ].append(
601 { "type":"TCP_SRC",
602 "tcpPort": tcpSrc } )
603 if tcpDst:
604 intentJson[ 'selector' ][ 'criteria' ].append(
605 { "type":"TCP_DST",
606 "tcpPort": tcpDst } )
607 if ipProto:
608 intentJson[ 'selector' ][ 'criteria' ].append(
609 { "type":"IP_PROTO",
610 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700611 if vlanId:
612 intentJson[ 'selector' ][ 'criteria' ].append(
613 { "type":"VLAN_VID",
614 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700615
616 # TODO: Bandwidth and Lambda will be implemented if needed
617
618 main.log.debug( intentJson )
619
620 output = None
621 if ip == "DEFAULT":
622 main.log.warn( "No ip given, reverting to ip from topo file" )
623 ip = self.ip_address
624 if port == "DEFAULT":
625 main.log.warn( "No port given, reverting to port " +
626 "from topo file" )
627 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700628 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700629 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700630 data=json.dumps( intentJson ) )
631 if response:
632 if 201:
633 main.log.info( self.name + ": Successfully POST point" +
634 " intent between ingress: " + ingressDevice +
635 " and egress: " + egressDevice + " devices" )
636 return main.TRUE
637 else:
638 main.log.error( "Error with REST request, response was: " +
639 str( response ) )
640 return main.FALSE
641
Jon Halle401b092015-09-23 13:34:24 -0700642 except ( AttributeError, TypeError ):
643 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700644 return None
Jon Halle401b092015-09-23 13:34:24 -0700645 except Exception:
646 main.log.exception( self.name + ": Uncaught exception!" )
647 main.cleanup()
648 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700649
650 def removeIntent( self, intentId, appId='org.onosproject.cli',
651 ip="DEFAULT", port="DEFAULT" ):
652 """
653 Remove intent for specified application id and intent id;
654 Returns None for exception
655 """
656 try:
657 output = None
658 if ip == "DEFAULT":
659 main.log.warn( "No ip given, reverting to ip from topo file" )
660 ip = self.ip_address
661 if port == "DEFAULT":
662 main.log.warn( "No port given, reverting to port " +
663 "from topo file" )
664 port = self.port
665 # NOTE: REST url requires the intent id to be in decimal form
666 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700667 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700668 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700669 if response:
670 if 200 <= response[ 0 ] <= 299:
671 return main.TRUE
672 else:
673 main.log.error( "Error with REST request, response was: " +
674 str( response ) )
675 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700676 except ( AttributeError, TypeError ):
677 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700678 return None
Jon Halle401b092015-09-23 13:34:24 -0700679 except Exception:
680 main.log.exception( self.name + ": Uncaught exception!" )
681 main.cleanup()
682 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700683
684 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
685 """
686 Returns a list of intents id; Returns None for exception
687 """
688 try:
689 intentIdList = []
690 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700691 for intent in intentsJson:
692 intentIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700693 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700694 except ( AttributeError, TypeError ):
695 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700696 return None
Jon Halle401b092015-09-23 13:34:24 -0700697 except Exception:
698 main.log.exception( self.name + ": Uncaught exception!" )
699 main.cleanup()
700 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700701
702 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
703 ip="DEFAULT", port="DEFAULT", delay=5 ):
704 """
705 Description:
706 Remove all the intents
707 Returns:
708 Returns main.TRUE if all intents are removed, otherwise returns
709 main.FALSE; Returns None for exception
710 """
711 try:
712 results = []
713 if intentIdList == 'ALL':
714 intentIdList = self.getIntentsId( ip=ip, port=port )
715
716 main.log.info( self.name + ": Removing intents " +
717 str( intentIdList ) )
718
719 if isinstance( intentIdList, types.ListType ):
720 for intent in intentIdList:
721 results.append( self.removeIntent( intentId=intent,
722 appId=appId,
723 ip=ip,
724 port=port ) )
725 # Check for remaining intents
726 # NOTE: Noticing some delay on Deleting the intents so i put
727 # this time out
728 import time
729 time.sleep( delay )
730 intentRemain = len( json.loads( self.intents() ) )
731 if all( result==main.TRUE for result in results ) and \
732 intentRemain == 0:
733 main.log.info( self.name + ": All intents are removed " )
734 return main.TRUE
735 else:
736 main.log.error( self.name + ": Did not removed all intents,"
737 + " there are " + str( intentRemain )
738 + " intents remaining" )
739 return main.FALSE
740 else:
741 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700742 except ( AttributeError, TypeError ):
743 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700744 return None
Jon Halle401b092015-09-23 13:34:24 -0700745 except Exception:
746 main.log.exception( self.name + ": Uncaught exception!" )
747 main.cleanup()
748 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700749
750 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
751 """
752 Description:
753 Get a list of dictionary of all discovered hosts
754 Returns:
755 Returns a list of dictionary of information of the hosts currently
756 discovered by ONOS; Returns main.FALSE if error on requests;
757 Returns None for exception
758 """
759 try:
760 output = None
761 if ip == "DEFAULT":
762 main.log.warn( "No ip given, reverting to ip from topo file" )
763 ip = self.ip_address
764 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700765 main.log.warn( "No port given, reverting to port " +
766 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700767 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700768 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700769 if response:
770 if 200 <= response[ 0 ] <= 299:
771 output = response[ 1 ]
772 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700773 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700774 b = json.dumps( a )
775 return b
776 else:
777 main.log.error( "Error with REST request, response was: " +
778 str( response ) )
779 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700780 except ( AttributeError, AssertionError, TypeError ):
781 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700782 return None
Jon Halle401b092015-09-23 13:34:24 -0700783 except Exception:
784 main.log.exception( self.name + ": Uncaught exception!" )
785 main.cleanup()
786 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700787
788 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
789 """
790 Description:
791 Gets the information from the given host
792 Required:
793 str mac - MAC address of the host
794 Optional:
795 str vlan - VLAN tag of the host, defaults to -1
796 Returns:
797 Return the host id from the hosts/mac/vlan output in REST api
798 whose 'id' contains mac/vlan; Returns None for exception;
799 Returns main.FALSE if error on requests
800
801 NOTE:
802 Not sure what this function should do, any suggestion?
803 """
804 try:
805 output = None
806 if ip == "DEFAULT":
807 main.log.warn( "No ip given, reverting to ip from topo file" )
808 ip = self.ip_address
809 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700810 main.log.warn( "No port given, reverting to port " +
811 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700812 port = self.port
813 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700814 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700815 if response:
816 # NOTE: What if the person wants other values? would it be better
817 # to have a function that gets a key and return a value instead?
818 # This function requires mac and vlan and returns an ID which
819 # makes this current function useless
820 if 200 <= response[ 0 ] <= 299:
821 output = response[ 1 ]
822 hostId = json.loads( output ).get( 'id' )
823 return hostId
824 else:
825 main.log.error( "Error with REST request, response was: " +
826 str( response ) )
827 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700828 except ( AttributeError, TypeError ):
829 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700830 return None
Jon Halle401b092015-09-23 13:34:24 -0700831 except Exception:
832 main.log.exception( self.name + ": Uncaught exception!" )
833 main.cleanup()
834 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700835
836 def topology( self, ip="DEFAULT", port="DEFAULT" ):
837 """
838 Description:
839 Gets the overview of network topology
840 Returns:
841 Returns a dictionary containing information about network topology;
842 Returns None for exception
843 """
844 try:
845 output = None
846 if ip == "DEFAULT":
847 main.log.warn( "No ip given, reverting to ip from topo file" )
848 ip = self.ip_address
849 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700850 main.log.warn( "No port given, reverting to port " +
851 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700852 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700853 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700854 if response:
855 if 200 <= response[ 0 ] <= 299:
856 output = response[ 1 ]
857 a = json.loads( output )
858 b = json.dumps( a )
859 return b
860 else:
861 main.log.error( "Error with REST request, response was: " +
862 str( response ) )
863 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700864 except ( AttributeError, TypeError ):
865 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700866 return None
Jon Halle401b092015-09-23 13:34:24 -0700867 except Exception:
868 main.log.exception( self.name + ": Uncaught exception!" )
869 main.cleanup()
870 main.exit()
871
872 def devices( self, ip="DEFAULT", port="DEFAULT" ):
873 """
874 Description:
875 Get the devices discovered by ONOS is json string format
876 Returns:
877 a json string of the devices currently discovered by ONOS OR
878 main.FALSE if there is an error in the request OR
879 Returns None for exception
880 """
881 try:
882 output = None
883 if ip == "DEFAULT":
884 main.log.warn( "No ip given, reverting to ip from topo file" )
885 ip = self.ip_address
886 if port == "DEFAULT":
887 main.log.warn( "No port given, reverting to port " +
888 "from topo file" )
889 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700890 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -0700891 if response:
892 if 200 <= response[ 0 ] <= 299:
893 output = response[ 1 ]
894 a = json.loads( output ).get( 'devices' )
895 assert a is not None, "Error parsing json object"
896 b = json.dumps( a )
897 return b
898 else:
899 main.log.error( "Error with REST request, response was: " +
900 str( response ) )
901 return main.FALSE
902 except ( AttributeError, AssertionError, TypeError ):
903 main.log.exception( self.name + ": Object not as expected" )
904 return None
905 except Exception:
906 main.log.exception( self.name + ": Uncaught exception!" )
907 main.cleanup()
908 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700909
910 def getIntentState( self, intentsId, intentsJson=None,
911 ip="DEFAULT", port="DEFAULT" ):
912 """
913 Description:
914 Get intent state.
915 Accepts a single intent ID (string type) or a list of intent IDs.
916 Returns the state(string type) of the id if a single intent ID is
917 accepted.
918 Required:
919 intentId: intent ID (string type)
920 intentsJson: parsed json object from the onos:intents api
921 Returns:
922 Returns a dictionary with intent IDs as the key and its
923 corresponding states as the values; Returns None for invalid IDs or
924 Type error and any exceptions
925 NOTE:
926 An intent's state consist of INSTALLED,WITHDRAWN etc.
927 """
928 try:
929 state = "State is Undefined"
930 if not intentsJson:
931 intentsJsonTemp = json.loads( self.intents() )
932 else:
933 intentsJsonTemp = json.loads( intentsJson )
934 if isinstance( intentsId, types.StringType ):
935 for intent in intentsJsonTemp:
936 if intentsId == intent[ 'id' ]:
937 state = intent[ 'state' ]
938 return state
939 main.log.info( "Cannot find intent ID" + str( intentsId ) +
940 " on the list" )
941 return state
942 elif isinstance( intentsId, types.ListType ):
943 dictList = []
944 for i in xrange( len( intentsId ) ):
945 stateDict = {}
946 for intents in intentsJsonTemp:
947 if intentsId[ i ] == intents[ 'id' ]:
948 stateDict[ 'state' ] = intents[ 'state' ]
949 stateDict[ 'id' ] = intentsId[ i ]
950 dictList.append( stateDict )
951 break
952 if len( intentsId ) != len( dictList ):
953 main.log.info( "Cannot find some of the intent ID state" )
954 return dictList
955 else:
956 main.log.info( "Invalid intents ID entry" )
957 return None
958
Jon Halle401b092015-09-23 13:34:24 -0700959 except ( AttributeError, TypeError ):
960 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700961 return None
Jon Halle401b092015-09-23 13:34:24 -0700962 except Exception:
963 main.log.exception( self.name + ": Uncaught exception!" )
964 main.cleanup()
965 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700966
967 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
968 ip="DEFAULT", port="DEFAULT"):
969 """
970 Description:
971 Check intents state based on expected state which defaults to
972 INSTALLED state
973 Required:
974 intentsId - List of intents ID to be checked
975 Optional:
976 expectedState - Check the expected state(s) of each intents
977 state in the list.
978 *NOTE: You can pass in a list of expected state,
979 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
980 Return:
981 Returns main.TRUE only if all intent are the same as expected states
982 , otherwise, returns main.FALSE; Returns None for general exception
983 """
984 try:
985 # Generating a dictionary: intent id as a key and state as value
986 returnValue = main.TRUE
987 if intentsId == "ALL":
988 intentsId = self.getIntentsId( ip=ip, port=port )
989 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
990
991 #print "len of intentsDict ", str( len( intentsDict ) )
992 if len( intentsId ) != len( intentsDict ):
993 main.log.error( self.name + ": There is something wrong " +
994 "getting intents state" )
995 return main.FALSE
996
997 if isinstance( expectedState, types.StringType ):
998 for intents in intentsDict:
999 if intents.get( 'state' ) != expectedState:
1000 main.log.debug( self.name + " : Intent ID - " +
1001 intents.get( 'id' ) +
1002 " actual state = " +
1003 intents.get( 'state' )
1004 + " does not equal expected state = "
1005 + expectedState )
1006 returnValue = main.FALSE
1007
1008 elif isinstance( expectedState, types.ListType ):
1009 for intents in intentsDict:
1010 if not any( state == intents.get( 'state' ) for state in
1011 expectedState ):
1012 main.log.debug( self.name + " : Intent ID - " +
1013 intents.get( 'id' ) +
1014 " actual state = " +
1015 intents.get( 'state' ) +
1016 " does not equal expected states = "
1017 + str( expectedState ) )
1018 returnValue = main.FALSE
1019
1020 if returnValue == main.TRUE:
1021 main.log.info( self.name + ": All " +
1022 str( len( intentsDict ) ) +
1023 " intents are in " + str( expectedState ) +
1024 " state" )
1025 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001026 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001027 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001028 return None
Jon Halle401b092015-09-23 13:34:24 -07001029 except Exception:
1030 main.log.exception( self.name + ": Uncaught exception!" )
1031 main.cleanup()
1032 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001033
1034 def flows( self, ip="DEFAULT", port="DEFAULT" ):
1035 """
1036 Description:
1037 Get flows currently added to the system
1038 NOTE:
1039 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001040 the REST output
1041
1042 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001043 """
1044 try:
1045 output = None
1046 if ip == "DEFAULT":
1047 main.log.warn( "No ip given, reverting to ip from topo file" )
1048 ip = self.ip_address
1049 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001050 main.log.warn( "No port given, reverting to port " +
1051 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001052 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001053 response = self.send( url="/flows", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001054 if response:
1055 if 200 <= response[ 0 ] <= 299:
1056 output = response[ 1 ]
1057 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001058 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001059 b = json.dumps( a )
1060 return b
1061 else:
1062 main.log.error( "Error with REST request, response was: " +
1063 str( response ) )
1064 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001065 except ( AttributeError, AssertionError, TypeError ):
1066 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001067 return None
Jon Halle401b092015-09-23 13:34:24 -07001068 except Exception:
1069 main.log.exception( self.name + ": Uncaught exception!" )
1070 main.cleanup()
1071 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001072
Jon Halle401b092015-09-23 13:34:24 -07001073 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001074 """
1075 Description:
1076 Gets all the flows of the device or get a specific flow in the
1077 device by giving its flow ID
1078 Required:
Jon Halle401b092015-09-23 13:34:24 -07001079 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001080 Optional:
1081 int/hex flowId - ID of the flow
1082 """
1083 try:
1084 output = None
1085 if ip == "DEFAULT":
1086 main.log.warn( "No ip given, reverting to ip from topo file" )
1087 ip = self.ip_address
1088 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001089 main.log.warn( "No port given, reverting to port " +
1090 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001091 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001092 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001093 if flowId:
1094 url += "/" + str( int( flowId ) )
1095 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001096 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001097 if response:
1098 if 200 <= response[ 0 ] <= 299:
1099 output = response[ 1 ]
1100 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001101 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001102 b = json.dumps( a )
1103 return b
1104 else:
1105 main.log.error( "Error with REST request, response was: " +
1106 str( response ) )
1107 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001108 except ( AttributeError, AssertionError, TypeError ):
1109 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001110 return None
Jon Halle401b092015-09-23 13:34:24 -07001111 except Exception:
1112 main.log.exception( self.name + ": Uncaught exception!" )
1113 main.cleanup()
1114 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001115
GlennRC073e8bc2015-10-27 17:11:28 -07001116 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001117 """
1118 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001119 Sends a single flow to the specified device. This function exists
1120 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001121 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001122 * The flow in json
1123 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001124 Returns:
1125 Returns main.TRUE for successful requests; Returns main.FALSE
1126 if error on requests;
1127 Returns None for exceptions
1128 NOTE:
1129 The ip and port option are for the requests input's ip and port
1130 of the ONOS node
1131 """
GlennRC073e8bc2015-10-27 17:11:28 -07001132
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001133 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001134 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001135 output = None
1136 if ip == "DEFAULT":
1137 main.log.warn( "No ip given, reverting to ip from topo file" )
1138 ip = self.ip_address
1139 if port == "DEFAULT":
1140 main.log.warn( "No port given, reverting to port " +
1141 "from topo file" )
1142 port = self.port
1143 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001144 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001145 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001146 data=json.dumps( flowJson ) )
1147 if response:
1148 if 201:
1149 main.log.info( self.name + ": Successfully POST flow" +
1150 "in device: " + str( deviceId ) )
1151 return main.TRUE
1152 else:
1153 main.log.error( "Error with REST request, response was: " +
1154 str( response ) )
1155 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001156 except NotImplementedError as e:
1157 raise e # Inform the caller
1158 except ( AttributeError, TypeError ):
1159 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001160 return None
Jon Halle401b092015-09-23 13:34:24 -07001161 except Exception:
1162 main.log.exception( self.name + ": Uncaught exception!" )
1163 main.cleanup()
1164 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001165
GlennRC073e8bc2015-10-27 17:11:28 -07001166 def addFlow( self,
1167 deviceId,
1168 appId=0,
1169 ingressPort="",
1170 egressPort="",
1171 ethType="",
1172 ethSrc="",
1173 ethDst="",
1174 vlan="",
1175 ipProto="",
1176 ipSrc=(),
1177 ipDst=(),
1178 tcpSrc="",
1179 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001180 udpDst="",
1181 udpSrc="",
1182 mpls="",
GlennRC073e8bc2015-10-27 17:11:28 -07001183 ip="DEFAULT",
1184 port="DEFAULT",
1185 debug=False ):
1186 """
1187 Description:
1188 Creates a single flow in the specified device
1189 Required:
1190 * deviceId: id of the device
1191 Optional:
1192 * ingressPort: port ingress device
1193 * egressPort: port of egress device
1194 * ethType: specify ethType
1195 * ethSrc: specify ethSrc ( i.e. src mac addr )
1196 * ethDst: specify ethDst ( i.e. dst mac addr )
1197 * ipProto: specify ip protocol
1198 * ipSrc: specify ip source address with mask eg. ip#/24
1199 as a tuple (type, ip#)
1200 * ipDst: specify ip destination address eg. ip#/24
1201 as a tuple (type, ip#)
1202 * tcpSrc: specify tcp source port
1203 * tcpDst: specify tcp destination port
1204 Returns:
1205 Returns main.TRUE for successful requests; Returns main.FALSE
1206 if error on requests;
1207 Returns None for exceptions
1208 NOTE:
1209 The ip and port option are for the requests input's ip and port
1210 of the ONOS node
1211 """
1212 try:
1213 flowJson = { "priority":100,
1214 "isPermanent":"true",
1215 "timeout":0,
1216 "deviceId":deviceId,
1217 "treatment":{"instructions":[]},
1218 "selector": {"criteria":[]}}
1219 if appId:
1220 flowJson[ "appId" ] = appId
1221 if egressPort:
1222 flowJson[ 'treatment' ][ 'instructions' ].append( {
1223 "type":"OUTPUT",
1224 "port":egressPort } )
1225 if ingressPort:
1226 flowJson[ 'selector' ][ 'criteria' ].append( {
1227 "type":"IN_PORT",
1228 "port":ingressPort } )
1229 if ethType:
1230 flowJson[ 'selector' ][ 'criteria' ].append( {
1231 "type":"ETH_TYPE",
1232 "ethType":ethType } )
1233 if ethSrc:
1234 flowJson[ 'selector' ][ 'criteria' ].append( {
1235 "type":"ETH_SRC",
1236 "mac":ethSrc } )
1237 if ethDst:
1238 flowJson[ 'selector' ][ 'criteria' ].append( {
1239 "type":"ETH_DST",
1240 "mac":ethDst } )
1241 if vlan:
1242 flowJson[ 'selector' ][ 'criteria' ].append( {
1243 "type":"VLAN_VID",
1244 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001245 if mpls:
1246 flowJson[ 'selector' ][ 'criteria' ].append( {
1247 "type":"MPLS_LABEL",
1248 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001249 if ipSrc:
1250 flowJson[ 'selector' ][ 'criteria' ].append( {
1251 "type":ipSrc[0],
1252 "ip":ipSrc[1] } )
1253 if ipDst:
1254 flowJson[ 'selector' ][ 'criteria' ].append( {
1255 "type":ipDst[0],
1256 "ip":ipDst[1] } )
1257 if tcpSrc:
1258 flowJson[ 'selector' ][ 'criteria' ].append( {
1259 "type":"TCP_SRC",
1260 "tcpPort": tcpSrc } )
1261 if tcpDst:
1262 flowJson[ 'selector' ][ 'criteria' ].append( {
1263 "type":"TCP_DST",
1264 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001265 if udpSrc:
1266 flowJson[ 'selector' ][ 'criteria' ].append( {
1267 "type":"UDP_SRC",
1268 "udpPort": udpSrc } )
1269 if udpDst:
1270 flowJson[ 'selector' ][ 'criteria' ].append( {
1271 "type":"UDP_DST",
1272 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001273 if ipProto:
1274 flowJson[ 'selector' ][ 'criteria' ].append( {
1275 "type":"IP_PROTO",
1276 "protocol": ipProto } )
1277
1278 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1279
1280 except ( AttributeError, TypeError ):
1281 main.log.exception( self.name + ": Object not as expected" )
1282 return None
1283 except Exception:
1284 main.log.exception( self.name + ": Uncaught exception!" )
1285 main.cleanup()
1286 main.exit()
1287
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001288 def removeFlow( self, deviceId, flowId,
1289 ip="DEFAULT", port="DEFAULT" ):
1290 """
1291 Description:
1292 Remove specific device flow
1293 Required:
1294 str deviceId - id of the device
1295 str flowId - id of the flow
1296 Return:
1297 Returns main.TRUE if successfully deletes flows, otherwise
1298 Returns main.FALSE, Returns None on error
1299 """
1300 try:
1301 output = None
1302 if ip == "DEFAULT":
1303 main.log.warn( "No ip given, reverting to ip from topo file" )
1304 ip = self.ip_address
1305 if port == "DEFAULT":
1306 main.log.warn( "No port given, reverting to port " +
1307 "from topo file" )
1308 port = self.port
1309 # NOTE: REST url requires the intent id to be in decimal form
1310 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001311 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001312 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001313 if response:
1314 if 200 <= response[ 0 ] <= 299:
1315 return main.TRUE
1316 else:
1317 main.log.error( "Error with REST request, response was: " +
1318 str( response ) )
1319 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001320 except ( AttributeError, TypeError ):
1321 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001322 return None
Jon Halle401b092015-09-23 13:34:24 -07001323 except Exception:
1324 main.log.exception( self.name + ": Uncaught exception!" )
1325 main.cleanup()
1326 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001327
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001328 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1329 """
1330 Description:
1331 Check if all the current flows are in ADDED state
1332 Return:
1333 returnValue - Returns main.TRUE only if all flows are in
1334 return main.FALSE otherwise;
1335 Returns None for exception
1336 """
1337 try:
1338 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1339 returnValue = main.TRUE
1340 for flow in tempFlows:
1341 if flow.get( 'state' ) != 'ADDED':
1342 main.log.info( self.name + ": flow Id: " +
1343 str( flow.get( 'groupId' ) ) +
1344 " | state:" +
1345 str( flow.get( 'state' ) ) )
1346 returnValue = main.FALSE
1347 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001348 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001349 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001350 return None
1351 except Exception:
1352 main.log.exception( self.name + ": Uncaught exception!" )
1353 main.cleanup()
1354 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001355
1356 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1357 subjectClass=None, subjectKey=None, configKey=None ):
1358 """
1359 Description:
1360 Get a json object with the ONOS network configurations
1361 Returns:
1362 A json object containing the network configuration in
1363 ONOS; Returns main.FALSE if error on requests;
1364 Returns None for exception
1365 """
1366 try:
1367 output = None
1368 if ip == "DEFAULT":
1369 main.log.warn( "No ip given, reverting to ip from topo file" )
1370 ip = self.ip_address
1371 if port == "DEFAULT":
1372 main.log.warn( "No port given, reverting to port " +
1373 "from topo file" )
1374 port = self.port
1375 url = "/network/configuration"
1376 if subjectClass:
1377 url += "/" + subjectClass
1378 if subjectKey:
1379 url += "/" + subjectKey
1380 if configKey:
1381 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001382 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001383 if response:
1384 if 200 <= response[ 0 ] <= 299:
1385 output = response[ 1 ]
1386 a = json.loads( output )
1387 b = json.dumps( a )
1388 return b
1389 elif response[ 0 ] == 404:
1390 main.log.error( "Requested configuration doesn't exist: " +
1391 str( response ) )
1392 return {}
1393 else:
1394 main.log.error( "Error with REST request, response was: " +
1395 str( response ) )
1396 return main.FALSE
1397 except ( AttributeError, TypeError ):
1398 main.log.exception( self.name + ": Object not as expected" )
1399 return None
1400 except Exception:
1401 main.log.exception( self.name + ": Uncaught exception!" )
1402 main.cleanup()
1403 main.exit()
1404
1405 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1406 subjectClass=None, subjectKey=None, configKey=None ):
1407 """
1408 Description:
1409 Set a json object with the ONOS network configurations
1410 Returns:
1411 Returns main.TRUE for successful requests; Returns main.FALSE
1412 if error on requests;
1413 Returns None for exceptions
1414
1415 """
1416 try:
1417 output = None
1418 if ip == "DEFAULT":
1419 main.log.warn( "No ip given, reverting to ip from topo file" )
1420 ip = self.ip_address
1421 if port == "DEFAULT":
1422 main.log.warn( "No port given, reverting to port " +
1423 "from topo file" )
1424 port = self.port
1425 url = "/network/configuration"
1426 if subjectClass:
1427 url += "/" + subjectClass
1428 if subjectKey:
1429 url += "/" + subjectKey
1430 if configKey:
1431 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001432 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001433 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001434 data=json.dumps( cfgJson ) )
1435 if response:
1436 if 200 <= response[ 0 ] <= 299:
1437 main.log.info( self.name + ": Successfully POST cfg" )
1438 return main.TRUE
1439 else:
1440 main.log.error( "Error with REST request, response was: " +
1441 str( response ) )
1442 return main.FALSE
1443 except ( AttributeError, TypeError ):
1444 main.log.exception( self.name + ": Object not as expected" )
1445 return None
1446 except Exception:
1447 main.log.exception( self.name + ": Uncaught exception!" )
1448 main.cleanup()
1449 main.exit()
1450
1451 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1452 subjectClass=None, subjectKey=None, configKey=None ):
1453 """
1454 Description:
1455 Remove a json object from the ONOS network configurations
1456 Returns:
1457 Returns main.TRUE for successful requests; Returns main.FALSE
1458 if error on requests;
1459 Returns None for exceptions
1460
1461 """
1462 try:
1463 output = None
1464 if ip == "DEFAULT":
1465 main.log.warn( "No ip given, reverting to ip from topo file" )
1466 ip = self.ip_address
1467 if port == "DEFAULT":
1468 main.log.warn( "No port given, reverting to port " +
1469 "from topo file" )
1470 port = self.port
1471 url = "/network/configuration"
1472 if subjectClass:
1473 url += "/" + subjectClass
1474 if subjectKey:
1475 url += "/" + subjectKey
1476 if configKey:
1477 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001478 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001479 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001480 if response:
1481 if 200 <= response[ 0 ] <= 299:
1482 main.log.info( self.name + ": Successfully delete cfg" )
1483 return main.TRUE
1484 else:
1485 main.log.error( "Error with REST request, response was: " +
1486 str( response ) )
1487 return main.FALSE
1488 except ( AttributeError, TypeError ):
1489 main.log.exception( self.name + ": Object not as expected" )
1490 return None
1491 except Exception:
1492 main.log.exception( self.name + ": Uncaught exception!" )
1493 main.cleanup()
1494 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001495
1496 def createFlowBatch( self,
1497 numSw = 1,
1498 swIndex = 1,
1499 batchSize = 1,
1500 batchIndex = 1,
1501 deviceIdpreFix = "of:",
1502 appId=0,
1503 deviceID="",
1504 ingressPort="",
1505 egressPort="",
1506 ethType="",
1507 ethSrc="",
1508 ethDst="",
1509 vlan="",
1510 ipProto="",
1511 ipSrc=(),
1512 ipDst=(),
1513 tcpSrc="",
1514 tcpDst="",
1515 udpDst="",
1516 udpSrc="",
1517 mpls="",
1518 ip="DEFAULT",
1519 port="DEFAULT",
1520 debug=False ):
1521 """
1522 Description:
1523 Creates batches of MAC-rule flows for POST.
1524 Predefined MAC: 2 MS Hex digit for iterating devices
1525 Next 5 Hex digit for iterating batch numbers
1526 Next 5 Hex digit for iterating flows within a batch
1527 Required:
1528 * deviceId: id of the device
1529 Optional:
1530 * ingressPort: port ingress device
1531 * egressPort: port of egress device
1532 * ethType: specify ethType
1533 * ethSrc: specify ethSrc ( i.e. src mac addr )
1534 * ethDst: specify ethDst ( i.e. dst mac addr )
1535 * ipProto: specify ip protocol
1536 * ipSrc: specify ip source address with mask eg. ip#/24
1537 as a tuple (type, ip#)
1538 * ipDst: specify ip destination address eg. ip#/24
1539 as a tuple (type, ip#)
1540 * tcpSrc: specify tcp source port
1541 * tcpDst: specify tcp destination port
1542 Returns:
1543 Returns main.TRUE for successful requests; Returns main.FALSE
1544 if error on requests;
1545 Returns None for exceptions
1546 NOTE:
1547 The ip and port option are for the requests input's ip and port
1548 of the ONOS node
1549 """
1550 #from pprint import pprint
1551
1552 flowJsonList = []
1553 flowJsonBatch = {"flows":flowJsonList}
1554 dev = swIndex
1555
1556 for fl in range(1, batchSize + 1):
1557 flowJson = { "priority":100,
1558 "deviceId":"",
1559 "isPermanent":"true",
1560 "timeout":0,
1561 "treatment":{"instructions":[]},
1562 "selector": {"criteria":[]}}
1563
1564 #main.log.info("fl: " + str(fl))
1565 if dev <= numSw:
1566 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1567 #print deviceId
1568 flowJson['deviceId'] = deviceId
1569 dev += 1
1570 else:
1571 dev = 1
1572 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1573 #print deviceId
1574 flowJson['deviceId'] = deviceId
1575 dev += 1
1576
1577 # ethSrc starts with "0"; ethDst starts with "1"
1578 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1579 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1580 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1581 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1582 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1583 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1584 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1585
1586 if appId:
1587 flowJson[ "appId" ] = appId
1588
1589 if egressPort:
1590 flowJson[ 'treatment' ][ 'instructions' ].append( {
1591 "type":"OUTPUT",
1592 "port":egressPort } )
1593 if ingressPort:
1594 flowJson[ 'selector' ][ 'criteria' ].append( {
1595 "type":"IN_PORT",
1596 "port":ingressPort } )
1597 if ethType:
1598 flowJson[ 'selector' ][ 'criteria' ].append( {
1599 "type":"ETH_TYPE",
1600 "ethType":ethType } )
1601 if ethSrc:
1602 flowJson[ 'selector' ][ 'criteria' ].append( {
1603 "type":"ETH_SRC",
1604 "mac":ethSrc } )
1605 if ethDst:
1606 flowJson[ 'selector' ][ 'criteria' ].append( {
1607 "type":"ETH_DST",
1608 "mac":ethDst } )
1609 if vlan:
1610 flowJson[ 'selector' ][ 'criteria' ].append( {
1611 "type":"VLAN_VID",
1612 "vlanId":vlan } )
1613 if mpls:
1614 flowJson[ 'selector' ][ 'criteria' ].append( {
1615 "type":"MPLS_LABEL",
1616 "label":mpls } )
1617 if ipSrc:
1618 flowJson[ 'selector' ][ 'criteria' ].append( {
1619 "type":ipSrc[0],
1620 "ip":ipSrc[1] } )
1621 if ipDst:
1622 flowJson[ 'selector' ][ 'criteria' ].append( {
1623 "type":ipDst[0],
1624 "ip":ipDst[1] } )
1625 if tcpSrc:
1626 flowJson[ 'selector' ][ 'criteria' ].append( {
1627 "type":"TCP_SRC",
1628 "tcpPort": tcpSrc } )
1629 if tcpDst:
1630 flowJson[ 'selector' ][ 'criteria' ].append( {
1631 "type":"TCP_DST",
1632 "tcpPort": tcpDst } )
1633 if udpSrc:
1634 flowJson[ 'selector' ][ 'criteria' ].append( {
1635 "type":"UDP_SRC",
1636 "udpPort": udpSrc } )
1637 if udpDst:
1638 flowJson[ 'selector' ][ 'criteria' ].append( {
1639 "type":"UDP_DST",
1640 "udpPort": udpDst } )
1641 if ipProto:
1642 flowJson[ 'selector' ][ 'criteria' ].append( {
1643 "type":"IP_PROTO",
1644 "protocol": ipProto } )
1645 #pprint(flowJson)
1646 flowJsonList.append(flowJson)
1647
1648 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1649 flowJsonBatch['flows'] = flowJsonList
1650 #pprint(flowJsonBatch)
1651
1652 return flowJsonBatch
1653
1654
1655 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1656 """
1657 Description:
1658 Sends a single flow batch through /flows REST API.
1659 Required:
1660 * The batch of flows
1661 Returns:
1662 Returns main.TRUE for successful requests; Returns main.FALSE
1663 if error on requests;
1664 Returns None for exceptions
1665 NOTE:
1666 The ip and port option are for the requests input's ip and port
1667 of the ONOS node
1668 """
1669 import time
1670
1671 try:
1672 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1673 output = None
1674 if ip == "DEFAULT":
1675 main.log.warn( "No ip given, reverting to ip from topo file" )
1676 ip = self.ip_address
1677 if port == "DEFAULT":
1678 main.log.warn( "No port given, reverting to port " +
1679 "from topo file" )
1680 port = self.port
1681 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001682 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001683 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001684 data=json.dumps( batch ) )
1685 #main.log.info("Post response is: ", str(response[0]))
1686 if response[0] == 200:
1687 main.log.info( self.name + ": Successfully POST flow batch" )
1688 return main.TRUE, response
1689 else:
1690 main.log.error( "Error with REST request, response was: " +
1691 str( response ) )
1692 return main.FALSE
1693 except NotImplementedError as e:
1694 raise e # Inform the caller
1695 except ( AttributeError, TypeError ):
1696 main.log.exception( self.name + ": Object not as expected" )
1697 return None
1698 except Exception:
1699 main.log.exception( self.name + ": Uncaught exception!" )
1700 main.cleanup()
1701 main.exit()
1702
1703 def removeFlowBatch( self, batch={},
1704 ip="DEFAULT", port="DEFAULT" ):
1705 """
1706 Description:
1707 Remove a batch of flows
1708 Required:
1709 flow batch
1710 Return:
1711 Returns main.TRUE if successfully deletes flows, otherwise
1712 Returns main.FALSE, Returns None on error
1713 """
1714 try:
1715 output = None
1716 if ip == "DEFAULT":
1717 main.log.warn( "No ip given, reverting to ip from topo file" )
1718 ip = self.ip_address
1719 if port == "DEFAULT":
1720 main.log.warn( "No port given, reverting to port " +
1721 "from topo file" )
1722 port = self.port
1723 # NOTE: REST url requires the intent id to be in decimal form
1724
suibin zhang116647a2016-05-06 16:30:09 -07001725 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001726 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001727 data = json.dumps(batch) )
1728 if response:
1729 if 200 <= response[ 0 ] <= 299:
1730 return main.TRUE
1731 else:
1732 main.log.error( "Error with REST request, response was: " +
1733 str( response ) )
1734 return main.FALSE
1735 except ( AttributeError, TypeError ):
1736 main.log.exception( self.name + ": Object not as expected" )
1737 return None
1738 except Exception:
1739 main.log.exception( self.name + ": Uncaught exception!" )
1740 main.cleanup()
1741 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001742
1743 def getTopology( self, topologyOutput ):
1744 """
1745 Definition:
1746 Loads a json topology output
1747 Return:
1748 topology = current ONOS topology
1749 """
1750 import json
1751 try:
1752 # either onos:topology or 'topology' will work in CLI
1753 topology = json.loads(topologyOutput)
1754 main.log.debug( topology )
1755 return topology
1756 except pexpect.EOF:
1757 main.log.error( self.name + ": EOF exception found" )
1758 main.log.error( self.name + ": " + self.handle.before )
1759 main.cleanup()
1760 main.exit()
1761 except Exception:
1762 main.log.exception( self.name + ": Uncaught exception!" )
1763 main.cleanup()
1764 main.exit()
1765
1766 def checkStatus(
1767 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001768 numoswitch,
1769 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001770 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001771 logLevel="info" ):
1772 """
1773 Checks the number of switches & links that ONOS sees against the
1774 supplied values. By default this will report to main.log, but the
1775 log level can be specific.
1776
Flavio Castro82ee2f62016-06-07 15:04:12 -07001777 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001778 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001779 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001780 logLevel = level to log to.
1781 Currently accepts 'info', 'warn' and 'report'
1782
1783 Returns: main.TRUE if the number of switches and links are correct,
1784 main.FALSE if the number of switches and links is incorrect,
1785 and main.ERROR otherwise
1786 """
1787 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001788 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001789 if topology == {}:
1790 return main.ERROR
1791 output = ""
1792 # Is the number of switches is what we expected
1793 devices = topology.get( 'devices', False )
1794 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001795 nodes = topology.get( 'nodes' , False )
1796 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001797 return main.ERROR
1798 switchCheck = ( int( devices ) == int( numoswitch ) )
1799 # Is the number of links is what we expected
1800 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001801 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1802 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001803 # We expected the correct numbers
1804 output = output + "The number of links and switches match "\
1805 + "what was expected"
1806 result = main.TRUE
1807 else:
1808 output = output + \
1809 "The number of links and switches does not match " + \
1810 "what was expected"
1811 result = main.FALSE
1812 output = output + "\n ONOS sees %i devices" % int( devices )
1813 output = output + " (%i expected) " % int( numoswitch )
1814 output = output + "and %i links " % int( links )
1815 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001816 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001817 output = output + "and %i controllers " % int( nodes )
1818 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001819 if logLevel == "report":
1820 main.log.report( output )
1821 elif logLevel == "warn":
1822 main.log.warn( output )
1823 else:
1824 main.log.info( output )
1825 return result
1826 except pexpect.EOF:
1827 main.log.error( self.name + ": EOF exception found" )
1828 main.log.error( self.name + ": " + self.handle.before )
1829 main.cleanup()
1830 main.exit()
1831 except Exception:
1832 main.log.exception( self.name + ": Uncaught exception!" )
1833 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001834 main.exit()