blob: 979b0b60b87fa82cd2d94ffcff2025321885f603 [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
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001034 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001035 """
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
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001046 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001047 if ip == "DEFAULT":
1048 main.log.warn( "No ip given, reverting to ip from topo file" )
1049 ip = self.ip_address
1050 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001051 main.log.warn( "No port given, reverting to port " +
1052 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001053 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001054 if subjectKey and not subjectClass:
1055 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1056 if subjectClass:
1057 url += "/" + subjectClass
1058 if subjectKey:
1059 url += "/" + subjectKey
1060 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001061 if response:
1062 if 200 <= response[ 0 ] <= 299:
1063 output = response[ 1 ]
1064 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001065 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001066 b = json.dumps( a )
1067 return b
1068 else:
1069 main.log.error( "Error with REST request, response was: " +
1070 str( response ) )
1071 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001072 except ( AttributeError, AssertionError, TypeError ):
1073 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001074 return None
Jon Halle401b092015-09-23 13:34:24 -07001075 except Exception:
1076 main.log.exception( self.name + ": Uncaught exception!" )
1077 main.cleanup()
1078 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001079
Jon Halle401b092015-09-23 13:34:24 -07001080 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001081 """
1082 Description:
1083 Gets all the flows of the device or get a specific flow in the
1084 device by giving its flow ID
1085 Required:
Jon Halle401b092015-09-23 13:34:24 -07001086 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001087 Optional:
1088 int/hex flowId - ID of the flow
1089 """
1090 try:
1091 output = None
1092 if ip == "DEFAULT":
1093 main.log.warn( "No ip given, reverting to ip from topo file" )
1094 ip = self.ip_address
1095 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001096 main.log.warn( "No port given, reverting to port " +
1097 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001098 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001099 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001100 if flowId:
1101 url += "/" + str( int( flowId ) )
1102 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001103 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001104 if response:
1105 if 200 <= response[ 0 ] <= 299:
1106 output = response[ 1 ]
1107 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001108 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001109 b = json.dumps( a )
1110 return b
1111 else:
1112 main.log.error( "Error with REST request, response was: " +
1113 str( response ) )
1114 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001115 except ( AttributeError, AssertionError, TypeError ):
1116 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001117 return None
Jon Halle401b092015-09-23 13:34:24 -07001118 except Exception:
1119 main.log.exception( self.name + ": Uncaught exception!" )
1120 main.cleanup()
1121 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001122
GlennRC073e8bc2015-10-27 17:11:28 -07001123 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001124 """
1125 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001126 Sends a single flow to the specified device. This function exists
1127 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001128 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001129 * The flow in json
1130 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001131 Returns:
1132 Returns main.TRUE for successful requests; Returns main.FALSE
1133 if error on requests;
1134 Returns None for exceptions
1135 NOTE:
1136 The ip and port option are for the requests input's ip and port
1137 of the ONOS node
1138 """
GlennRC073e8bc2015-10-27 17:11:28 -07001139
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001140 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001141 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001142 output = None
1143 if ip == "DEFAULT":
1144 main.log.warn( "No ip given, reverting to ip from topo file" )
1145 ip = self.ip_address
1146 if port == "DEFAULT":
1147 main.log.warn( "No port given, reverting to port " +
1148 "from topo file" )
1149 port = self.port
1150 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001151 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001152 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001153 data=json.dumps( flowJson ) )
1154 if response:
1155 if 201:
1156 main.log.info( self.name + ": Successfully POST flow" +
1157 "in device: " + str( deviceId ) )
1158 return main.TRUE
1159 else:
1160 main.log.error( "Error with REST request, response was: " +
1161 str( response ) )
1162 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001163 except NotImplementedError as e:
1164 raise e # Inform the caller
1165 except ( AttributeError, TypeError ):
1166 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001167 return None
Jon Halle401b092015-09-23 13:34:24 -07001168 except Exception:
1169 main.log.exception( self.name + ": Uncaught exception!" )
1170 main.cleanup()
1171 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001172
GlennRC073e8bc2015-10-27 17:11:28 -07001173 def addFlow( self,
1174 deviceId,
1175 appId=0,
1176 ingressPort="",
1177 egressPort="",
1178 ethType="",
1179 ethSrc="",
1180 ethDst="",
1181 vlan="",
1182 ipProto="",
1183 ipSrc=(),
1184 ipDst=(),
1185 tcpSrc="",
1186 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001187 udpDst="",
1188 udpSrc="",
1189 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001190 priority=100,
GlennRC073e8bc2015-10-27 17:11:28 -07001191 ip="DEFAULT",
1192 port="DEFAULT",
1193 debug=False ):
1194 """
1195 Description:
1196 Creates a single flow in the specified device
1197 Required:
1198 * deviceId: id of the device
1199 Optional:
1200 * ingressPort: port ingress device
1201 * egressPort: port of egress device
1202 * ethType: specify ethType
1203 * ethSrc: specify ethSrc ( i.e. src mac addr )
1204 * ethDst: specify ethDst ( i.e. dst mac addr )
1205 * ipProto: specify ip protocol
1206 * ipSrc: specify ip source address with mask eg. ip#/24
1207 as a tuple (type, ip#)
1208 * ipDst: specify ip destination address eg. ip#/24
1209 as a tuple (type, ip#)
1210 * tcpSrc: specify tcp source port
1211 * tcpDst: specify tcp destination port
1212 Returns:
1213 Returns main.TRUE for successful requests; Returns main.FALSE
1214 if error on requests;
1215 Returns None for exceptions
1216 NOTE:
1217 The ip and port option are for the requests input's ip and port
1218 of the ONOS node
1219 """
1220 try:
alisone14d7b02016-07-06 10:31:51 -07001221 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001222 "isPermanent":"true",
1223 "timeout":0,
1224 "deviceId":deviceId,
1225 "treatment":{"instructions":[]},
1226 "selector": {"criteria":[]}}
1227 if appId:
1228 flowJson[ "appId" ] = appId
1229 if egressPort:
1230 flowJson[ 'treatment' ][ 'instructions' ].append( {
1231 "type":"OUTPUT",
1232 "port":egressPort } )
1233 if ingressPort:
1234 flowJson[ 'selector' ][ 'criteria' ].append( {
1235 "type":"IN_PORT",
1236 "port":ingressPort } )
1237 if ethType:
1238 flowJson[ 'selector' ][ 'criteria' ].append( {
1239 "type":"ETH_TYPE",
1240 "ethType":ethType } )
1241 if ethSrc:
1242 flowJson[ 'selector' ][ 'criteria' ].append( {
1243 "type":"ETH_SRC",
1244 "mac":ethSrc } )
1245 if ethDst:
1246 flowJson[ 'selector' ][ 'criteria' ].append( {
1247 "type":"ETH_DST",
1248 "mac":ethDst } )
1249 if vlan:
1250 flowJson[ 'selector' ][ 'criteria' ].append( {
1251 "type":"VLAN_VID",
1252 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001253 if mpls:
1254 flowJson[ 'selector' ][ 'criteria' ].append( {
1255 "type":"MPLS_LABEL",
1256 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001257 if ipSrc:
1258 flowJson[ 'selector' ][ 'criteria' ].append( {
1259 "type":ipSrc[0],
1260 "ip":ipSrc[1] } )
1261 if ipDst:
1262 flowJson[ 'selector' ][ 'criteria' ].append( {
1263 "type":ipDst[0],
1264 "ip":ipDst[1] } )
1265 if tcpSrc:
1266 flowJson[ 'selector' ][ 'criteria' ].append( {
1267 "type":"TCP_SRC",
1268 "tcpPort": tcpSrc } )
1269 if tcpDst:
1270 flowJson[ 'selector' ][ 'criteria' ].append( {
1271 "type":"TCP_DST",
1272 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001273 if udpSrc:
1274 flowJson[ 'selector' ][ 'criteria' ].append( {
1275 "type":"UDP_SRC",
1276 "udpPort": udpSrc } )
1277 if udpDst:
1278 flowJson[ 'selector' ][ 'criteria' ].append( {
1279 "type":"UDP_DST",
1280 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001281 if ipProto:
1282 flowJson[ 'selector' ][ 'criteria' ].append( {
1283 "type":"IP_PROTO",
1284 "protocol": ipProto } )
1285
1286 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1287
1288 except ( AttributeError, TypeError ):
1289 main.log.exception( self.name + ": Object not as expected" )
1290 return None
1291 except Exception:
1292 main.log.exception( self.name + ": Uncaught exception!" )
1293 main.cleanup()
1294 main.exit()
1295
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001296 def removeFlow( self, deviceId, flowId,
1297 ip="DEFAULT", port="DEFAULT" ):
1298 """
1299 Description:
1300 Remove specific device flow
1301 Required:
1302 str deviceId - id of the device
1303 str flowId - id of the flow
1304 Return:
1305 Returns main.TRUE if successfully deletes flows, otherwise
1306 Returns main.FALSE, Returns None on error
1307 """
1308 try:
1309 output = None
1310 if ip == "DEFAULT":
1311 main.log.warn( "No ip given, reverting to ip from topo file" )
1312 ip = self.ip_address
1313 if port == "DEFAULT":
1314 main.log.warn( "No port given, reverting to port " +
1315 "from topo file" )
1316 port = self.port
1317 # NOTE: REST url requires the intent id to be in decimal form
1318 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001319 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001320 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001321 if response:
1322 if 200 <= response[ 0 ] <= 299:
1323 return main.TRUE
1324 else:
1325 main.log.error( "Error with REST request, response was: " +
1326 str( response ) )
1327 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001328 except ( AttributeError, TypeError ):
1329 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001330 return None
Jon Halle401b092015-09-23 13:34:24 -07001331 except Exception:
1332 main.log.exception( self.name + ": Uncaught exception!" )
1333 main.cleanup()
1334 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001335
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001336 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1337 """
1338 Description:
1339 Check if all the current flows are in ADDED state
1340 Return:
1341 returnValue - Returns main.TRUE only if all flows are in
1342 return main.FALSE otherwise;
1343 Returns None for exception
1344 """
1345 try:
1346 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1347 returnValue = main.TRUE
1348 for flow in tempFlows:
1349 if flow.get( 'state' ) != 'ADDED':
1350 main.log.info( self.name + ": flow Id: " +
1351 str( flow.get( 'groupId' ) ) +
1352 " | state:" +
1353 str( flow.get( 'state' ) ) )
1354 returnValue = main.FALSE
1355 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001356 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001357 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001358 return None
1359 except Exception:
1360 main.log.exception( self.name + ": Uncaught exception!" )
1361 main.cleanup()
1362 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001363
1364 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1365 subjectClass=None, subjectKey=None, configKey=None ):
1366 """
1367 Description:
1368 Get a json object with the ONOS network configurations
1369 Returns:
1370 A json object containing the network configuration in
1371 ONOS; Returns main.FALSE if error on requests;
1372 Returns None for exception
1373 """
1374 try:
1375 output = None
1376 if ip == "DEFAULT":
1377 main.log.warn( "No ip given, reverting to ip from topo file" )
1378 ip = self.ip_address
1379 if port == "DEFAULT":
1380 main.log.warn( "No port given, reverting to port " +
1381 "from topo file" )
1382 port = self.port
1383 url = "/network/configuration"
1384 if subjectClass:
1385 url += "/" + subjectClass
1386 if subjectKey:
1387 url += "/" + subjectKey
1388 if configKey:
1389 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001390 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001391 if response:
1392 if 200 <= response[ 0 ] <= 299:
1393 output = response[ 1 ]
1394 a = json.loads( output )
1395 b = json.dumps( a )
1396 return b
1397 elif response[ 0 ] == 404:
1398 main.log.error( "Requested configuration doesn't exist: " +
1399 str( response ) )
1400 return {}
1401 else:
1402 main.log.error( "Error with REST request, response was: " +
1403 str( response ) )
1404 return main.FALSE
1405 except ( AttributeError, TypeError ):
1406 main.log.exception( self.name + ": Object not as expected" )
1407 return None
1408 except Exception:
1409 main.log.exception( self.name + ": Uncaught exception!" )
1410 main.cleanup()
1411 main.exit()
1412
1413 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1414 subjectClass=None, subjectKey=None, configKey=None ):
1415 """
1416 Description:
1417 Set a json object with the ONOS network configurations
1418 Returns:
1419 Returns main.TRUE for successful requests; Returns main.FALSE
1420 if error on requests;
1421 Returns None for exceptions
1422
1423 """
1424 try:
1425 output = None
1426 if ip == "DEFAULT":
1427 main.log.warn( "No ip given, reverting to ip from topo file" )
1428 ip = self.ip_address
1429 if port == "DEFAULT":
1430 main.log.warn( "No port given, reverting to port " +
1431 "from topo file" )
1432 port = self.port
1433 url = "/network/configuration"
1434 if subjectClass:
1435 url += "/" + subjectClass
1436 if subjectKey:
1437 url += "/" + subjectKey
1438 if configKey:
1439 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001440 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001441 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001442 data=json.dumps( cfgJson ) )
1443 if response:
1444 if 200 <= response[ 0 ] <= 299:
1445 main.log.info( self.name + ": Successfully POST cfg" )
1446 return main.TRUE
1447 else:
1448 main.log.error( "Error with REST request, response was: " +
1449 str( response ) )
1450 return main.FALSE
1451 except ( AttributeError, TypeError ):
1452 main.log.exception( self.name + ": Object not as expected" )
1453 return None
1454 except Exception:
1455 main.log.exception( self.name + ": Uncaught exception!" )
1456 main.cleanup()
1457 main.exit()
1458
1459 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1460 subjectClass=None, subjectKey=None, configKey=None ):
1461 """
1462 Description:
1463 Remove a json object from the ONOS network configurations
1464 Returns:
1465 Returns main.TRUE for successful requests; Returns main.FALSE
1466 if error on requests;
1467 Returns None for exceptions
1468
1469 """
1470 try:
1471 output = None
1472 if ip == "DEFAULT":
1473 main.log.warn( "No ip given, reverting to ip from topo file" )
1474 ip = self.ip_address
1475 if port == "DEFAULT":
1476 main.log.warn( "No port given, reverting to port " +
1477 "from topo file" )
1478 port = self.port
1479 url = "/network/configuration"
1480 if subjectClass:
1481 url += "/" + subjectClass
1482 if subjectKey:
1483 url += "/" + subjectKey
1484 if configKey:
1485 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001486 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001487 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001488 if response:
1489 if 200 <= response[ 0 ] <= 299:
1490 main.log.info( self.name + ": Successfully delete cfg" )
1491 return main.TRUE
1492 else:
1493 main.log.error( "Error with REST request, response was: " +
1494 str( response ) )
1495 return main.FALSE
1496 except ( AttributeError, TypeError ):
1497 main.log.exception( self.name + ": Object not as expected" )
1498 return None
1499 except Exception:
1500 main.log.exception( self.name + ": Uncaught exception!" )
1501 main.cleanup()
1502 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001503
1504 def createFlowBatch( self,
1505 numSw = 1,
1506 swIndex = 1,
1507 batchSize = 1,
1508 batchIndex = 1,
1509 deviceIdpreFix = "of:",
1510 appId=0,
1511 deviceID="",
1512 ingressPort="",
1513 egressPort="",
1514 ethType="",
1515 ethSrc="",
1516 ethDst="",
1517 vlan="",
1518 ipProto="",
1519 ipSrc=(),
1520 ipDst=(),
1521 tcpSrc="",
1522 tcpDst="",
1523 udpDst="",
1524 udpSrc="",
1525 mpls="",
1526 ip="DEFAULT",
1527 port="DEFAULT",
1528 debug=False ):
1529 """
1530 Description:
1531 Creates batches of MAC-rule flows for POST.
1532 Predefined MAC: 2 MS Hex digit for iterating devices
1533 Next 5 Hex digit for iterating batch numbers
1534 Next 5 Hex digit for iterating flows within a batch
1535 Required:
1536 * deviceId: id of the device
1537 Optional:
1538 * ingressPort: port ingress device
1539 * egressPort: port of egress device
1540 * ethType: specify ethType
1541 * ethSrc: specify ethSrc ( i.e. src mac addr )
1542 * ethDst: specify ethDst ( i.e. dst mac addr )
1543 * ipProto: specify ip protocol
1544 * ipSrc: specify ip source address with mask eg. ip#/24
1545 as a tuple (type, ip#)
1546 * ipDst: specify ip destination address eg. ip#/24
1547 as a tuple (type, ip#)
1548 * tcpSrc: specify tcp source port
1549 * tcpDst: specify tcp destination port
1550 Returns:
1551 Returns main.TRUE for successful requests; Returns main.FALSE
1552 if error on requests;
1553 Returns None for exceptions
1554 NOTE:
1555 The ip and port option are for the requests input's ip and port
1556 of the ONOS node
1557 """
1558 #from pprint import pprint
1559
1560 flowJsonList = []
1561 flowJsonBatch = {"flows":flowJsonList}
1562 dev = swIndex
1563
1564 for fl in range(1, batchSize + 1):
1565 flowJson = { "priority":100,
1566 "deviceId":"",
1567 "isPermanent":"true",
1568 "timeout":0,
1569 "treatment":{"instructions":[]},
1570 "selector": {"criteria":[]}}
1571
1572 #main.log.info("fl: " + str(fl))
1573 if dev <= numSw:
1574 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1575 #print deviceId
1576 flowJson['deviceId'] = deviceId
1577 dev += 1
1578 else:
1579 dev = 1
1580 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1581 #print deviceId
1582 flowJson['deviceId'] = deviceId
1583 dev += 1
1584
1585 # ethSrc starts with "0"; ethDst starts with "1"
1586 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1587 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1588 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1589 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1590 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1591 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1592 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1593
1594 if appId:
1595 flowJson[ "appId" ] = appId
1596
1597 if egressPort:
1598 flowJson[ 'treatment' ][ 'instructions' ].append( {
1599 "type":"OUTPUT",
1600 "port":egressPort } )
1601 if ingressPort:
1602 flowJson[ 'selector' ][ 'criteria' ].append( {
1603 "type":"IN_PORT",
1604 "port":ingressPort } )
1605 if ethType:
1606 flowJson[ 'selector' ][ 'criteria' ].append( {
1607 "type":"ETH_TYPE",
1608 "ethType":ethType } )
1609 if ethSrc:
1610 flowJson[ 'selector' ][ 'criteria' ].append( {
1611 "type":"ETH_SRC",
1612 "mac":ethSrc } )
1613 if ethDst:
1614 flowJson[ 'selector' ][ 'criteria' ].append( {
1615 "type":"ETH_DST",
1616 "mac":ethDst } )
1617 if vlan:
1618 flowJson[ 'selector' ][ 'criteria' ].append( {
1619 "type":"VLAN_VID",
1620 "vlanId":vlan } )
1621 if mpls:
1622 flowJson[ 'selector' ][ 'criteria' ].append( {
1623 "type":"MPLS_LABEL",
1624 "label":mpls } )
1625 if ipSrc:
1626 flowJson[ 'selector' ][ 'criteria' ].append( {
1627 "type":ipSrc[0],
1628 "ip":ipSrc[1] } )
1629 if ipDst:
1630 flowJson[ 'selector' ][ 'criteria' ].append( {
1631 "type":ipDst[0],
1632 "ip":ipDst[1] } )
1633 if tcpSrc:
1634 flowJson[ 'selector' ][ 'criteria' ].append( {
1635 "type":"TCP_SRC",
1636 "tcpPort": tcpSrc } )
1637 if tcpDst:
1638 flowJson[ 'selector' ][ 'criteria' ].append( {
1639 "type":"TCP_DST",
1640 "tcpPort": tcpDst } )
1641 if udpSrc:
1642 flowJson[ 'selector' ][ 'criteria' ].append( {
1643 "type":"UDP_SRC",
1644 "udpPort": udpSrc } )
1645 if udpDst:
1646 flowJson[ 'selector' ][ 'criteria' ].append( {
1647 "type":"UDP_DST",
1648 "udpPort": udpDst } )
1649 if ipProto:
1650 flowJson[ 'selector' ][ 'criteria' ].append( {
1651 "type":"IP_PROTO",
1652 "protocol": ipProto } )
1653 #pprint(flowJson)
1654 flowJsonList.append(flowJson)
1655
1656 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1657 flowJsonBatch['flows'] = flowJsonList
1658 #pprint(flowJsonBatch)
1659
1660 return flowJsonBatch
1661
1662
1663 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1664 """
1665 Description:
1666 Sends a single flow batch through /flows REST API.
1667 Required:
1668 * The batch of flows
1669 Returns:
1670 Returns main.TRUE for successful requests; Returns main.FALSE
1671 if error on requests;
1672 Returns None for exceptions
1673 NOTE:
1674 The ip and port option are for the requests input's ip and port
1675 of the ONOS node
1676 """
1677 import time
1678
1679 try:
1680 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1681 output = None
1682 if ip == "DEFAULT":
1683 main.log.warn( "No ip given, reverting to ip from topo file" )
1684 ip = self.ip_address
1685 if port == "DEFAULT":
1686 main.log.warn( "No port given, reverting to port " +
1687 "from topo file" )
1688 port = self.port
1689 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001690 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001691 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001692 data=json.dumps( batch ) )
1693 #main.log.info("Post response is: ", str(response[0]))
1694 if response[0] == 200:
1695 main.log.info( self.name + ": Successfully POST flow batch" )
1696 return main.TRUE, response
1697 else:
1698 main.log.error( "Error with REST request, response was: " +
1699 str( response ) )
1700 return main.FALSE
1701 except NotImplementedError as e:
1702 raise e # Inform the caller
1703 except ( AttributeError, TypeError ):
1704 main.log.exception( self.name + ": Object not as expected" )
1705 return None
1706 except Exception:
1707 main.log.exception( self.name + ": Uncaught exception!" )
1708 main.cleanup()
1709 main.exit()
1710
1711 def removeFlowBatch( self, batch={},
1712 ip="DEFAULT", port="DEFAULT" ):
1713 """
1714 Description:
1715 Remove a batch of flows
1716 Required:
1717 flow batch
1718 Return:
1719 Returns main.TRUE if successfully deletes flows, otherwise
1720 Returns main.FALSE, Returns None on error
1721 """
1722 try:
1723 output = None
1724 if ip == "DEFAULT":
1725 main.log.warn( "No ip given, reverting to ip from topo file" )
1726 ip = self.ip_address
1727 if port == "DEFAULT":
1728 main.log.warn( "No port given, reverting to port " +
1729 "from topo file" )
1730 port = self.port
1731 # NOTE: REST url requires the intent id to be in decimal form
1732
suibin zhang116647a2016-05-06 16:30:09 -07001733 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001734 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001735 data = json.dumps(batch) )
1736 if response:
1737 if 200 <= response[ 0 ] <= 299:
1738 return main.TRUE
1739 else:
1740 main.log.error( "Error with REST request, response was: " +
1741 str( response ) )
1742 return main.FALSE
1743 except ( AttributeError, TypeError ):
1744 main.log.exception( self.name + ": Object not as expected" )
1745 return None
1746 except Exception:
1747 main.log.exception( self.name + ": Uncaught exception!" )
1748 main.cleanup()
1749 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001750
1751 def getTopology( self, topologyOutput ):
1752 """
1753 Definition:
1754 Loads a json topology output
1755 Return:
1756 topology = current ONOS topology
1757 """
1758 import json
1759 try:
1760 # either onos:topology or 'topology' will work in CLI
1761 topology = json.loads(topologyOutput)
1762 main.log.debug( topology )
1763 return topology
1764 except pexpect.EOF:
1765 main.log.error( self.name + ": EOF exception found" )
1766 main.log.error( self.name + ": " + self.handle.before )
1767 main.cleanup()
1768 main.exit()
1769 except Exception:
1770 main.log.exception( self.name + ": Uncaught exception!" )
1771 main.cleanup()
1772 main.exit()
1773
1774 def checkStatus(
1775 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001776 numoswitch,
1777 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001778 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001779 logLevel="info" ):
1780 """
1781 Checks the number of switches & links that ONOS sees against the
1782 supplied values. By default this will report to main.log, but the
1783 log level can be specific.
1784
Flavio Castro82ee2f62016-06-07 15:04:12 -07001785 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001786 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001787 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001788 logLevel = level to log to.
1789 Currently accepts 'info', 'warn' and 'report'
1790
1791 Returns: main.TRUE if the number of switches and links are correct,
1792 main.FALSE if the number of switches and links is incorrect,
1793 and main.ERROR otherwise
1794 """
1795 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001796 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001797 if topology == {}:
1798 return main.ERROR
1799 output = ""
1800 # Is the number of switches is what we expected
1801 devices = topology.get( 'devices', False )
1802 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001803 nodes = topology.get( 'nodes' , False )
1804 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001805 return main.ERROR
1806 switchCheck = ( int( devices ) == int( numoswitch ) )
1807 # Is the number of links is what we expected
1808 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001809 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1810 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001811 # We expected the correct numbers
1812 output = output + "The number of links and switches match "\
1813 + "what was expected"
1814 result = main.TRUE
1815 else:
1816 output = output + \
1817 "The number of links and switches does not match " + \
1818 "what was expected"
1819 result = main.FALSE
1820 output = output + "\n ONOS sees %i devices" % int( devices )
1821 output = output + " (%i expected) " % int( numoswitch )
1822 output = output + "and %i links " % int( links )
1823 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001824 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001825 output = output + "and %i controllers " % int( nodes )
1826 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001827 if logLevel == "report":
1828 main.log.report( output )
1829 elif logLevel == "warn":
1830 main.log.warn( output )
1831 else:
1832 main.log.info( output )
1833 return result
1834 except pexpect.EOF:
1835 main.log.error( self.name + ": EOF exception found" )
1836 main.log.error( self.name + ": " + self.handle.before )
1837 main.cleanup()
1838 main.exit()
1839 except Exception:
1840 main.log.exception( self.name + ": Uncaught exception!" )
1841 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001842 main.exit()