blob: d5c11149b8a15cf78b7ab25780e49740967d0223 [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
5
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jon Hallfc915882015-07-14 13:33:17 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 ( at your option ) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23"""
24import json
25import os
26import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070027import types
Jon Halle401b092015-09-23 13:34:24 -070028import sys
Jon Hallfc915882015-07-14 13:33:17 -070029
Jon Hallfc915882015-07-14 13:33:17 -070030from drivers.common.api.controllerdriver import Controller
31
32
33class OnosRestDriver( Controller ):
34
35 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070036 self.pwd = None
37 self.user_name = "user"
Jon Hallfc915882015-07-14 13:33:17 -070038 super( Controller, self ).__init__()
39 self.ip_address = "localhost"
40 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070041 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070042
43 def connect( self, **connectargs ):
44 try:
45 for key in connectargs:
46 vars( self )[ key ] = connectargs[ key ]
47 self.name = self.options[ 'name' ]
48 except Exception as e:
49 main.log.exception( e )
50 try:
51 if os.getenv( str( self.ip_address ) ) != None:
52 self.ip_address = os.getenv( str( self.ip_address ) )
53 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070054 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070055 except KeyError:
56 main.log.info( "Invalid host name," +
57 "defaulting to 'localhost' instead" )
58 self.ip_address = 'localhost'
59 except Exception as inst:
60 main.log.error( "Uncaught exception: " + str( inst ) )
61
62 self.handle = super( OnosRestDriver, self ).connect()
63 return self.handle
64
Jon Halle401b092015-09-23 13:34:24 -070065 def pprint( self, jsonObject ):
66 """
67 Pretty Prints a json object
68
69 arguments:
70 jsonObject - a parsed json object
71 returns:
72 A formatted string for printing or None on error
73 """
74 try:
75 if isinstance( jsonObject, str ):
76 jsonObject = json.loads( jsonObject )
77 return json.dumps( jsonObject, sort_keys=True,
78 indent=4, separators=(',', ': '))
79 except ( TypeError, ValueError ):
80 main.log.exception( "Error parsing jsonObject" )
81 return None
82
suibin zhangd5b6fe42016-05-12 08:48:58 -070083 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070084 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070085 """
86 Arguments:
87 str ip: ONOS IP Address
88 str port: ONOS REST Port
89 str url: ONOS REST url path.
90 NOTE that this is is only the relative path. IE "/devices"
91 str base: The base url for the given REST api. Applications could
92 potentially have their own base url
93 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070094 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070095 the request
96 dict data: Dictionary to be sent in the body of the request
97 """
98 # TODO: Authentication - simple http (user,pass) tuple
99 # TODO: should we maybe just pass kwargs straight to response?
100 # TODO: Do we need to allow for other protocols besides http?
101 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700102 if ip == "DEFAULT":
103 main.log.warn( "No ip given, reverting to ip from topo file" )
104 ip = self.ip_address
105 if port == "DEFAULT":
106 main.log.warn( "No port given, reverting to port " +
107 "from topo file" )
108 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700109
Jon Hallfc915882015-07-14 13:33:17 -0700110 try:
111 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700112 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800113 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700114 auth = (self.user_name, self.pwd)
115 else:
116 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700117 main.log.info( "Sending request " + path + " using " +
118 method.upper() + " method." )
119 response = requests.request( method.upper(),
120 path,
121 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700122 data=data,
123 auth=auth )
124 if debug:
125 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700126 return ( response.status_code, response.text.encode( 'utf8' ) )
127 except requests.exceptions:
128 main.log.exception( "Error sending request." )
129 return None
Jon Halle401b092015-09-23 13:34:24 -0700130 except Exception:
131 main.log.exception( self.name + ": Uncaught exception!" )
132 main.cleanup()
133 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700134
135 def intents( self, ip="DEFAULT", port="DEFAULT" ):
136 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700137 Description:
138 Gets a list of dictionary of all intents in the system
139 Returns:
140 A list of dictionary of intents in string type to match the cli
141 version for now; Returns main.FALSE if error on request;
142 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700143 """
144 try:
145 output = None
146 if ip == "DEFAULT":
147 main.log.warn( "No ip given, reverting to ip from topo file" )
148 ip = self.ip_address
149 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700150 main.log.warn( "No port given, reverting to port " +
151 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700152 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700153 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700154 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700155 if 200 <= response[ 0 ] <= 299:
156 output = response[ 1 ]
157 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700158 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700159 b = json.dumps( a )
160 return b
Jon Hallfc915882015-07-14 13:33:17 -0700161 else:
162 main.log.error( "Error with REST request, response was: " +
163 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700164 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700165 except ( AttributeError, AssertionError, TypeError ):
166 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700167 return None
Jon Halle401b092015-09-23 13:34:24 -0700168 except Exception:
169 main.log.exception( self.name + ": Uncaught exception!" )
170 main.cleanup()
171 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700172
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700173 def intent( self, intentId, appId="org.onosproject.cli",
174 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700175 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700176 Description:
177 Get the specific intent information of the given application ID and
178 intent ID
179 Required:
180 str intentId - Intent id in hexadecimal form
181 Optional:
182 str appId - application id of intent
183 Returns:
184 Returns an information dictionary of the given intent;
185 Returns main.FALSE if error on requests; Returns None for exception
186 NOTE:
187 The GET /intents REST api command accepts application id but the
188 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700189 """
190 try:
191 output = None
192 if ip == "DEFAULT":
193 main.log.warn( "No ip given, reverting to ip from topo file" )
194 ip = self.ip_address
195 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700196 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700197 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700198 port = self.port
199 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700200 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700201 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700202 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700203 if 200 <= response[ 0 ] <= 299:
204 output = response[ 1 ]
205 a = json.loads( output )
206 return a
Jon Hallfc915882015-07-14 13:33:17 -0700207 else:
208 main.log.error( "Error with REST request, response was: " +
209 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700210 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700211 except ( AttributeError, TypeError ):
212 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700213 return None
Jon Halle401b092015-09-23 13:34:24 -0700214 except Exception:
215 main.log.exception( self.name + ": Uncaught exception!" )
216 main.cleanup()
217 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700218
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700219 def apps( self, ip="DEFAULT", port="DEFAULT" ):
220 """
221 Description:
222 Returns all the current application installed in the system
223 Returns:
224 List of dictionary of installed application; Returns main.FALSE for
225 error on request; Returns None for exception
226 """
227 try:
228 output = None
229 if ip == "DEFAULT":
230 main.log.warn( "No ip given, reverting to ip from topo file" )
231 ip = self.ip_address
232 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700233 main.log.warn( "No port given, reverting to port " +
234 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700235 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700236 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700237 if response:
238 if 200 <= response[ 0 ] <= 299:
239 output = response[ 1 ]
240 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700241 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700242 b = json.dumps( a )
243 return b
244 else:
245 main.log.error( "Error with REST request, response was: " +
246 str( response ) )
247 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700248 except ( AttributeError, AssertionError, TypeError ):
249 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700250 return None
Jon Halle401b092015-09-23 13:34:24 -0700251 except Exception:
252 main.log.exception( self.name + ": Uncaught exception!" )
253 main.cleanup()
254 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700255
256 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
257 """
258 Decription:
259 Activate an app that is already installed in ONOS
260 Optional:
261 bool check - If check is True, method will check the status
262 of the app after the command is issued
263 Returns:
264 Returns main.TRUE if the command was successfully or main.FALSE
265 if the REST responded with an error or given incorrect input;
266 Returns None for exception
267
268 """
269 try:
270 output = None
271 if ip == "DEFAULT":
272 main.log.warn( "No ip given, reverting to ip from topo file" )
273 ip = self.ip_address
274 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700275 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700276 "from topo file" )
277 port = self.port
278 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700279 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700280 url="/applications" + query,
281 ip = ip, port = port)
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700282 if response:
283 output = response[ 1 ]
284 app = json.loads( output )
285 if 200 <= response[ 0 ] <= 299:
286 if check:
287 if app.get( 'state' ) == 'ACTIVE':
288 main.log.info( self.name + ": " + appName +
289 " application" +
290 " is in ACTIVE state" )
291 return main.TRUE
292 else:
293 main.log.error( self.name + ": " + appName +
294 " application" + " is in " +
295 app.get( 'state' ) + " state" )
296 return main.FALSE
297 else:
298 main.log.warn( "Skipping " + appName +
299 "application check" )
300 return main.TRUE
301 else:
302 main.log.error( "Error with REST request, response was: " +
303 str( response ) )
304 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700305 except ( AttributeError, TypeError ):
306 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700307 return None
Jon Halle401b092015-09-23 13:34:24 -0700308 except Exception:
309 main.log.exception( self.name + ": Uncaught exception!" )
310 main.cleanup()
311 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700312
313 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
314 check=True ):
315 """
316 Required:
317 Deactivate an app that is already activated in ONOS
318 Optional:
319 bool check - If check is True, method will check the status of the
320 app after the command is issued
321 Returns:
322 Returns main.TRUE if the command was successfully sent
323 main.FALSE if the REST responded with an error or given
324 incorrect input; Returns None for exception
325 """
326 try:
327 output = None
328 if ip == "DEFAULT":
329 main.log.warn( "No ip given, reverting to ip from topo file" )
330 ip = self.ip_address
331 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700332 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700333 "from topo file" )
334 port = self.port
335 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700336 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700337 url="/applications" + query,
338 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700339 if response:
340 output = response[ 1 ]
341 app = json.loads( output )
342 if 200 <= response[ 0 ] <= 299:
343 if check:
344 if app.get( 'state' ) == 'INSTALLED':
345 main.log.info( self.name + ": " + appName +
346 " application" +
347 " is in INSTALLED state" )
348 return main.TRUE
349 else:
350 main.log.error( self.name + ": " + appName +
351 " application" + " is in " +
352 app.get( 'state' ) + " state" )
353 return main.FALSE
354 else:
355 main.log.warn( "Skipping " + appName +
356 "application check" )
357 return main.TRUE
358 else:
359 main.log.error( "Error with REST request, response was: " +
360 str( response ) )
361 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700362 except ( AttributeError, TypeError ):
363 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700364 return None
Jon Halle401b092015-09-23 13:34:24 -0700365 except Exception:
366 main.log.exception( self.name + ": Uncaught exception!" )
367 main.cleanup()
368 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700369
370 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
371 port="DEFAULT" ):
372 """
373 Decription:
374 Gets the informaion of the given application
375 Required:
376 str name - Name of onos application
377 Returns:
378 Returns a dictionary of information ONOS application in string type;
379 Returns main.FALSE if error on requests; Returns None for exception
380 """
381 try:
382 output = None
383 if ip == "DEFAULT":
384 main.log.warn( "No ip given, reverting to ip from topo file" )
385 ip = self.ip_address
386 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700387 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700388 "from topo file" )
389 port = self.port
390 query = "/" + project + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700391 response = self.send( url="/applications" + query,
392 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700393 if response:
394 if 200 <= response[ 0 ] <= 299:
395 output = response[ 1 ]
396 a = json.loads( output )
397 return a
398 else:
399 main.log.error( "Error with REST request, response was: " +
400 str( response ) )
401 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700402 except ( AttributeError, TypeError ):
403 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700404 return None
Jon Halle401b092015-09-23 13:34:24 -0700405 except Exception:
406 main.log.exception( self.name + ": Uncaught exception!" )
407 main.cleanup()
408 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700409
410 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700411 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700412 """
413 Description:
414 Adds a host-to-host intent ( bidirectional ) by
415 specifying the two hosts.
416 Required:
417 * hostIdOne: ONOS host id for host1
418 * hostIdTwo: ONOS host id for host2
419 Optional:
420 str appId - Application name of intent identifier
421 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700422 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700423 error on requests; Returns None for exceptions
424 """
425 try:
426 intentJson = {"two": str( hostIdTwo ),
427 "selector": {"criteria": []}, "priority": 7,
428 "treatment": {"deferred": [], "instructions": []},
429 "appId": appId, "one": str( hostIdOne ),
430 "type": "HostToHostIntent",
431 "constraints": [{"type": "LinkTypeConstraint",
432 "types": ["OPTICAL"],
433 "inclusive": 'false' }]}
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700434 if vlanId:
435 intentJson[ 'selector' ][ 'criteria' ].append( { "type":"VLAN_VID",
436 "vlanId":vlanId } )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700437 output = None
438 if ip == "DEFAULT":
439 main.log.warn( "No ip given, reverting to ip from topo file" )
440 ip = self.ip_address
441 if port == "DEFAULT":
442 main.log.warn( "No port given, reverting to port " +
443 "from topo file" )
444 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700445 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700446 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700447 data=json.dumps( intentJson ) )
448 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700449 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700450 main.log.info( self.name + ": Successfully POST host" +
451 " intent between host: " + hostIdOne +
452 " and host: " + hostIdTwo )
453 return main.TRUE
454 else:
455 main.log.error( "Error with REST request, response was: " +
456 str( response ) )
457 return main.FALSE
458
Jon Halle401b092015-09-23 13:34:24 -0700459 except ( AttributeError, TypeError ):
460 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700461 return None
Jon Halle401b092015-09-23 13:34:24 -0700462 except Exception:
463 main.log.exception( self.name + ": Uncaught exception!" )
464 main.cleanup()
465 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700466
kelvin-onlabb50074f2015-07-27 16:18:32 -0700467 def addPointIntent( self,
468 ingressDevice,
469 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700470 appId='org.onosproject.cli',
471 ingressPort="",
472 egressPort="",
473 ethType="",
474 ethSrc="",
475 ethDst="",
476 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800477 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700478 lambdaAlloc=False,
479 ipProto="",
480 ipSrc="",
481 ipDst="",
482 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700483 tcpDst="",
484 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700485 port="DEFAULT",
486 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700487 """
488 Description:
489 Adds a point-to-point intent ( uni-directional ) by
490 specifying device id's and optional fields
491 Required:
492 * ingressDevice: device id of ingress device
493 * egressDevice: device id of egress device
494 Optional:
495 * ethType: specify ethType
496 * ethSrc: specify ethSrc ( i.e. src mac addr )
497 * ethDst: specify ethDst ( i.e. dst mac addr )
498 * bandwidth: specify bandwidth capacity of link (TODO)
499 * lambdaAlloc: if True, intent will allocate lambda
500 for the specified intent (TODO)
501 * ipProto: specify ip protocol
502 * ipSrc: specify ip source address with mask eg. ip#/24
503 * ipDst: specify ip destination address eg. ip#/24
504 * tcpSrc: specify tcp source port
505 * tcpDst: specify tcp destination port
506 Returns:
507 Returns main.TRUE for successful requests; Returns main.FALSE if
508 no ingress|egress port found and if error on requests;
509 Returns None for exceptions
510 NOTE:
511 The ip and port option are for the requests input's ip and port
512 of the ONOS node
513 """
514 try:
515 if "/" in ingressDevice:
516 if not ingressPort:
517 ingressPort = ingressDevice.split( "/" )[ 1 ]
518 ingressDevice = ingressDevice.split( "/" )[ 0 ]
519 else:
520 if not ingressPort:
521 main.log.debug( self.name + ": Ingress port not specified" )
522 return main.FALSE
523
524 if "/" in egressDevice:
525 if not egressPort:
526 egressPort = egressDevice.split( "/" )[ 1 ]
527 egressDevice = egressDevice.split( "/" )[ 0 ]
528 else:
529 if not egressPort:
530 main.log.debug( self.name + ": Egress port not specified" )
531 return main.FALSE
532
533 intentJson ={ "ingressPoint": { "device": ingressDevice,
534 "port": ingressPort },
535 "selector": { "criteria": [] },
536 "priority": 55,
537 "treatment": { "deferred": [],
538 "instructions": [] },
539 "egressPoint": { "device": egressDevice,
540 "port": egressPort },
541 "appId": appId,
542 "type": "PointToPointIntent",
543 "constraints": [ { "type": "LinkTypeConstraint",
544 "types": [ "OPTICAL" ],
545 "inclusive": "false" } ] }
546
alisonda157272016-12-22 01:13:21 -0800547 # if protected:
548 # intentJson['constraints'].append( { "type": "Protection", "types": ["Protection"], "inclusive": "true" } )
549
kelvin-onlabb50074f2015-07-27 16:18:32 -0700550 if ethType == "IPV4":
551 intentJson[ 'selector' ][ 'criteria' ].append( {
552 "type":"ETH_TYPE",
553 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700554 elif ethType:
555 intentJson[ 'selector' ][ 'criteria' ].append( {
556 "type":"ETH_TYPE",
557 "ethType":ethType } )
558
kelvin-onlabb50074f2015-07-27 16:18:32 -0700559 if ethSrc:
560 intentJson[ 'selector' ][ 'criteria' ].append(
561 { "type":"ETH_SRC",
562 "mac":ethSrc } )
563 if ethDst:
564 intentJson[ 'selector' ][ 'criteria' ].append(
565 { "type":"ETH_DST",
566 "mac":ethDst } )
567 if ipSrc:
568 intentJson[ 'selector' ][ 'criteria' ].append(
569 { "type":"IPV4_SRC",
570 "ip":ipSrc } )
571 if ipDst:
572 intentJson[ 'selector' ][ 'criteria' ].append(
573 { "type":"IPV4_DST",
574 "ip":ipDst } )
575 if tcpSrc:
576 intentJson[ 'selector' ][ 'criteria' ].append(
577 { "type":"TCP_SRC",
578 "tcpPort": tcpSrc } )
579 if tcpDst:
580 intentJson[ 'selector' ][ 'criteria' ].append(
581 { "type":"TCP_DST",
582 "tcpPort": tcpDst } )
583 if ipProto:
584 intentJson[ 'selector' ][ 'criteria' ].append(
585 { "type":"IP_PROTO",
586 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700587 if vlanId:
588 intentJson[ 'selector' ][ 'criteria' ].append(
589 { "type":"VLAN_VID",
590 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700591
592 # TODO: Bandwidth and Lambda will be implemented if needed
593
kelvin-onlabb50074f2015-07-27 16:18:32 -0700594 output = None
595 if ip == "DEFAULT":
596 main.log.warn( "No ip given, reverting to ip from topo file" )
597 ip = self.ip_address
598 if port == "DEFAULT":
599 main.log.warn( "No port given, reverting to port " +
600 "from topo file" )
601 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700602 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700603 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700604 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700605
606 main.log.debug( intentJson )
607
kelvin-onlabb50074f2015-07-27 16:18:32 -0700608 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700609 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700610 main.log.info( self.name + ": Successfully POST point" +
611 " intent between ingress: " + ingressDevice +
612 " and egress: " + egressDevice + " devices" )
613 return main.TRUE
614 else:
615 main.log.error( "Error with REST request, response was: " +
616 str( response ) )
617 return main.FALSE
618
Jon Halle401b092015-09-23 13:34:24 -0700619 except ( AttributeError, TypeError ):
620 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700621 return None
Jon Halle401b092015-09-23 13:34:24 -0700622 except Exception:
623 main.log.exception( self.name + ": Uncaught exception!" )
624 main.cleanup()
625 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700626
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700627 def addSinglepointToMultipointIntent(self,
628 ingressDevice,
629 egressDeviceList,
630 portEgressList,
631 appId='org.onosproject.cli',
632 portIngress="",
633 ethType="",
634 ethSrc="",
635 ethDst="",
636 bandwidth="",
637 lambdaAlloc=False,
638 ipProto="",
639 ipSrc="",
640 ipDst="",
641 tcpSrc="",
642 tcpDst="",
643 partial=False,
644 ip="DEFAULT",
645 port="DEFAULT",
646 vlanId="" ):
647 """
648 Description:
649 Adds a point-to-multi point intent ( uni-directional ) by
650 specifying device id's and optional fields
651 Required:
652 * ingressDevice: device id of ingress device
653 * egressDevice: device id of egress device
654 * portEgressList: a list of port id of egress device
655
656 Optional:
657 * portIngress: port id of ingress device
658 * ethType: specify ethType
659 * ethSrc: specify ethSrc ( i.e. src mac addr )
660 * ethDst: specify ethDst ( i.e. dst mac addr )
661 * bandwidth: specify bandwidth capacity of link (TODO)
662 * lambdaAlloc: if True, intent will allocate lambda
663 for the specified intent (TODO)
664 * ipProto: specify ip protocol
665 * ipSrc: specify ip source address with mask eg. ip#/24
666 * ipDst: specify ip destination address eg. ip#/24
667 * tcpSrc: specify tcp source port
668 * tcpDst: specify tcp destination port
669 Returns:
670 Returns main.TRUE for successful requests; Returns main.FALSE if
671 no ingress|egress port found and if error on requests;
672 Returns None for exceptions
673 NOTE:
674 The ip and port option are for the requests input's ip and port
675 of the ONOS node
676 """
677 try:
678
679 if "/" in ingressDevice:
680 if not portIngress:
681 ingressPort = ingressDevice.split( "/" )[ 1 ]
682 ingressDevice = ingressDevice.split( "/" )[ 0 ]
683 else:
684 if not portIngress:
685 main.log.debug( self.name + ": Ingress port not specified" )
686 return main.FALSE
687 index = 0
688 for egressDevice in egressDeviceList:
689 if "/" in egressDevice:
690 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
691 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
692 else:
693 if not portEgressList:
694 main.log.debug( self.name + ": Egress port not specified" )
695 return main.FALSE
696 index = index + 1
697
698 intentJson = { "ingressPoint": { "device": ingressDevice,
699 "port": ingressPort },
700 "selector": { "criteria": [] },
701 "priority": 55,
702 "treatment": { "deferred": [],
703 "instructions": [] },
704 "egressPoint": { "connectPoints": [] },
705 "appId": appId,
706 "type": "SinglePointToMultiPointIntent",
707 "constraints": [ { "type": "LinkTypeConstraint",
708 "types": ["OPTICAL"],
709 "inclusive": "false" } ] }
710
711 index = 0
712 for ep in portEgressList:
713 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
714 { "device": egressDeviceList[ index ],
715 "port": ep } )
716 index += 1
717
718 if ethType == "IPV4":
719 intentJson[ 'selector' ][ 'criteria' ].append(
720 { "type": "ETH_TYPE",
721 "ethType": 2048 } )
722 elif ethType:
723 intentJson[ 'selector' ][ 'criteria' ].append(
724 { "type": "ETH_TYPE",
725 "ethType": ethType } )
726
727 if ethSrc:
728 intentJson[ 'selector' ][ 'criteria' ].append(
729 { "type": "ETH_SRC",
730 "mac": ethSrc } )
731
732 if ethDst:
733 for dst in ethDst:
734 if dst:
735 intentJson[ 'selector' ][ 'criteria' ].append(
736 { "type": "ETH_DST",
737 "mac": dst } )
738 if tcpSrc:
739 intentJson[ 'selector' ][ 'criteria' ].append(
740 { "type": "TCP_SRC",
741 "tcpPort": tcpSrc } )
742 if tcpDst:
743 intentJson[ 'selector' ][ 'criteria' ].append(
744 { "type": "TCP_DST",
745 "tcpPort": tcpDst } )
746 if ipProto:
747 intentJson[ 'selector' ][ 'criteria' ].append(
748 { "type": "IP_PROTO",
749 "protocol": ipProto } )
750 if vlanId:
751 intentJson[ 'selector' ][ 'criteria' ].append(
752 { "type": "VLAN_VID",
753 "vlanId": vlanId } )
754
755 # TODO: Bandwidth and Lambda will be implemented if needed
756
757 output = None
758 if ip == "DEFAULT":
759 main.log.warn( "No ip given, reverting to ip from topo file" )
760 ip = self.ip_address
761 if port == "DEFAULT":
762 main.log.warn( "No port given, reverting to port " +
763 "from topo file" )
764 port = self.port
765 response = self.send( method="POST",
766 url="/intents", ip=ip, port=port,
767 data=json.dumps( intentJson ) )
768
769 main.log.debug(intentJson)
770
771 if response:
772 if "201" in str( response[ 0 ] ):
773 main.log.info( self.name + ": Successfully POST point" +
774 " intent between ingress: " + ingressDevice +
775 " and egress: " + str(egressDeviceList) + " devices" )
776 return main.TRUE
777 else:
778 main.log.error( "Error with REST request, response was: " + str( response ) )
779 return main.FALSE
780 else:
781 main.log.error( "REST request has no response." )
782 return main.FALSE
783
784 except ( AttributeError, TypeError ):
785 main.log.exception( self.name + ": Object not as expected" )
786 return None
787 except Exception:
788 main.log.exception( self.name + ": Uncaught exception!" )
789 main.cleanup()
790 main.exit()
791
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700792 def removeIntent( self, intentId, appId='org.onosproject.cli',
793 ip="DEFAULT", port="DEFAULT" ):
794 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700795 Remove intent for specified application id and intent id;
796 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700797 """
798 try:
799 output = None
800 if ip == "DEFAULT":
801 main.log.warn( "No ip given, reverting to ip from topo file" )
802 ip = self.ip_address
803 if port == "DEFAULT":
804 main.log.warn( "No port given, reverting to port " +
805 "from topo file" )
806 port = self.port
807 # NOTE: REST url requires the intent id to be in decimal form
808 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700809 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700810 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700811 if response:
812 if 200 <= response[ 0 ] <= 299:
813 return main.TRUE
814 else:
815 main.log.error( "Error with REST request, response was: " +
816 str( response ) )
817 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700818 except ( AttributeError, TypeError ):
819 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700820 return None
Jon Halle401b092015-09-23 13:34:24 -0700821 except Exception:
822 main.log.exception( self.name + ": Uncaught exception!" )
823 main.cleanup()
824 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700825
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700826 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700827 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700828 Description:
829 Gets all intents ID using intents function
830 Returns:
831 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700832 """
833 try:
834 intentIdList = []
835 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700836 for intent in intentsJson:
837 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700838 if not intentIdList:
839 main.log.debug( "Cannot find any intents" )
840 return main.FALSE
841 else:
842 return intentIdList
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()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700850
851 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
852 ip="DEFAULT", port="DEFAULT", delay=5 ):
853 """
854 Description:
855 Remove all the intents
856 Returns:
857 Returns main.TRUE if all intents are removed, otherwise returns
858 main.FALSE; Returns None for exception
859 """
860 try:
861 results = []
862 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700863 # intentIdList = self.getIntentsId( ip=ip, port=port )
864 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700865
866 main.log.info( self.name + ": Removing intents " +
867 str( intentIdList ) )
868
869 if isinstance( intentIdList, types.ListType ):
870 for intent in intentIdList:
871 results.append( self.removeIntent( intentId=intent,
872 appId=appId,
873 ip=ip,
874 port=port ) )
875 # Check for remaining intents
876 # NOTE: Noticing some delay on Deleting the intents so i put
877 # this time out
878 import time
879 time.sleep( delay )
880 intentRemain = len( json.loads( self.intents() ) )
881 if all( result==main.TRUE for result in results ) and \
882 intentRemain == 0:
883 main.log.info( self.name + ": All intents are removed " )
884 return main.TRUE
885 else:
886 main.log.error( self.name + ": Did not removed all intents,"
887 + " there are " + str( intentRemain )
888 + " intents remaining" )
889 return main.FALSE
890 else:
891 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700892 except ( AttributeError, TypeError ):
893 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700894 return None
Jon Halle401b092015-09-23 13:34:24 -0700895 except Exception:
896 main.log.exception( self.name + ": Uncaught exception!" )
897 main.cleanup()
898 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700899
900 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
901 """
902 Description:
903 Get a list of dictionary of all discovered hosts
904 Returns:
905 Returns a list of dictionary of information of the hosts currently
906 discovered by ONOS; Returns main.FALSE if error on requests;
907 Returns None for exception
908 """
909 try:
910 output = None
911 if ip == "DEFAULT":
912 main.log.warn( "No ip given, reverting to ip from topo file" )
913 ip = self.ip_address
914 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700915 main.log.warn( "No port given, reverting to port " +
916 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700917 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700918 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700919 if response:
920 if 200 <= response[ 0 ] <= 299:
921 output = response[ 1 ]
922 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700923 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700924 b = json.dumps( a )
925 return b
926 else:
927 main.log.error( "Error with REST request, response was: " +
928 str( response ) )
929 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700930 except ( AttributeError, AssertionError, TypeError ):
931 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700932 return None
Jon Halle401b092015-09-23 13:34:24 -0700933 except Exception:
934 main.log.exception( self.name + ": Uncaught exception!" )
935 main.cleanup()
936 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700937
938 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
939 """
940 Description:
941 Gets the information from the given host
942 Required:
943 str mac - MAC address of the host
944 Optional:
945 str vlan - VLAN tag of the host, defaults to -1
946 Returns:
947 Return the host id from the hosts/mac/vlan output in REST api
948 whose 'id' contains mac/vlan; Returns None for exception;
949 Returns main.FALSE if error on requests
950
951 NOTE:
952 Not sure what this function should do, any suggestion?
953 """
954 try:
955 output = None
956 if ip == "DEFAULT":
957 main.log.warn( "No ip given, reverting to ip from topo file" )
958 ip = self.ip_address
959 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700960 main.log.warn( "No port given, reverting to port " +
961 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700962 port = self.port
963 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700964 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700965 if response:
966 # NOTE: What if the person wants other values? would it be better
967 # to have a function that gets a key and return a value instead?
968 # This function requires mac and vlan and returns an ID which
969 # makes this current function useless
970 if 200 <= response[ 0 ] <= 299:
971 output = response[ 1 ]
972 hostId = json.loads( output ).get( 'id' )
973 return hostId
974 else:
975 main.log.error( "Error with REST request, response was: " +
976 str( response ) )
977 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700978 except ( AttributeError, TypeError ):
979 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700980 return None
Jon Halle401b092015-09-23 13:34:24 -0700981 except Exception:
982 main.log.exception( self.name + ": Uncaught exception!" )
983 main.cleanup()
984 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700985
986 def topology( self, ip="DEFAULT", port="DEFAULT" ):
987 """
988 Description:
989 Gets the overview of network topology
990 Returns:
991 Returns a dictionary containing information about network topology;
992 Returns None for exception
993 """
994 try:
995 output = None
996 if ip == "DEFAULT":
997 main.log.warn( "No ip given, reverting to ip from topo file" )
998 ip = self.ip_address
999 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001000 main.log.warn( "No port given, reverting to port " +
1001 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001002 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001003 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001004 if response:
1005 if 200 <= response[ 0 ] <= 299:
1006 output = response[ 1 ]
1007 a = json.loads( output )
1008 b = json.dumps( a )
1009 return b
1010 else:
1011 main.log.error( "Error with REST request, response was: " +
1012 str( response ) )
1013 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001014 except ( AttributeError, TypeError ):
1015 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001016 return None
Jon Halle401b092015-09-23 13:34:24 -07001017 except Exception:
1018 main.log.exception( self.name + ": Uncaught exception!" )
1019 main.cleanup()
1020 main.exit()
1021
1022 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1023 """
1024 Description:
1025 Get the devices discovered by ONOS is json string format
1026 Returns:
1027 a json string of the devices currently discovered by ONOS OR
1028 main.FALSE if there is an error in the request OR
1029 Returns None for exception
1030 """
1031 try:
1032 output = None
1033 if ip == "DEFAULT":
1034 main.log.warn( "No ip given, reverting to ip from topo file" )
1035 ip = self.ip_address
1036 if port == "DEFAULT":
1037 main.log.warn( "No port given, reverting to port " +
1038 "from topo file" )
1039 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001040 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001041 if response:
1042 if 200 <= response[ 0 ] <= 299:
1043 output = response[ 1 ]
1044 a = json.loads( output ).get( 'devices' )
1045 assert a is not None, "Error parsing json object"
1046 b = json.dumps( a )
1047 return b
1048 else:
1049 main.log.error( "Error with REST request, response was: " +
1050 str( response ) )
1051 return main.FALSE
1052 except ( AttributeError, AssertionError, TypeError ):
1053 main.log.exception( self.name + ": Object not as expected" )
1054 return None
1055 except Exception:
1056 main.log.exception( self.name + ": Uncaught exception!" )
1057 main.cleanup()
1058 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001059
1060 def getIntentState( self, intentsId, intentsJson=None,
1061 ip="DEFAULT", port="DEFAULT" ):
1062 """
1063 Description:
1064 Get intent state.
1065 Accepts a single intent ID (string type) or a list of intent IDs.
1066 Returns the state(string type) of the id if a single intent ID is
1067 accepted.
1068 Required:
1069 intentId: intent ID (string type)
1070 intentsJson: parsed json object from the onos:intents api
1071 Returns:
1072 Returns a dictionary with intent IDs as the key and its
1073 corresponding states as the values; Returns None for invalid IDs or
1074 Type error and any exceptions
1075 NOTE:
1076 An intent's state consist of INSTALLED,WITHDRAWN etc.
1077 """
1078 try:
1079 state = "State is Undefined"
1080 if not intentsJson:
1081 intentsJsonTemp = json.loads( self.intents() )
1082 else:
1083 intentsJsonTemp = json.loads( intentsJson )
1084 if isinstance( intentsId, types.StringType ):
1085 for intent in intentsJsonTemp:
1086 if intentsId == intent[ 'id' ]:
1087 state = intent[ 'state' ]
1088 return state
1089 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1090 " on the list" )
1091 return state
1092 elif isinstance( intentsId, types.ListType ):
1093 dictList = []
1094 for i in xrange( len( intentsId ) ):
1095 stateDict = {}
1096 for intents in intentsJsonTemp:
1097 if intentsId[ i ] == intents[ 'id' ]:
1098 stateDict[ 'state' ] = intents[ 'state' ]
1099 stateDict[ 'id' ] = intentsId[ i ]
1100 dictList.append( stateDict )
1101 break
1102 if len( intentsId ) != len( dictList ):
1103 main.log.info( "Cannot find some of the intent ID state" )
1104 return dictList
1105 else:
1106 main.log.info( "Invalid intents ID entry" )
1107 return None
1108
Jon Halle401b092015-09-23 13:34:24 -07001109 except ( AttributeError, TypeError ):
1110 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001111 return None
Jon Halle401b092015-09-23 13:34:24 -07001112 except Exception:
1113 main.log.exception( self.name + ": Uncaught exception!" )
1114 main.cleanup()
1115 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001116
1117 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
1118 ip="DEFAULT", port="DEFAULT"):
1119 """
1120 Description:
1121 Check intents state based on expected state which defaults to
1122 INSTALLED state
1123 Required:
1124 intentsId - List of intents ID to be checked
1125 Optional:
1126 expectedState - Check the expected state(s) of each intents
1127 state in the list.
1128 *NOTE: You can pass in a list of expected state,
1129 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1130 Return:
1131 Returns main.TRUE only if all intent are the same as expected states
1132 , otherwise, returns main.FALSE; Returns None for general exception
1133 """
1134 try:
1135 # Generating a dictionary: intent id as a key and state as value
1136 returnValue = main.TRUE
1137 if intentsId == "ALL":
1138 intentsId = self.getIntentsId( ip=ip, port=port )
1139 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1140
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001141 if len( intentsId ) != len( intentsDict ):
1142 main.log.error( self.name + ": There is something wrong " +
1143 "getting intents state" )
1144 return main.FALSE
1145
1146 if isinstance( expectedState, types.StringType ):
1147 for intents in intentsDict:
1148 if intents.get( 'state' ) != expectedState:
1149 main.log.debug( self.name + " : Intent ID - " +
1150 intents.get( 'id' ) +
1151 " actual state = " +
1152 intents.get( 'state' )
1153 + " does not equal expected state = "
1154 + expectedState )
1155 returnValue = main.FALSE
1156
1157 elif isinstance( expectedState, types.ListType ):
1158 for intents in intentsDict:
1159 if not any( state == intents.get( 'state' ) for state in
1160 expectedState ):
1161 main.log.debug( self.name + " : Intent ID - " +
1162 intents.get( 'id' ) +
1163 " actual state = " +
1164 intents.get( 'state' ) +
1165 " does not equal expected states = "
1166 + str( expectedState ) )
1167 returnValue = main.FALSE
1168
1169 if returnValue == main.TRUE:
1170 main.log.info( self.name + ": All " +
1171 str( len( intentsDict ) ) +
1172 " intents are in " + str( expectedState ) +
1173 " state" )
1174 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001175 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001176 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001177 return None
Jon Halle401b092015-09-23 13:34:24 -07001178 except Exception:
1179 main.log.exception( self.name + ": Uncaught exception!" )
1180 main.cleanup()
1181 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001182
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001183 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001184 """
1185 Description:
1186 Get flows currently added to the system
1187 NOTE:
1188 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001189 the REST output
1190
1191 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001192 """
1193 try:
1194 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001195 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001196 if ip == "DEFAULT":
1197 main.log.warn( "No ip given, reverting to ip from topo file" )
1198 ip = self.ip_address
1199 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001200 main.log.warn( "No port given, reverting to port " +
1201 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001202 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001203 if subjectKey and not subjectClass:
1204 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1205 if subjectClass:
1206 url += "/" + subjectClass
1207 if subjectKey:
1208 url += "/" + subjectKey
1209 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001210 if response:
1211 if 200 <= response[ 0 ] <= 299:
1212 output = response[ 1 ]
1213 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001214 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001215 b = json.dumps( a )
1216 return b
1217 else:
1218 main.log.error( "Error with REST request, response was: " +
1219 str( response ) )
1220 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001221 except ( AttributeError, AssertionError, TypeError ):
1222 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001223 return None
Jon Halle401b092015-09-23 13:34:24 -07001224 except Exception:
1225 main.log.exception( self.name + ": Uncaught exception!" )
1226 main.cleanup()
1227 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001228
Jon Halle401b092015-09-23 13:34:24 -07001229 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001230 """
1231 Description:
1232 Gets all the flows of the device or get a specific flow in the
1233 device by giving its flow ID
1234 Required:
Jon Halle401b092015-09-23 13:34:24 -07001235 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001236 Optional:
1237 int/hex flowId - ID of the flow
1238 """
1239 try:
1240 output = None
1241 if ip == "DEFAULT":
1242 main.log.warn( "No ip given, reverting to ip from topo file" )
1243 ip = self.ip_address
1244 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001245 main.log.warn( "No port given, reverting to port " +
1246 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001247 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001248 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001249 if flowId:
1250 url += "/" + str( int( flowId ) )
1251 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001252 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001253 if response:
1254 if 200 <= response[ 0 ] <= 299:
1255 output = response[ 1 ]
1256 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001257 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001258 b = json.dumps( a )
1259 return b
1260 else:
1261 main.log.error( "Error with REST request, response was: " +
1262 str( response ) )
1263 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001264 except ( AttributeError, AssertionError, TypeError ):
1265 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001266 return None
Jon Halle401b092015-09-23 13:34:24 -07001267 except Exception:
1268 main.log.exception( self.name + ": Uncaught exception!" )
1269 main.cleanup()
1270 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001271
GlennRC073e8bc2015-10-27 17:11:28 -07001272 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001273 """
1274 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001275 Sends a single flow to the specified device. This function exists
1276 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001277 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001278 * The flow in json
1279 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001280 Returns:
1281 Returns main.TRUE for successful requests; Returns main.FALSE
1282 if error on requests;
1283 Returns None for exceptions
1284 NOTE:
1285 The ip and port option are for the requests input's ip and port
1286 of the ONOS node
1287 """
GlennRC073e8bc2015-10-27 17:11:28 -07001288
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001289 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001290 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001291 output = None
1292 if ip == "DEFAULT":
1293 main.log.warn( "No ip given, reverting to ip from topo file" )
1294 ip = self.ip_address
1295 if port == "DEFAULT":
1296 main.log.warn( "No port given, reverting to port " +
1297 "from topo file" )
1298 port = self.port
1299 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001300 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001301 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001302 data=json.dumps( flowJson ) )
1303 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001304 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001305 main.log.info( self.name + ": Successfully POST flow" +
1306 "in device: " + str( deviceId ) )
1307 return main.TRUE
1308 else:
1309 main.log.error( "Error with REST request, response was: " +
1310 str( response ) )
1311 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001312 except NotImplementedError as e:
1313 raise e # Inform the caller
1314 except ( AttributeError, TypeError ):
1315 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001316 return None
Jon Halle401b092015-09-23 13:34:24 -07001317 except Exception:
1318 main.log.exception( self.name + ": Uncaught exception!" )
1319 main.cleanup()
1320 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001321
GlennRC073e8bc2015-10-27 17:11:28 -07001322 def addFlow( self,
1323 deviceId,
1324 appId=0,
1325 ingressPort="",
1326 egressPort="",
1327 ethType="",
1328 ethSrc="",
1329 ethDst="",
1330 vlan="",
1331 ipProto="",
1332 ipSrc=(),
1333 ipDst=(),
1334 tcpSrc="",
1335 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001336 udpDst="",
1337 udpSrc="",
1338 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001339 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301340 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001341 ip="DEFAULT",
1342 port="DEFAULT",
1343 debug=False ):
1344 """
1345 Description:
1346 Creates a single flow in the specified device
1347 Required:
1348 * deviceId: id of the device
1349 Optional:
1350 * ingressPort: port ingress device
1351 * egressPort: port of egress device
1352 * ethType: specify ethType
1353 * ethSrc: specify ethSrc ( i.e. src mac addr )
1354 * ethDst: specify ethDst ( i.e. dst mac addr )
1355 * ipProto: specify ip protocol
1356 * ipSrc: specify ip source address with mask eg. ip#/24
1357 as a tuple (type, ip#)
1358 * ipDst: specify ip destination address eg. ip#/24
1359 as a tuple (type, ip#)
1360 * tcpSrc: specify tcp source port
1361 * tcpDst: specify tcp destination port
1362 Returns:
1363 Returns main.TRUE for successful requests; Returns main.FALSE
1364 if error on requests;
1365 Returns None for exceptions
1366 NOTE:
1367 The ip and port option are for the requests input's ip and port
1368 of the ONOS node
1369 """
1370 try:
alisone14d7b02016-07-06 10:31:51 -07001371 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001372 "isPermanent":"true",
1373 "timeout":0,
1374 "deviceId":deviceId,
1375 "treatment":{"instructions":[]},
1376 "selector": {"criteria":[]}}
1377 if appId:
1378 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301379
1380 if groupId:
1381 flowJson[ 'treatment' ][ 'instructions' ].append( {
1382 "type":"GROUP",
1383 "groupId":groupId } )
1384
GlennRC073e8bc2015-10-27 17:11:28 -07001385 if egressPort:
1386 flowJson[ 'treatment' ][ 'instructions' ].append( {
1387 "type":"OUTPUT",
1388 "port":egressPort } )
1389 if ingressPort:
1390 flowJson[ 'selector' ][ 'criteria' ].append( {
1391 "type":"IN_PORT",
1392 "port":ingressPort } )
1393 if ethType:
1394 flowJson[ 'selector' ][ 'criteria' ].append( {
1395 "type":"ETH_TYPE",
1396 "ethType":ethType } )
1397 if ethSrc:
1398 flowJson[ 'selector' ][ 'criteria' ].append( {
1399 "type":"ETH_SRC",
1400 "mac":ethSrc } )
1401 if ethDst:
1402 flowJson[ 'selector' ][ 'criteria' ].append( {
1403 "type":"ETH_DST",
1404 "mac":ethDst } )
1405 if vlan:
1406 flowJson[ 'selector' ][ 'criteria' ].append( {
1407 "type":"VLAN_VID",
1408 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001409 if mpls:
1410 flowJson[ 'selector' ][ 'criteria' ].append( {
1411 "type":"MPLS_LABEL",
1412 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001413 if ipSrc:
1414 flowJson[ 'selector' ][ 'criteria' ].append( {
1415 "type":ipSrc[0],
1416 "ip":ipSrc[1] } )
1417 if ipDst:
1418 flowJson[ 'selector' ][ 'criteria' ].append( {
1419 "type":ipDst[0],
1420 "ip":ipDst[1] } )
1421 if tcpSrc:
1422 flowJson[ 'selector' ][ 'criteria' ].append( {
1423 "type":"TCP_SRC",
1424 "tcpPort": tcpSrc } )
1425 if tcpDst:
1426 flowJson[ 'selector' ][ 'criteria' ].append( {
1427 "type":"TCP_DST",
1428 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001429 if udpSrc:
1430 flowJson[ 'selector' ][ 'criteria' ].append( {
1431 "type":"UDP_SRC",
1432 "udpPort": udpSrc } )
1433 if udpDst:
1434 flowJson[ 'selector' ][ 'criteria' ].append( {
1435 "type":"UDP_DST",
1436 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001437 if ipProto:
1438 flowJson[ 'selector' ][ 'criteria' ].append( {
1439 "type":"IP_PROTO",
1440 "protocol": ipProto } )
1441
1442 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1443
1444 except ( AttributeError, TypeError ):
1445 main.log.exception( self.name + ": Object not as expected" )
1446 return None
1447 except Exception:
1448 main.log.exception( self.name + ": Uncaught exception!" )
1449 main.cleanup()
1450 main.exit()
1451
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001452 def removeFlow( self, deviceId, flowId,
1453 ip="DEFAULT", port="DEFAULT" ):
1454 """
1455 Description:
1456 Remove specific device flow
1457 Required:
1458 str deviceId - id of the device
1459 str flowId - id of the flow
1460 Return:
1461 Returns main.TRUE if successfully deletes flows, otherwise
1462 Returns main.FALSE, Returns None on error
1463 """
1464 try:
1465 output = None
1466 if ip == "DEFAULT":
1467 main.log.warn( "No ip given, reverting to ip from topo file" )
1468 ip = self.ip_address
1469 if port == "DEFAULT":
1470 main.log.warn( "No port given, reverting to port " +
1471 "from topo file" )
1472 port = self.port
1473 # NOTE: REST url requires the intent id to be in decimal form
1474 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001475 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001476 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001477 if response:
1478 if 200 <= response[ 0 ] <= 299:
1479 return main.TRUE
1480 else:
1481 main.log.error( "Error with REST request, response was: " +
1482 str( response ) )
1483 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001484 except ( AttributeError, TypeError ):
1485 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001486 return None
Jon Halle401b092015-09-23 13:34:24 -07001487 except Exception:
1488 main.log.exception( self.name + ": Uncaught exception!" )
1489 main.cleanup()
1490 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001491
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001492 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1493 """
1494 Description:
1495 Check if all the current flows are in ADDED state
1496 Return:
1497 returnValue - Returns main.TRUE only if all flows are in
1498 return main.FALSE otherwise;
1499 Returns None for exception
1500 """
1501 try:
1502 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1503 returnValue = main.TRUE
1504 for flow in tempFlows:
1505 if flow.get( 'state' ) != 'ADDED':
1506 main.log.info( self.name + ": flow Id: " +
1507 str( flow.get( 'groupId' ) ) +
1508 " | state:" +
1509 str( flow.get( 'state' ) ) )
1510 returnValue = main.FALSE
1511 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001512 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001513 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001514 return None
1515 except Exception:
1516 main.log.exception( self.name + ": Uncaught exception!" )
1517 main.cleanup()
1518 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001519
1520 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1521 subjectClass=None, subjectKey=None, configKey=None ):
1522 """
1523 Description:
1524 Get a json object with the ONOS network configurations
1525 Returns:
1526 A json object containing the network configuration in
1527 ONOS; Returns main.FALSE if error on requests;
1528 Returns None for exception
1529 """
1530 try:
1531 output = None
1532 if ip == "DEFAULT":
1533 main.log.warn( "No ip given, reverting to ip from topo file" )
1534 ip = self.ip_address
1535 if port == "DEFAULT":
1536 main.log.warn( "No port given, reverting to port " +
1537 "from topo file" )
1538 port = self.port
1539 url = "/network/configuration"
1540 if subjectClass:
1541 url += "/" + subjectClass
1542 if subjectKey:
1543 url += "/" + subjectKey
1544 if configKey:
1545 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001546 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001547 if response:
1548 if 200 <= response[ 0 ] <= 299:
1549 output = response[ 1 ]
1550 a = json.loads( output )
1551 b = json.dumps( a )
1552 return b
1553 elif response[ 0 ] == 404:
1554 main.log.error( "Requested configuration doesn't exist: " +
1555 str( response ) )
1556 return {}
1557 else:
1558 main.log.error( "Error with REST request, response was: " +
1559 str( response ) )
1560 return main.FALSE
1561 except ( AttributeError, TypeError ):
1562 main.log.exception( self.name + ": Object not as expected" )
1563 return None
1564 except Exception:
1565 main.log.exception( self.name + ": Uncaught exception!" )
1566 main.cleanup()
1567 main.exit()
1568
1569 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1570 subjectClass=None, subjectKey=None, configKey=None ):
1571 """
1572 Description:
1573 Set a json object with the ONOS network configurations
1574 Returns:
1575 Returns main.TRUE for successful requests; Returns main.FALSE
1576 if error on requests;
1577 Returns None for exceptions
1578
1579 """
1580 try:
1581 output = None
1582 if ip == "DEFAULT":
1583 main.log.warn( "No ip given, reverting to ip from topo file" )
1584 ip = self.ip_address
1585 if port == "DEFAULT":
1586 main.log.warn( "No port given, reverting to port " +
1587 "from topo file" )
1588 port = self.port
1589 url = "/network/configuration"
1590 if subjectClass:
1591 url += "/" + subjectClass
1592 if subjectKey:
1593 url += "/" + subjectKey
1594 if configKey:
1595 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001596 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001597 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001598 data=json.dumps( cfgJson ) )
1599 if response:
1600 if 200 <= response[ 0 ] <= 299:
1601 main.log.info( self.name + ": Successfully POST cfg" )
1602 return main.TRUE
1603 else:
1604 main.log.error( "Error with REST request, response was: " +
1605 str( response ) )
1606 return main.FALSE
1607 except ( AttributeError, TypeError ):
1608 main.log.exception( self.name + ": Object not as expected" )
1609 return None
1610 except Exception:
1611 main.log.exception( self.name + ": Uncaught exception!" )
1612 main.cleanup()
1613 main.exit()
1614
1615 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1616 subjectClass=None, subjectKey=None, configKey=None ):
1617 """
1618 Description:
1619 Remove a json object from the ONOS network configurations
1620 Returns:
1621 Returns main.TRUE for successful requests; Returns main.FALSE
1622 if error on requests;
1623 Returns None for exceptions
1624
1625 """
1626 try:
1627 output = None
1628 if ip == "DEFAULT":
1629 main.log.warn( "No ip given, reverting to ip from topo file" )
1630 ip = self.ip_address
1631 if port == "DEFAULT":
1632 main.log.warn( "No port given, reverting to port " +
1633 "from topo file" )
1634 port = self.port
1635 url = "/network/configuration"
1636 if subjectClass:
1637 url += "/" + subjectClass
1638 if subjectKey:
1639 url += "/" + subjectKey
1640 if configKey:
1641 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001642 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001643 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001644 if response:
1645 if 200 <= response[ 0 ] <= 299:
1646 main.log.info( self.name + ": Successfully delete cfg" )
1647 return main.TRUE
1648 else:
1649 main.log.error( "Error with REST request, response was: " +
1650 str( response ) )
1651 return main.FALSE
1652 except ( AttributeError, TypeError ):
1653 main.log.exception( self.name + ": Object not as expected" )
1654 return None
1655 except Exception:
1656 main.log.exception( self.name + ": Uncaught exception!" )
1657 main.cleanup()
1658 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001659
1660 def createFlowBatch( self,
1661 numSw = 1,
1662 swIndex = 1,
1663 batchSize = 1,
1664 batchIndex = 1,
1665 deviceIdpreFix = "of:",
1666 appId=0,
1667 deviceID="",
1668 ingressPort="",
1669 egressPort="",
1670 ethType="",
1671 ethSrc="",
1672 ethDst="",
1673 vlan="",
1674 ipProto="",
1675 ipSrc=(),
1676 ipDst=(),
1677 tcpSrc="",
1678 tcpDst="",
1679 udpDst="",
1680 udpSrc="",
1681 mpls="",
1682 ip="DEFAULT",
1683 port="DEFAULT",
1684 debug=False ):
1685 """
1686 Description:
1687 Creates batches of MAC-rule flows for POST.
1688 Predefined MAC: 2 MS Hex digit for iterating devices
1689 Next 5 Hex digit for iterating batch numbers
1690 Next 5 Hex digit for iterating flows within a batch
1691 Required:
1692 * deviceId: id of the device
1693 Optional:
1694 * ingressPort: port ingress device
1695 * egressPort: port of egress device
1696 * ethType: specify ethType
1697 * ethSrc: specify ethSrc ( i.e. src mac addr )
1698 * ethDst: specify ethDst ( i.e. dst mac addr )
1699 * ipProto: specify ip protocol
1700 * ipSrc: specify ip source address with mask eg. ip#/24
1701 as a tuple (type, ip#)
1702 * ipDst: specify ip destination address eg. ip#/24
1703 as a tuple (type, ip#)
1704 * tcpSrc: specify tcp source port
1705 * tcpDst: specify tcp destination port
1706 Returns:
1707 Returns main.TRUE for successful requests; Returns main.FALSE
1708 if error on requests;
1709 Returns None for exceptions
1710 NOTE:
1711 The ip and port option are for the requests input's ip and port
1712 of the ONOS node
1713 """
1714 #from pprint import pprint
1715
1716 flowJsonList = []
1717 flowJsonBatch = {"flows":flowJsonList}
1718 dev = swIndex
1719
1720 for fl in range(1, batchSize + 1):
1721 flowJson = { "priority":100,
1722 "deviceId":"",
1723 "isPermanent":"true",
1724 "timeout":0,
1725 "treatment":{"instructions":[]},
1726 "selector": {"criteria":[]}}
1727
1728 #main.log.info("fl: " + str(fl))
1729 if dev <= numSw:
1730 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1731 #print deviceId
1732 flowJson['deviceId'] = deviceId
1733 dev += 1
1734 else:
1735 dev = 1
1736 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1737 #print deviceId
1738 flowJson['deviceId'] = deviceId
1739 dev += 1
1740
1741 # ethSrc starts with "0"; ethDst starts with "1"
1742 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1743 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1744 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1745 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1746 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1747 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1748 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1749
1750 if appId:
1751 flowJson[ "appId" ] = appId
1752
1753 if egressPort:
1754 flowJson[ 'treatment' ][ 'instructions' ].append( {
1755 "type":"OUTPUT",
1756 "port":egressPort } )
1757 if ingressPort:
1758 flowJson[ 'selector' ][ 'criteria' ].append( {
1759 "type":"IN_PORT",
1760 "port":ingressPort } )
1761 if ethType:
1762 flowJson[ 'selector' ][ 'criteria' ].append( {
1763 "type":"ETH_TYPE",
1764 "ethType":ethType } )
1765 if ethSrc:
1766 flowJson[ 'selector' ][ 'criteria' ].append( {
1767 "type":"ETH_SRC",
1768 "mac":ethSrc } )
1769 if ethDst:
1770 flowJson[ 'selector' ][ 'criteria' ].append( {
1771 "type":"ETH_DST",
1772 "mac":ethDst } )
1773 if vlan:
1774 flowJson[ 'selector' ][ 'criteria' ].append( {
1775 "type":"VLAN_VID",
1776 "vlanId":vlan } )
1777 if mpls:
1778 flowJson[ 'selector' ][ 'criteria' ].append( {
1779 "type":"MPLS_LABEL",
1780 "label":mpls } )
1781 if ipSrc:
1782 flowJson[ 'selector' ][ 'criteria' ].append( {
1783 "type":ipSrc[0],
1784 "ip":ipSrc[1] } )
1785 if ipDst:
1786 flowJson[ 'selector' ][ 'criteria' ].append( {
1787 "type":ipDst[0],
1788 "ip":ipDst[1] } )
1789 if tcpSrc:
1790 flowJson[ 'selector' ][ 'criteria' ].append( {
1791 "type":"TCP_SRC",
1792 "tcpPort": tcpSrc } )
1793 if tcpDst:
1794 flowJson[ 'selector' ][ 'criteria' ].append( {
1795 "type":"TCP_DST",
1796 "tcpPort": tcpDst } )
1797 if udpSrc:
1798 flowJson[ 'selector' ][ 'criteria' ].append( {
1799 "type":"UDP_SRC",
1800 "udpPort": udpSrc } )
1801 if udpDst:
1802 flowJson[ 'selector' ][ 'criteria' ].append( {
1803 "type":"UDP_DST",
1804 "udpPort": udpDst } )
1805 if ipProto:
1806 flowJson[ 'selector' ][ 'criteria' ].append( {
1807 "type":"IP_PROTO",
1808 "protocol": ipProto } )
1809 #pprint(flowJson)
1810 flowJsonList.append(flowJson)
1811
1812 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1813 flowJsonBatch['flows'] = flowJsonList
1814 #pprint(flowJsonBatch)
1815
1816 return flowJsonBatch
1817
1818
1819 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1820 """
1821 Description:
1822 Sends a single flow batch through /flows REST API.
1823 Required:
1824 * The batch of flows
1825 Returns:
1826 Returns main.TRUE for successful requests; Returns main.FALSE
1827 if error on requests;
1828 Returns None for exceptions
1829 NOTE:
1830 The ip and port option are for the requests input's ip and port
1831 of the ONOS node
1832 """
1833 import time
1834
1835 try:
1836 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1837 output = None
1838 if ip == "DEFAULT":
1839 main.log.warn( "No ip given, reverting to ip from topo file" )
1840 ip = self.ip_address
1841 if port == "DEFAULT":
1842 main.log.warn( "No port given, reverting to port " +
1843 "from topo file" )
1844 port = self.port
1845 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001846 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001847 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001848 data=json.dumps( batch ) )
1849 #main.log.info("Post response is: ", str(response[0]))
1850 if response[0] == 200:
1851 main.log.info( self.name + ": Successfully POST flow batch" )
1852 return main.TRUE, response
1853 else:
1854 main.log.error( "Error with REST request, response was: " +
1855 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001856 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001857 except NotImplementedError as e:
1858 raise e # Inform the caller
1859 except ( AttributeError, TypeError ):
1860 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001861 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001862 except Exception:
1863 main.log.exception( self.name + ": Uncaught exception!" )
1864 main.cleanup()
1865 main.exit()
1866
1867 def removeFlowBatch( self, batch={},
1868 ip="DEFAULT", port="DEFAULT" ):
1869 """
1870 Description:
1871 Remove a batch of flows
1872 Required:
1873 flow batch
1874 Return:
1875 Returns main.TRUE if successfully deletes flows, otherwise
1876 Returns main.FALSE, Returns None on error
1877 """
1878 try:
1879 output = None
1880 if ip == "DEFAULT":
1881 main.log.warn( "No ip given, reverting to ip from topo file" )
1882 ip = self.ip_address
1883 if port == "DEFAULT":
1884 main.log.warn( "No port given, reverting to port " +
1885 "from topo file" )
1886 port = self.port
1887 # NOTE: REST url requires the intent id to be in decimal form
1888
suibin zhang116647a2016-05-06 16:30:09 -07001889 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001890 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001891 data = json.dumps(batch) )
1892 if response:
1893 if 200 <= response[ 0 ] <= 299:
1894 return main.TRUE
1895 else:
1896 main.log.error( "Error with REST request, response was: " +
1897 str( response ) )
1898 return main.FALSE
1899 except ( AttributeError, TypeError ):
1900 main.log.exception( self.name + ": Object not as expected" )
1901 return None
1902 except Exception:
1903 main.log.exception( self.name + ": Uncaught exception!" )
1904 main.cleanup()
1905 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001906
1907 def getTopology( self, topologyOutput ):
1908 """
1909 Definition:
1910 Loads a json topology output
1911 Return:
1912 topology = current ONOS topology
1913 """
1914 import json
1915 try:
1916 # either onos:topology or 'topology' will work in CLI
1917 topology = json.loads(topologyOutput)
1918 main.log.debug( topology )
1919 return topology
1920 except pexpect.EOF:
1921 main.log.error( self.name + ": EOF exception found" )
1922 main.log.error( self.name + ": " + self.handle.before )
1923 main.cleanup()
1924 main.exit()
1925 except Exception:
1926 main.log.exception( self.name + ": Uncaught exception!" )
1927 main.cleanup()
1928 main.exit()
1929
1930 def checkStatus(
1931 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001932 numoswitch,
1933 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001934 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001935 logLevel="info" ):
1936 """
1937 Checks the number of switches & links that ONOS sees against the
1938 supplied values. By default this will report to main.log, but the
1939 log level can be specific.
1940
Flavio Castro82ee2f62016-06-07 15:04:12 -07001941 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001942 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001943 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001944 logLevel = level to log to.
1945 Currently accepts 'info', 'warn' and 'report'
1946
1947 Returns: main.TRUE if the number of switches and links are correct,
1948 main.FALSE if the number of switches and links is incorrect,
1949 and main.ERROR otherwise
1950 """
1951 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001952 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001953 if topology == {}:
1954 return main.ERROR
1955 output = ""
1956 # Is the number of switches is what we expected
1957 devices = topology.get( 'devices', False )
1958 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001959 nodes = topology.get( 'nodes' , False )
1960 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001961 return main.ERROR
1962 switchCheck = ( int( devices ) == int( numoswitch ) )
1963 # Is the number of links is what we expected
1964 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001965 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1966 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001967 # We expected the correct numbers
1968 output = output + "The number of links and switches match "\
1969 + "what was expected"
1970 result = main.TRUE
1971 else:
1972 output = output + \
1973 "The number of links and switches does not match " + \
1974 "what was expected"
1975 result = main.FALSE
1976 output = output + "\n ONOS sees %i devices" % int( devices )
1977 output = output + " (%i expected) " % int( numoswitch )
1978 output = output + "and %i links " % int( links )
1979 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001980 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001981 output = output + "and %i controllers " % int( nodes )
1982 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001983 if logLevel == "report":
1984 main.log.report( output )
1985 elif logLevel == "warn":
1986 main.log.warn( output )
1987 else:
1988 main.log.info( output )
1989 return result
1990 except pexpect.EOF:
1991 main.log.error( self.name + ": EOF exception found" )
1992 main.log.error( self.name + ": " + self.handle.before )
1993 main.cleanup()
1994 main.exit()
1995 except Exception:
1996 main.log.exception( self.name + ": Uncaught exception!" )
1997 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001998 main.exit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301999
2000 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
2001 """
2002 Description:
2003 Creates a single Group for the specified device.
2004 Required:
2005 * deviceId: id of the device
2006 * type: Type of the Group
2007 * bucketList: Buckets to be added to the group
2008 * appCookie: Cookie for the Group
2009 * groupId: Id of the Group
2010 Returns:
2011 Returns main.TRUE for successful requests; Returns main.FALSE
2012 if error on requests;
2013 Returns None for exceptions
2014 Note:
2015 The ip and port option are for the requests input's ip and port
2016 of the ONOS node
2017 """
2018 try:
2019 groupJson = { "type": groupType,
2020 "appCookie": appCookie,
2021 "groupId": groupId,
2022 "buckets": bucketList
2023 }
2024 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
2025
2026 except ( AttributeError, TypeError ):
2027 main.log.exception( self.name + ": Object not as expected" )
2028 return None
2029 except Exception:
2030 main.log.exception( self.name + ": Uncaught exception!" )
2031 main.cleanup()
2032 main.exit()
2033
2034 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
2035 """
2036 Description:
2037 Sends a single group to the specified device.
2038 Required:
2039 * deviceId: id of the device
2040 * groupJson: the group in json
2041 Returns:
2042 Returns main.TRUE for successful requests; Returns main.FALSE
2043 if error on requests;
2044 Returns None for exceptions
2045 NOTE:
2046 The ip and port option are for the requests input's ip and port
2047 of the ONOS node
2048 """
2049 try:
2050 if debug: main.log.debug( "Adding group: " + self.pprint( groupJson ) )
2051 output = None
2052 if ip == "DEFAULT":
2053 main.log.warn( "No ip given, reverting to ip from topo file" )
2054 ip = self.ip_address
2055 if port == "DEFAULT":
2056 main.log.warn( "No port given, reverting to port " +
2057 "from topo file" )
2058 port = self.port
2059 url = "/groups/" + deviceId
2060 response = self.send( method="POST",
2061 url=url, ip = ip, port = port,
2062 data=json.dumps( groupJson ) )
2063 if response:
2064 if "201" in str( response[ 0 ] ):
2065 main.log.info( self.name + ": Successfully POST group " +
2066 "in device: " + str( deviceId ) )
2067 return main.TRUE
2068 else:
2069 main.log.error( "Error with REST request, response was: " +
2070 str( response ) )
2071 return main.FALSE
2072 except NotImplementedError as e:
2073 raise e # Inform the caller
2074 except ( AttributeError, TypeError ):
2075 main.log.exception( self.name + ": Object not as expected" )
2076 return None
2077 except Exception:
2078 main.log.exception( self.name + ": Uncaught exception!" )
2079 main.cleanup()
2080 main.exit()
2081
2082 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2083 """
2084 Description:
2085 Get all the groups or get a specific group by giving the
2086 deviceId and appCookie
2087 Optional:
2088 * deviceId: id of the Device
2089 * appCookie: Cookie of the Group
2090 Returns:
2091 Returns Groups for successful requests; Returns main.FALSE
2092 if error on requests;
2093 Returns None for exceptions
2094 NOTE:
2095 The ip and port option are for the requests input's ip and port
2096 of the ONOS node
2097 """
2098 try:
2099 output = None
2100 if ip == "DEFAULT":
2101 main.log.warn( "No ip given, reverting to ip from topo file" )
2102 ip = self.ip_address
2103 if port == "DEFAULT":
2104 main.log.warn( "No port given, reverting to port " +
2105 "from topo file" )
2106 port = self.port
2107 url = "/groups"
2108 if deviceId:
2109 url += "/" + deviceId
2110 if appCookie:
2111 url += "/" + appCookie
2112 response = self.send( url=url, ip = ip, port = port )
2113 if response:
2114 if 200 <= response[ 0 ] <= 299:
2115 output = response[ 1 ]
2116 groupsJson = json.loads( output ).get( 'groups' )
2117 assert groupsJson is not None, "Error parsing json object"
2118 groups = json.dumps( groupsJson )
2119 return groups
2120 else:
2121 main.log.error( "Error with REST request, response was: " +
2122 str( response ) )
2123 return main.FALSE
2124 except ( AttributeError, AssertionError, TypeError ):
2125 main.log.exception( self.name + ": Object not as expected" )
2126 return None
2127 except Exception:
2128 main.log.exception( self.name + ": Uncaught exception!" )
2129 main.cleanup()
2130 main.exit()
2131
2132 def removeGroup( self, deviceId, appCookie,
2133 ip="DEFAULT", port="DEFAULT" ):
2134 """
2135 Description:
2136 Removes specific device group
2137 Required:
2138 * deviceId: id of the Device
2139 * appCookie: Cookie of the Group
2140 Returns:
2141 Returns main.TRUE for successful requests; Returns main.FALSE
2142 if error on requests;
2143 Returns None for exceptions
2144 NOTE:
2145 The ip and port option are for the requests input's ip and port
2146 of the ONOS node
2147
2148 """
2149 try:
2150 output = None
2151 if ip == "DEFAULT":
2152 main.log.warn( "No ip given, reverting to ip from topo file" )
2153 ip = self.ip_address
2154 if port == "DEFAULT":
2155 main.log.warn( "No port given, reverting to port " +
2156 "from topo file" )
2157 port = self.port
2158 query = "/" + str( deviceId ) + "/" + str( appCookie )
2159 response = self.send( method="DELETE",
2160 url="/groups" + query, ip = ip, port = port )
2161 if response:
2162 if 200 <= response[ 0 ] <= 299:
2163 return main.TRUE
2164 else:
2165 main.log.error( "Error with REST request, response was: " +
2166 str( response ) )
2167 return main.FALSE
2168 except ( AttributeError, TypeError ):
2169 main.log.exception( self.name + ": Object not as expected" )
2170 return None
2171 except Exception:
2172 main.log.exception( self.name + ": Uncaught exception!" )
2173 main.cleanup()
2174 main.exit()
2175