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