blob: fbb9472f683fd9908f96427ec3dc35cca460ea60 [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="",
477 lambdaAlloc=False,
478 ipProto="",
479 ipSrc="",
480 ipDst="",
481 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700482 tcpDst="",
483 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700484 port="DEFAULT",
485 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700486 """
487 Description:
488 Adds a point-to-point intent ( uni-directional ) by
489 specifying device id's and optional fields
490 Required:
491 * ingressDevice: device id of ingress device
492 * egressDevice: device id of egress device
493 Optional:
494 * ethType: specify ethType
495 * ethSrc: specify ethSrc ( i.e. src mac addr )
496 * ethDst: specify ethDst ( i.e. dst mac addr )
497 * bandwidth: specify bandwidth capacity of link (TODO)
498 * lambdaAlloc: if True, intent will allocate lambda
499 for the specified intent (TODO)
500 * ipProto: specify ip protocol
501 * ipSrc: specify ip source address with mask eg. ip#/24
502 * ipDst: specify ip destination address eg. ip#/24
503 * tcpSrc: specify tcp source port
504 * tcpDst: specify tcp destination port
505 Returns:
506 Returns main.TRUE for successful requests; Returns main.FALSE if
507 no ingress|egress port found and if error on requests;
508 Returns None for exceptions
509 NOTE:
510 The ip and port option are for the requests input's ip and port
511 of the ONOS node
512 """
513 try:
514 if "/" in ingressDevice:
515 if not ingressPort:
516 ingressPort = ingressDevice.split( "/" )[ 1 ]
517 ingressDevice = ingressDevice.split( "/" )[ 0 ]
518 else:
519 if not ingressPort:
520 main.log.debug( self.name + ": Ingress port not specified" )
521 return main.FALSE
522
523 if "/" in egressDevice:
524 if not egressPort:
525 egressPort = egressDevice.split( "/" )[ 1 ]
526 egressDevice = egressDevice.split( "/" )[ 0 ]
527 else:
528 if not egressPort:
529 main.log.debug( self.name + ": Egress port not specified" )
530 return main.FALSE
531
532 intentJson ={ "ingressPoint": { "device": ingressDevice,
533 "port": ingressPort },
534 "selector": { "criteria": [] },
535 "priority": 55,
536 "treatment": { "deferred": [],
537 "instructions": [] },
538 "egressPoint": { "device": egressDevice,
539 "port": egressPort },
540 "appId": appId,
541 "type": "PointToPointIntent",
542 "constraints": [ { "type": "LinkTypeConstraint",
543 "types": [ "OPTICAL" ],
544 "inclusive": "false" } ] }
545
546 if ethType == "IPV4":
547 intentJson[ 'selector' ][ 'criteria' ].append( {
548 "type":"ETH_TYPE",
549 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700550 elif ethType:
551 intentJson[ 'selector' ][ 'criteria' ].append( {
552 "type":"ETH_TYPE",
553 "ethType":ethType } )
554
kelvin-onlabb50074f2015-07-27 16:18:32 -0700555 if ethSrc:
556 intentJson[ 'selector' ][ 'criteria' ].append(
557 { "type":"ETH_SRC",
558 "mac":ethSrc } )
559 if ethDst:
560 intentJson[ 'selector' ][ 'criteria' ].append(
561 { "type":"ETH_DST",
562 "mac":ethDst } )
563 if ipSrc:
564 intentJson[ 'selector' ][ 'criteria' ].append(
565 { "type":"IPV4_SRC",
566 "ip":ipSrc } )
567 if ipDst:
568 intentJson[ 'selector' ][ 'criteria' ].append(
569 { "type":"IPV4_DST",
570 "ip":ipDst } )
571 if tcpSrc:
572 intentJson[ 'selector' ][ 'criteria' ].append(
573 { "type":"TCP_SRC",
574 "tcpPort": tcpSrc } )
575 if tcpDst:
576 intentJson[ 'selector' ][ 'criteria' ].append(
577 { "type":"TCP_DST",
578 "tcpPort": tcpDst } )
579 if ipProto:
580 intentJson[ 'selector' ][ 'criteria' ].append(
581 { "type":"IP_PROTO",
582 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700583 if vlanId:
584 intentJson[ 'selector' ][ 'criteria' ].append(
585 { "type":"VLAN_VID",
586 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700587
588 # TODO: Bandwidth and Lambda will be implemented if needed
589
kelvin-onlabb50074f2015-07-27 16:18:32 -0700590 output = None
591 if ip == "DEFAULT":
592 main.log.warn( "No ip given, reverting to ip from topo file" )
593 ip = self.ip_address
594 if port == "DEFAULT":
595 main.log.warn( "No port given, reverting to port " +
596 "from topo file" )
597 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700598 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700599 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700600 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700601
602 main.log.debug( intentJson )
603
kelvin-onlabb50074f2015-07-27 16:18:32 -0700604 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700605 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700606 main.log.info( self.name + ": Successfully POST point" +
607 " intent between ingress: " + ingressDevice +
608 " and egress: " + egressDevice + " devices" )
609 return main.TRUE
610 else:
611 main.log.error( "Error with REST request, response was: " +
612 str( response ) )
613 return main.FALSE
614
Jon Halle401b092015-09-23 13:34:24 -0700615 except ( AttributeError, TypeError ):
616 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700617 return None
Jon Halle401b092015-09-23 13:34:24 -0700618 except Exception:
619 main.log.exception( self.name + ": Uncaught exception!" )
620 main.cleanup()
621 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700622
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700623 def addSinglepointToMultipointIntent(self,
624 ingressDevice,
625 egressDeviceList,
626 portEgressList,
627 appId='org.onosproject.cli',
628 portIngress="",
629 ethType="",
630 ethSrc="",
631 ethDst="",
632 bandwidth="",
633 lambdaAlloc=False,
634 ipProto="",
635 ipSrc="",
636 ipDst="",
637 tcpSrc="",
638 tcpDst="",
639 partial=False,
640 ip="DEFAULT",
641 port="DEFAULT",
642 vlanId="" ):
643 """
644 Description:
645 Adds a point-to-multi point intent ( uni-directional ) by
646 specifying device id's and optional fields
647 Required:
648 * ingressDevice: device id of ingress device
649 * egressDevice: device id of egress device
650 * portEgressList: a list of port id of egress device
651
652 Optional:
653 * portIngress: port id of ingress device
654 * ethType: specify ethType
655 * ethSrc: specify ethSrc ( i.e. src mac addr )
656 * ethDst: specify ethDst ( i.e. dst mac addr )
657 * bandwidth: specify bandwidth capacity of link (TODO)
658 * lambdaAlloc: if True, intent will allocate lambda
659 for the specified intent (TODO)
660 * ipProto: specify ip protocol
661 * ipSrc: specify ip source address with mask eg. ip#/24
662 * ipDst: specify ip destination address eg. ip#/24
663 * tcpSrc: specify tcp source port
664 * tcpDst: specify tcp destination port
665 Returns:
666 Returns main.TRUE for successful requests; Returns main.FALSE if
667 no ingress|egress port found and if error on requests;
668 Returns None for exceptions
669 NOTE:
670 The ip and port option are for the requests input's ip and port
671 of the ONOS node
672 """
673 try:
674
675 if "/" in ingressDevice:
676 if not portIngress:
677 ingressPort = ingressDevice.split( "/" )[ 1 ]
678 ingressDevice = ingressDevice.split( "/" )[ 0 ]
679 else:
680 if not portIngress:
681 main.log.debug( self.name + ": Ingress port not specified" )
682 return main.FALSE
683 index = 0
684 for egressDevice in egressDeviceList:
685 if "/" in egressDevice:
686 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
687 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
688 else:
689 if not portEgressList:
690 main.log.debug( self.name + ": Egress port not specified" )
691 return main.FALSE
692 index = index + 1
693
694 intentJson = { "ingressPoint": { "device": ingressDevice,
695 "port": ingressPort },
696 "selector": { "criteria": [] },
697 "priority": 55,
698 "treatment": { "deferred": [],
699 "instructions": [] },
700 "egressPoint": { "connectPoints": [] },
701 "appId": appId,
702 "type": "SinglePointToMultiPointIntent",
703 "constraints": [ { "type": "LinkTypeConstraint",
704 "types": ["OPTICAL"],
705 "inclusive": "false" } ] }
706
707 index = 0
708 for ep in portEgressList:
709 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
710 { "device": egressDeviceList[ index ],
711 "port": ep } )
712 index += 1
713
714 if ethType == "IPV4":
715 intentJson[ 'selector' ][ 'criteria' ].append(
716 { "type": "ETH_TYPE",
717 "ethType": 2048 } )
718 elif ethType:
719 intentJson[ 'selector' ][ 'criteria' ].append(
720 { "type": "ETH_TYPE",
721 "ethType": ethType } )
722
723 if ethSrc:
724 intentJson[ 'selector' ][ 'criteria' ].append(
725 { "type": "ETH_SRC",
726 "mac": ethSrc } )
727
728 if ethDst:
729 for dst in ethDst:
730 if dst:
731 intentJson[ 'selector' ][ 'criteria' ].append(
732 { "type": "ETH_DST",
733 "mac": dst } )
734 if tcpSrc:
735 intentJson[ 'selector' ][ 'criteria' ].append(
736 { "type": "TCP_SRC",
737 "tcpPort": tcpSrc } )
738 if tcpDst:
739 intentJson[ 'selector' ][ 'criteria' ].append(
740 { "type": "TCP_DST",
741 "tcpPort": tcpDst } )
742 if ipProto:
743 intentJson[ 'selector' ][ 'criteria' ].append(
744 { "type": "IP_PROTO",
745 "protocol": ipProto } )
746 if vlanId:
747 intentJson[ 'selector' ][ 'criteria' ].append(
748 { "type": "VLAN_VID",
749 "vlanId": vlanId } )
750
751 # TODO: Bandwidth and Lambda will be implemented if needed
752
753 output = None
754 if ip == "DEFAULT":
755 main.log.warn( "No ip given, reverting to ip from topo file" )
756 ip = self.ip_address
757 if port == "DEFAULT":
758 main.log.warn( "No port given, reverting to port " +
759 "from topo file" )
760 port = self.port
761 response = self.send( method="POST",
762 url="/intents", ip=ip, port=port,
763 data=json.dumps( intentJson ) )
764
765 main.log.debug(intentJson)
766
767 if response:
768 if "201" in str( response[ 0 ] ):
769 main.log.info( self.name + ": Successfully POST point" +
770 " intent between ingress: " + ingressDevice +
771 " and egress: " + str(egressDeviceList) + " devices" )
772 return main.TRUE
773 else:
774 main.log.error( "Error with REST request, response was: " + str( response ) )
775 return main.FALSE
776 else:
777 main.log.error( "REST request has no response." )
778 return main.FALSE
779
780 except ( AttributeError, TypeError ):
781 main.log.exception( self.name + ": Object not as expected" )
782 return None
783 except Exception:
784 main.log.exception( self.name + ": Uncaught exception!" )
785 main.cleanup()
786 main.exit()
787
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700788 def removeIntent( self, intentId, appId='org.onosproject.cli',
789 ip="DEFAULT", port="DEFAULT" ):
790 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700791 Remove intent for specified application id and intent id;
792 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700793 """
794 try:
795 output = None
796 if ip == "DEFAULT":
797 main.log.warn( "No ip given, reverting to ip from topo file" )
798 ip = self.ip_address
799 if port == "DEFAULT":
800 main.log.warn( "No port given, reverting to port " +
801 "from topo file" )
802 port = self.port
803 # NOTE: REST url requires the intent id to be in decimal form
804 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700805 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700806 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700807 if response:
808 if 200 <= response[ 0 ] <= 299:
809 return main.TRUE
810 else:
811 main.log.error( "Error with REST request, response was: " +
812 str( response ) )
813 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700814 except ( AttributeError, TypeError ):
815 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700816 return None
Jon Halle401b092015-09-23 13:34:24 -0700817 except Exception:
818 main.log.exception( self.name + ": Uncaught exception!" )
819 main.cleanup()
820 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700821
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700822 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700823 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700824 Description:
825 Gets all intents ID using intents function
826 Returns:
827 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700828 """
829 try:
830 intentIdList = []
831 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700832 for intent in intentsJson:
833 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700834 if not intentIdList:
835 main.log.debug( "Cannot find any intents" )
836 return main.FALSE
837 else:
838 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700839 except ( AttributeError, TypeError ):
840 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700841 return None
Jon Halle401b092015-09-23 13:34:24 -0700842 except Exception:
843 main.log.exception( self.name + ": Uncaught exception!" )
844 main.cleanup()
845 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700846
847 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
848 ip="DEFAULT", port="DEFAULT", delay=5 ):
849 """
850 Description:
851 Remove all the intents
852 Returns:
853 Returns main.TRUE if all intents are removed, otherwise returns
854 main.FALSE; Returns None for exception
855 """
856 try:
857 results = []
858 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700859 # intentIdList = self.getIntentsId( ip=ip, port=port )
860 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700861
862 main.log.info( self.name + ": Removing intents " +
863 str( intentIdList ) )
864
865 if isinstance( intentIdList, types.ListType ):
866 for intent in intentIdList:
867 results.append( self.removeIntent( intentId=intent,
868 appId=appId,
869 ip=ip,
870 port=port ) )
871 # Check for remaining intents
872 # NOTE: Noticing some delay on Deleting the intents so i put
873 # this time out
874 import time
875 time.sleep( delay )
876 intentRemain = len( json.loads( self.intents() ) )
877 if all( result==main.TRUE for result in results ) and \
878 intentRemain == 0:
879 main.log.info( self.name + ": All intents are removed " )
880 return main.TRUE
881 else:
882 main.log.error( self.name + ": Did not removed all intents,"
883 + " there are " + str( intentRemain )
884 + " intents remaining" )
885 return main.FALSE
886 else:
887 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700888 except ( AttributeError, TypeError ):
889 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700890 return None
Jon Halle401b092015-09-23 13:34:24 -0700891 except Exception:
892 main.log.exception( self.name + ": Uncaught exception!" )
893 main.cleanup()
894 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700895
896 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
897 """
898 Description:
899 Get a list of dictionary of all discovered hosts
900 Returns:
901 Returns a list of dictionary of information of the hosts currently
902 discovered by ONOS; Returns main.FALSE if error on requests;
903 Returns None for exception
904 """
905 try:
906 output = None
907 if ip == "DEFAULT":
908 main.log.warn( "No ip given, reverting to ip from topo file" )
909 ip = self.ip_address
910 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700911 main.log.warn( "No port given, reverting to port " +
912 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700913 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700914 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700915 if response:
916 if 200 <= response[ 0 ] <= 299:
917 output = response[ 1 ]
918 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700919 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700920 b = json.dumps( a )
921 return b
922 else:
923 main.log.error( "Error with REST request, response was: " +
924 str( response ) )
925 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700926 except ( AttributeError, AssertionError, TypeError ):
927 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700928 return None
Jon Halle401b092015-09-23 13:34:24 -0700929 except Exception:
930 main.log.exception( self.name + ": Uncaught exception!" )
931 main.cleanup()
932 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700933
934 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
935 """
936 Description:
937 Gets the information from the given host
938 Required:
939 str mac - MAC address of the host
940 Optional:
941 str vlan - VLAN tag of the host, defaults to -1
942 Returns:
943 Return the host id from the hosts/mac/vlan output in REST api
944 whose 'id' contains mac/vlan; Returns None for exception;
945 Returns main.FALSE if error on requests
946
947 NOTE:
948 Not sure what this function should do, any suggestion?
949 """
950 try:
951 output = None
952 if ip == "DEFAULT":
953 main.log.warn( "No ip given, reverting to ip from topo file" )
954 ip = self.ip_address
955 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700956 main.log.warn( "No port given, reverting to port " +
957 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700958 port = self.port
959 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700960 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700961 if response:
962 # NOTE: What if the person wants other values? would it be better
963 # to have a function that gets a key and return a value instead?
964 # This function requires mac and vlan and returns an ID which
965 # makes this current function useless
966 if 200 <= response[ 0 ] <= 299:
967 output = response[ 1 ]
968 hostId = json.loads( output ).get( 'id' )
969 return hostId
970 else:
971 main.log.error( "Error with REST request, response was: " +
972 str( response ) )
973 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700974 except ( AttributeError, TypeError ):
975 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700976 return None
Jon Halle401b092015-09-23 13:34:24 -0700977 except Exception:
978 main.log.exception( self.name + ": Uncaught exception!" )
979 main.cleanup()
980 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700981
982 def topology( self, ip="DEFAULT", port="DEFAULT" ):
983 """
984 Description:
985 Gets the overview of network topology
986 Returns:
987 Returns a dictionary containing information about network topology;
988 Returns None for exception
989 """
990 try:
991 output = None
992 if ip == "DEFAULT":
993 main.log.warn( "No ip given, reverting to ip from topo file" )
994 ip = self.ip_address
995 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700996 main.log.warn( "No port given, reverting to port " +
997 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700998 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700999 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001000 if response:
1001 if 200 <= response[ 0 ] <= 299:
1002 output = response[ 1 ]
1003 a = json.loads( output )
1004 b = json.dumps( a )
1005 return b
1006 else:
1007 main.log.error( "Error with REST request, response was: " +
1008 str( response ) )
1009 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001010 except ( AttributeError, TypeError ):
1011 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001012 return None
Jon Halle401b092015-09-23 13:34:24 -07001013 except Exception:
1014 main.log.exception( self.name + ": Uncaught exception!" )
1015 main.cleanup()
1016 main.exit()
1017
1018 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1019 """
1020 Description:
1021 Get the devices discovered by ONOS is json string format
1022 Returns:
1023 a json string of the devices currently discovered by ONOS OR
1024 main.FALSE if there is an error in the request OR
1025 Returns None for exception
1026 """
1027 try:
1028 output = None
1029 if ip == "DEFAULT":
1030 main.log.warn( "No ip given, reverting to ip from topo file" )
1031 ip = self.ip_address
1032 if port == "DEFAULT":
1033 main.log.warn( "No port given, reverting to port " +
1034 "from topo file" )
1035 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001036 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001037 if response:
1038 if 200 <= response[ 0 ] <= 299:
1039 output = response[ 1 ]
1040 a = json.loads( output ).get( 'devices' )
1041 assert a is not None, "Error parsing json object"
1042 b = json.dumps( a )
1043 return b
1044 else:
1045 main.log.error( "Error with REST request, response was: " +
1046 str( response ) )
1047 return main.FALSE
1048 except ( AttributeError, AssertionError, TypeError ):
1049 main.log.exception( self.name + ": Object not as expected" )
1050 return None
1051 except Exception:
1052 main.log.exception( self.name + ": Uncaught exception!" )
1053 main.cleanup()
1054 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001055
1056 def getIntentState( self, intentsId, intentsJson=None,
1057 ip="DEFAULT", port="DEFAULT" ):
1058 """
1059 Description:
1060 Get intent state.
1061 Accepts a single intent ID (string type) or a list of intent IDs.
1062 Returns the state(string type) of the id if a single intent ID is
1063 accepted.
1064 Required:
1065 intentId: intent ID (string type)
1066 intentsJson: parsed json object from the onos:intents api
1067 Returns:
1068 Returns a dictionary with intent IDs as the key and its
1069 corresponding states as the values; Returns None for invalid IDs or
1070 Type error and any exceptions
1071 NOTE:
1072 An intent's state consist of INSTALLED,WITHDRAWN etc.
1073 """
1074 try:
1075 state = "State is Undefined"
1076 if not intentsJson:
1077 intentsJsonTemp = json.loads( self.intents() )
1078 else:
1079 intentsJsonTemp = json.loads( intentsJson )
1080 if isinstance( intentsId, types.StringType ):
1081 for intent in intentsJsonTemp:
1082 if intentsId == intent[ 'id' ]:
1083 state = intent[ 'state' ]
1084 return state
1085 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1086 " on the list" )
1087 return state
1088 elif isinstance( intentsId, types.ListType ):
1089 dictList = []
1090 for i in xrange( len( intentsId ) ):
1091 stateDict = {}
1092 for intents in intentsJsonTemp:
1093 if intentsId[ i ] == intents[ 'id' ]:
1094 stateDict[ 'state' ] = intents[ 'state' ]
1095 stateDict[ 'id' ] = intentsId[ i ]
1096 dictList.append( stateDict )
1097 break
1098 if len( intentsId ) != len( dictList ):
1099 main.log.info( "Cannot find some of the intent ID state" )
1100 return dictList
1101 else:
1102 main.log.info( "Invalid intents ID entry" )
1103 return None
1104
Jon Halle401b092015-09-23 13:34:24 -07001105 except ( AttributeError, TypeError ):
1106 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001107 return None
Jon Halle401b092015-09-23 13:34:24 -07001108 except Exception:
1109 main.log.exception( self.name + ": Uncaught exception!" )
1110 main.cleanup()
1111 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001112
1113 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
1114 ip="DEFAULT", port="DEFAULT"):
1115 """
1116 Description:
1117 Check intents state based on expected state which defaults to
1118 INSTALLED state
1119 Required:
1120 intentsId - List of intents ID to be checked
1121 Optional:
1122 expectedState - Check the expected state(s) of each intents
1123 state in the list.
1124 *NOTE: You can pass in a list of expected state,
1125 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1126 Return:
1127 Returns main.TRUE only if all intent are the same as expected states
1128 , otherwise, returns main.FALSE; Returns None for general exception
1129 """
1130 try:
1131 # Generating a dictionary: intent id as a key and state as value
1132 returnValue = main.TRUE
1133 if intentsId == "ALL":
1134 intentsId = self.getIntentsId( ip=ip, port=port )
1135 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1136
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001137 if len( intentsId ) != len( intentsDict ):
1138 main.log.error( self.name + ": There is something wrong " +
1139 "getting intents state" )
1140 return main.FALSE
1141
1142 if isinstance( expectedState, types.StringType ):
1143 for intents in intentsDict:
1144 if intents.get( 'state' ) != expectedState:
1145 main.log.debug( self.name + " : Intent ID - " +
1146 intents.get( 'id' ) +
1147 " actual state = " +
1148 intents.get( 'state' )
1149 + " does not equal expected state = "
1150 + expectedState )
1151 returnValue = main.FALSE
1152
1153 elif isinstance( expectedState, types.ListType ):
1154 for intents in intentsDict:
1155 if not any( state == intents.get( 'state' ) for state in
1156 expectedState ):
1157 main.log.debug( self.name + " : Intent ID - " +
1158 intents.get( 'id' ) +
1159 " actual state = " +
1160 intents.get( 'state' ) +
1161 " does not equal expected states = "
1162 + str( expectedState ) )
1163 returnValue = main.FALSE
1164
1165 if returnValue == main.TRUE:
1166 main.log.info( self.name + ": All " +
1167 str( len( intentsDict ) ) +
1168 " intents are in " + str( expectedState ) +
1169 " state" )
1170 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001171 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001172 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001173 return None
Jon Halle401b092015-09-23 13:34:24 -07001174 except Exception:
1175 main.log.exception( self.name + ": Uncaught exception!" )
1176 main.cleanup()
1177 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001178
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001179 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001180 """
1181 Description:
1182 Get flows currently added to the system
1183 NOTE:
1184 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001185 the REST output
1186
1187 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001188 """
1189 try:
1190 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001191 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001192 if ip == "DEFAULT":
1193 main.log.warn( "No ip given, reverting to ip from topo file" )
1194 ip = self.ip_address
1195 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001196 main.log.warn( "No port given, reverting to port " +
1197 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001198 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001199 if subjectKey and not subjectClass:
1200 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1201 if subjectClass:
1202 url += "/" + subjectClass
1203 if subjectKey:
1204 url += "/" + subjectKey
1205 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001206 if response:
1207 if 200 <= response[ 0 ] <= 299:
1208 output = response[ 1 ]
1209 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001210 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001211 b = json.dumps( a )
1212 return b
1213 else:
1214 main.log.error( "Error with REST request, response was: " +
1215 str( response ) )
1216 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001217 except ( AttributeError, AssertionError, TypeError ):
1218 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001219 return None
Jon Halle401b092015-09-23 13:34:24 -07001220 except Exception:
1221 main.log.exception( self.name + ": Uncaught exception!" )
1222 main.cleanup()
1223 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001224
Jon Halle401b092015-09-23 13:34:24 -07001225 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001226 """
1227 Description:
1228 Gets all the flows of the device or get a specific flow in the
1229 device by giving its flow ID
1230 Required:
Jon Halle401b092015-09-23 13:34:24 -07001231 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001232 Optional:
1233 int/hex flowId - ID of the flow
1234 """
1235 try:
1236 output = None
1237 if ip == "DEFAULT":
1238 main.log.warn( "No ip given, reverting to ip from topo file" )
1239 ip = self.ip_address
1240 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001241 main.log.warn( "No port given, reverting to port " +
1242 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001243 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001244 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001245 if flowId:
1246 url += "/" + str( int( flowId ) )
1247 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001248 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001249 if response:
1250 if 200 <= response[ 0 ] <= 299:
1251 output = response[ 1 ]
1252 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001253 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001254 b = json.dumps( a )
1255 return b
1256 else:
1257 main.log.error( "Error with REST request, response was: " +
1258 str( response ) )
1259 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001260 except ( AttributeError, AssertionError, TypeError ):
1261 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001262 return None
Jon Halle401b092015-09-23 13:34:24 -07001263 except Exception:
1264 main.log.exception( self.name + ": Uncaught exception!" )
1265 main.cleanup()
1266 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001267
GlennRC073e8bc2015-10-27 17:11:28 -07001268 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001269 """
1270 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001271 Sends a single flow to the specified device. This function exists
1272 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001273 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001274 * The flow in json
1275 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001276 Returns:
1277 Returns main.TRUE for successful requests; Returns main.FALSE
1278 if error on requests;
1279 Returns None for exceptions
1280 NOTE:
1281 The ip and port option are for the requests input's ip and port
1282 of the ONOS node
1283 """
GlennRC073e8bc2015-10-27 17:11:28 -07001284
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001285 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001286 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001287 output = None
1288 if ip == "DEFAULT":
1289 main.log.warn( "No ip given, reverting to ip from topo file" )
1290 ip = self.ip_address
1291 if port == "DEFAULT":
1292 main.log.warn( "No port given, reverting to port " +
1293 "from topo file" )
1294 port = self.port
1295 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001296 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001297 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001298 data=json.dumps( flowJson ) )
1299 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001300 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001301 main.log.info( self.name + ": Successfully POST flow" +
1302 "in device: " + str( deviceId ) )
1303 return main.TRUE
1304 else:
1305 main.log.error( "Error with REST request, response was: " +
1306 str( response ) )
1307 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001308 except NotImplementedError as e:
1309 raise e # Inform the caller
1310 except ( AttributeError, TypeError ):
1311 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001312 return None
Jon Halle401b092015-09-23 13:34:24 -07001313 except Exception:
1314 main.log.exception( self.name + ": Uncaught exception!" )
1315 main.cleanup()
1316 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001317
GlennRC073e8bc2015-10-27 17:11:28 -07001318 def addFlow( self,
1319 deviceId,
1320 appId=0,
1321 ingressPort="",
1322 egressPort="",
1323 ethType="",
1324 ethSrc="",
1325 ethDst="",
1326 vlan="",
1327 ipProto="",
1328 ipSrc=(),
1329 ipDst=(),
1330 tcpSrc="",
1331 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001332 udpDst="",
1333 udpSrc="",
1334 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001335 priority=100,
kavitha Alagesan308c5962016-11-22 05:22:05 +05301336 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001337 ip="DEFAULT",
1338 port="DEFAULT",
1339 debug=False ):
1340 """
1341 Description:
1342 Creates a single flow in the specified device
1343 Required:
1344 * deviceId: id of the device
1345 Optional:
1346 * ingressPort: port ingress device
1347 * egressPort: port of egress device
1348 * ethType: specify ethType
1349 * ethSrc: specify ethSrc ( i.e. src mac addr )
1350 * ethDst: specify ethDst ( i.e. dst mac addr )
1351 * ipProto: specify ip protocol
1352 * ipSrc: specify ip source address with mask eg. ip#/24
1353 as a tuple (type, ip#)
1354 * ipDst: specify ip destination address eg. ip#/24
1355 as a tuple (type, ip#)
1356 * tcpSrc: specify tcp source port
1357 * tcpDst: specify tcp destination port
1358 Returns:
1359 Returns main.TRUE for successful requests; Returns main.FALSE
1360 if error on requests;
1361 Returns None for exceptions
1362 NOTE:
1363 The ip and port option are for the requests input's ip and port
1364 of the ONOS node
1365 """
1366 try:
alisone14d7b02016-07-06 10:31:51 -07001367 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001368 "isPermanent":"true",
1369 "timeout":0,
1370 "deviceId":deviceId,
1371 "treatment":{"instructions":[]},
1372 "selector": {"criteria":[]}}
1373 if appId:
1374 flowJson[ "appId" ] = appId
kavitha Alagesan308c5962016-11-22 05:22:05 +05301375
1376 if groupId:
1377 flowJson[ 'treatment' ][ 'instructions' ].append( {
1378 "type":"GROUP",
1379 "groupId":groupId } )
1380
GlennRC073e8bc2015-10-27 17:11:28 -07001381 if egressPort:
1382 flowJson[ 'treatment' ][ 'instructions' ].append( {
1383 "type":"OUTPUT",
1384 "port":egressPort } )
1385 if ingressPort:
1386 flowJson[ 'selector' ][ 'criteria' ].append( {
1387 "type":"IN_PORT",
1388 "port":ingressPort } )
1389 if ethType:
1390 flowJson[ 'selector' ][ 'criteria' ].append( {
1391 "type":"ETH_TYPE",
1392 "ethType":ethType } )
1393 if ethSrc:
1394 flowJson[ 'selector' ][ 'criteria' ].append( {
1395 "type":"ETH_SRC",
1396 "mac":ethSrc } )
1397 if ethDst:
1398 flowJson[ 'selector' ][ 'criteria' ].append( {
1399 "type":"ETH_DST",
1400 "mac":ethDst } )
1401 if vlan:
1402 flowJson[ 'selector' ][ 'criteria' ].append( {
1403 "type":"VLAN_VID",
1404 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001405 if mpls:
1406 flowJson[ 'selector' ][ 'criteria' ].append( {
1407 "type":"MPLS_LABEL",
1408 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001409 if ipSrc:
1410 flowJson[ 'selector' ][ 'criteria' ].append( {
1411 "type":ipSrc[0],
1412 "ip":ipSrc[1] } )
1413 if ipDst:
1414 flowJson[ 'selector' ][ 'criteria' ].append( {
1415 "type":ipDst[0],
1416 "ip":ipDst[1] } )
1417 if tcpSrc:
1418 flowJson[ 'selector' ][ 'criteria' ].append( {
1419 "type":"TCP_SRC",
1420 "tcpPort": tcpSrc } )
1421 if tcpDst:
1422 flowJson[ 'selector' ][ 'criteria' ].append( {
1423 "type":"TCP_DST",
1424 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001425 if udpSrc:
1426 flowJson[ 'selector' ][ 'criteria' ].append( {
1427 "type":"UDP_SRC",
1428 "udpPort": udpSrc } )
1429 if udpDst:
1430 flowJson[ 'selector' ][ 'criteria' ].append( {
1431 "type":"UDP_DST",
1432 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001433 if ipProto:
1434 flowJson[ 'selector' ][ 'criteria' ].append( {
1435 "type":"IP_PROTO",
1436 "protocol": ipProto } )
1437
1438 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1439
1440 except ( AttributeError, TypeError ):
1441 main.log.exception( self.name + ": Object not as expected" )
1442 return None
1443 except Exception:
1444 main.log.exception( self.name + ": Uncaught exception!" )
1445 main.cleanup()
1446 main.exit()
1447
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001448 def removeFlow( self, deviceId, flowId,
1449 ip="DEFAULT", port="DEFAULT" ):
1450 """
1451 Description:
1452 Remove specific device flow
1453 Required:
1454 str deviceId - id of the device
1455 str flowId - id of the flow
1456 Return:
1457 Returns main.TRUE if successfully deletes flows, otherwise
1458 Returns main.FALSE, Returns None on error
1459 """
1460 try:
1461 output = None
1462 if ip == "DEFAULT":
1463 main.log.warn( "No ip given, reverting to ip from topo file" )
1464 ip = self.ip_address
1465 if port == "DEFAULT":
1466 main.log.warn( "No port given, reverting to port " +
1467 "from topo file" )
1468 port = self.port
1469 # NOTE: REST url requires the intent id to be in decimal form
1470 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001471 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001472 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001473 if response:
1474 if 200 <= response[ 0 ] <= 299:
1475 return main.TRUE
1476 else:
1477 main.log.error( "Error with REST request, response was: " +
1478 str( response ) )
1479 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001480 except ( AttributeError, TypeError ):
1481 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001482 return None
Jon Halle401b092015-09-23 13:34:24 -07001483 except Exception:
1484 main.log.exception( self.name + ": Uncaught exception!" )
1485 main.cleanup()
1486 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001487
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001488 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1489 """
1490 Description:
1491 Check if all the current flows are in ADDED state
1492 Return:
1493 returnValue - Returns main.TRUE only if all flows are in
1494 return main.FALSE otherwise;
1495 Returns None for exception
1496 """
1497 try:
1498 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1499 returnValue = main.TRUE
1500 for flow in tempFlows:
1501 if flow.get( 'state' ) != 'ADDED':
1502 main.log.info( self.name + ": flow Id: " +
1503 str( flow.get( 'groupId' ) ) +
1504 " | state:" +
1505 str( flow.get( 'state' ) ) )
1506 returnValue = main.FALSE
1507 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001508 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001509 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001510 return None
1511 except Exception:
1512 main.log.exception( self.name + ": Uncaught exception!" )
1513 main.cleanup()
1514 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001515
1516 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1517 subjectClass=None, subjectKey=None, configKey=None ):
1518 """
1519 Description:
1520 Get a json object with the ONOS network configurations
1521 Returns:
1522 A json object containing the network configuration in
1523 ONOS; Returns main.FALSE if error on requests;
1524 Returns None for exception
1525 """
1526 try:
1527 output = None
1528 if ip == "DEFAULT":
1529 main.log.warn( "No ip given, reverting to ip from topo file" )
1530 ip = self.ip_address
1531 if port == "DEFAULT":
1532 main.log.warn( "No port given, reverting to port " +
1533 "from topo file" )
1534 port = self.port
1535 url = "/network/configuration"
1536 if subjectClass:
1537 url += "/" + subjectClass
1538 if subjectKey:
1539 url += "/" + subjectKey
1540 if configKey:
1541 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001542 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001543 if response:
1544 if 200 <= response[ 0 ] <= 299:
1545 output = response[ 1 ]
1546 a = json.loads( output )
1547 b = json.dumps( a )
1548 return b
1549 elif response[ 0 ] == 404:
1550 main.log.error( "Requested configuration doesn't exist: " +
1551 str( response ) )
1552 return {}
1553 else:
1554 main.log.error( "Error with REST request, response was: " +
1555 str( response ) )
1556 return main.FALSE
1557 except ( AttributeError, TypeError ):
1558 main.log.exception( self.name + ": Object not as expected" )
1559 return None
1560 except Exception:
1561 main.log.exception( self.name + ": Uncaught exception!" )
1562 main.cleanup()
1563 main.exit()
1564
1565 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1566 subjectClass=None, subjectKey=None, configKey=None ):
1567 """
1568 Description:
1569 Set a json object with the ONOS network configurations
1570 Returns:
1571 Returns main.TRUE for successful requests; Returns main.FALSE
1572 if error on requests;
1573 Returns None for exceptions
1574
1575 """
1576 try:
1577 output = None
1578 if ip == "DEFAULT":
1579 main.log.warn( "No ip given, reverting to ip from topo file" )
1580 ip = self.ip_address
1581 if port == "DEFAULT":
1582 main.log.warn( "No port given, reverting to port " +
1583 "from topo file" )
1584 port = self.port
1585 url = "/network/configuration"
1586 if subjectClass:
1587 url += "/" + subjectClass
1588 if subjectKey:
1589 url += "/" + subjectKey
1590 if configKey:
1591 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001592 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001593 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001594 data=json.dumps( cfgJson ) )
1595 if response:
1596 if 200 <= response[ 0 ] <= 299:
1597 main.log.info( self.name + ": Successfully POST cfg" )
1598 return main.TRUE
1599 else:
1600 main.log.error( "Error with REST request, response was: " +
1601 str( response ) )
1602 return main.FALSE
1603 except ( AttributeError, TypeError ):
1604 main.log.exception( self.name + ": Object not as expected" )
1605 return None
1606 except Exception:
1607 main.log.exception( self.name + ": Uncaught exception!" )
1608 main.cleanup()
1609 main.exit()
1610
1611 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1612 subjectClass=None, subjectKey=None, configKey=None ):
1613 """
1614 Description:
1615 Remove a json object from the ONOS network configurations
1616 Returns:
1617 Returns main.TRUE for successful requests; Returns main.FALSE
1618 if error on requests;
1619 Returns None for exceptions
1620
1621 """
1622 try:
1623 output = None
1624 if ip == "DEFAULT":
1625 main.log.warn( "No ip given, reverting to ip from topo file" )
1626 ip = self.ip_address
1627 if port == "DEFAULT":
1628 main.log.warn( "No port given, reverting to port " +
1629 "from topo file" )
1630 port = self.port
1631 url = "/network/configuration"
1632 if subjectClass:
1633 url += "/" + subjectClass
1634 if subjectKey:
1635 url += "/" + subjectKey
1636 if configKey:
1637 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001638 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001639 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001640 if response:
1641 if 200 <= response[ 0 ] <= 299:
1642 main.log.info( self.name + ": Successfully delete cfg" )
1643 return main.TRUE
1644 else:
1645 main.log.error( "Error with REST request, response was: " +
1646 str( response ) )
1647 return main.FALSE
1648 except ( AttributeError, TypeError ):
1649 main.log.exception( self.name + ": Object not as expected" )
1650 return None
1651 except Exception:
1652 main.log.exception( self.name + ": Uncaught exception!" )
1653 main.cleanup()
1654 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001655
1656 def createFlowBatch( self,
1657 numSw = 1,
1658 swIndex = 1,
1659 batchSize = 1,
1660 batchIndex = 1,
1661 deviceIdpreFix = "of:",
1662 appId=0,
1663 deviceID="",
1664 ingressPort="",
1665 egressPort="",
1666 ethType="",
1667 ethSrc="",
1668 ethDst="",
1669 vlan="",
1670 ipProto="",
1671 ipSrc=(),
1672 ipDst=(),
1673 tcpSrc="",
1674 tcpDst="",
1675 udpDst="",
1676 udpSrc="",
1677 mpls="",
1678 ip="DEFAULT",
1679 port="DEFAULT",
1680 debug=False ):
1681 """
1682 Description:
1683 Creates batches of MAC-rule flows for POST.
1684 Predefined MAC: 2 MS Hex digit for iterating devices
1685 Next 5 Hex digit for iterating batch numbers
1686 Next 5 Hex digit for iterating flows within a batch
1687 Required:
1688 * deviceId: id of the device
1689 Optional:
1690 * ingressPort: port ingress device
1691 * egressPort: port of egress device
1692 * ethType: specify ethType
1693 * ethSrc: specify ethSrc ( i.e. src mac addr )
1694 * ethDst: specify ethDst ( i.e. dst mac addr )
1695 * ipProto: specify ip protocol
1696 * ipSrc: specify ip source address with mask eg. ip#/24
1697 as a tuple (type, ip#)
1698 * ipDst: specify ip destination address eg. ip#/24
1699 as a tuple (type, ip#)
1700 * tcpSrc: specify tcp source port
1701 * tcpDst: specify tcp destination port
1702 Returns:
1703 Returns main.TRUE for successful requests; Returns main.FALSE
1704 if error on requests;
1705 Returns None for exceptions
1706 NOTE:
1707 The ip and port option are for the requests input's ip and port
1708 of the ONOS node
1709 """
1710 #from pprint import pprint
1711
1712 flowJsonList = []
1713 flowJsonBatch = {"flows":flowJsonList}
1714 dev = swIndex
1715
1716 for fl in range(1, batchSize + 1):
1717 flowJson = { "priority":100,
1718 "deviceId":"",
1719 "isPermanent":"true",
1720 "timeout":0,
1721 "treatment":{"instructions":[]},
1722 "selector": {"criteria":[]}}
1723
1724 #main.log.info("fl: " + str(fl))
1725 if dev <= numSw:
1726 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1727 #print deviceId
1728 flowJson['deviceId'] = deviceId
1729 dev += 1
1730 else:
1731 dev = 1
1732 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1733 #print deviceId
1734 flowJson['deviceId'] = deviceId
1735 dev += 1
1736
1737 # ethSrc starts with "0"; ethDst starts with "1"
1738 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1739 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1740 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1741 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1742 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1743 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1744 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1745
1746 if appId:
1747 flowJson[ "appId" ] = appId
1748
1749 if egressPort:
1750 flowJson[ 'treatment' ][ 'instructions' ].append( {
1751 "type":"OUTPUT",
1752 "port":egressPort } )
1753 if ingressPort:
1754 flowJson[ 'selector' ][ 'criteria' ].append( {
1755 "type":"IN_PORT",
1756 "port":ingressPort } )
1757 if ethType:
1758 flowJson[ 'selector' ][ 'criteria' ].append( {
1759 "type":"ETH_TYPE",
1760 "ethType":ethType } )
1761 if ethSrc:
1762 flowJson[ 'selector' ][ 'criteria' ].append( {
1763 "type":"ETH_SRC",
1764 "mac":ethSrc } )
1765 if ethDst:
1766 flowJson[ 'selector' ][ 'criteria' ].append( {
1767 "type":"ETH_DST",
1768 "mac":ethDst } )
1769 if vlan:
1770 flowJson[ 'selector' ][ 'criteria' ].append( {
1771 "type":"VLAN_VID",
1772 "vlanId":vlan } )
1773 if mpls:
1774 flowJson[ 'selector' ][ 'criteria' ].append( {
1775 "type":"MPLS_LABEL",
1776 "label":mpls } )
1777 if ipSrc:
1778 flowJson[ 'selector' ][ 'criteria' ].append( {
1779 "type":ipSrc[0],
1780 "ip":ipSrc[1] } )
1781 if ipDst:
1782 flowJson[ 'selector' ][ 'criteria' ].append( {
1783 "type":ipDst[0],
1784 "ip":ipDst[1] } )
1785 if tcpSrc:
1786 flowJson[ 'selector' ][ 'criteria' ].append( {
1787 "type":"TCP_SRC",
1788 "tcpPort": tcpSrc } )
1789 if tcpDst:
1790 flowJson[ 'selector' ][ 'criteria' ].append( {
1791 "type":"TCP_DST",
1792 "tcpPort": tcpDst } )
1793 if udpSrc:
1794 flowJson[ 'selector' ][ 'criteria' ].append( {
1795 "type":"UDP_SRC",
1796 "udpPort": udpSrc } )
1797 if udpDst:
1798 flowJson[ 'selector' ][ 'criteria' ].append( {
1799 "type":"UDP_DST",
1800 "udpPort": udpDst } )
1801 if ipProto:
1802 flowJson[ 'selector' ][ 'criteria' ].append( {
1803 "type":"IP_PROTO",
1804 "protocol": ipProto } )
1805 #pprint(flowJson)
1806 flowJsonList.append(flowJson)
1807
1808 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1809 flowJsonBatch['flows'] = flowJsonList
1810 #pprint(flowJsonBatch)
1811
1812 return flowJsonBatch
1813
1814
1815 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1816 """
1817 Description:
1818 Sends a single flow batch through /flows REST API.
1819 Required:
1820 * The batch of flows
1821 Returns:
1822 Returns main.TRUE for successful requests; Returns main.FALSE
1823 if error on requests;
1824 Returns None for exceptions
1825 NOTE:
1826 The ip and port option are for the requests input's ip and port
1827 of the ONOS node
1828 """
1829 import time
1830
1831 try:
1832 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1833 output = None
1834 if ip == "DEFAULT":
1835 main.log.warn( "No ip given, reverting to ip from topo file" )
1836 ip = self.ip_address
1837 if port == "DEFAULT":
1838 main.log.warn( "No port given, reverting to port " +
1839 "from topo file" )
1840 port = self.port
1841 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001842 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001843 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001844 data=json.dumps( batch ) )
1845 #main.log.info("Post response is: ", str(response[0]))
1846 if response[0] == 200:
1847 main.log.info( self.name + ": Successfully POST flow batch" )
1848 return main.TRUE, response
1849 else:
1850 main.log.error( "Error with REST request, response was: " +
1851 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001852 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001853 except NotImplementedError as e:
1854 raise e # Inform the caller
1855 except ( AttributeError, TypeError ):
1856 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001857 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001858 except Exception:
1859 main.log.exception( self.name + ": Uncaught exception!" )
1860 main.cleanup()
1861 main.exit()
1862
1863 def removeFlowBatch( self, batch={},
1864 ip="DEFAULT", port="DEFAULT" ):
1865 """
1866 Description:
1867 Remove a batch of flows
1868 Required:
1869 flow batch
1870 Return:
1871 Returns main.TRUE if successfully deletes flows, otherwise
1872 Returns main.FALSE, Returns None on error
1873 """
1874 try:
1875 output = None
1876 if ip == "DEFAULT":
1877 main.log.warn( "No ip given, reverting to ip from topo file" )
1878 ip = self.ip_address
1879 if port == "DEFAULT":
1880 main.log.warn( "No port given, reverting to port " +
1881 "from topo file" )
1882 port = self.port
1883 # NOTE: REST url requires the intent id to be in decimal form
1884
suibin zhang116647a2016-05-06 16:30:09 -07001885 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001886 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001887 data = json.dumps(batch) )
1888 if response:
1889 if 200 <= response[ 0 ] <= 299:
1890 return main.TRUE
1891 else:
1892 main.log.error( "Error with REST request, response was: " +
1893 str( response ) )
1894 return main.FALSE
1895 except ( AttributeError, TypeError ):
1896 main.log.exception( self.name + ": Object not as expected" )
1897 return None
1898 except Exception:
1899 main.log.exception( self.name + ": Uncaught exception!" )
1900 main.cleanup()
1901 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001902
1903 def getTopology( self, topologyOutput ):
1904 """
1905 Definition:
1906 Loads a json topology output
1907 Return:
1908 topology = current ONOS topology
1909 """
1910 import json
1911 try:
1912 # either onos:topology or 'topology' will work in CLI
1913 topology = json.loads(topologyOutput)
1914 main.log.debug( topology )
1915 return topology
1916 except pexpect.EOF:
1917 main.log.error( self.name + ": EOF exception found" )
1918 main.log.error( self.name + ": " + self.handle.before )
1919 main.cleanup()
1920 main.exit()
1921 except Exception:
1922 main.log.exception( self.name + ": Uncaught exception!" )
1923 main.cleanup()
1924 main.exit()
1925
1926 def checkStatus(
1927 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001928 numoswitch,
1929 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001930 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001931 logLevel="info" ):
1932 """
1933 Checks the number of switches & links that ONOS sees against the
1934 supplied values. By default this will report to main.log, but the
1935 log level can be specific.
1936
Flavio Castro82ee2f62016-06-07 15:04:12 -07001937 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001938 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001939 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001940 logLevel = level to log to.
1941 Currently accepts 'info', 'warn' and 'report'
1942
1943 Returns: main.TRUE if the number of switches and links are correct,
1944 main.FALSE if the number of switches and links is incorrect,
1945 and main.ERROR otherwise
1946 """
1947 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001948 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001949 if topology == {}:
1950 return main.ERROR
1951 output = ""
1952 # Is the number of switches is what we expected
1953 devices = topology.get( 'devices', False )
1954 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001955 nodes = topology.get( 'nodes' , False )
1956 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001957 return main.ERROR
1958 switchCheck = ( int( devices ) == int( numoswitch ) )
1959 # Is the number of links is what we expected
1960 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001961 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1962 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001963 # We expected the correct numbers
1964 output = output + "The number of links and switches match "\
1965 + "what was expected"
1966 result = main.TRUE
1967 else:
1968 output = output + \
1969 "The number of links and switches does not match " + \
1970 "what was expected"
1971 result = main.FALSE
1972 output = output + "\n ONOS sees %i devices" % int( devices )
1973 output = output + " (%i expected) " % int( numoswitch )
1974 output = output + "and %i links " % int( links )
1975 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001976 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001977 output = output + "and %i controllers " % int( nodes )
1978 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001979 if logLevel == "report":
1980 main.log.report( output )
1981 elif logLevel == "warn":
1982 main.log.warn( output )
1983 else:
1984 main.log.info( output )
1985 return result
1986 except pexpect.EOF:
1987 main.log.error( self.name + ": EOF exception found" )
1988 main.log.error( self.name + ": " + self.handle.before )
1989 main.cleanup()
1990 main.exit()
1991 except Exception:
1992 main.log.exception( self.name + ": Uncaught exception!" )
1993 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001994 main.exit()
kavitha Alagesan308c5962016-11-22 05:22:05 +05301995
1996 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
1997 """
1998 Description:
1999 Creates a single Group for the specified device.
2000 Required:
2001 * deviceId: id of the device
2002 * type: Type of the Group
2003 * bucketList: Buckets to be added to the group
2004 * appCookie: Cookie for the Group
2005 * groupId: Id of the Group
2006 Returns:
2007 Returns main.TRUE for successful requests; Returns main.FALSE
2008 if error on requests;
2009 Returns None for exceptions
2010 Note:
2011 The ip and port option are for the requests input's ip and port
2012 of the ONOS node
2013 """
2014 try:
2015 groupJson = { "type": groupType,
2016 "appCookie": appCookie,
2017 "groupId": groupId,
2018 "buckets": bucketList
2019 }
2020 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
2021
2022 except ( AttributeError, TypeError ):
2023 main.log.exception( self.name + ": Object not as expected" )
2024 return None
2025 except Exception:
2026 main.log.exception( self.name + ": Uncaught exception!" )
2027 main.cleanup()
2028 main.exit()
2029
2030 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
2031 """
2032 Description:
2033 Sends a single group to the specified device.
2034 Required:
2035 * deviceId: id of the device
2036 * groupJson: the group in json
2037 Returns:
2038 Returns main.TRUE for successful requests; Returns main.FALSE
2039 if error on requests;
2040 Returns None for exceptions
2041 NOTE:
2042 The ip and port option are for the requests input's ip and port
2043 of the ONOS node
2044 """
2045 try:
2046 if debug: main.log.debug( "Adding group: " + self.pprint( groupJson ) )
2047 output = None
2048 if ip == "DEFAULT":
2049 main.log.warn( "No ip given, reverting to ip from topo file" )
2050 ip = self.ip_address
2051 if port == "DEFAULT":
2052 main.log.warn( "No port given, reverting to port " +
2053 "from topo file" )
2054 port = self.port
2055 url = "/groups/" + deviceId
2056 response = self.send( method="POST",
2057 url=url, ip = ip, port = port,
2058 data=json.dumps( groupJson ) )
2059 if response:
2060 if "201" in str( response[ 0 ] ):
2061 main.log.info( self.name + ": Successfully POST group " +
2062 "in device: " + str( deviceId ) )
2063 return main.TRUE
2064 else:
2065 main.log.error( "Error with REST request, response was: " +
2066 str( response ) )
2067 return main.FALSE
2068 except NotImplementedError as e:
2069 raise e # Inform the caller
2070 except ( AttributeError, TypeError ):
2071 main.log.exception( self.name + ": Object not as expected" )
2072 return None
2073 except Exception:
2074 main.log.exception( self.name + ": Uncaught exception!" )
2075 main.cleanup()
2076 main.exit()
2077
2078 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2079 """
2080 Description:
2081 Get all the groups or get a specific group by giving the
2082 deviceId and appCookie
2083 Optional:
2084 * deviceId: id of the Device
2085 * appCookie: Cookie of the Group
2086 Returns:
2087 Returns Groups for successful requests; Returns main.FALSE
2088 if error on requests;
2089 Returns None for exceptions
2090 NOTE:
2091 The ip and port option are for the requests input's ip and port
2092 of the ONOS node
2093 """
2094 try:
2095 output = None
2096 if ip == "DEFAULT":
2097 main.log.warn( "No ip given, reverting to ip from topo file" )
2098 ip = self.ip_address
2099 if port == "DEFAULT":
2100 main.log.warn( "No port given, reverting to port " +
2101 "from topo file" )
2102 port = self.port
2103 url = "/groups"
2104 if deviceId:
2105 url += "/" + deviceId
2106 if appCookie:
2107 url += "/" + appCookie
2108 response = self.send( url=url, ip = ip, port = port )
2109 if response:
2110 if 200 <= response[ 0 ] <= 299:
2111 output = response[ 1 ]
2112 groupsJson = json.loads( output ).get( 'groups' )
2113 assert groupsJson is not None, "Error parsing json object"
2114 groups = json.dumps( groupsJson )
2115 return groups
2116 else:
2117 main.log.error( "Error with REST request, response was: " +
2118 str( response ) )
2119 return main.FALSE
2120 except ( AttributeError, AssertionError, TypeError ):
2121 main.log.exception( self.name + ": Object not as expected" )
2122 return None
2123 except Exception:
2124 main.log.exception( self.name + ": Uncaught exception!" )
2125 main.cleanup()
2126 main.exit()
2127
2128 def removeGroup( self, deviceId, appCookie,
2129 ip="DEFAULT", port="DEFAULT" ):
2130 """
2131 Description:
2132 Removes specific device group
2133 Required:
2134 * deviceId: id of the Device
2135 * appCookie: Cookie of the Group
2136 Returns:
2137 Returns main.TRUE for successful requests; Returns main.FALSE
2138 if error on requests;
2139 Returns None for exceptions
2140 NOTE:
2141 The ip and port option are for the requests input's ip and port
2142 of the ONOS node
2143
2144 """
2145 try:
2146 output = None
2147 if ip == "DEFAULT":
2148 main.log.warn( "No ip given, reverting to ip from topo file" )
2149 ip = self.ip_address
2150 if port == "DEFAULT":
2151 main.log.warn( "No port given, reverting to port " +
2152 "from topo file" )
2153 port = self.port
2154 query = "/" + str( deviceId ) + "/" + str( appCookie )
2155 response = self.send( method="DELETE",
2156 url="/groups" + query, ip = ip, port = port )
2157 if response:
2158 if 200 <= response[ 0 ] <= 299:
2159 return main.TRUE
2160 else:
2161 main.log.error( "Error with REST request, response was: " +
2162 str( response ) )
2163 return main.FALSE
2164 except ( AttributeError, TypeError ):
2165 main.log.exception( self.name + ": Object not as expected" )
2166 return None
2167 except Exception:
2168 main.log.exception( self.name + ": Uncaught exception!" )
2169 main.cleanup()
2170 main.exit()
2171