blob: 033353b3d4bd8009e3226c47e6f670326db6eacb [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="",
alisone14d7b02016-07-06 10:31:51 -07001183 priority=100,
GlennRC073e8bc2015-10-27 17:11:28 -07001184 ip="DEFAULT",
1185 port="DEFAULT",
1186 debug=False ):
1187 """
1188 Description:
1189 Creates a single flow in the specified device
1190 Required:
1191 * deviceId: id of the device
1192 Optional:
1193 * ingressPort: port ingress device
1194 * egressPort: port of egress device
1195 * ethType: specify ethType
1196 * ethSrc: specify ethSrc ( i.e. src mac addr )
1197 * ethDst: specify ethDst ( i.e. dst mac addr )
1198 * ipProto: specify ip protocol
1199 * ipSrc: specify ip source address with mask eg. ip#/24
1200 as a tuple (type, ip#)
1201 * ipDst: specify ip destination address eg. ip#/24
1202 as a tuple (type, ip#)
1203 * tcpSrc: specify tcp source port
1204 * tcpDst: specify tcp destination port
1205 Returns:
1206 Returns main.TRUE for successful requests; Returns main.FALSE
1207 if error on requests;
1208 Returns None for exceptions
1209 NOTE:
1210 The ip and port option are for the requests input's ip and port
1211 of the ONOS node
1212 """
1213 try:
alisone14d7b02016-07-06 10:31:51 -07001214 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001215 "isPermanent":"true",
1216 "timeout":0,
1217 "deviceId":deviceId,
1218 "treatment":{"instructions":[]},
1219 "selector": {"criteria":[]}}
1220 if appId:
1221 flowJson[ "appId" ] = appId
1222 if egressPort:
1223 flowJson[ 'treatment' ][ 'instructions' ].append( {
1224 "type":"OUTPUT",
1225 "port":egressPort } )
1226 if ingressPort:
1227 flowJson[ 'selector' ][ 'criteria' ].append( {
1228 "type":"IN_PORT",
1229 "port":ingressPort } )
1230 if ethType:
1231 flowJson[ 'selector' ][ 'criteria' ].append( {
1232 "type":"ETH_TYPE",
1233 "ethType":ethType } )
1234 if ethSrc:
1235 flowJson[ 'selector' ][ 'criteria' ].append( {
1236 "type":"ETH_SRC",
1237 "mac":ethSrc } )
1238 if ethDst:
1239 flowJson[ 'selector' ][ 'criteria' ].append( {
1240 "type":"ETH_DST",
1241 "mac":ethDst } )
1242 if vlan:
1243 flowJson[ 'selector' ][ 'criteria' ].append( {
1244 "type":"VLAN_VID",
1245 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001246 if mpls:
1247 flowJson[ 'selector' ][ 'criteria' ].append( {
1248 "type":"MPLS_LABEL",
1249 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001250 if ipSrc:
1251 flowJson[ 'selector' ][ 'criteria' ].append( {
1252 "type":ipSrc[0],
1253 "ip":ipSrc[1] } )
1254 if ipDst:
1255 flowJson[ 'selector' ][ 'criteria' ].append( {
1256 "type":ipDst[0],
1257 "ip":ipDst[1] } )
1258 if tcpSrc:
1259 flowJson[ 'selector' ][ 'criteria' ].append( {
1260 "type":"TCP_SRC",
1261 "tcpPort": tcpSrc } )
1262 if tcpDst:
1263 flowJson[ 'selector' ][ 'criteria' ].append( {
1264 "type":"TCP_DST",
1265 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001266 if udpSrc:
1267 flowJson[ 'selector' ][ 'criteria' ].append( {
1268 "type":"UDP_SRC",
1269 "udpPort": udpSrc } )
1270 if udpDst:
1271 flowJson[ 'selector' ][ 'criteria' ].append( {
1272 "type":"UDP_DST",
1273 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001274 if ipProto:
1275 flowJson[ 'selector' ][ 'criteria' ].append( {
1276 "type":"IP_PROTO",
1277 "protocol": ipProto } )
1278
1279 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1280
1281 except ( AttributeError, TypeError ):
1282 main.log.exception( self.name + ": Object not as expected" )
1283 return None
1284 except Exception:
1285 main.log.exception( self.name + ": Uncaught exception!" )
1286 main.cleanup()
1287 main.exit()
1288
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001289 def removeFlow( self, deviceId, flowId,
1290 ip="DEFAULT", port="DEFAULT" ):
1291 """
1292 Description:
1293 Remove specific device flow
1294 Required:
1295 str deviceId - id of the device
1296 str flowId - id of the flow
1297 Return:
1298 Returns main.TRUE if successfully deletes flows, otherwise
1299 Returns main.FALSE, Returns None on error
1300 """
1301 try:
1302 output = None
1303 if ip == "DEFAULT":
1304 main.log.warn( "No ip given, reverting to ip from topo file" )
1305 ip = self.ip_address
1306 if port == "DEFAULT":
1307 main.log.warn( "No port given, reverting to port " +
1308 "from topo file" )
1309 port = self.port
1310 # NOTE: REST url requires the intent id to be in decimal form
1311 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001312 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001313 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001314 if response:
1315 if 200 <= response[ 0 ] <= 299:
1316 return main.TRUE
1317 else:
1318 main.log.error( "Error with REST request, response was: " +
1319 str( response ) )
1320 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001321 except ( AttributeError, TypeError ):
1322 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001323 return None
Jon Halle401b092015-09-23 13:34:24 -07001324 except Exception:
1325 main.log.exception( self.name + ": Uncaught exception!" )
1326 main.cleanup()
1327 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001328
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001329 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1330 """
1331 Description:
1332 Check if all the current flows are in ADDED state
1333 Return:
1334 returnValue - Returns main.TRUE only if all flows are in
1335 return main.FALSE otherwise;
1336 Returns None for exception
1337 """
1338 try:
1339 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1340 returnValue = main.TRUE
1341 for flow in tempFlows:
1342 if flow.get( 'state' ) != 'ADDED':
1343 main.log.info( self.name + ": flow Id: " +
1344 str( flow.get( 'groupId' ) ) +
1345 " | state:" +
1346 str( flow.get( 'state' ) ) )
1347 returnValue = main.FALSE
1348 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001349 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001350 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001351 return None
1352 except Exception:
1353 main.log.exception( self.name + ": Uncaught exception!" )
1354 main.cleanup()
1355 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001356
1357 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1358 subjectClass=None, subjectKey=None, configKey=None ):
1359 """
1360 Description:
1361 Get a json object with the ONOS network configurations
1362 Returns:
1363 A json object containing the network configuration in
1364 ONOS; Returns main.FALSE if error on requests;
1365 Returns None for exception
1366 """
1367 try:
1368 output = None
1369 if ip == "DEFAULT":
1370 main.log.warn( "No ip given, reverting to ip from topo file" )
1371 ip = self.ip_address
1372 if port == "DEFAULT":
1373 main.log.warn( "No port given, reverting to port " +
1374 "from topo file" )
1375 port = self.port
1376 url = "/network/configuration"
1377 if subjectClass:
1378 url += "/" + subjectClass
1379 if subjectKey:
1380 url += "/" + subjectKey
1381 if configKey:
1382 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001383 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001384 if response:
1385 if 200 <= response[ 0 ] <= 299:
1386 output = response[ 1 ]
1387 a = json.loads( output )
1388 b = json.dumps( a )
1389 return b
1390 elif response[ 0 ] == 404:
1391 main.log.error( "Requested configuration doesn't exist: " +
1392 str( response ) )
1393 return {}
1394 else:
1395 main.log.error( "Error with REST request, response was: " +
1396 str( response ) )
1397 return main.FALSE
1398 except ( AttributeError, TypeError ):
1399 main.log.exception( self.name + ": Object not as expected" )
1400 return None
1401 except Exception:
1402 main.log.exception( self.name + ": Uncaught exception!" )
1403 main.cleanup()
1404 main.exit()
1405
1406 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1407 subjectClass=None, subjectKey=None, configKey=None ):
1408 """
1409 Description:
1410 Set a json object with the ONOS network configurations
1411 Returns:
1412 Returns main.TRUE for successful requests; Returns main.FALSE
1413 if error on requests;
1414 Returns None for exceptions
1415
1416 """
1417 try:
1418 output = None
1419 if ip == "DEFAULT":
1420 main.log.warn( "No ip given, reverting to ip from topo file" )
1421 ip = self.ip_address
1422 if port == "DEFAULT":
1423 main.log.warn( "No port given, reverting to port " +
1424 "from topo file" )
1425 port = self.port
1426 url = "/network/configuration"
1427 if subjectClass:
1428 url += "/" + subjectClass
1429 if subjectKey:
1430 url += "/" + subjectKey
1431 if configKey:
1432 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001433 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001434 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001435 data=json.dumps( cfgJson ) )
1436 if response:
1437 if 200 <= response[ 0 ] <= 299:
1438 main.log.info( self.name + ": Successfully POST cfg" )
1439 return main.TRUE
1440 else:
1441 main.log.error( "Error with REST request, response was: " +
1442 str( response ) )
1443 return main.FALSE
1444 except ( AttributeError, TypeError ):
1445 main.log.exception( self.name + ": Object not as expected" )
1446 return None
1447 except Exception:
1448 main.log.exception( self.name + ": Uncaught exception!" )
1449 main.cleanup()
1450 main.exit()
1451
1452 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1453 subjectClass=None, subjectKey=None, configKey=None ):
1454 """
1455 Description:
1456 Remove a json object from the ONOS network configurations
1457 Returns:
1458 Returns main.TRUE for successful requests; Returns main.FALSE
1459 if error on requests;
1460 Returns None for exceptions
1461
1462 """
1463 try:
1464 output = None
1465 if ip == "DEFAULT":
1466 main.log.warn( "No ip given, reverting to ip from topo file" )
1467 ip = self.ip_address
1468 if port == "DEFAULT":
1469 main.log.warn( "No port given, reverting to port " +
1470 "from topo file" )
1471 port = self.port
1472 url = "/network/configuration"
1473 if subjectClass:
1474 url += "/" + subjectClass
1475 if subjectKey:
1476 url += "/" + subjectKey
1477 if configKey:
1478 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001479 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001480 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001481 if response:
1482 if 200 <= response[ 0 ] <= 299:
1483 main.log.info( self.name + ": Successfully delete cfg" )
1484 return main.TRUE
1485 else:
1486 main.log.error( "Error with REST request, response was: " +
1487 str( response ) )
1488 return main.FALSE
1489 except ( AttributeError, TypeError ):
1490 main.log.exception( self.name + ": Object not as expected" )
1491 return None
1492 except Exception:
1493 main.log.exception( self.name + ": Uncaught exception!" )
1494 main.cleanup()
1495 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001496
1497 def createFlowBatch( self,
1498 numSw = 1,
1499 swIndex = 1,
1500 batchSize = 1,
1501 batchIndex = 1,
1502 deviceIdpreFix = "of:",
1503 appId=0,
1504 deviceID="",
1505 ingressPort="",
1506 egressPort="",
1507 ethType="",
1508 ethSrc="",
1509 ethDst="",
1510 vlan="",
1511 ipProto="",
1512 ipSrc=(),
1513 ipDst=(),
1514 tcpSrc="",
1515 tcpDst="",
1516 udpDst="",
1517 udpSrc="",
1518 mpls="",
1519 ip="DEFAULT",
1520 port="DEFAULT",
1521 debug=False ):
1522 """
1523 Description:
1524 Creates batches of MAC-rule flows for POST.
1525 Predefined MAC: 2 MS Hex digit for iterating devices
1526 Next 5 Hex digit for iterating batch numbers
1527 Next 5 Hex digit for iterating flows within a batch
1528 Required:
1529 * deviceId: id of the device
1530 Optional:
1531 * ingressPort: port ingress device
1532 * egressPort: port of egress device
1533 * ethType: specify ethType
1534 * ethSrc: specify ethSrc ( i.e. src mac addr )
1535 * ethDst: specify ethDst ( i.e. dst mac addr )
1536 * ipProto: specify ip protocol
1537 * ipSrc: specify ip source address with mask eg. ip#/24
1538 as a tuple (type, ip#)
1539 * ipDst: specify ip destination address eg. ip#/24
1540 as a tuple (type, ip#)
1541 * tcpSrc: specify tcp source port
1542 * tcpDst: specify tcp destination port
1543 Returns:
1544 Returns main.TRUE for successful requests; Returns main.FALSE
1545 if error on requests;
1546 Returns None for exceptions
1547 NOTE:
1548 The ip and port option are for the requests input's ip and port
1549 of the ONOS node
1550 """
1551 #from pprint import pprint
1552
1553 flowJsonList = []
1554 flowJsonBatch = {"flows":flowJsonList}
1555 dev = swIndex
1556
1557 for fl in range(1, batchSize + 1):
1558 flowJson = { "priority":100,
1559 "deviceId":"",
1560 "isPermanent":"true",
1561 "timeout":0,
1562 "treatment":{"instructions":[]},
1563 "selector": {"criteria":[]}}
1564
1565 #main.log.info("fl: " + str(fl))
1566 if dev <= numSw:
1567 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1568 #print deviceId
1569 flowJson['deviceId'] = deviceId
1570 dev += 1
1571 else:
1572 dev = 1
1573 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1574 #print deviceId
1575 flowJson['deviceId'] = deviceId
1576 dev += 1
1577
1578 # ethSrc starts with "0"; ethDst starts with "1"
1579 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1580 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1581 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1582 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1583 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1584 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1585 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1586
1587 if appId:
1588 flowJson[ "appId" ] = appId
1589
1590 if egressPort:
1591 flowJson[ 'treatment' ][ 'instructions' ].append( {
1592 "type":"OUTPUT",
1593 "port":egressPort } )
1594 if ingressPort:
1595 flowJson[ 'selector' ][ 'criteria' ].append( {
1596 "type":"IN_PORT",
1597 "port":ingressPort } )
1598 if ethType:
1599 flowJson[ 'selector' ][ 'criteria' ].append( {
1600 "type":"ETH_TYPE",
1601 "ethType":ethType } )
1602 if ethSrc:
1603 flowJson[ 'selector' ][ 'criteria' ].append( {
1604 "type":"ETH_SRC",
1605 "mac":ethSrc } )
1606 if ethDst:
1607 flowJson[ 'selector' ][ 'criteria' ].append( {
1608 "type":"ETH_DST",
1609 "mac":ethDst } )
1610 if vlan:
1611 flowJson[ 'selector' ][ 'criteria' ].append( {
1612 "type":"VLAN_VID",
1613 "vlanId":vlan } )
1614 if mpls:
1615 flowJson[ 'selector' ][ 'criteria' ].append( {
1616 "type":"MPLS_LABEL",
1617 "label":mpls } )
1618 if ipSrc:
1619 flowJson[ 'selector' ][ 'criteria' ].append( {
1620 "type":ipSrc[0],
1621 "ip":ipSrc[1] } )
1622 if ipDst:
1623 flowJson[ 'selector' ][ 'criteria' ].append( {
1624 "type":ipDst[0],
1625 "ip":ipDst[1] } )
1626 if tcpSrc:
1627 flowJson[ 'selector' ][ 'criteria' ].append( {
1628 "type":"TCP_SRC",
1629 "tcpPort": tcpSrc } )
1630 if tcpDst:
1631 flowJson[ 'selector' ][ 'criteria' ].append( {
1632 "type":"TCP_DST",
1633 "tcpPort": tcpDst } )
1634 if udpSrc:
1635 flowJson[ 'selector' ][ 'criteria' ].append( {
1636 "type":"UDP_SRC",
1637 "udpPort": udpSrc } )
1638 if udpDst:
1639 flowJson[ 'selector' ][ 'criteria' ].append( {
1640 "type":"UDP_DST",
1641 "udpPort": udpDst } )
1642 if ipProto:
1643 flowJson[ 'selector' ][ 'criteria' ].append( {
1644 "type":"IP_PROTO",
1645 "protocol": ipProto } )
1646 #pprint(flowJson)
1647 flowJsonList.append(flowJson)
1648
1649 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1650 flowJsonBatch['flows'] = flowJsonList
1651 #pprint(flowJsonBatch)
1652
1653 return flowJsonBatch
1654
1655
1656 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1657 """
1658 Description:
1659 Sends a single flow batch through /flows REST API.
1660 Required:
1661 * The batch of flows
1662 Returns:
1663 Returns main.TRUE for successful requests; Returns main.FALSE
1664 if error on requests;
1665 Returns None for exceptions
1666 NOTE:
1667 The ip and port option are for the requests input's ip and port
1668 of the ONOS node
1669 """
1670 import time
1671
1672 try:
1673 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1674 output = None
1675 if ip == "DEFAULT":
1676 main.log.warn( "No ip given, reverting to ip from topo file" )
1677 ip = self.ip_address
1678 if port == "DEFAULT":
1679 main.log.warn( "No port given, reverting to port " +
1680 "from topo file" )
1681 port = self.port
1682 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001683 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001684 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001685 data=json.dumps( batch ) )
1686 #main.log.info("Post response is: ", str(response[0]))
1687 if response[0] == 200:
1688 main.log.info( self.name + ": Successfully POST flow batch" )
1689 return main.TRUE, response
1690 else:
1691 main.log.error( "Error with REST request, response was: " +
1692 str( response ) )
1693 return main.FALSE
1694 except NotImplementedError as e:
1695 raise e # Inform the caller
1696 except ( AttributeError, TypeError ):
1697 main.log.exception( self.name + ": Object not as expected" )
1698 return None
1699 except Exception:
1700 main.log.exception( self.name + ": Uncaught exception!" )
1701 main.cleanup()
1702 main.exit()
1703
1704 def removeFlowBatch( self, batch={},
1705 ip="DEFAULT", port="DEFAULT" ):
1706 """
1707 Description:
1708 Remove a batch of flows
1709 Required:
1710 flow batch
1711 Return:
1712 Returns main.TRUE if successfully deletes flows, otherwise
1713 Returns main.FALSE, Returns None on error
1714 """
1715 try:
1716 output = None
1717 if ip == "DEFAULT":
1718 main.log.warn( "No ip given, reverting to ip from topo file" )
1719 ip = self.ip_address
1720 if port == "DEFAULT":
1721 main.log.warn( "No port given, reverting to port " +
1722 "from topo file" )
1723 port = self.port
1724 # NOTE: REST url requires the intent id to be in decimal form
1725
suibin zhang116647a2016-05-06 16:30:09 -07001726 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001727 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001728 data = json.dumps(batch) )
1729 if response:
1730 if 200 <= response[ 0 ] <= 299:
1731 return main.TRUE
1732 else:
1733 main.log.error( "Error with REST request, response was: " +
1734 str( response ) )
1735 return main.FALSE
1736 except ( AttributeError, TypeError ):
1737 main.log.exception( self.name + ": Object not as expected" )
1738 return None
1739 except Exception:
1740 main.log.exception( self.name + ": Uncaught exception!" )
1741 main.cleanup()
1742 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001743
1744 def getTopology( self, topologyOutput ):
1745 """
1746 Definition:
1747 Loads a json topology output
1748 Return:
1749 topology = current ONOS topology
1750 """
1751 import json
1752 try:
1753 # either onos:topology or 'topology' will work in CLI
1754 topology = json.loads(topologyOutput)
1755 main.log.debug( topology )
1756 return topology
1757 except pexpect.EOF:
1758 main.log.error( self.name + ": EOF exception found" )
1759 main.log.error( self.name + ": " + self.handle.before )
1760 main.cleanup()
1761 main.exit()
1762 except Exception:
1763 main.log.exception( self.name + ": Uncaught exception!" )
1764 main.cleanup()
1765 main.exit()
1766
1767 def checkStatus(
1768 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001769 numoswitch,
1770 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001771 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001772 logLevel="info" ):
1773 """
1774 Checks the number of switches & links that ONOS sees against the
1775 supplied values. By default this will report to main.log, but the
1776 log level can be specific.
1777
Flavio Castro82ee2f62016-06-07 15:04:12 -07001778 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001779 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001780 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001781 logLevel = level to log to.
1782 Currently accepts 'info', 'warn' and 'report'
1783
1784 Returns: main.TRUE if the number of switches and links are correct,
1785 main.FALSE if the number of switches and links is incorrect,
1786 and main.ERROR otherwise
1787 """
1788 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001789 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001790 if topology == {}:
1791 return main.ERROR
1792 output = ""
1793 # Is the number of switches is what we expected
1794 devices = topology.get( 'devices', False )
1795 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001796 nodes = topology.get( 'nodes' , False )
1797 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001798 return main.ERROR
1799 switchCheck = ( int( devices ) == int( numoswitch ) )
1800 # Is the number of links is what we expected
1801 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001802 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1803 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001804 # We expected the correct numbers
1805 output = output + "The number of links and switches match "\
1806 + "what was expected"
1807 result = main.TRUE
1808 else:
1809 output = output + \
1810 "The number of links and switches does not match " + \
1811 "what was expected"
1812 result = main.FALSE
1813 output = output + "\n ONOS sees %i devices" % int( devices )
1814 output = output + " (%i expected) " % int( numoswitch )
1815 output = output + "and %i links " % int( links )
1816 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001817 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001818 output = output + "and %i controllers " % int( nodes )
1819 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001820 if logLevel == "report":
1821 main.log.report( output )
1822 elif logLevel == "warn":
1823 main.log.warn( output )
1824 else:
1825 main.log.info( output )
1826 return result
1827 except pexpect.EOF:
1828 main.log.error( self.name + ": EOF exception found" )
1829 main.log.error( self.name + ": " + self.handle.before )
1830 main.cleanup()
1831 main.exit()
1832 except Exception:
1833 main.log.exception( self.name + ": Uncaught exception!" )
1834 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001835 main.exit()