blob: 50e4847c3fcb47c3ba6d285b6395bbfa195461ae [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 zhang116647a2016-05-06 16:30:09 -070078 def send( self, url, 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 zhang116647a2016-05-06 16:30:09 -070097 ip = self.ip_address
98 port = self.port
99
Jon Hallfc915882015-07-14 13:33:17 -0700100 try:
101 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700102 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800103 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700104 auth = (self.user_name, self.pwd)
105 else:
106 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700107 main.log.info( "Sending request " + path + " using " +
108 method.upper() + " method." )
109 response = requests.request( method.upper(),
110 path,
111 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700112 data=data,
113 auth=auth )
114 if debug:
115 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700116 return ( response.status_code, response.text.encode( 'utf8' ) )
117 except requests.exceptions:
118 main.log.exception( "Error sending request." )
119 return None
Jon Halle401b092015-09-23 13:34:24 -0700120 except Exception:
121 main.log.exception( self.name + ": Uncaught exception!" )
122 main.cleanup()
123 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700124
125 def intents( self, ip="DEFAULT", port="DEFAULT" ):
126 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700127 Description:
128 Gets a list of dictionary of all intents in the system
129 Returns:
130 A list of dictionary of intents in string type to match the cli
131 version for now; Returns main.FALSE if error on request;
132 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700133 """
134 try:
135 output = None
136 if ip == "DEFAULT":
137 main.log.warn( "No ip given, reverting to ip from topo file" )
138 ip = self.ip_address
139 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700140 main.log.warn( "No port given, reverting to port " +
141 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700142 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700143 response = self.send( url="/intents" )
Jon Hallfc915882015-07-14 13:33:17 -0700144 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700145 if 200 <= response[ 0 ] <= 299:
146 output = response[ 1 ]
147 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700148 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700149 b = json.dumps( a )
150 return b
Jon Hallfc915882015-07-14 13:33:17 -0700151 else:
152 main.log.error( "Error with REST request, response was: " +
153 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700154 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700155 except ( AttributeError, AssertionError, TypeError ):
156 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700157 return None
Jon Halle401b092015-09-23 13:34:24 -0700158 except Exception:
159 main.log.exception( self.name + ": Uncaught exception!" )
160 main.cleanup()
161 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700162
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700163 def intent( self, intentId, appId="org.onosproject.cli",
164 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700165 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700166 Description:
167 Get the specific intent information of the given application ID and
168 intent ID
169 Required:
170 str intentId - Intent id in hexadecimal form
171 Optional:
172 str appId - application id of intent
173 Returns:
174 Returns an information dictionary of the given intent;
175 Returns main.FALSE if error on requests; Returns None for exception
176 NOTE:
177 The GET /intents REST api command accepts application id but the
178 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700179 """
180 try:
181 output = None
182 if ip == "DEFAULT":
183 main.log.warn( "No ip given, reverting to ip from topo file" )
184 ip = self.ip_address
185 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700186 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700187 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700188 port = self.port
189 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700190 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhang116647a2016-05-06 16:30:09 -0700191 response = self.send( url="/intents" + query )
Jon Hallfc915882015-07-14 13:33:17 -0700192 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700193 if 200 <= response[ 0 ] <= 299:
194 output = response[ 1 ]
195 a = json.loads( output )
196 return a
Jon Hallfc915882015-07-14 13:33:17 -0700197 else:
198 main.log.error( "Error with REST request, response was: " +
199 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700200 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700201 except ( AttributeError, TypeError ):
202 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700203 return None
Jon Halle401b092015-09-23 13:34:24 -0700204 except Exception:
205 main.log.exception( self.name + ": Uncaught exception!" )
206 main.cleanup()
207 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700208
209 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
210 """
211 Description:
212 Gets all intents ID using intents function
213 Returns:
214 List of intents ID;Returns None for exception; Returns None for
215 exception; Returns None for exception
216 """
217 try:
218 intentsDict = {}
219 intentsIdList = []
220 intentsDict = json.loads( self.intents( ip=ip, port=port ) )
221 for intent in intentsDict:
222 intentsIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700223 if not intentsIdList:
224 main.log.debug( "Cannot find any intents" )
225 return main.FALSE
226 else:
227 main.log.info( "Found intents: " + str( intentsIdList ) )
228 return main.TRUE
Jon Halle401b092015-09-23 13:34:24 -0700229 except ( AttributeError, TypeError ):
230 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700231 return None
Jon Halle401b092015-09-23 13:34:24 -0700232 except Exception:
233 main.log.exception( self.name + ": Uncaught exception!" )
234 main.cleanup()
235 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700236
237 def apps( self, ip="DEFAULT", port="DEFAULT" ):
238 """
239 Description:
240 Returns all the current application installed in the system
241 Returns:
242 List of dictionary of installed application; Returns main.FALSE for
243 error on request; Returns None for exception
244 """
245 try:
246 output = None
247 if ip == "DEFAULT":
248 main.log.warn( "No ip given, reverting to ip from topo file" )
249 ip = self.ip_address
250 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700251 main.log.warn( "No port given, reverting to port " +
252 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700253 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700254 response = self.send( url="/applications" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700255 if response:
256 if 200 <= response[ 0 ] <= 299:
257 output = response[ 1 ]
258 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700259 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700260 b = json.dumps( a )
261 return b
262 else:
263 main.log.error( "Error with REST request, response was: " +
264 str( response ) )
265 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700266 except ( AttributeError, AssertionError, TypeError ):
267 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700268 return None
Jon Halle401b092015-09-23 13:34:24 -0700269 except Exception:
270 main.log.exception( self.name + ": Uncaught exception!" )
271 main.cleanup()
272 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700273
274 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
275 """
276 Decription:
277 Activate an app that is already installed in ONOS
278 Optional:
279 bool check - If check is True, method will check the status
280 of the app after the command is issued
281 Returns:
282 Returns main.TRUE if the command was successfully or main.FALSE
283 if the REST responded with an error or given incorrect input;
284 Returns None for exception
285
286 """
287 try:
288 output = None
289 if ip == "DEFAULT":
290 main.log.warn( "No ip given, reverting to ip from topo file" )
291 ip = self.ip_address
292 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700293 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700294 "from topo file" )
295 port = self.port
296 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700297 response = self.send( method="POST",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700298 url="/applications" + query )
299 if response:
300 output = response[ 1 ]
301 app = json.loads( output )
302 if 200 <= response[ 0 ] <= 299:
303 if check:
304 if app.get( 'state' ) == 'ACTIVE':
305 main.log.info( self.name + ": " + appName +
306 " application" +
307 " is in ACTIVE state" )
308 return main.TRUE
309 else:
310 main.log.error( self.name + ": " + appName +
311 " application" + " is in " +
312 app.get( 'state' ) + " state" )
313 return main.FALSE
314 else:
315 main.log.warn( "Skipping " + appName +
316 "application check" )
317 return main.TRUE
318 else:
319 main.log.error( "Error with REST request, response was: " +
320 str( response ) )
321 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700322 except ( AttributeError, TypeError ):
323 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700324 return None
Jon Halle401b092015-09-23 13:34:24 -0700325 except Exception:
326 main.log.exception( self.name + ": Uncaught exception!" )
327 main.cleanup()
328 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700329
330 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
331 check=True ):
332 """
333 Required:
334 Deactivate an app that is already activated in ONOS
335 Optional:
336 bool check - If check is True, method will check the status of the
337 app after the command is issued
338 Returns:
339 Returns main.TRUE if the command was successfully sent
340 main.FALSE if the REST responded with an error or given
341 incorrect input; Returns None for exception
342 """
343 try:
344 output = None
345 if ip == "DEFAULT":
346 main.log.warn( "No ip given, reverting to ip from topo file" )
347 ip = self.ip_address
348 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700349 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700350 "from topo file" )
351 port = self.port
352 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700353 response = self.send( method="DELETE",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700354 url="/applications" + query )
355 if response:
356 output = response[ 1 ]
357 app = json.loads( output )
358 if 200 <= response[ 0 ] <= 299:
359 if check:
360 if app.get( 'state' ) == 'INSTALLED':
361 main.log.info( self.name + ": " + appName +
362 " application" +
363 " is in INSTALLED state" )
364 return main.TRUE
365 else:
366 main.log.error( self.name + ": " + appName +
367 " application" + " is in " +
368 app.get( 'state' ) + " state" )
369 return main.FALSE
370 else:
371 main.log.warn( "Skipping " + appName +
372 "application check" )
373 return main.TRUE
374 else:
375 main.log.error( "Error with REST request, response was: " +
376 str( response ) )
377 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700378 except ( AttributeError, TypeError ):
379 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700380 return None
Jon Halle401b092015-09-23 13:34:24 -0700381 except Exception:
382 main.log.exception( self.name + ": Uncaught exception!" )
383 main.cleanup()
384 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700385
386 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
387 port="DEFAULT" ):
388 """
389 Decription:
390 Gets the informaion of the given application
391 Required:
392 str name - Name of onos application
393 Returns:
394 Returns a dictionary of information ONOS application in string type;
395 Returns main.FALSE if error on requests; Returns None for exception
396 """
397 try:
398 output = None
399 if ip == "DEFAULT":
400 main.log.warn( "No ip given, reverting to ip from topo file" )
401 ip = self.ip_address
402 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700403 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700404 "from topo file" )
405 port = self.port
406 query = "/" + project + str( appName )
suibin zhang116647a2016-05-06 16:30:09 -0700407 response = self.send( url="/applications" + query )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700408 if response:
409 if 200 <= response[ 0 ] <= 299:
410 output = response[ 1 ]
411 a = json.loads( output )
412 return a
413 else:
414 main.log.error( "Error with REST request, response was: " +
415 str( response ) )
416 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700417 except ( AttributeError, TypeError ):
418 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700419 return None
Jon Halle401b092015-09-23 13:34:24 -0700420 except Exception:
421 main.log.exception( self.name + ": Uncaught exception!" )
422 main.cleanup()
423 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700424
425 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
426 ip="DEFAULT", port="DEFAULT" ):
427 """
428 Description:
429 Adds a host-to-host intent ( bidirectional ) by
430 specifying the two hosts.
431 Required:
432 * hostIdOne: ONOS host id for host1
433 * hostIdTwo: ONOS host id for host2
434 Optional:
435 str appId - Application name of intent identifier
436 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700437 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700438 error on requests; Returns None for exceptions
439 """
440 try:
441 intentJson = {"two": str( hostIdTwo ),
442 "selector": {"criteria": []}, "priority": 7,
443 "treatment": {"deferred": [], "instructions": []},
444 "appId": appId, "one": str( hostIdOne ),
445 "type": "HostToHostIntent",
446 "constraints": [{"type": "LinkTypeConstraint",
447 "types": ["OPTICAL"],
448 "inclusive": 'false' }]}
449 output = None
450 if ip == "DEFAULT":
451 main.log.warn( "No ip given, reverting to ip from topo file" )
452 ip = self.ip_address
453 if port == "DEFAULT":
454 main.log.warn( "No port given, reverting to port " +
455 "from topo file" )
456 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700457 response = self.send( method="POST",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700458 url="/intents",
459 data=json.dumps( intentJson ) )
460 if response:
461 if 201:
462 main.log.info( self.name + ": Successfully POST host" +
463 " intent between host: " + hostIdOne +
464 " and host: " + hostIdTwo )
465 return main.TRUE
466 else:
467 main.log.error( "Error with REST request, response was: " +
468 str( response ) )
469 return main.FALSE
470
Jon Halle401b092015-09-23 13:34:24 -0700471 except ( AttributeError, TypeError ):
472 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700473 return None
Jon Halle401b092015-09-23 13:34:24 -0700474 except Exception:
475 main.log.exception( self.name + ": Uncaught exception!" )
476 main.cleanup()
477 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700478
kelvin-onlabb50074f2015-07-27 16:18:32 -0700479 def addPointIntent( self,
480 ingressDevice,
481 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700482 appId='org.onosproject.cli',
483 ingressPort="",
484 egressPort="",
485 ethType="",
486 ethSrc="",
487 ethDst="",
488 bandwidth="",
489 lambdaAlloc=False,
490 ipProto="",
491 ipSrc="",
492 ipDst="",
493 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700494 tcpDst="",
495 ip="DEFAULT",
496 port="DEFAULT" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700497 """
498 Description:
499 Adds a point-to-point intent ( uni-directional ) by
500 specifying device id's and optional fields
501 Required:
502 * ingressDevice: device id of ingress device
503 * egressDevice: device id of egress device
504 Optional:
505 * ethType: specify ethType
506 * ethSrc: specify ethSrc ( i.e. src mac addr )
507 * ethDst: specify ethDst ( i.e. dst mac addr )
508 * bandwidth: specify bandwidth capacity of link (TODO)
509 * lambdaAlloc: if True, intent will allocate lambda
510 for the specified intent (TODO)
511 * ipProto: specify ip protocol
512 * ipSrc: specify ip source address with mask eg. ip#/24
513 * ipDst: specify ip destination address eg. ip#/24
514 * tcpSrc: specify tcp source port
515 * tcpDst: specify tcp destination port
516 Returns:
517 Returns main.TRUE for successful requests; Returns main.FALSE if
518 no ingress|egress port found and if error on requests;
519 Returns None for exceptions
520 NOTE:
521 The ip and port option are for the requests input's ip and port
522 of the ONOS node
523 """
524 try:
525 if "/" in ingressDevice:
526 if not ingressPort:
527 ingressPort = ingressDevice.split( "/" )[ 1 ]
528 ingressDevice = ingressDevice.split( "/" )[ 0 ]
529 else:
530 if not ingressPort:
531 main.log.debug( self.name + ": Ingress port not specified" )
532 return main.FALSE
533
534 if "/" in egressDevice:
535 if not egressPort:
536 egressPort = egressDevice.split( "/" )[ 1 ]
537 egressDevice = egressDevice.split( "/" )[ 0 ]
538 else:
539 if not egressPort:
540 main.log.debug( self.name + ": Egress port not specified" )
541 return main.FALSE
542
543 intentJson ={ "ingressPoint": { "device": ingressDevice,
544 "port": ingressPort },
545 "selector": { "criteria": [] },
546 "priority": 55,
547 "treatment": { "deferred": [],
548 "instructions": [] },
549 "egressPoint": { "device": egressDevice,
550 "port": egressPort },
551 "appId": appId,
552 "type": "PointToPointIntent",
553 "constraints": [ { "type": "LinkTypeConstraint",
554 "types": [ "OPTICAL" ],
555 "inclusive": "false" } ] }
556
557 if ethType == "IPV4":
558 intentJson[ 'selector' ][ 'criteria' ].append( {
559 "type":"ETH_TYPE",
560 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700561 elif ethType:
562 intentJson[ 'selector' ][ 'criteria' ].append( {
563 "type":"ETH_TYPE",
564 "ethType":ethType } )
565
kelvin-onlabb50074f2015-07-27 16:18:32 -0700566 if ethSrc:
567 intentJson[ 'selector' ][ 'criteria' ].append(
568 { "type":"ETH_SRC",
569 "mac":ethSrc } )
570 if ethDst:
571 intentJson[ 'selector' ][ 'criteria' ].append(
572 { "type":"ETH_DST",
573 "mac":ethDst } )
574 if ipSrc:
575 intentJson[ 'selector' ][ 'criteria' ].append(
576 { "type":"IPV4_SRC",
577 "ip":ipSrc } )
578 if ipDst:
579 intentJson[ 'selector' ][ 'criteria' ].append(
580 { "type":"IPV4_DST",
581 "ip":ipDst } )
582 if tcpSrc:
583 intentJson[ 'selector' ][ 'criteria' ].append(
584 { "type":"TCP_SRC",
585 "tcpPort": tcpSrc } )
586 if tcpDst:
587 intentJson[ 'selector' ][ 'criteria' ].append(
588 { "type":"TCP_DST",
589 "tcpPort": tcpDst } )
590 if ipProto:
591 intentJson[ 'selector' ][ 'criteria' ].append(
592 { "type":"IP_PROTO",
593 "protocol": ipProto } )
594
595 # TODO: Bandwidth and Lambda will be implemented if needed
596
597 main.log.debug( intentJson )
598
599 output = None
600 if ip == "DEFAULT":
601 main.log.warn( "No ip given, reverting to ip from topo file" )
602 ip = self.ip_address
603 if port == "DEFAULT":
604 main.log.warn( "No port given, reverting to port " +
605 "from topo file" )
606 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700607 response = self.send( method="POST",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700608 url="/intents",
609 data=json.dumps( intentJson ) )
610 if response:
611 if 201:
612 main.log.info( self.name + ": Successfully POST point" +
613 " intent between ingress: " + ingressDevice +
614 " and egress: " + egressDevice + " devices" )
615 return main.TRUE
616 else:
617 main.log.error( "Error with REST request, response was: " +
618 str( response ) )
619 return main.FALSE
620
Jon Halle401b092015-09-23 13:34:24 -0700621 except ( AttributeError, TypeError ):
622 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700623 return None
Jon Halle401b092015-09-23 13:34:24 -0700624 except Exception:
625 main.log.exception( self.name + ": Uncaught exception!" )
626 main.cleanup()
627 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700628
629 def removeIntent( self, intentId, appId='org.onosproject.cli',
630 ip="DEFAULT", port="DEFAULT" ):
631 """
632 Remove intent for specified application id and intent id;
633 Returns None for exception
634 """
635 try:
636 output = None
637 if ip == "DEFAULT":
638 main.log.warn( "No ip given, reverting to ip from topo file" )
639 ip = self.ip_address
640 if port == "DEFAULT":
641 main.log.warn( "No port given, reverting to port " +
642 "from topo file" )
643 port = self.port
644 # NOTE: REST url requires the intent id to be in decimal form
645 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700646 response = self.send( method="DELETE",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700647 url="/intents" + query )
648 if response:
649 if 200 <= response[ 0 ] <= 299:
650 return main.TRUE
651 else:
652 main.log.error( "Error with REST request, response was: " +
653 str( response ) )
654 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700655 except ( AttributeError, TypeError ):
656 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700657 return None
Jon Halle401b092015-09-23 13:34:24 -0700658 except Exception:
659 main.log.exception( self.name + ": Uncaught exception!" )
660 main.cleanup()
661 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700662
663 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
664 """
665 Returns a list of intents id; Returns None for exception
666 """
667 try:
668 intentIdList = []
669 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700670 for intent in intentsJson:
671 intentIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700672 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700673 except ( AttributeError, TypeError ):
674 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700675 return None
Jon Halle401b092015-09-23 13:34:24 -0700676 except Exception:
677 main.log.exception( self.name + ": Uncaught exception!" )
678 main.cleanup()
679 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700680
681 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
682 ip="DEFAULT", port="DEFAULT", delay=5 ):
683 """
684 Description:
685 Remove all the intents
686 Returns:
687 Returns main.TRUE if all intents are removed, otherwise returns
688 main.FALSE; Returns None for exception
689 """
690 try:
691 results = []
692 if intentIdList == 'ALL':
693 intentIdList = self.getIntentsId( ip=ip, port=port )
694
695 main.log.info( self.name + ": Removing intents " +
696 str( intentIdList ) )
697
698 if isinstance( intentIdList, types.ListType ):
699 for intent in intentIdList:
700 results.append( self.removeIntent( intentId=intent,
701 appId=appId,
702 ip=ip,
703 port=port ) )
704 # Check for remaining intents
705 # NOTE: Noticing some delay on Deleting the intents so i put
706 # this time out
707 import time
708 time.sleep( delay )
709 intentRemain = len( json.loads( self.intents() ) )
710 if all( result==main.TRUE for result in results ) and \
711 intentRemain == 0:
712 main.log.info( self.name + ": All intents are removed " )
713 return main.TRUE
714 else:
715 main.log.error( self.name + ": Did not removed all intents,"
716 + " there are " + str( intentRemain )
717 + " intents remaining" )
718 return main.FALSE
719 else:
720 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700721 except ( AttributeError, TypeError ):
722 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700723 return None
Jon Halle401b092015-09-23 13:34:24 -0700724 except Exception:
725 main.log.exception( self.name + ": Uncaught exception!" )
726 main.cleanup()
727 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700728
729 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
730 """
731 Description:
732 Get a list of dictionary of all discovered hosts
733 Returns:
734 Returns a list of dictionary of information of the hosts currently
735 discovered by ONOS; Returns main.FALSE if error on requests;
736 Returns None for exception
737 """
738 try:
739 output = None
740 if ip == "DEFAULT":
741 main.log.warn( "No ip given, reverting to ip from topo file" )
742 ip = self.ip_address
743 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700744 main.log.warn( "No port given, reverting to port " +
745 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700746 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700747 response = self.send( url="/hosts" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700748 if response:
749 if 200 <= response[ 0 ] <= 299:
750 output = response[ 1 ]
751 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700752 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700753 b = json.dumps( a )
754 return b
755 else:
756 main.log.error( "Error with REST request, response was: " +
757 str( response ) )
758 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700759 except ( AttributeError, AssertionError, TypeError ):
760 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700761 return None
Jon Halle401b092015-09-23 13:34:24 -0700762 except Exception:
763 main.log.exception( self.name + ": Uncaught exception!" )
764 main.cleanup()
765 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700766
767 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
768 """
769 Description:
770 Gets the information from the given host
771 Required:
772 str mac - MAC address of the host
773 Optional:
774 str vlan - VLAN tag of the host, defaults to -1
775 Returns:
776 Return the host id from the hosts/mac/vlan output in REST api
777 whose 'id' contains mac/vlan; Returns None for exception;
778 Returns main.FALSE if error on requests
779
780 NOTE:
781 Not sure what this function should do, any suggestion?
782 """
783 try:
784 output = None
785 if ip == "DEFAULT":
786 main.log.warn( "No ip given, reverting to ip from topo file" )
787 ip = self.ip_address
788 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700789 main.log.warn( "No port given, reverting to port " +
790 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700791 port = self.port
792 query = "/" + mac + "/" + vlan
suibin zhang116647a2016-05-06 16:30:09 -0700793 response = self.send( url="/hosts" + query )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700794 if response:
795 # NOTE: What if the person wants other values? would it be better
796 # to have a function that gets a key and return a value instead?
797 # This function requires mac and vlan and returns an ID which
798 # makes this current function useless
799 if 200 <= response[ 0 ] <= 299:
800 output = response[ 1 ]
801 hostId = json.loads( output ).get( 'id' )
802 return hostId
803 else:
804 main.log.error( "Error with REST request, response was: " +
805 str( response ) )
806 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700807 except ( AttributeError, TypeError ):
808 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700809 return None
Jon Halle401b092015-09-23 13:34:24 -0700810 except Exception:
811 main.log.exception( self.name + ": Uncaught exception!" )
812 main.cleanup()
813 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700814
815 def topology( self, ip="DEFAULT", port="DEFAULT" ):
816 """
817 Description:
818 Gets the overview of network topology
819 Returns:
820 Returns a dictionary containing information about network topology;
821 Returns None for exception
822 """
823 try:
824 output = None
825 if ip == "DEFAULT":
826 main.log.warn( "No ip given, reverting to ip from topo file" )
827 ip = self.ip_address
828 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700829 main.log.warn( "No port given, reverting to port " +
830 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700831 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700832 response = self.send( url="/topology" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700833 if response:
834 if 200 <= response[ 0 ] <= 299:
835 output = response[ 1 ]
836 a = json.loads( output )
837 b = json.dumps( a )
838 return b
839 else:
840 main.log.error( "Error with REST request, response was: " +
841 str( response ) )
842 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700843 except ( AttributeError, TypeError ):
844 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700845 return None
Jon Halle401b092015-09-23 13:34:24 -0700846 except Exception:
847 main.log.exception( self.name + ": Uncaught exception!" )
848 main.cleanup()
849 main.exit()
850
851 def devices( self, ip="DEFAULT", port="DEFAULT" ):
852 """
853 Description:
854 Get the devices discovered by ONOS is json string format
855 Returns:
856 a json string of the devices currently discovered by ONOS OR
857 main.FALSE if there is an error in the request OR
858 Returns None for exception
859 """
860 try:
861 output = None
862 if ip == "DEFAULT":
863 main.log.warn( "No ip given, reverting to ip from topo file" )
864 ip = self.ip_address
865 if port == "DEFAULT":
866 main.log.warn( "No port given, reverting to port " +
867 "from topo file" )
868 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700869 response = self.send( url="/devices" )
Jon Halle401b092015-09-23 13:34:24 -0700870 if response:
871 if 200 <= response[ 0 ] <= 299:
872 output = response[ 1 ]
873 a = json.loads( output ).get( 'devices' )
874 assert a is not None, "Error parsing json object"
875 b = json.dumps( a )
876 return b
877 else:
878 main.log.error( "Error with REST request, response was: " +
879 str( response ) )
880 return main.FALSE
881 except ( AttributeError, AssertionError, TypeError ):
882 main.log.exception( self.name + ": Object not as expected" )
883 return None
884 except Exception:
885 main.log.exception( self.name + ": Uncaught exception!" )
886 main.cleanup()
887 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700888
889 def getIntentState( self, intentsId, intentsJson=None,
890 ip="DEFAULT", port="DEFAULT" ):
891 """
892 Description:
893 Get intent state.
894 Accepts a single intent ID (string type) or a list of intent IDs.
895 Returns the state(string type) of the id if a single intent ID is
896 accepted.
897 Required:
898 intentId: intent ID (string type)
899 intentsJson: parsed json object from the onos:intents api
900 Returns:
901 Returns a dictionary with intent IDs as the key and its
902 corresponding states as the values; Returns None for invalid IDs or
903 Type error and any exceptions
904 NOTE:
905 An intent's state consist of INSTALLED,WITHDRAWN etc.
906 """
907 try:
908 state = "State is Undefined"
909 if not intentsJson:
910 intentsJsonTemp = json.loads( self.intents() )
911 else:
912 intentsJsonTemp = json.loads( intentsJson )
913 if isinstance( intentsId, types.StringType ):
914 for intent in intentsJsonTemp:
915 if intentsId == intent[ 'id' ]:
916 state = intent[ 'state' ]
917 return state
918 main.log.info( "Cannot find intent ID" + str( intentsId ) +
919 " on the list" )
920 return state
921 elif isinstance( intentsId, types.ListType ):
922 dictList = []
923 for i in xrange( len( intentsId ) ):
924 stateDict = {}
925 for intents in intentsJsonTemp:
926 if intentsId[ i ] == intents[ 'id' ]:
927 stateDict[ 'state' ] = intents[ 'state' ]
928 stateDict[ 'id' ] = intentsId[ i ]
929 dictList.append( stateDict )
930 break
931 if len( intentsId ) != len( dictList ):
932 main.log.info( "Cannot find some of the intent ID state" )
933 return dictList
934 else:
935 main.log.info( "Invalid intents ID entry" )
936 return None
937
Jon Halle401b092015-09-23 13:34:24 -0700938 except ( AttributeError, TypeError ):
939 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700940 return None
Jon Halle401b092015-09-23 13:34:24 -0700941 except Exception:
942 main.log.exception( self.name + ": Uncaught exception!" )
943 main.cleanup()
944 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700945
946 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
947 ip="DEFAULT", port="DEFAULT"):
948 """
949 Description:
950 Check intents state based on expected state which defaults to
951 INSTALLED state
952 Required:
953 intentsId - List of intents ID to be checked
954 Optional:
955 expectedState - Check the expected state(s) of each intents
956 state in the list.
957 *NOTE: You can pass in a list of expected state,
958 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
959 Return:
960 Returns main.TRUE only if all intent are the same as expected states
961 , otherwise, returns main.FALSE; Returns None for general exception
962 """
963 try:
964 # Generating a dictionary: intent id as a key and state as value
965 returnValue = main.TRUE
966 if intentsId == "ALL":
967 intentsId = self.getIntentsId( ip=ip, port=port )
968 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
969
970 #print "len of intentsDict ", str( len( intentsDict ) )
971 if len( intentsId ) != len( intentsDict ):
972 main.log.error( self.name + ": There is something wrong " +
973 "getting intents state" )
974 return main.FALSE
975
976 if isinstance( expectedState, types.StringType ):
977 for intents in intentsDict:
978 if intents.get( 'state' ) != expectedState:
979 main.log.debug( self.name + " : Intent ID - " +
980 intents.get( 'id' ) +
981 " actual state = " +
982 intents.get( 'state' )
983 + " does not equal expected state = "
984 + expectedState )
985 returnValue = main.FALSE
986
987 elif isinstance( expectedState, types.ListType ):
988 for intents in intentsDict:
989 if not any( state == intents.get( 'state' ) for state in
990 expectedState ):
991 main.log.debug( self.name + " : Intent ID - " +
992 intents.get( 'id' ) +
993 " actual state = " +
994 intents.get( 'state' ) +
995 " does not equal expected states = "
996 + str( expectedState ) )
997 returnValue = main.FALSE
998
999 if returnValue == main.TRUE:
1000 main.log.info( self.name + ": All " +
1001 str( len( intentsDict ) ) +
1002 " intents are in " + str( expectedState ) +
1003 " state" )
1004 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001005 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001006 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001007 return None
Jon Halle401b092015-09-23 13:34:24 -07001008 except Exception:
1009 main.log.exception( self.name + ": Uncaught exception!" )
1010 main.cleanup()
1011 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001012
1013 def flows( self, ip="DEFAULT", port="DEFAULT" ):
1014 """
1015 Description:
1016 Get flows currently added to the system
1017 NOTE:
1018 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001019 the REST output
1020
1021 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001022 """
1023 try:
1024 output = None
1025 if ip == "DEFAULT":
1026 main.log.warn( "No ip given, reverting to ip from topo file" )
1027 ip = self.ip_address
1028 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001029 main.log.warn( "No port given, reverting to port " +
1030 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001031 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -07001032 response = self.send( url="/flows" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001033 if response:
1034 if 200 <= response[ 0 ] <= 299:
1035 output = response[ 1 ]
1036 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001037 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001038 b = json.dumps( a )
1039 return b
1040 else:
1041 main.log.error( "Error with REST request, response was: " +
1042 str( response ) )
1043 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001044 except ( AttributeError, AssertionError, TypeError ):
1045 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001046 return None
Jon Halle401b092015-09-23 13:34:24 -07001047 except Exception:
1048 main.log.exception( self.name + ": Uncaught exception!" )
1049 main.cleanup()
1050 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001051
Jon Halle401b092015-09-23 13:34:24 -07001052 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001053 """
1054 Description:
1055 Gets all the flows of the device or get a specific flow in the
1056 device by giving its flow ID
1057 Required:
Jon Halle401b092015-09-23 13:34:24 -07001058 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001059 Optional:
1060 int/hex flowId - ID of the flow
1061 """
1062 try:
1063 output = None
1064 if ip == "DEFAULT":
1065 main.log.warn( "No ip given, reverting to ip from topo file" )
1066 ip = self.ip_address
1067 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001068 main.log.warn( "No port given, reverting to port " +
1069 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001070 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001071 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001072 if flowId:
1073 url += "/" + str( int( flowId ) )
1074 print url
suibin zhang116647a2016-05-06 16:30:09 -07001075 response = self.send( url=url )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001076 if response:
1077 if 200 <= response[ 0 ] <= 299:
1078 output = response[ 1 ]
1079 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001080 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001081 b = json.dumps( a )
1082 return b
1083 else:
1084 main.log.error( "Error with REST request, response was: " +
1085 str( response ) )
1086 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001087 except ( AttributeError, AssertionError, TypeError ):
1088 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001089 return None
Jon Halle401b092015-09-23 13:34:24 -07001090 except Exception:
1091 main.log.exception( self.name + ": Uncaught exception!" )
1092 main.cleanup()
1093 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001094
GlennRC073e8bc2015-10-27 17:11:28 -07001095 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001096 """
1097 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001098 Sends a single flow to the specified device. This function exists
1099 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001100 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001101 * The flow in json
1102 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001103 Returns:
1104 Returns main.TRUE for successful requests; Returns main.FALSE
1105 if error on requests;
1106 Returns None for exceptions
1107 NOTE:
1108 The ip and port option are for the requests input's ip and port
1109 of the ONOS node
1110 """
GlennRC073e8bc2015-10-27 17:11:28 -07001111
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001112 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001113 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001114 output = None
1115 if ip == "DEFAULT":
1116 main.log.warn( "No ip given, reverting to ip from topo file" )
1117 ip = self.ip_address
1118 if port == "DEFAULT":
1119 main.log.warn( "No port given, reverting to port " +
1120 "from topo file" )
1121 port = self.port
1122 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001123 response = self.send( method="POST",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001124 url=url,
1125 data=json.dumps( flowJson ) )
1126 if response:
1127 if 201:
1128 main.log.info( self.name + ": Successfully POST flow" +
1129 "in device: " + str( deviceId ) )
1130 return main.TRUE
1131 else:
1132 main.log.error( "Error with REST request, response was: " +
1133 str( response ) )
1134 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001135 except NotImplementedError as e:
1136 raise e # Inform the caller
1137 except ( AttributeError, TypeError ):
1138 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001139 return None
Jon Halle401b092015-09-23 13:34:24 -07001140 except Exception:
1141 main.log.exception( self.name + ": Uncaught exception!" )
1142 main.cleanup()
1143 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001144
GlennRC073e8bc2015-10-27 17:11:28 -07001145 def addFlow( self,
1146 deviceId,
1147 appId=0,
1148 ingressPort="",
1149 egressPort="",
1150 ethType="",
1151 ethSrc="",
1152 ethDst="",
1153 vlan="",
1154 ipProto="",
1155 ipSrc=(),
1156 ipDst=(),
1157 tcpSrc="",
1158 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001159 udpDst="",
1160 udpSrc="",
1161 mpls="",
GlennRC073e8bc2015-10-27 17:11:28 -07001162 ip="DEFAULT",
1163 port="DEFAULT",
1164 debug=False ):
1165 """
1166 Description:
1167 Creates a single flow in the specified device
1168 Required:
1169 * deviceId: id of the device
1170 Optional:
1171 * ingressPort: port ingress device
1172 * egressPort: port of egress device
1173 * ethType: specify ethType
1174 * ethSrc: specify ethSrc ( i.e. src mac addr )
1175 * ethDst: specify ethDst ( i.e. dst mac addr )
1176 * ipProto: specify ip protocol
1177 * ipSrc: specify ip source address with mask eg. ip#/24
1178 as a tuple (type, ip#)
1179 * ipDst: specify ip destination address eg. ip#/24
1180 as a tuple (type, ip#)
1181 * tcpSrc: specify tcp source port
1182 * tcpDst: specify tcp destination port
1183 Returns:
1184 Returns main.TRUE for successful requests; Returns main.FALSE
1185 if error on requests;
1186 Returns None for exceptions
1187 NOTE:
1188 The ip and port option are for the requests input's ip and port
1189 of the ONOS node
1190 """
1191 try:
1192 flowJson = { "priority":100,
1193 "isPermanent":"true",
1194 "timeout":0,
1195 "deviceId":deviceId,
1196 "treatment":{"instructions":[]},
1197 "selector": {"criteria":[]}}
1198 if appId:
1199 flowJson[ "appId" ] = appId
1200 if egressPort:
1201 flowJson[ 'treatment' ][ 'instructions' ].append( {
1202 "type":"OUTPUT",
1203 "port":egressPort } )
1204 if ingressPort:
1205 flowJson[ 'selector' ][ 'criteria' ].append( {
1206 "type":"IN_PORT",
1207 "port":ingressPort } )
1208 if ethType:
1209 flowJson[ 'selector' ][ 'criteria' ].append( {
1210 "type":"ETH_TYPE",
1211 "ethType":ethType } )
1212 if ethSrc:
1213 flowJson[ 'selector' ][ 'criteria' ].append( {
1214 "type":"ETH_SRC",
1215 "mac":ethSrc } )
1216 if ethDst:
1217 flowJson[ 'selector' ][ 'criteria' ].append( {
1218 "type":"ETH_DST",
1219 "mac":ethDst } )
1220 if vlan:
1221 flowJson[ 'selector' ][ 'criteria' ].append( {
1222 "type":"VLAN_VID",
1223 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001224 if mpls:
1225 flowJson[ 'selector' ][ 'criteria' ].append( {
1226 "type":"MPLS_LABEL",
1227 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001228 if ipSrc:
1229 flowJson[ 'selector' ][ 'criteria' ].append( {
1230 "type":ipSrc[0],
1231 "ip":ipSrc[1] } )
1232 if ipDst:
1233 flowJson[ 'selector' ][ 'criteria' ].append( {
1234 "type":ipDst[0],
1235 "ip":ipDst[1] } )
1236 if tcpSrc:
1237 flowJson[ 'selector' ][ 'criteria' ].append( {
1238 "type":"TCP_SRC",
1239 "tcpPort": tcpSrc } )
1240 if tcpDst:
1241 flowJson[ 'selector' ][ 'criteria' ].append( {
1242 "type":"TCP_DST",
1243 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001244 if udpSrc:
1245 flowJson[ 'selector' ][ 'criteria' ].append( {
1246 "type":"UDP_SRC",
1247 "udpPort": udpSrc } )
1248 if udpDst:
1249 flowJson[ 'selector' ][ 'criteria' ].append( {
1250 "type":"UDP_DST",
1251 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001252 if ipProto:
1253 flowJson[ 'selector' ][ 'criteria' ].append( {
1254 "type":"IP_PROTO",
1255 "protocol": ipProto } )
1256
1257 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1258
1259 except ( AttributeError, TypeError ):
1260 main.log.exception( self.name + ": Object not as expected" )
1261 return None
1262 except Exception:
1263 main.log.exception( self.name + ": Uncaught exception!" )
1264 main.cleanup()
1265 main.exit()
1266
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001267 def removeFlow( self, deviceId, flowId,
1268 ip="DEFAULT", port="DEFAULT" ):
1269 """
1270 Description:
1271 Remove specific device flow
1272 Required:
1273 str deviceId - id of the device
1274 str flowId - id of the flow
1275 Return:
1276 Returns main.TRUE if successfully deletes flows, otherwise
1277 Returns main.FALSE, Returns None on error
1278 """
1279 try:
1280 output = None
1281 if ip == "DEFAULT":
1282 main.log.warn( "No ip given, reverting to ip from topo file" )
1283 ip = self.ip_address
1284 if port == "DEFAULT":
1285 main.log.warn( "No port given, reverting to port " +
1286 "from topo file" )
1287 port = self.port
1288 # NOTE: REST url requires the intent id to be in decimal form
1289 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001290 response = self.send( method="DELETE",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001291 url="/flows" + query )
1292 if response:
1293 if 200 <= response[ 0 ] <= 299:
1294 return main.TRUE
1295 else:
1296 main.log.error( "Error with REST request, response was: " +
1297 str( response ) )
1298 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001299 except ( AttributeError, TypeError ):
1300 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001301 return None
Jon Halle401b092015-09-23 13:34:24 -07001302 except Exception:
1303 main.log.exception( self.name + ": Uncaught exception!" )
1304 main.cleanup()
1305 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001306
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001307 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1308 """
1309 Description:
1310 Check if all the current flows are in ADDED state
1311 Return:
1312 returnValue - Returns main.TRUE only if all flows are in
1313 return main.FALSE otherwise;
1314 Returns None for exception
1315 """
1316 try:
1317 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1318 returnValue = main.TRUE
1319 for flow in tempFlows:
1320 if flow.get( 'state' ) != 'ADDED':
1321 main.log.info( self.name + ": flow Id: " +
1322 str( flow.get( 'groupId' ) ) +
1323 " | state:" +
1324 str( flow.get( 'state' ) ) )
1325 returnValue = main.FALSE
1326 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001327 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001328 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001329 return None
1330 except Exception:
1331 main.log.exception( self.name + ": Uncaught exception!" )
1332 main.cleanup()
1333 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001334
1335 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1336 subjectClass=None, subjectKey=None, configKey=None ):
1337 """
1338 Description:
1339 Get a json object with the ONOS network configurations
1340 Returns:
1341 A json object containing the network configuration in
1342 ONOS; Returns main.FALSE if error on requests;
1343 Returns None for exception
1344 """
1345 try:
1346 output = None
1347 if ip == "DEFAULT":
1348 main.log.warn( "No ip given, reverting to ip from topo file" )
1349 ip = self.ip_address
1350 if port == "DEFAULT":
1351 main.log.warn( "No port given, reverting to port " +
1352 "from topo file" )
1353 port = self.port
1354 url = "/network/configuration"
1355 if subjectClass:
1356 url += "/" + subjectClass
1357 if subjectKey:
1358 url += "/" + subjectKey
1359 if configKey:
1360 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001361 response = self.send( url=url )
Jon Hall66e001c2015-11-12 09:45:10 -08001362 if response:
1363 if 200 <= response[ 0 ] <= 299:
1364 output = response[ 1 ]
1365 a = json.loads( output )
1366 b = json.dumps( a )
1367 return b
1368 elif response[ 0 ] == 404:
1369 main.log.error( "Requested configuration doesn't exist: " +
1370 str( response ) )
1371 return {}
1372 else:
1373 main.log.error( "Error with REST request, response was: " +
1374 str( response ) )
1375 return main.FALSE
1376 except ( AttributeError, TypeError ):
1377 main.log.exception( self.name + ": Object not as expected" )
1378 return None
1379 except Exception:
1380 main.log.exception( self.name + ": Uncaught exception!" )
1381 main.cleanup()
1382 main.exit()
1383
1384 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1385 subjectClass=None, subjectKey=None, configKey=None ):
1386 """
1387 Description:
1388 Set a json object with the ONOS network configurations
1389 Returns:
1390 Returns main.TRUE for successful requests; Returns main.FALSE
1391 if error on requests;
1392 Returns None for exceptions
1393
1394 """
1395 try:
1396 output = None
1397 if ip == "DEFAULT":
1398 main.log.warn( "No ip given, reverting to ip from topo file" )
1399 ip = self.ip_address
1400 if port == "DEFAULT":
1401 main.log.warn( "No port given, reverting to port " +
1402 "from topo file" )
1403 port = self.port
1404 url = "/network/configuration"
1405 if subjectClass:
1406 url += "/" + subjectClass
1407 if subjectKey:
1408 url += "/" + subjectKey
1409 if configKey:
1410 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001411 response = self.send( method="POST",
Jon Hall66e001c2015-11-12 09:45:10 -08001412 url=url,
1413 data=json.dumps( cfgJson ) )
1414 if response:
1415 if 200 <= response[ 0 ] <= 299:
1416 main.log.info( self.name + ": Successfully POST cfg" )
1417 return main.TRUE
1418 else:
1419 main.log.error( "Error with REST request, response was: " +
1420 str( response ) )
1421 return main.FALSE
1422 except ( AttributeError, TypeError ):
1423 main.log.exception( self.name + ": Object not as expected" )
1424 return None
1425 except Exception:
1426 main.log.exception( self.name + ": Uncaught exception!" )
1427 main.cleanup()
1428 main.exit()
1429
1430 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1431 subjectClass=None, subjectKey=None, configKey=None ):
1432 """
1433 Description:
1434 Remove a json object from the ONOS network configurations
1435 Returns:
1436 Returns main.TRUE for successful requests; Returns main.FALSE
1437 if error on requests;
1438 Returns None for exceptions
1439
1440 """
1441 try:
1442 output = None
1443 if ip == "DEFAULT":
1444 main.log.warn( "No ip given, reverting to ip from topo file" )
1445 ip = self.ip_address
1446 if port == "DEFAULT":
1447 main.log.warn( "No port given, reverting to port " +
1448 "from topo file" )
1449 port = self.port
1450 url = "/network/configuration"
1451 if subjectClass:
1452 url += "/" + subjectClass
1453 if subjectKey:
1454 url += "/" + subjectKey
1455 if configKey:
1456 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001457 response = self.send( method="DELETE",
Jon Hall66e001c2015-11-12 09:45:10 -08001458 url=url )
1459 if response:
1460 if 200 <= response[ 0 ] <= 299:
1461 main.log.info( self.name + ": Successfully delete cfg" )
1462 return main.TRUE
1463 else:
1464 main.log.error( "Error with REST request, response was: " +
1465 str( response ) )
1466 return main.FALSE
1467 except ( AttributeError, TypeError ):
1468 main.log.exception( self.name + ": Object not as expected" )
1469 return None
1470 except Exception:
1471 main.log.exception( self.name + ": Uncaught exception!" )
1472 main.cleanup()
1473 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001474
1475 def createFlowBatch( self,
1476 numSw = 1,
1477 swIndex = 1,
1478 batchSize = 1,
1479 batchIndex = 1,
1480 deviceIdpreFix = "of:",
1481 appId=0,
1482 deviceID="",
1483 ingressPort="",
1484 egressPort="",
1485 ethType="",
1486 ethSrc="",
1487 ethDst="",
1488 vlan="",
1489 ipProto="",
1490 ipSrc=(),
1491 ipDst=(),
1492 tcpSrc="",
1493 tcpDst="",
1494 udpDst="",
1495 udpSrc="",
1496 mpls="",
1497 ip="DEFAULT",
1498 port="DEFAULT",
1499 debug=False ):
1500 """
1501 Description:
1502 Creates batches of MAC-rule flows for POST.
1503 Predefined MAC: 2 MS Hex digit for iterating devices
1504 Next 5 Hex digit for iterating batch numbers
1505 Next 5 Hex digit for iterating flows within a batch
1506 Required:
1507 * deviceId: id of the device
1508 Optional:
1509 * ingressPort: port ingress device
1510 * egressPort: port of egress device
1511 * ethType: specify ethType
1512 * ethSrc: specify ethSrc ( i.e. src mac addr )
1513 * ethDst: specify ethDst ( i.e. dst mac addr )
1514 * ipProto: specify ip protocol
1515 * ipSrc: specify ip source address with mask eg. ip#/24
1516 as a tuple (type, ip#)
1517 * ipDst: specify ip destination address eg. ip#/24
1518 as a tuple (type, ip#)
1519 * tcpSrc: specify tcp source port
1520 * tcpDst: specify tcp destination port
1521 Returns:
1522 Returns main.TRUE for successful requests; Returns main.FALSE
1523 if error on requests;
1524 Returns None for exceptions
1525 NOTE:
1526 The ip and port option are for the requests input's ip and port
1527 of the ONOS node
1528 """
1529 #from pprint import pprint
1530
1531 flowJsonList = []
1532 flowJsonBatch = {"flows":flowJsonList}
1533 dev = swIndex
1534
1535 for fl in range(1, batchSize + 1):
1536 flowJson = { "priority":100,
1537 "deviceId":"",
1538 "isPermanent":"true",
1539 "timeout":0,
1540 "treatment":{"instructions":[]},
1541 "selector": {"criteria":[]}}
1542
1543 #main.log.info("fl: " + str(fl))
1544 if dev <= numSw:
1545 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1546 #print deviceId
1547 flowJson['deviceId'] = deviceId
1548 dev += 1
1549 else:
1550 dev = 1
1551 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1552 #print deviceId
1553 flowJson['deviceId'] = deviceId
1554 dev += 1
1555
1556 # ethSrc starts with "0"; ethDst starts with "1"
1557 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1558 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1559 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1560 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1561 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1562 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1563 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1564
1565 if appId:
1566 flowJson[ "appId" ] = appId
1567
1568 if egressPort:
1569 flowJson[ 'treatment' ][ 'instructions' ].append( {
1570 "type":"OUTPUT",
1571 "port":egressPort } )
1572 if ingressPort:
1573 flowJson[ 'selector' ][ 'criteria' ].append( {
1574 "type":"IN_PORT",
1575 "port":ingressPort } )
1576 if ethType:
1577 flowJson[ 'selector' ][ 'criteria' ].append( {
1578 "type":"ETH_TYPE",
1579 "ethType":ethType } )
1580 if ethSrc:
1581 flowJson[ 'selector' ][ 'criteria' ].append( {
1582 "type":"ETH_SRC",
1583 "mac":ethSrc } )
1584 if ethDst:
1585 flowJson[ 'selector' ][ 'criteria' ].append( {
1586 "type":"ETH_DST",
1587 "mac":ethDst } )
1588 if vlan:
1589 flowJson[ 'selector' ][ 'criteria' ].append( {
1590 "type":"VLAN_VID",
1591 "vlanId":vlan } )
1592 if mpls:
1593 flowJson[ 'selector' ][ 'criteria' ].append( {
1594 "type":"MPLS_LABEL",
1595 "label":mpls } )
1596 if ipSrc:
1597 flowJson[ 'selector' ][ 'criteria' ].append( {
1598 "type":ipSrc[0],
1599 "ip":ipSrc[1] } )
1600 if ipDst:
1601 flowJson[ 'selector' ][ 'criteria' ].append( {
1602 "type":ipDst[0],
1603 "ip":ipDst[1] } )
1604 if tcpSrc:
1605 flowJson[ 'selector' ][ 'criteria' ].append( {
1606 "type":"TCP_SRC",
1607 "tcpPort": tcpSrc } )
1608 if tcpDst:
1609 flowJson[ 'selector' ][ 'criteria' ].append( {
1610 "type":"TCP_DST",
1611 "tcpPort": tcpDst } )
1612 if udpSrc:
1613 flowJson[ 'selector' ][ 'criteria' ].append( {
1614 "type":"UDP_SRC",
1615 "udpPort": udpSrc } )
1616 if udpDst:
1617 flowJson[ 'selector' ][ 'criteria' ].append( {
1618 "type":"UDP_DST",
1619 "udpPort": udpDst } )
1620 if ipProto:
1621 flowJson[ 'selector' ][ 'criteria' ].append( {
1622 "type":"IP_PROTO",
1623 "protocol": ipProto } )
1624 #pprint(flowJson)
1625 flowJsonList.append(flowJson)
1626
1627 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1628 flowJsonBatch['flows'] = flowJsonList
1629 #pprint(flowJsonBatch)
1630
1631 return flowJsonBatch
1632
1633
1634 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1635 """
1636 Description:
1637 Sends a single flow batch through /flows REST API.
1638 Required:
1639 * The batch of flows
1640 Returns:
1641 Returns main.TRUE for successful requests; Returns main.FALSE
1642 if error on requests;
1643 Returns None for exceptions
1644 NOTE:
1645 The ip and port option are for the requests input's ip and port
1646 of the ONOS node
1647 """
1648 import time
1649
1650 try:
1651 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1652 output = None
1653 if ip == "DEFAULT":
1654 main.log.warn( "No ip given, reverting to ip from topo file" )
1655 ip = self.ip_address
1656 if port == "DEFAULT":
1657 main.log.warn( "No port given, reverting to port " +
1658 "from topo file" )
1659 port = self.port
1660 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001661 response = self.send( method="POST",
suibin zhang17308622016-04-14 15:45:30 -07001662 url=url,
1663 data=json.dumps( batch ) )
1664 #main.log.info("Post response is: ", str(response[0]))
1665 if response[0] == 200:
1666 main.log.info( self.name + ": Successfully POST flow batch" )
1667 return main.TRUE, response
1668 else:
1669 main.log.error( "Error with REST request, response was: " +
1670 str( response ) )
1671 return main.FALSE
1672 except NotImplementedError as e:
1673 raise e # Inform the caller
1674 except ( AttributeError, TypeError ):
1675 main.log.exception( self.name + ": Object not as expected" )
1676 return None
1677 except Exception:
1678 main.log.exception( self.name + ": Uncaught exception!" )
1679 main.cleanup()
1680 main.exit()
1681
1682 def removeFlowBatch( self, batch={},
1683 ip="DEFAULT", port="DEFAULT" ):
1684 """
1685 Description:
1686 Remove a batch of flows
1687 Required:
1688 flow batch
1689 Return:
1690 Returns main.TRUE if successfully deletes flows, otherwise
1691 Returns main.FALSE, Returns None on error
1692 """
1693 try:
1694 output = None
1695 if ip == "DEFAULT":
1696 main.log.warn( "No ip given, reverting to ip from topo file" )
1697 ip = self.ip_address
1698 if port == "DEFAULT":
1699 main.log.warn( "No port given, reverting to port " +
1700 "from topo file" )
1701 port = self.port
1702 # NOTE: REST url requires the intent id to be in decimal form
1703
suibin zhang116647a2016-05-06 16:30:09 -07001704 response = self.send( method="DELETE",
suibin zhang17308622016-04-14 15:45:30 -07001705 url="/flows/",
1706 data = json.dumps(batch) )
1707 if response:
1708 if 200 <= response[ 0 ] <= 299:
1709 return main.TRUE
1710 else:
1711 main.log.error( "Error with REST request, response was: " +
1712 str( response ) )
1713 return main.FALSE
1714 except ( AttributeError, TypeError ):
1715 main.log.exception( self.name + ": Object not as expected" )
1716 return None
1717 except Exception:
1718 main.log.exception( self.name + ": Uncaught exception!" )
1719 main.cleanup()
1720 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001721
1722 def getTopology( self, topologyOutput ):
1723 """
1724 Definition:
1725 Loads a json topology output
1726 Return:
1727 topology = current ONOS topology
1728 """
1729 import json
1730 try:
1731 # either onos:topology or 'topology' will work in CLI
1732 topology = json.loads(topologyOutput)
1733 main.log.debug( topology )
1734 return topology
1735 except pexpect.EOF:
1736 main.log.error( self.name + ": EOF exception found" )
1737 main.log.error( self.name + ": " + self.handle.before )
1738 main.cleanup()
1739 main.exit()
1740 except Exception:
1741 main.log.exception( self.name + ": Uncaught exception!" )
1742 main.cleanup()
1743 main.exit()
1744
1745 def checkStatus(
1746 self,
1747 topologyResult,
1748 numoswitch,
1749 numolink,
1750 logLevel="info" ):
1751 """
1752 Checks the number of switches & links that ONOS sees against the
1753 supplied values. By default this will report to main.log, but the
1754 log level can be specific.
1755
1756 Params: topologyResult = the output of topology command
1757 numoswitch = expected number of switches
1758 numolink = expected number of links
1759 logLevel = level to log to.
1760 Currently accepts 'info', 'warn' and 'report'
1761
1762 Returns: main.TRUE if the number of switches and links are correct,
1763 main.FALSE if the number of switches and links is incorrect,
1764 and main.ERROR otherwise
1765 """
1766 try:
1767 topology = self.getTopology( topologyResult )
1768 if topology == {}:
1769 return main.ERROR
1770 output = ""
1771 # Is the number of switches is what we expected
1772 devices = topology.get( 'devices', False )
1773 links = topology.get( 'links', False )
1774 if devices is False or links is False:
1775 return main.ERROR
1776 switchCheck = ( int( devices ) == int( numoswitch ) )
1777 # Is the number of links is what we expected
1778 linkCheck = ( int( links ) == int( numolink ) )
1779 if switchCheck and linkCheck:
1780 # We expected the correct numbers
1781 output = output + "The number of links and switches match "\
1782 + "what was expected"
1783 result = main.TRUE
1784 else:
1785 output = output + \
1786 "The number of links and switches does not match " + \
1787 "what was expected"
1788 result = main.FALSE
1789 output = output + "\n ONOS sees %i devices" % int( devices )
1790 output = output + " (%i expected) " % int( numoswitch )
1791 output = output + "and %i links " % int( links )
1792 output = output + "(%i expected)" % int( numolink )
1793 if logLevel == "report":
1794 main.log.report( output )
1795 elif logLevel == "warn":
1796 main.log.warn( output )
1797 else:
1798 main.log.info( output )
1799 return result
1800 except pexpect.EOF:
1801 main.log.error( self.name + ": EOF exception found" )
1802 main.log.error( self.name + ": " + self.handle.before )
1803 main.cleanup()
1804 main.exit()
1805 except Exception:
1806 main.log.exception( self.name + ": Uncaught exception!" )
1807 main.cleanup()
1808 main.exit()