blob: 95039b06f29e5e4f2bd3d9d2934c295c0f066f86 [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07004Copyright 2015 Open Networking Foundation (ONF)
Jeremy Songsterae01bba2016-07-11 15:39:17 -07005
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jon Hallfc915882015-07-14 13:33:17 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 ( at your option ) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23"""
24import json
25import os
26import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070027import types
Jon Halle401b092015-09-23 13:34:24 -070028import sys
Jon Hallfc915882015-07-14 13:33:17 -070029
Jon Hallfc915882015-07-14 13:33:17 -070030from drivers.common.api.controllerdriver import Controller
31
32
33class OnosRestDriver( Controller ):
34
35 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070036 self.pwd = None
37 self.user_name = "user"
Devin Limdc78e202017-06-09 18:30:07 -070038 super( OnosRestDriver, self ).__init__()
Jon Hallfc915882015-07-14 13:33:17 -070039 self.ip_address = "localhost"
40 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070041 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070042
43 def connect( self, **connectargs ):
44 try:
45 for key in connectargs:
46 vars( self )[ key ] = connectargs[ key ]
47 self.name = self.options[ 'name' ]
48 except Exception as e:
49 main.log.exception( e )
50 try:
51 if os.getenv( str( self.ip_address ) ) != None:
52 self.ip_address = os.getenv( str( self.ip_address ) )
53 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070054 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070055 except KeyError:
56 main.log.info( "Invalid host name," +
57 "defaulting to 'localhost' instead" )
58 self.ip_address = 'localhost'
59 except Exception as inst:
60 main.log.error( "Uncaught exception: " + str( inst ) )
61
62 self.handle = super( OnosRestDriver, self ).connect()
63 return self.handle
64
Jon Halle401b092015-09-23 13:34:24 -070065 def pprint( self, jsonObject ):
66 """
67 Pretty Prints a json object
68
69 arguments:
70 jsonObject - a parsed json object
71 returns:
72 A formatted string for printing or None on error
73 """
74 try:
75 if isinstance( jsonObject, str ):
76 jsonObject = json.loads( jsonObject )
77 return json.dumps( jsonObject, sort_keys=True,
78 indent=4, separators=(',', ': '))
79 except ( TypeError, ValueError ):
80 main.log.exception( "Error parsing jsonObject" )
81 return None
82
suibin zhangd5b6fe42016-05-12 08:48:58 -070083 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070084 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070085 """
86 Arguments:
87 str ip: ONOS IP Address
88 str port: ONOS REST Port
89 str url: ONOS REST url path.
90 NOTE that this is is only the relative path. IE "/devices"
91 str base: The base url for the given REST api. Applications could
92 potentially have their own base url
93 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070094 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070095 the request
96 dict data: Dictionary to be sent in the body of the request
97 """
98 # TODO: Authentication - simple http (user,pass) tuple
99 # TODO: should we maybe just pass kwargs straight to response?
100 # TODO: Do we need to allow for other protocols besides http?
101 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700102 if ip == "DEFAULT":
103 main.log.warn( "No ip given, reverting to ip from topo file" )
104 ip = self.ip_address
105 if port == "DEFAULT":
106 main.log.warn( "No port given, reverting to port " +
107 "from topo file" )
108 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700109
Jon Hallfc915882015-07-14 13:33:17 -0700110 try:
111 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700112 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800113 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700114 auth = (self.user_name, self.pwd)
115 else:
116 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700117 main.log.info( "Sending request " + path + " using " +
118 method.upper() + " method." )
119 response = requests.request( method.upper(),
120 path,
121 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700122 data=data,
123 auth=auth )
124 if debug:
125 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700126 return ( response.status_code, response.text.encode( 'utf8' ) )
127 except requests.exceptions:
128 main.log.exception( "Error sending request." )
129 return None
Jon Halle401b092015-09-23 13:34:24 -0700130 except Exception:
131 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700132 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700133
134 def intents( self, ip="DEFAULT", port="DEFAULT" ):
135 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700136 Description:
137 Gets a list of dictionary of all intents in the system
138 Returns:
139 A list of dictionary of intents in string type to match the cli
140 version for now; Returns main.FALSE if error on request;
141 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700142 """
143 try:
144 output = None
145 if ip == "DEFAULT":
146 main.log.warn( "No ip given, reverting to ip from topo file" )
147 ip = self.ip_address
148 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700149 main.log.warn( "No port given, reverting to port " +
150 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700151 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700152 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700153 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700154 if 200 <= response[ 0 ] <= 299:
155 output = response[ 1 ]
156 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700157 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700158 b = json.dumps( a )
159 return b
Jon Hallfc915882015-07-14 13:33:17 -0700160 else:
161 main.log.error( "Error with REST request, response was: " +
162 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700163 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700164 except ( AttributeError, AssertionError, TypeError ):
165 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700166 return None
Jon Halle401b092015-09-23 13:34:24 -0700167 except Exception:
168 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700169 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700170
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700171 def intent( self, intentId, appId="org.onosproject.cli",
172 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700173 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700174 Description:
175 Get the specific intent information of the given application ID and
176 intent ID
177 Required:
178 str intentId - Intent id in hexadecimal form
179 Optional:
180 str appId - application id of intent
181 Returns:
182 Returns an information dictionary of the given intent;
183 Returns main.FALSE if error on requests; Returns None for exception
184 NOTE:
185 The GET /intents REST api command accepts application id but the
186 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700187 """
188 try:
189 output = None
190 if ip == "DEFAULT":
191 main.log.warn( "No ip given, reverting to ip from topo file" )
192 ip = self.ip_address
193 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700194 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700195 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700196 port = self.port
197 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700198 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700199 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700200 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700201 if 200 <= response[ 0 ] <= 299:
202 output = response[ 1 ]
203 a = json.loads( output )
204 return a
Jon Hallfc915882015-07-14 13:33:17 -0700205 else:
206 main.log.error( "Error with REST request, response was: " +
207 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700208 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700209 except ( AttributeError, TypeError ):
210 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700211 return None
Jon Halle401b092015-09-23 13:34:24 -0700212 except Exception:
213 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700214 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700215
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700216 def apps( self, ip="DEFAULT", port="DEFAULT" ):
217 """
218 Description:
219 Returns all the current application installed in the system
220 Returns:
221 List of dictionary of installed application; Returns main.FALSE for
222 error on request; Returns None for exception
223 """
224 try:
225 output = None
226 if ip == "DEFAULT":
227 main.log.warn( "No ip given, reverting to ip from topo file" )
228 ip = self.ip_address
229 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700230 main.log.warn( "No port given, reverting to port " +
231 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700232 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700233 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700234 if response:
235 if 200 <= response[ 0 ] <= 299:
236 output = response[ 1 ]
237 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700238 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700239 b = json.dumps( a )
240 return b
241 else:
242 main.log.error( "Error with REST request, response was: " +
243 str( response ) )
244 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700245 except ( AttributeError, AssertionError, TypeError ):
246 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700247 return None
Jon Halle401b092015-09-23 13:34:24 -0700248 except Exception:
249 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700250 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700251
252 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
253 """
254 Decription:
255 Activate an app that is already installed in ONOS
256 Optional:
257 bool check - If check is True, method will check the status
258 of the app after the command is issued
259 Returns:
260 Returns main.TRUE if the command was successfully or main.FALSE
261 if the REST responded with an error or given incorrect input;
262 Returns None for exception
263
264 """
265 try:
266 output = None
267 if ip == "DEFAULT":
268 main.log.warn( "No ip given, reverting to ip from topo file" )
269 ip = self.ip_address
270 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700271 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700272 "from topo file" )
273 port = self.port
274 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700275 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700276 url="/applications" + query,
277 ip = ip, port = port)
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700278 if response:
279 output = response[ 1 ]
280 app = json.loads( output )
281 if 200 <= response[ 0 ] <= 299:
282 if check:
283 if app.get( 'state' ) == 'ACTIVE':
284 main.log.info( self.name + ": " + appName +
285 " application" +
286 " is in ACTIVE state" )
287 return main.TRUE
288 else:
289 main.log.error( self.name + ": " + appName +
290 " application" + " is in " +
291 app.get( 'state' ) + " state" )
292 return main.FALSE
293 else:
294 main.log.warn( "Skipping " + appName +
295 "application check" )
296 return main.TRUE
297 else:
298 main.log.error( "Error with REST request, response was: " +
299 str( response ) )
300 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700301 except ( AttributeError, TypeError ):
302 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700303 return None
Jon Halle401b092015-09-23 13:34:24 -0700304 except Exception:
305 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700306 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700307
308 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
309 check=True ):
310 """
311 Required:
312 Deactivate an app that is already activated in ONOS
313 Optional:
314 bool check - If check is True, method will check the status of the
315 app after the command is issued
316 Returns:
317 Returns main.TRUE if the command was successfully sent
318 main.FALSE if the REST responded with an error or given
319 incorrect input; Returns None for exception
320 """
321 try:
322 output = None
323 if ip == "DEFAULT":
324 main.log.warn( "No ip given, reverting to ip from topo file" )
325 ip = self.ip_address
326 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700327 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700328 "from topo file" )
329 port = self.port
330 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700331 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700332 url="/applications" + query,
333 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700334 if response:
335 output = response[ 1 ]
336 app = json.loads( output )
337 if 200 <= response[ 0 ] <= 299:
338 if check:
339 if app.get( 'state' ) == 'INSTALLED':
340 main.log.info( self.name + ": " + appName +
341 " application" +
342 " is in INSTALLED state" )
343 return main.TRUE
344 else:
345 main.log.error( self.name + ": " + appName +
346 " application" + " is in " +
347 app.get( 'state' ) + " state" )
348 return main.FALSE
349 else:
350 main.log.warn( "Skipping " + appName +
351 "application check" )
352 return main.TRUE
353 else:
354 main.log.error( "Error with REST request, response was: " +
355 str( response ) )
356 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700357 except ( AttributeError, TypeError ):
358 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700359 return None
Jon Halle401b092015-09-23 13:34:24 -0700360 except Exception:
361 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700362 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700363
364 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
365 port="DEFAULT" ):
366 """
367 Decription:
368 Gets the informaion of the given application
369 Required:
370 str name - Name of onos application
371 Returns:
372 Returns a dictionary of information ONOS application in string type;
373 Returns main.FALSE if error on requests; Returns None for exception
374 """
375 try:
376 output = None
377 if ip == "DEFAULT":
378 main.log.warn( "No ip given, reverting to ip from topo file" )
379 ip = self.ip_address
380 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700381 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700382 "from topo file" )
383 port = self.port
384 query = "/" + project + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700385 response = self.send( url="/applications" + query,
386 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700387 if response:
388 if 200 <= response[ 0 ] <= 299:
389 output = response[ 1 ]
390 a = json.loads( output )
391 return a
392 else:
393 main.log.error( "Error with REST request, response was: " +
394 str( response ) )
395 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700396 except ( AttributeError, TypeError ):
397 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700398 return None
Jon Halle401b092015-09-23 13:34:24 -0700399 except Exception:
400 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700401 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700402
403 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700404 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700405 """
406 Description:
407 Adds a host-to-host intent ( bidirectional ) by
408 specifying the two hosts.
409 Required:
410 * hostIdOne: ONOS host id for host1
411 * hostIdTwo: ONOS host id for host2
412 Optional:
413 str appId - Application name of intent identifier
414 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700415 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700416 error on requests; Returns None for exceptions
417 """
418 try:
419 intentJson = {"two": str( hostIdTwo ),
420 "selector": {"criteria": []}, "priority": 7,
421 "treatment": {"deferred": [], "instructions": []},
422 "appId": appId, "one": str( hostIdOne ),
423 "type": "HostToHostIntent",
424 "constraints": [{"type": "LinkTypeConstraint",
425 "types": ["OPTICAL"],
426 "inclusive": 'false' }]}
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700427 if vlanId:
428 intentJson[ 'selector' ][ 'criteria' ].append( { "type":"VLAN_VID",
429 "vlanId":vlanId } )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700430 output = None
431 if ip == "DEFAULT":
432 main.log.warn( "No ip given, reverting to ip from topo file" )
433 ip = self.ip_address
434 if port == "DEFAULT":
435 main.log.warn( "No port given, reverting to port " +
436 "from topo file" )
437 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700438 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700439 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700440 data=json.dumps( intentJson ) )
441 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700442 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700443 main.log.info( self.name + ": Successfully POST host" +
444 " intent between host: " + hostIdOne +
445 " and host: " + hostIdTwo )
446 return main.TRUE
447 else:
448 main.log.error( "Error with REST request, response was: " +
449 str( response ) )
450 return main.FALSE
451
Jon Halle401b092015-09-23 13:34:24 -0700452 except ( AttributeError, TypeError ):
453 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700454 return None
Jon Halle401b092015-09-23 13:34:24 -0700455 except Exception:
456 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700457 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700458
kelvin-onlabb50074f2015-07-27 16:18:32 -0700459 def addPointIntent( self,
460 ingressDevice,
461 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700462 appId='org.onosproject.cli',
463 ingressPort="",
464 egressPort="",
465 ethType="",
466 ethSrc="",
467 ethDst="",
468 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800469 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700470 lambdaAlloc=False,
471 ipProto="",
472 ipSrc="",
473 ipDst="",
474 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700475 tcpDst="",
476 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700477 port="DEFAULT",
478 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700479 """
480 Description:
481 Adds a point-to-point intent ( uni-directional ) by
482 specifying device id's and optional fields
483 Required:
484 * ingressDevice: device id of ingress device
485 * egressDevice: device id of egress device
486 Optional:
487 * ethType: specify ethType
488 * ethSrc: specify ethSrc ( i.e. src mac addr )
489 * ethDst: specify ethDst ( i.e. dst mac addr )
490 * bandwidth: specify bandwidth capacity of link (TODO)
491 * lambdaAlloc: if True, intent will allocate lambda
492 for the specified intent (TODO)
493 * ipProto: specify ip protocol
494 * ipSrc: specify ip source address with mask eg. ip#/24
495 * ipDst: specify ip destination address eg. ip#/24
496 * tcpSrc: specify tcp source port
497 * tcpDst: specify tcp destination port
498 Returns:
499 Returns main.TRUE for successful requests; Returns main.FALSE if
500 no ingress|egress port found and if error on requests;
501 Returns None for exceptions
502 NOTE:
503 The ip and port option are for the requests input's ip and port
504 of the ONOS node
505 """
506 try:
507 if "/" in ingressDevice:
508 if not ingressPort:
509 ingressPort = ingressDevice.split( "/" )[ 1 ]
510 ingressDevice = ingressDevice.split( "/" )[ 0 ]
511 else:
512 if not ingressPort:
513 main.log.debug( self.name + ": Ingress port not specified" )
514 return main.FALSE
515
516 if "/" in egressDevice:
517 if not egressPort:
518 egressPort = egressDevice.split( "/" )[ 1 ]
519 egressDevice = egressDevice.split( "/" )[ 0 ]
520 else:
521 if not egressPort:
522 main.log.debug( self.name + ": Egress port not specified" )
523 return main.FALSE
524
525 intentJson ={ "ingressPoint": { "device": ingressDevice,
526 "port": ingressPort },
527 "selector": { "criteria": [] },
528 "priority": 55,
529 "treatment": { "deferred": [],
530 "instructions": [] },
531 "egressPoint": { "device": egressDevice,
532 "port": egressPort },
533 "appId": appId,
534 "type": "PointToPointIntent",
535 "constraints": [ { "type": "LinkTypeConstraint",
536 "types": [ "OPTICAL" ],
537 "inclusive": "false" } ] }
538
alisonda157272016-12-22 01:13:21 -0800539 # if protected:
540 # intentJson['constraints'].append( { "type": "Protection", "types": ["Protection"], "inclusive": "true" } )
541
kelvin-onlabb50074f2015-07-27 16:18:32 -0700542 if ethType == "IPV4":
543 intentJson[ 'selector' ][ 'criteria' ].append( {
544 "type":"ETH_TYPE",
545 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700546 elif ethType:
547 intentJson[ 'selector' ][ 'criteria' ].append( {
548 "type":"ETH_TYPE",
549 "ethType":ethType } )
550
kelvin-onlabb50074f2015-07-27 16:18:32 -0700551 if ethSrc:
552 intentJson[ 'selector' ][ 'criteria' ].append(
553 { "type":"ETH_SRC",
554 "mac":ethSrc } )
555 if ethDst:
556 intentJson[ 'selector' ][ 'criteria' ].append(
557 { "type":"ETH_DST",
558 "mac":ethDst } )
559 if ipSrc:
560 intentJson[ 'selector' ][ 'criteria' ].append(
561 { "type":"IPV4_SRC",
562 "ip":ipSrc } )
563 if ipDst:
564 intentJson[ 'selector' ][ 'criteria' ].append(
565 { "type":"IPV4_DST",
566 "ip":ipDst } )
567 if tcpSrc:
568 intentJson[ 'selector' ][ 'criteria' ].append(
569 { "type":"TCP_SRC",
570 "tcpPort": tcpSrc } )
571 if tcpDst:
572 intentJson[ 'selector' ][ 'criteria' ].append(
573 { "type":"TCP_DST",
574 "tcpPort": tcpDst } )
575 if ipProto:
576 intentJson[ 'selector' ][ 'criteria' ].append(
577 { "type":"IP_PROTO",
578 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700579 if vlanId:
580 intentJson[ 'selector' ][ 'criteria' ].append(
581 { "type":"VLAN_VID",
582 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700583
584 # TODO: Bandwidth and Lambda will be implemented if needed
585
kelvin-onlabb50074f2015-07-27 16:18:32 -0700586 output = None
587 if ip == "DEFAULT":
588 main.log.warn( "No ip given, reverting to ip from topo file" )
589 ip = self.ip_address
590 if port == "DEFAULT":
591 main.log.warn( "No port given, reverting to port " +
592 "from topo file" )
593 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700594 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700595 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700596 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700597
598 main.log.debug( intentJson )
599
kelvin-onlabb50074f2015-07-27 16:18:32 -0700600 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700601 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700602 main.log.info( self.name + ": Successfully POST point" +
603 " intent between ingress: " + ingressDevice +
604 " and egress: " + egressDevice + " devices" )
605 return main.TRUE
606 else:
607 main.log.error( "Error with REST request, response was: " +
608 str( response ) )
609 return main.FALSE
610
Jon Halle401b092015-09-23 13:34:24 -0700611 except ( AttributeError, TypeError ):
612 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700613 return None
Jon Halle401b092015-09-23 13:34:24 -0700614 except Exception:
615 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700616 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700617
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700618 def addSinglepointToMultipointIntent(self,
619 ingressDevice,
620 egressDeviceList,
621 portEgressList,
622 appId='org.onosproject.cli',
623 portIngress="",
624 ethType="",
625 ethSrc="",
626 ethDst="",
627 bandwidth="",
628 lambdaAlloc=False,
629 ipProto="",
630 ipSrc="",
631 ipDst="",
632 tcpSrc="",
633 tcpDst="",
634 partial=False,
635 ip="DEFAULT",
636 port="DEFAULT",
637 vlanId="" ):
638 """
639 Description:
640 Adds a point-to-multi point intent ( uni-directional ) by
641 specifying device id's and optional fields
642 Required:
643 * ingressDevice: device id of ingress device
644 * egressDevice: device id of egress device
645 * portEgressList: a list of port id of egress device
646
647 Optional:
648 * portIngress: port id of ingress device
649 * ethType: specify ethType
650 * ethSrc: specify ethSrc ( i.e. src mac addr )
651 * ethDst: specify ethDst ( i.e. dst mac addr )
652 * bandwidth: specify bandwidth capacity of link (TODO)
653 * lambdaAlloc: if True, intent will allocate lambda
654 for the specified intent (TODO)
655 * ipProto: specify ip protocol
656 * ipSrc: specify ip source address with mask eg. ip#/24
657 * ipDst: specify ip destination address eg. ip#/24
658 * tcpSrc: specify tcp source port
659 * tcpDst: specify tcp destination port
660 Returns:
661 Returns main.TRUE for successful requests; Returns main.FALSE if
662 no ingress|egress port found and if error on requests;
663 Returns None for exceptions
664 NOTE:
665 The ip and port option are for the requests input's ip and port
666 of the ONOS node
667 """
668 try:
669
670 if "/" in ingressDevice:
671 if not portIngress:
672 ingressPort = ingressDevice.split( "/" )[ 1 ]
673 ingressDevice = ingressDevice.split( "/" )[ 0 ]
674 else:
675 if not portIngress:
676 main.log.debug( self.name + ": Ingress port not specified" )
677 return main.FALSE
678 index = 0
679 for egressDevice in egressDeviceList:
680 if "/" in egressDevice:
681 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
682 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
683 else:
684 if not portEgressList:
685 main.log.debug( self.name + ": Egress port not specified" )
686 return main.FALSE
687 index = index + 1
688
689 intentJson = { "ingressPoint": { "device": ingressDevice,
690 "port": ingressPort },
691 "selector": { "criteria": [] },
692 "priority": 55,
693 "treatment": { "deferred": [],
694 "instructions": [] },
695 "egressPoint": { "connectPoints": [] },
696 "appId": appId,
697 "type": "SinglePointToMultiPointIntent",
698 "constraints": [ { "type": "LinkTypeConstraint",
699 "types": ["OPTICAL"],
700 "inclusive": "false" } ] }
701
702 index = 0
703 for ep in portEgressList:
704 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
705 { "device": egressDeviceList[ index ],
706 "port": ep } )
707 index += 1
708
709 if ethType == "IPV4":
710 intentJson[ 'selector' ][ 'criteria' ].append(
711 { "type": "ETH_TYPE",
712 "ethType": 2048 } )
713 elif ethType:
714 intentJson[ 'selector' ][ 'criteria' ].append(
715 { "type": "ETH_TYPE",
716 "ethType": ethType } )
717
718 if ethSrc:
719 intentJson[ 'selector' ][ 'criteria' ].append(
720 { "type": "ETH_SRC",
721 "mac": ethSrc } )
722
723 if ethDst:
724 for dst in ethDst:
725 if dst:
726 intentJson[ 'selector' ][ 'criteria' ].append(
727 { "type": "ETH_DST",
728 "mac": dst } )
729 if tcpSrc:
730 intentJson[ 'selector' ][ 'criteria' ].append(
731 { "type": "TCP_SRC",
732 "tcpPort": tcpSrc } )
733 if tcpDst:
734 intentJson[ 'selector' ][ 'criteria' ].append(
735 { "type": "TCP_DST",
736 "tcpPort": tcpDst } )
737 if ipProto:
738 intentJson[ 'selector' ][ 'criteria' ].append(
739 { "type": "IP_PROTO",
740 "protocol": ipProto } )
741 if vlanId:
742 intentJson[ 'selector' ][ 'criteria' ].append(
743 { "type": "VLAN_VID",
744 "vlanId": vlanId } )
745
746 # TODO: Bandwidth and Lambda will be implemented if needed
747
748 output = None
749 if ip == "DEFAULT":
750 main.log.warn( "No ip given, reverting to ip from topo file" )
751 ip = self.ip_address
752 if port == "DEFAULT":
753 main.log.warn( "No port given, reverting to port " +
754 "from topo file" )
755 port = self.port
756 response = self.send( method="POST",
757 url="/intents", ip=ip, port=port,
758 data=json.dumps( intentJson ) )
759
760 main.log.debug(intentJson)
761
762 if response:
763 if "201" in str( response[ 0 ] ):
764 main.log.info( self.name + ": Successfully POST point" +
765 " intent between ingress: " + ingressDevice +
766 " and egress: " + str(egressDeviceList) + " devices" )
767 return main.TRUE
768 else:
769 main.log.error( "Error with REST request, response was: " + str( response ) )
770 return main.FALSE
771 else:
772 main.log.error( "REST request has no response." )
773 return main.FALSE
774
775 except ( AttributeError, TypeError ):
776 main.log.exception( self.name + ": Object not as expected" )
777 return None
778 except Exception:
779 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700780 main.cleanAndExit()
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700781
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700782 def removeIntent( self, intentId, appId='org.onosproject.cli',
783 ip="DEFAULT", port="DEFAULT" ):
784 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700785 Remove intent for specified application id and intent id;
786 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700787 """
788 try:
789 output = None
790 if ip == "DEFAULT":
791 main.log.warn( "No ip given, reverting to ip from topo file" )
792 ip = self.ip_address
793 if port == "DEFAULT":
794 main.log.warn( "No port given, reverting to port " +
795 "from topo file" )
796 port = self.port
797 # NOTE: REST url requires the intent id to be in decimal form
798 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700799 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700800 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700801 if response:
802 if 200 <= response[ 0 ] <= 299:
803 return main.TRUE
804 else:
805 main.log.error( "Error with REST request, response was: " +
806 str( response ) )
807 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700808 except ( AttributeError, TypeError ):
809 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700810 return None
Jon Halle401b092015-09-23 13:34:24 -0700811 except Exception:
812 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700813 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700814
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700815 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700816 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700817 Description:
818 Gets all intents ID using intents function
819 Returns:
820 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700821 """
822 try:
823 intentIdList = []
824 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700825 for intent in intentsJson:
826 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700827 if not intentIdList:
828 main.log.debug( "Cannot find any intents" )
829 return main.FALSE
830 else:
831 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700832 except ( AttributeError, TypeError ):
833 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700834 return None
Jon Halle401b092015-09-23 13:34:24 -0700835 except Exception:
836 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700837 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700838
839 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
840 ip="DEFAULT", port="DEFAULT", delay=5 ):
841 """
842 Description:
843 Remove all the intents
844 Returns:
845 Returns main.TRUE if all intents are removed, otherwise returns
846 main.FALSE; Returns None for exception
847 """
848 try:
849 results = []
850 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700851 # intentIdList = self.getIntentsId( ip=ip, port=port )
852 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700853
854 main.log.info( self.name + ": Removing intents " +
855 str( intentIdList ) )
856
857 if isinstance( intentIdList, types.ListType ):
858 for intent in intentIdList:
859 results.append( self.removeIntent( intentId=intent,
860 appId=appId,
861 ip=ip,
862 port=port ) )
863 # Check for remaining intents
864 # NOTE: Noticing some delay on Deleting the intents so i put
865 # this time out
866 import time
867 time.sleep( delay )
868 intentRemain = len( json.loads( self.intents() ) )
869 if all( result==main.TRUE for result in results ) and \
870 intentRemain == 0:
871 main.log.info( self.name + ": All intents are removed " )
872 return main.TRUE
873 else:
874 main.log.error( self.name + ": Did not removed all intents,"
875 + " there are " + str( intentRemain )
876 + " intents remaining" )
877 return main.FALSE
878 else:
879 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700880 except ( AttributeError, TypeError ):
881 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700882 return None
Jon Halle401b092015-09-23 13:34:24 -0700883 except Exception:
884 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700885 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700886
887 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
888 """
889 Description:
890 Get a list of dictionary of all discovered hosts
891 Returns:
892 Returns a list of dictionary of information of the hosts currently
893 discovered by ONOS; Returns main.FALSE if error on requests;
894 Returns None for exception
895 """
896 try:
897 output = None
898 if ip == "DEFAULT":
899 main.log.warn( "No ip given, reverting to ip from topo file" )
900 ip = self.ip_address
901 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700902 main.log.warn( "No port given, reverting to port " +
903 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700904 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700905 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700906 if response:
907 if 200 <= response[ 0 ] <= 299:
908 output = response[ 1 ]
909 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700910 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700911 b = json.dumps( a )
912 return b
913 else:
914 main.log.error( "Error with REST request, response was: " +
915 str( response ) )
916 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700917 except ( AttributeError, AssertionError, TypeError ):
918 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700919 return None
Jon Halle401b092015-09-23 13:34:24 -0700920 except Exception:
921 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700922 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700923
924 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
925 """
926 Description:
927 Gets the information from the given host
928 Required:
929 str mac - MAC address of the host
930 Optional:
931 str vlan - VLAN tag of the host, defaults to -1
932 Returns:
933 Return the host id from the hosts/mac/vlan output in REST api
934 whose 'id' contains mac/vlan; Returns None for exception;
935 Returns main.FALSE if error on requests
936
937 NOTE:
938 Not sure what this function should do, any suggestion?
939 """
940 try:
941 output = None
942 if ip == "DEFAULT":
943 main.log.warn( "No ip given, reverting to ip from topo file" )
944 ip = self.ip_address
945 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700946 main.log.warn( "No port given, reverting to port " +
947 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700948 port = self.port
949 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700950 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700951 if response:
952 # NOTE: What if the person wants other values? would it be better
953 # to have a function that gets a key and return a value instead?
954 # This function requires mac and vlan and returns an ID which
955 # makes this current function useless
956 if 200 <= response[ 0 ] <= 299:
957 output = response[ 1 ]
958 hostId = json.loads( output ).get( 'id' )
959 return hostId
960 else:
961 main.log.error( "Error with REST request, response was: " +
962 str( response ) )
963 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700964 except ( AttributeError, TypeError ):
965 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700966 return None
Jon Halle401b092015-09-23 13:34:24 -0700967 except Exception:
968 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700969 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700970
971 def topology( self, ip="DEFAULT", port="DEFAULT" ):
972 """
973 Description:
974 Gets the overview of network topology
975 Returns:
976 Returns a dictionary containing information about network topology;
977 Returns None for exception
978 """
979 try:
980 output = None
981 if ip == "DEFAULT":
982 main.log.warn( "No ip given, reverting to ip from topo file" )
983 ip = self.ip_address
984 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700985 main.log.warn( "No port given, reverting to port " +
986 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700987 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700988 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700989 if response:
990 if 200 <= response[ 0 ] <= 299:
991 output = response[ 1 ]
992 a = json.loads( output )
993 b = json.dumps( a )
994 return b
995 else:
996 main.log.error( "Error with REST request, response was: " +
997 str( response ) )
998 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700999 except ( AttributeError, TypeError ):
1000 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001001 return None
Jon Halle401b092015-09-23 13:34:24 -07001002 except Exception:
1003 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001004 main.cleanAndExit()
Jon Halle401b092015-09-23 13:34:24 -07001005
1006 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1007 """
1008 Description:
1009 Get the devices discovered by ONOS is json string format
1010 Returns:
1011 a json string of the devices currently discovered by ONOS OR
1012 main.FALSE if there is an error in the request OR
1013 Returns None for exception
1014 """
1015 try:
1016 output = None
1017 if ip == "DEFAULT":
1018 main.log.warn( "No ip given, reverting to ip from topo file" )
1019 ip = self.ip_address
1020 if port == "DEFAULT":
1021 main.log.warn( "No port given, reverting to port " +
1022 "from topo file" )
1023 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001024 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001025 if response:
1026 if 200 <= response[ 0 ] <= 299:
1027 output = response[ 1 ]
1028 a = json.loads( output ).get( 'devices' )
1029 assert a is not None, "Error parsing json object"
1030 b = json.dumps( a )
1031 return b
1032 else:
1033 main.log.error( "Error with REST request, response was: " +
1034 str( response ) )
1035 return main.FALSE
1036 except ( AttributeError, AssertionError, TypeError ):
1037 main.log.exception( self.name + ": Object not as expected" )
1038 return None
1039 except Exception:
1040 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001041 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001042
1043 def getIntentState( self, intentsId, intentsJson=None,
1044 ip="DEFAULT", port="DEFAULT" ):
1045 """
1046 Description:
1047 Get intent state.
1048 Accepts a single intent ID (string type) or a list of intent IDs.
1049 Returns the state(string type) of the id if a single intent ID is
1050 accepted.
1051 Required:
1052 intentId: intent ID (string type)
1053 intentsJson: parsed json object from the onos:intents api
1054 Returns:
1055 Returns a dictionary with intent IDs as the key and its
1056 corresponding states as the values; Returns None for invalid IDs or
1057 Type error and any exceptions
1058 NOTE:
1059 An intent's state consist of INSTALLED,WITHDRAWN etc.
1060 """
1061 try:
1062 state = "State is Undefined"
1063 if not intentsJson:
1064 intentsJsonTemp = json.loads( self.intents() )
1065 else:
1066 intentsJsonTemp = json.loads( intentsJson )
1067 if isinstance( intentsId, types.StringType ):
1068 for intent in intentsJsonTemp:
1069 if intentsId == intent[ 'id' ]:
1070 state = intent[ 'state' ]
1071 return state
1072 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1073 " on the list" )
1074 return state
1075 elif isinstance( intentsId, types.ListType ):
1076 dictList = []
1077 for i in xrange( len( intentsId ) ):
1078 stateDict = {}
1079 for intents in intentsJsonTemp:
1080 if intentsId[ i ] == intents[ 'id' ]:
1081 stateDict[ 'state' ] = intents[ 'state' ]
1082 stateDict[ 'id' ] = intentsId[ i ]
1083 dictList.append( stateDict )
1084 break
1085 if len( intentsId ) != len( dictList ):
1086 main.log.info( "Cannot find some of the intent ID state" )
1087 return dictList
1088 else:
1089 main.log.info( "Invalid intents ID entry" )
1090 return None
1091
Jon Halle401b092015-09-23 13:34:24 -07001092 except ( AttributeError, TypeError ):
1093 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001094 return None
Jon Halle401b092015-09-23 13:34:24 -07001095 except Exception:
1096 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001097 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001098
1099 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
1100 ip="DEFAULT", port="DEFAULT"):
1101 """
1102 Description:
1103 Check intents state based on expected state which defaults to
1104 INSTALLED state
1105 Required:
1106 intentsId - List of intents ID to be checked
1107 Optional:
1108 expectedState - Check the expected state(s) of each intents
1109 state in the list.
1110 *NOTE: You can pass in a list of expected state,
1111 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1112 Return:
1113 Returns main.TRUE only if all intent are the same as expected states
1114 , otherwise, returns main.FALSE; Returns None for general exception
1115 """
1116 try:
1117 # Generating a dictionary: intent id as a key and state as value
1118 returnValue = main.TRUE
1119 if intentsId == "ALL":
1120 intentsId = self.getIntentsId( ip=ip, port=port )
1121 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1122
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001123 if len( intentsId ) != len( intentsDict ):
1124 main.log.error( self.name + ": There is something wrong " +
1125 "getting intents state" )
1126 return main.FALSE
1127
1128 if isinstance( expectedState, types.StringType ):
1129 for intents in intentsDict:
1130 if intents.get( 'state' ) != expectedState:
1131 main.log.debug( self.name + " : Intent ID - " +
1132 intents.get( 'id' ) +
1133 " actual state = " +
1134 intents.get( 'state' )
1135 + " does not equal expected state = "
1136 + expectedState )
1137 returnValue = main.FALSE
1138
1139 elif isinstance( expectedState, types.ListType ):
1140 for intents in intentsDict:
1141 if not any( state == intents.get( 'state' ) for state in
1142 expectedState ):
1143 main.log.debug( self.name + " : Intent ID - " +
1144 intents.get( 'id' ) +
1145 " actual state = " +
1146 intents.get( 'state' ) +
1147 " does not equal expected states = "
1148 + str( expectedState ) )
1149 returnValue = main.FALSE
1150
1151 if returnValue == main.TRUE:
1152 main.log.info( self.name + ": All " +
1153 str( len( intentsDict ) ) +
1154 " intents are in " + str( expectedState ) +
1155 " state" )
1156 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001157 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001158 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001159 return None
Jon Halle401b092015-09-23 13:34:24 -07001160 except Exception:
1161 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001162 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001163
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001164 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001165 """
1166 Description:
1167 Get flows currently added to the system
1168 NOTE:
1169 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001170 the REST output
1171
1172 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001173 """
1174 try:
1175 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001176 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001177 if ip == "DEFAULT":
1178 main.log.warn( "No ip given, reverting to ip from topo file" )
1179 ip = self.ip_address
1180 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001181 main.log.warn( "No port given, reverting to port " +
1182 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001183 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001184 if subjectKey and not subjectClass:
1185 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1186 if subjectClass:
1187 url += "/" + subjectClass
1188 if subjectKey:
1189 url += "/" + subjectKey
1190 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001191 if response:
1192 if 200 <= response[ 0 ] <= 299:
1193 output = response[ 1 ]
1194 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001195 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001196 b = json.dumps( a )
1197 return b
1198 else:
1199 main.log.error( "Error with REST request, response was: " +
1200 str( response ) )
1201 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001202 except ( AttributeError, AssertionError, TypeError ):
1203 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001204 return None
Jon Halle401b092015-09-23 13:34:24 -07001205 except Exception:
1206 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001207 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001208
Jon Halle401b092015-09-23 13:34:24 -07001209 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001210 """
1211 Description:
1212 Gets all the flows of the device or get a specific flow in the
1213 device by giving its flow ID
1214 Required:
Jon Halle401b092015-09-23 13:34:24 -07001215 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001216 Optional:
1217 int/hex flowId - ID of the flow
1218 """
1219 try:
1220 output = None
1221 if ip == "DEFAULT":
1222 main.log.warn( "No ip given, reverting to ip from topo file" )
1223 ip = self.ip_address
1224 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001225 main.log.warn( "No port given, reverting to port " +
1226 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001227 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001228 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001229 if flowId:
1230 url += "/" + str( int( flowId ) )
1231 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001232 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001233 if response:
1234 if 200 <= response[ 0 ] <= 299:
1235 output = response[ 1 ]
1236 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001237 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001238 b = json.dumps( a )
1239 return b
1240 else:
1241 main.log.error( "Error with REST request, response was: " +
1242 str( response ) )
1243 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001244 except ( AttributeError, AssertionError, TypeError ):
1245 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001246 return None
Jon Halle401b092015-09-23 13:34:24 -07001247 except Exception:
1248 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001249 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001250
GlennRC073e8bc2015-10-27 17:11:28 -07001251 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001252 """
1253 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001254 Sends a single flow to the specified device. This function exists
1255 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001256 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001257 * The flow in json
1258 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001259 Returns:
1260 Returns main.TRUE for successful requests; Returns main.FALSE
1261 if error on requests;
1262 Returns None for exceptions
1263 NOTE:
1264 The ip and port option are for the requests input's ip and port
1265 of the ONOS node
1266 """
GlennRC073e8bc2015-10-27 17:11:28 -07001267
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001268 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001269 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001270 output = None
1271 if ip == "DEFAULT":
1272 main.log.warn( "No ip given, reverting to ip from topo file" )
1273 ip = self.ip_address
1274 if port == "DEFAULT":
1275 main.log.warn( "No port given, reverting to port " +
1276 "from topo file" )
1277 port = self.port
1278 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001279 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001280 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001281 data=json.dumps( flowJson ) )
1282 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001283 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001284 main.log.info( self.name + ": Successfully POST flow" +
1285 "in device: " + str( deviceId ) )
1286 return main.TRUE
1287 else:
1288 main.log.error( "Error with REST request, response was: " +
1289 str( response ) )
1290 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001291 except NotImplementedError as e:
1292 raise e # Inform the caller
1293 except ( AttributeError, TypeError ):
1294 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001295 return None
Jon Halle401b092015-09-23 13:34:24 -07001296 except Exception:
1297 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001298 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001299
GlennRC073e8bc2015-10-27 17:11:28 -07001300 def addFlow( self,
1301 deviceId,
1302 appId=0,
1303 ingressPort="",
1304 egressPort="",
1305 ethType="",
1306 ethSrc="",
1307 ethDst="",
1308 vlan="",
1309 ipProto="",
1310 ipSrc=(),
1311 ipDst=(),
1312 tcpSrc="",
1313 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001314 udpDst="",
1315 udpSrc="",
1316 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001317 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301318 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001319 ip="DEFAULT",
1320 port="DEFAULT",
1321 debug=False ):
1322 """
1323 Description:
1324 Creates a single flow in the specified device
1325 Required:
1326 * deviceId: id of the device
1327 Optional:
1328 * ingressPort: port ingress device
1329 * egressPort: port of egress device
1330 * ethType: specify ethType
1331 * ethSrc: specify ethSrc ( i.e. src mac addr )
1332 * ethDst: specify ethDst ( i.e. dst mac addr )
1333 * ipProto: specify ip protocol
1334 * ipSrc: specify ip source address with mask eg. ip#/24
1335 as a tuple (type, ip#)
1336 * ipDst: specify ip destination address eg. ip#/24
1337 as a tuple (type, ip#)
1338 * tcpSrc: specify tcp source port
1339 * tcpDst: specify tcp destination port
1340 Returns:
1341 Returns main.TRUE for successful requests; Returns main.FALSE
1342 if error on requests;
1343 Returns None for exceptions
1344 NOTE:
1345 The ip and port option are for the requests input's ip and port
1346 of the ONOS node
1347 """
1348 try:
alisone14d7b02016-07-06 10:31:51 -07001349 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001350 "isPermanent":"true",
1351 "timeout":0,
1352 "deviceId":deviceId,
1353 "treatment":{"instructions":[]},
1354 "selector": {"criteria":[]}}
1355 if appId:
1356 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301357
1358 if groupId:
1359 flowJson[ 'treatment' ][ 'instructions' ].append( {
1360 "type":"GROUP",
1361 "groupId":groupId } )
1362
GlennRC073e8bc2015-10-27 17:11:28 -07001363 if egressPort:
1364 flowJson[ 'treatment' ][ 'instructions' ].append( {
1365 "type":"OUTPUT",
1366 "port":egressPort } )
1367 if ingressPort:
1368 flowJson[ 'selector' ][ 'criteria' ].append( {
1369 "type":"IN_PORT",
1370 "port":ingressPort } )
1371 if ethType:
1372 flowJson[ 'selector' ][ 'criteria' ].append( {
1373 "type":"ETH_TYPE",
1374 "ethType":ethType } )
1375 if ethSrc:
1376 flowJson[ 'selector' ][ 'criteria' ].append( {
1377 "type":"ETH_SRC",
1378 "mac":ethSrc } )
1379 if ethDst:
1380 flowJson[ 'selector' ][ 'criteria' ].append( {
1381 "type":"ETH_DST",
1382 "mac":ethDst } )
1383 if vlan:
1384 flowJson[ 'selector' ][ 'criteria' ].append( {
1385 "type":"VLAN_VID",
1386 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001387 if mpls:
1388 flowJson[ 'selector' ][ 'criteria' ].append( {
1389 "type":"MPLS_LABEL",
1390 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001391 if ipSrc:
1392 flowJson[ 'selector' ][ 'criteria' ].append( {
1393 "type":ipSrc[0],
1394 "ip":ipSrc[1] } )
1395 if ipDst:
1396 flowJson[ 'selector' ][ 'criteria' ].append( {
1397 "type":ipDst[0],
1398 "ip":ipDst[1] } )
1399 if tcpSrc:
1400 flowJson[ 'selector' ][ 'criteria' ].append( {
1401 "type":"TCP_SRC",
1402 "tcpPort": tcpSrc } )
1403 if tcpDst:
1404 flowJson[ 'selector' ][ 'criteria' ].append( {
1405 "type":"TCP_DST",
1406 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001407 if udpSrc:
1408 flowJson[ 'selector' ][ 'criteria' ].append( {
1409 "type":"UDP_SRC",
1410 "udpPort": udpSrc } )
1411 if udpDst:
1412 flowJson[ 'selector' ][ 'criteria' ].append( {
1413 "type":"UDP_DST",
1414 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001415 if ipProto:
1416 flowJson[ 'selector' ][ 'criteria' ].append( {
1417 "type":"IP_PROTO",
1418 "protocol": ipProto } )
1419
1420 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1421
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!" )
Devin Lim44075962017-08-11 10:56:37 -07001427 main.cleanAndExit()
GlennRC073e8bc2015-10-27 17:11:28 -07001428
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001429 def removeFlow( self, deviceId, flowId,
1430 ip="DEFAULT", port="DEFAULT" ):
1431 """
1432 Description:
1433 Remove specific device flow
1434 Required:
1435 str deviceId - id of the device
1436 str flowId - id of the flow
1437 Return:
1438 Returns main.TRUE if successfully deletes flows, otherwise
1439 Returns main.FALSE, Returns None on error
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 # NOTE: REST url requires the intent id to be in decimal form
1451 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001452 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001453 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001454 if response:
1455 if 200 <= response[ 0 ] <= 299:
1456 return main.TRUE
1457 else:
1458 main.log.error( "Error with REST request, response was: " +
1459 str( response ) )
1460 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001461 except ( AttributeError, TypeError ):
1462 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001463 return None
Jon Halle401b092015-09-23 13:34:24 -07001464 except Exception:
1465 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001466 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001467
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001468 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1469 """
1470 Description:
1471 Check if all the current flows are in ADDED state
1472 Return:
1473 returnValue - Returns main.TRUE only if all flows are in
1474 return main.FALSE otherwise;
1475 Returns None for exception
1476 """
1477 try:
1478 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1479 returnValue = main.TRUE
1480 for flow in tempFlows:
1481 if flow.get( 'state' ) != 'ADDED':
1482 main.log.info( self.name + ": flow Id: " +
1483 str( flow.get( 'groupId' ) ) +
1484 " | state:" +
1485 str( flow.get( 'state' ) ) )
1486 returnValue = main.FALSE
1487 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001488 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001489 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001490 return None
1491 except Exception:
1492 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001493 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001494
1495 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1496 subjectClass=None, subjectKey=None, configKey=None ):
1497 """
1498 Description:
1499 Get a json object with the ONOS network configurations
1500 Returns:
1501 A json object containing the network configuration in
1502 ONOS; Returns main.FALSE if error on requests;
1503 Returns None for exception
1504 """
1505 try:
1506 output = None
1507 if ip == "DEFAULT":
1508 main.log.warn( "No ip given, reverting to ip from topo file" )
1509 ip = self.ip_address
1510 if port == "DEFAULT":
1511 main.log.warn( "No port given, reverting to port " +
1512 "from topo file" )
1513 port = self.port
1514 url = "/network/configuration"
1515 if subjectClass:
1516 url += "/" + subjectClass
1517 if subjectKey:
1518 url += "/" + subjectKey
1519 if configKey:
1520 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001521 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001522 if response:
1523 if 200 <= response[ 0 ] <= 299:
1524 output = response[ 1 ]
1525 a = json.loads( output )
1526 b = json.dumps( a )
1527 return b
1528 elif response[ 0 ] == 404:
1529 main.log.error( "Requested configuration doesn't exist: " +
1530 str( response ) )
1531 return {}
1532 else:
1533 main.log.error( "Error with REST request, response was: " +
1534 str( response ) )
1535 return main.FALSE
1536 except ( AttributeError, TypeError ):
1537 main.log.exception( self.name + ": Object not as expected" )
1538 return None
1539 except Exception:
1540 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001541 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001542
1543 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1544 subjectClass=None, subjectKey=None, configKey=None ):
1545 """
1546 Description:
1547 Set a json object with the ONOS network configurations
1548 Returns:
1549 Returns main.TRUE for successful requests; Returns main.FALSE
1550 if error on requests;
1551 Returns None for exceptions
1552
1553 """
1554 try:
1555 output = None
1556 if ip == "DEFAULT":
1557 main.log.warn( "No ip given, reverting to ip from topo file" )
1558 ip = self.ip_address
1559 if port == "DEFAULT":
1560 main.log.warn( "No port given, reverting to port " +
1561 "from topo file" )
1562 port = self.port
1563 url = "/network/configuration"
1564 if subjectClass:
1565 url += "/" + subjectClass
1566 if subjectKey:
1567 url += "/" + subjectKey
1568 if configKey:
1569 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001570 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001571 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001572 data=json.dumps( cfgJson ) )
1573 if response:
1574 if 200 <= response[ 0 ] <= 299:
1575 main.log.info( self.name + ": Successfully POST cfg" )
1576 return main.TRUE
1577 else:
1578 main.log.error( "Error with REST request, response was: " +
1579 str( response ) )
1580 return main.FALSE
1581 except ( AttributeError, TypeError ):
1582 main.log.exception( self.name + ": Object not as expected" )
1583 return None
1584 except Exception:
1585 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001586 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001587
1588 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1589 subjectClass=None, subjectKey=None, configKey=None ):
1590 """
1591 Description:
1592 Remove a json object from the ONOS network configurations
1593 Returns:
1594 Returns main.TRUE for successful requests; Returns main.FALSE
1595 if error on requests;
1596 Returns None for exceptions
1597
1598 """
1599 try:
1600 output = None
1601 if ip == "DEFAULT":
1602 main.log.warn( "No ip given, reverting to ip from topo file" )
1603 ip = self.ip_address
1604 if port == "DEFAULT":
1605 main.log.warn( "No port given, reverting to port " +
1606 "from topo file" )
1607 port = self.port
1608 url = "/network/configuration"
1609 if subjectClass:
1610 url += "/" + subjectClass
1611 if subjectKey:
1612 url += "/" + subjectKey
1613 if configKey:
1614 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001615 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001616 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001617 if response:
1618 if 200 <= response[ 0 ] <= 299:
1619 main.log.info( self.name + ": Successfully delete cfg" )
1620 return main.TRUE
1621 else:
1622 main.log.error( "Error with REST request, response was: " +
1623 str( response ) )
1624 return main.FALSE
1625 except ( AttributeError, TypeError ):
1626 main.log.exception( self.name + ": Object not as expected" )
1627 return None
1628 except Exception:
1629 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001630 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001631
1632 def createFlowBatch( self,
1633 numSw = 1,
1634 swIndex = 1,
1635 batchSize = 1,
1636 batchIndex = 1,
1637 deviceIdpreFix = "of:",
1638 appId=0,
1639 deviceID="",
1640 ingressPort="",
1641 egressPort="",
1642 ethType="",
1643 ethSrc="",
1644 ethDst="",
1645 vlan="",
1646 ipProto="",
1647 ipSrc=(),
1648 ipDst=(),
1649 tcpSrc="",
1650 tcpDst="",
1651 udpDst="",
1652 udpSrc="",
1653 mpls="",
1654 ip="DEFAULT",
1655 port="DEFAULT",
1656 debug=False ):
1657 """
1658 Description:
1659 Creates batches of MAC-rule flows for POST.
1660 Predefined MAC: 2 MS Hex digit for iterating devices
1661 Next 5 Hex digit for iterating batch numbers
1662 Next 5 Hex digit for iterating flows within a batch
1663 Required:
1664 * deviceId: id of the device
1665 Optional:
1666 * ingressPort: port ingress device
1667 * egressPort: port of egress device
1668 * ethType: specify ethType
1669 * ethSrc: specify ethSrc ( i.e. src mac addr )
1670 * ethDst: specify ethDst ( i.e. dst mac addr )
1671 * ipProto: specify ip protocol
1672 * ipSrc: specify ip source address with mask eg. ip#/24
1673 as a tuple (type, ip#)
1674 * ipDst: specify ip destination address eg. ip#/24
1675 as a tuple (type, ip#)
1676 * tcpSrc: specify tcp source port
1677 * tcpDst: specify tcp destination port
1678 Returns:
1679 Returns main.TRUE for successful requests; Returns main.FALSE
1680 if error on requests;
1681 Returns None for exceptions
1682 NOTE:
1683 The ip and port option are for the requests input's ip and port
1684 of the ONOS node
1685 """
1686 #from pprint import pprint
1687
1688 flowJsonList = []
1689 flowJsonBatch = {"flows":flowJsonList}
1690 dev = swIndex
1691
1692 for fl in range(1, batchSize + 1):
1693 flowJson = { "priority":100,
1694 "deviceId":"",
1695 "isPermanent":"true",
1696 "timeout":0,
1697 "treatment":{"instructions":[]},
1698 "selector": {"criteria":[]}}
1699
1700 #main.log.info("fl: " + str(fl))
1701 if dev <= numSw:
1702 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1703 #print deviceId
1704 flowJson['deviceId'] = deviceId
1705 dev += 1
1706 else:
1707 dev = 1
1708 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1709 #print deviceId
1710 flowJson['deviceId'] = deviceId
1711 dev += 1
1712
1713 # ethSrc starts with "0"; ethDst starts with "1"
1714 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1715 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1716 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1717 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1718 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1719 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1720 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1721
1722 if appId:
1723 flowJson[ "appId" ] = appId
1724
1725 if egressPort:
1726 flowJson[ 'treatment' ][ 'instructions' ].append( {
1727 "type":"OUTPUT",
1728 "port":egressPort } )
1729 if ingressPort:
1730 flowJson[ 'selector' ][ 'criteria' ].append( {
1731 "type":"IN_PORT",
1732 "port":ingressPort } )
1733 if ethType:
1734 flowJson[ 'selector' ][ 'criteria' ].append( {
1735 "type":"ETH_TYPE",
1736 "ethType":ethType } )
1737 if ethSrc:
1738 flowJson[ 'selector' ][ 'criteria' ].append( {
1739 "type":"ETH_SRC",
1740 "mac":ethSrc } )
1741 if ethDst:
1742 flowJson[ 'selector' ][ 'criteria' ].append( {
1743 "type":"ETH_DST",
1744 "mac":ethDst } )
1745 if vlan:
1746 flowJson[ 'selector' ][ 'criteria' ].append( {
1747 "type":"VLAN_VID",
1748 "vlanId":vlan } )
1749 if mpls:
1750 flowJson[ 'selector' ][ 'criteria' ].append( {
1751 "type":"MPLS_LABEL",
1752 "label":mpls } )
1753 if ipSrc:
1754 flowJson[ 'selector' ][ 'criteria' ].append( {
1755 "type":ipSrc[0],
1756 "ip":ipSrc[1] } )
1757 if ipDst:
1758 flowJson[ 'selector' ][ 'criteria' ].append( {
1759 "type":ipDst[0],
1760 "ip":ipDst[1] } )
1761 if tcpSrc:
1762 flowJson[ 'selector' ][ 'criteria' ].append( {
1763 "type":"TCP_SRC",
1764 "tcpPort": tcpSrc } )
1765 if tcpDst:
1766 flowJson[ 'selector' ][ 'criteria' ].append( {
1767 "type":"TCP_DST",
1768 "tcpPort": tcpDst } )
1769 if udpSrc:
1770 flowJson[ 'selector' ][ 'criteria' ].append( {
1771 "type":"UDP_SRC",
1772 "udpPort": udpSrc } )
1773 if udpDst:
1774 flowJson[ 'selector' ][ 'criteria' ].append( {
1775 "type":"UDP_DST",
1776 "udpPort": udpDst } )
1777 if ipProto:
1778 flowJson[ 'selector' ][ 'criteria' ].append( {
1779 "type":"IP_PROTO",
1780 "protocol": ipProto } )
1781 #pprint(flowJson)
1782 flowJsonList.append(flowJson)
1783
1784 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1785 flowJsonBatch['flows'] = flowJsonList
1786 #pprint(flowJsonBatch)
1787
1788 return flowJsonBatch
1789
1790
1791 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1792 """
1793 Description:
1794 Sends a single flow batch through /flows REST API.
1795 Required:
1796 * The batch of flows
1797 Returns:
1798 Returns main.TRUE for successful requests; Returns main.FALSE
1799 if error on requests;
1800 Returns None for exceptions
1801 NOTE:
1802 The ip and port option are for the requests input's ip and port
1803 of the ONOS node
1804 """
1805 import time
1806
1807 try:
1808 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1809 output = None
1810 if ip == "DEFAULT":
1811 main.log.warn( "No ip given, reverting to ip from topo file" )
1812 ip = self.ip_address
1813 if port == "DEFAULT":
1814 main.log.warn( "No port given, reverting to port " +
1815 "from topo file" )
1816 port = self.port
1817 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001818 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001819 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001820 data=json.dumps( batch ) )
1821 #main.log.info("Post response is: ", str(response[0]))
1822 if response[0] == 200:
1823 main.log.info( self.name + ": Successfully POST flow batch" )
1824 return main.TRUE, response
1825 else:
1826 main.log.error( "Error with REST request, response was: " +
1827 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001828 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001829 except NotImplementedError as e:
1830 raise e # Inform the caller
1831 except ( AttributeError, TypeError ):
1832 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001833 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001834 except Exception:
1835 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001836 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001837
1838 def removeFlowBatch( self, batch={},
1839 ip="DEFAULT", port="DEFAULT" ):
1840 """
1841 Description:
1842 Remove a batch of flows
1843 Required:
1844 flow batch
1845 Return:
1846 Returns main.TRUE if successfully deletes flows, otherwise
1847 Returns main.FALSE, Returns None on error
1848 """
1849 try:
1850 output = None
1851 if ip == "DEFAULT":
1852 main.log.warn( "No ip given, reverting to ip from topo file" )
1853 ip = self.ip_address
1854 if port == "DEFAULT":
1855 main.log.warn( "No port given, reverting to port " +
1856 "from topo file" )
1857 port = self.port
1858 # NOTE: REST url requires the intent id to be in decimal form
1859
suibin zhang116647a2016-05-06 16:30:09 -07001860 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001861 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001862 data = json.dumps(batch) )
1863 if response:
1864 if 200 <= response[ 0 ] <= 299:
1865 return main.TRUE
1866 else:
1867 main.log.error( "Error with REST request, response was: " +
1868 str( response ) )
1869 return main.FALSE
1870 except ( AttributeError, TypeError ):
1871 main.log.exception( self.name + ": Object not as expected" )
1872 return None
1873 except Exception:
1874 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001875 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001876
1877 def getTopology( self, topologyOutput ):
1878 """
1879 Definition:
1880 Loads a json topology output
1881 Return:
1882 topology = current ONOS topology
1883 """
1884 import json
1885 try:
1886 # either onos:topology or 'topology' will work in CLI
1887 topology = json.loads(topologyOutput)
1888 main.log.debug( topology )
1889 return topology
1890 except pexpect.EOF:
1891 main.log.error( self.name + ": EOF exception found" )
1892 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001893 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001894 except Exception:
1895 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001896 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001897
1898 def checkStatus(
1899 self,
Devin Lim142b5342017-07-20 15:22:39 -07001900 numswitch,
1901 numlink,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001902 logLevel="info" ):
1903 """
1904 Checks the number of switches & links that ONOS sees against the
1905 supplied values. By default this will report to main.log, but the
1906 log level can be specific.
1907
Devin Lim142b5342017-07-20 15:22:39 -07001908 Params: numswitch = expected number of switches
1909 numlink = expected number of links
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001910 logLevel = level to log to.
1911 Currently accepts 'info', 'warn' and 'report'
1912
1913 Returns: main.TRUE if the number of switches and links are correct,
1914 main.FALSE if the number of switches and links is incorrect,
1915 and main.ERROR otherwise
1916 """
1917 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001918 topology = self.getTopology( self.topology() )
Devin Lim142b5342017-07-20 15:22:39 -07001919 #summary = self.summary()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001920 if topology == {}:
1921 return main.ERROR
1922 output = ""
1923 # Is the number of switches is what we expected
1924 devices = topology.get( 'devices', False )
1925 links = topology.get( 'links', False )
Devin Lim142b5342017-07-20 15:22:39 -07001926 if devices is False or links is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001927 return main.ERROR
Devin Lim142b5342017-07-20 15:22:39 -07001928 switchCheck = ( int( devices ) == int( numswitch ) )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001929 # Is the number of links is what we expected
Devin Lim142b5342017-07-20 15:22:39 -07001930 linkCheck = ( int( links ) == int( numlink ) )
1931 if switchCheck and linkCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001932 # We expected the correct numbers
1933 output = output + "The number of links and switches match "\
1934 + "what was expected"
1935 result = main.TRUE
1936 else:
1937 output = output + \
1938 "The number of links and switches does not match " + \
1939 "what was expected"
1940 result = main.FALSE
1941 output = output + "\n ONOS sees %i devices" % int( devices )
Devin Lim142b5342017-07-20 15:22:39 -07001942 output = output + " (%i expected) " % int( numswitch )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001943 output = output + "and %i links " % int( links )
Devin Lim142b5342017-07-20 15:22:39 -07001944 output = output + "(%i expected)" % int( numlink )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001945 if logLevel == "report":
1946 main.log.report( output )
1947 elif logLevel == "warn":
1948 main.log.warn( output )
1949 else:
1950 main.log.info( output )
1951 return result
1952 except pexpect.EOF:
1953 main.log.error( self.name + ": EOF exception found" )
1954 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001955 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001956 except Exception:
1957 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001958 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301959
1960 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
1961 """
1962 Description:
1963 Creates a single Group for the specified device.
1964 Required:
1965 * deviceId: id of the device
1966 * type: Type of the Group
1967 * bucketList: Buckets to be added to the group
1968 * appCookie: Cookie for the Group
1969 * groupId: Id of the Group
1970 Returns:
1971 Returns main.TRUE for successful requests; Returns main.FALSE
1972 if error on requests;
1973 Returns None for exceptions
1974 Note:
1975 The ip and port option are for the requests input's ip and port
1976 of the ONOS node
1977 """
1978 try:
1979 groupJson = { "type": groupType,
1980 "appCookie": appCookie,
1981 "groupId": groupId,
1982 "buckets": bucketList
1983 }
1984 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
1985
1986 except ( AttributeError, TypeError ):
1987 main.log.exception( self.name + ": Object not as expected" )
1988 return None
1989 except Exception:
1990 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001991 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301992
1993 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
1994 """
1995 Description:
1996 Sends a single group to the specified device.
1997 Required:
1998 * deviceId: id of the device
1999 * groupJson: the group in json
2000 Returns:
2001 Returns main.TRUE for successful requests; Returns main.FALSE
2002 if error on requests;
2003 Returns None for exceptions
2004 NOTE:
2005 The ip and port option are for the requests input's ip and port
2006 of the ONOS node
2007 """
2008 try:
2009 if debug: main.log.debug( "Adding group: " + self.pprint( groupJson ) )
2010 output = None
2011 if ip == "DEFAULT":
2012 main.log.warn( "No ip given, reverting to ip from topo file" )
2013 ip = self.ip_address
2014 if port == "DEFAULT":
2015 main.log.warn( "No port given, reverting to port " +
2016 "from topo file" )
2017 port = self.port
2018 url = "/groups/" + deviceId
2019 response = self.send( method="POST",
2020 url=url, ip = ip, port = port,
2021 data=json.dumps( groupJson ) )
2022 if response:
2023 if "201" in str( response[ 0 ] ):
2024 main.log.info( self.name + ": Successfully POST group " +
2025 "in device: " + str( deviceId ) )
2026 return main.TRUE
2027 else:
2028 main.log.error( "Error with REST request, response was: " +
2029 str( response ) )
2030 return main.FALSE
2031 except NotImplementedError as e:
2032 raise e # Inform the caller
2033 except ( AttributeError, TypeError ):
2034 main.log.exception( self.name + ": Object not as expected" )
2035 return None
2036 except Exception:
2037 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002038 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302039
2040 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2041 """
2042 Description:
2043 Get all the groups or get a specific group by giving the
2044 deviceId and appCookie
2045 Optional:
2046 * deviceId: id of the Device
2047 * appCookie: Cookie of the Group
2048 Returns:
2049 Returns Groups for successful requests; Returns main.FALSE
2050 if error on requests;
2051 Returns None for exceptions
2052 NOTE:
2053 The ip and port option are for the requests input's ip and port
2054 of the ONOS node
2055 """
2056 try:
2057 output = None
2058 if ip == "DEFAULT":
2059 main.log.warn( "No ip given, reverting to ip from topo file" )
2060 ip = self.ip_address
2061 if port == "DEFAULT":
2062 main.log.warn( "No port given, reverting to port " +
2063 "from topo file" )
2064 port = self.port
2065 url = "/groups"
2066 if deviceId:
2067 url += "/" + deviceId
2068 if appCookie:
2069 url += "/" + appCookie
2070 response = self.send( url=url, ip = ip, port = port )
2071 if response:
2072 if 200 <= response[ 0 ] <= 299:
2073 output = response[ 1 ]
2074 groupsJson = json.loads( output ).get( 'groups' )
2075 assert groupsJson is not None, "Error parsing json object"
2076 groups = json.dumps( groupsJson )
2077 return groups
2078 else:
2079 main.log.error( "Error with REST request, response was: " +
2080 str( response ) )
2081 return main.FALSE
2082 except ( AttributeError, AssertionError, TypeError ):
2083 main.log.exception( self.name + ": Object not as expected" )
2084 return None
2085 except Exception:
2086 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002087 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302088
2089 def removeGroup( self, deviceId, appCookie,
2090 ip="DEFAULT", port="DEFAULT" ):
2091 """
2092 Description:
2093 Removes specific device group
2094 Required:
2095 * deviceId: id of the Device
2096 * appCookie: Cookie of the Group
2097 Returns:
2098 Returns main.TRUE for successful requests; Returns main.FALSE
2099 if error on requests;
2100 Returns None for exceptions
2101 NOTE:
2102 The ip and port option are for the requests input's ip and port
2103 of the ONOS node
2104
2105 """
2106 try:
2107 output = None
2108 if ip == "DEFAULT":
2109 main.log.warn( "No ip given, reverting to ip from topo file" )
2110 ip = self.ip_address
2111 if port == "DEFAULT":
2112 main.log.warn( "No port given, reverting to port " +
2113 "from topo file" )
2114 port = self.port
2115 query = "/" + str( deviceId ) + "/" + str( appCookie )
2116 response = self.send( method="DELETE",
2117 url="/groups" + query, ip = ip, port = port )
2118 if response:
2119 if 200 <= response[ 0 ] <= 299:
2120 return main.TRUE
2121 else:
2122 main.log.error( "Error with REST request, response was: " +
2123 str( response ) )
2124 return main.FALSE
2125 except ( AttributeError, TypeError ):
2126 main.log.exception( self.name + ": Object not as expected" )
2127 return None
2128 except Exception:
2129 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002130 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302131