blob: 403b4362029653fac6cb36c9c5261da992deebb3 [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00004Copyright 2015 Open Networking Foundation (ONF)
Jeremy Songsterae01bba2016-07-11 15:39:17 -07005
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jon Hallfc915882015-07-14 13:33:17 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 ( at your option ) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23"""
24import json
25import os
26import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070027import types
Jon Halle401b092015-09-23 13:34:24 -070028import sys
Jon Hall3c0114c2020-08-11 15:07:42 -070029import time
Jon Hallfc915882015-07-14 13:33:17 -070030
Jon Hallfc915882015-07-14 13:33:17 -070031from drivers.common.api.controllerdriver import Controller
32
33
34class OnosRestDriver( Controller ):
35
36 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070037 self.pwd = None
38 self.user_name = "user"
Devin Limdc78e202017-06-09 18:30:07 -070039 super( OnosRestDriver, self ).__init__()
Jon Hallfc915882015-07-14 13:33:17 -070040 self.ip_address = "localhost"
41 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070042 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070043
44 def connect( self, **connectargs ):
45 try:
46 for key in connectargs:
47 vars( self )[ key ] = connectargs[ key ]
48 self.name = self.options[ 'name' ]
49 except Exception as e:
50 main.log.exception( e )
51 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -070052 if os.getenv( str( self.ip_address ) ) is not None:
Jon Hallfc915882015-07-14 13:33:17 -070053 self.ip_address = os.getenv( str( self.ip_address ) )
54 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070055 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070056 except KeyError:
Jon Hall3c0114c2020-08-11 15:07:42 -070057 main.log.info( self.name + ": Invalid host name," +
Jon Hallfc915882015-07-14 13:33:17 -070058 "defaulting to 'localhost' instead" )
59 self.ip_address = 'localhost'
60 except Exception as inst:
61 main.log.error( "Uncaught exception: " + str( inst ) )
62
Jon Hall6040bcf2017-08-14 11:15:41 -070063 return super( OnosRestDriver, self ).connect()
Jon Hallfc915882015-07-14 13:33:17 -070064
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,
Jeremy Ronquillo82705492017-10-18 14:19:55 -070078 indent=4, separators=( ',', ': ' ) )
Jon Halle401b092015-09-23 13:34:24 -070079 except ( TypeError, ValueError ):
80 main.log.exception( "Error parsing jsonObject" )
81 return None
82
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000083 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 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000098 # TODO: Authentication - simple http (user,pass) tuple
Jon Hallfc915882015-07-14 13:33:17 -070099 # 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":
Jon Hall3c0114c2020-08-11 15:07:42 -0700103 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700104 ip = self.ip_address
suibin zhangd5b6fe42016-05-12 08:48:58 -0700105 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700106 main.log.warn( self.name + ": No port given, reverting to port " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700107 "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:
Jon Hall3c0114c2020-08-11 15:07:42 -0700113 main.log.info( self.name + ": user/passwd is: " + self.user_name + "/" + self.pwd )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700114 auth = ( self.user_name, self.pwd )
Jon Hallf7234882015-08-28 13:16:31 -0700115 else:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700116 auth = None
Jon Hall3c0114c2020-08-11 15:07:42 -0700117 main.log.info( self.name + ": Sending request " + path + " using " +
Jon Hallfc915882015-07-14 13:33:17 -0700118 method.upper() + " method." )
Jon Hallf69e3162020-09-01 09:08:44 -0700119 if debug:
120 main.log.debug( self.name + ": request data: " + str( data ) )
Jon Hallfc915882015-07-14 13:33:17 -0700121 response = requests.request( method.upper(),
122 path,
123 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700124 data=data,
125 auth=auth )
126 if debug:
127 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700128 return ( response.status_code, response.text.encode( 'utf8' ) )
You Wang7880b372019-02-27 16:50:47 -0800129 except requests.ConnectionError:
Jon Hallfc915882015-07-14 13:33:17 -0700130 main.log.exception( "Error sending request." )
You Wang7880b372019-02-27 16:50:47 -0800131 return ( None, None )
Jon Halle401b092015-09-23 13:34:24 -0700132 except Exception:
133 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700134 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700135
136 def intents( self, ip="DEFAULT", port="DEFAULT" ):
137 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700138 Description:
139 Gets a list of dictionary of all intents in the system
140 Returns:
141 A list of dictionary of intents in string type to match the cli
142 version for now; Returns main.FALSE if error on request;
143 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700144 """
145 try:
146 output = None
147 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700148 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700149 ip = self.ip_address
150 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700151 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700152 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700153 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000154 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700155 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700156 if 200 <= response[ 0 ] <= 299:
157 output = response[ 1 ]
158 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700159 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700160 b = json.dumps( a )
161 return b
Jon Hallfc915882015-07-14 13:33:17 -0700162 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700163 main.log.error( "Error with REST request, response was: %s: %s" %
164 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700165 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700166 except ( AttributeError, AssertionError, TypeError ):
167 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700168 return None
Jon Halle401b092015-09-23 13:34:24 -0700169 except Exception:
170 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700171 main.cleanAndExit()
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":
Jon Hall3c0114c2020-08-11 15:07:42 -0700193 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700194 ip = self.ip_address
195 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700196 main.log.warn( self.name + ": 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 )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000201 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:
Jon Hall3c0114c2020-08-11 15:07:42 -0700208 main.log.error( "Error with REST request, response was: %s: %s" %
209 ( response[ 0 ], response[ 1 ] ) )
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!" )
Devin Lim44075962017-08-11 10:56:37 -0700216 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700217
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700218 def apps( self, ip="DEFAULT", port="DEFAULT" ):
219 """
220 Description:
221 Returns all the current application installed in the system
222 Returns:
223 List of dictionary of installed application; Returns main.FALSE for
224 error on request; Returns None for exception
225 """
226 try:
227 output = None
228 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700229 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700230 ip = self.ip_address
231 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700232 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -0700233 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700234 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000235 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700236 if response:
237 if 200 <= response[ 0 ] <= 299:
238 output = response[ 1 ]
239 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700240 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700241 b = json.dumps( a )
242 return b
243 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700244 main.log.error( "Error with REST request, response was: %s: %s" %
245 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700246 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700247 except ( AttributeError, AssertionError, TypeError ):
248 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700249 return None
Jon Halle401b092015-09-23 13:34:24 -0700250 except Exception:
251 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700252 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700253
254 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
255 """
256 Decription:
257 Activate an app that is already installed in ONOS
258 Optional:
259 bool check - If check is True, method will check the status
260 of the app after the command is issued
261 Returns:
262 Returns main.TRUE if the command was successfully or main.FALSE
263 if the REST responded with an error or given incorrect input;
264 Returns None for exception
265
266 """
267 try:
268 output = None
269 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700270 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700271 ip = self.ip_address
272 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700273 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700274 "from topo file" )
275 port = self.port
276 query = "/" + str( appName ) + "/active"
Jon Hall3c0114c2020-08-11 15:07:42 -0700277 retry = 0
278 retCode = main.TRUE
279 while retry < 50:
280 if retry > 0:
281 main.log.warn( self.name + ": Retrying " + query + " for the " + str( retry ) + " time" )
282
283 retry += 1
284 response = self.send( method="POST",
285 debug=True,
286 url="/applications" + query,
287 ip = ip, port = port )
288 if response:
289 output = response[ 1 ]
290 if 200 <= response[ 0 ] <= 299:
291 if check:
292 app = json.loads( output )
293 if app.get( 'state' ) == 'ACTIVE':
294 main.log.info( self.name + ": " + appName +
295 " application" +
296 " is in ACTIVE state" )
297 appHealth = self.getAppHealth( appName=appName, ip=ip, port=port )
298 if "ready" == json.loads( appHealth[1] ).get( 'message' ):
299 return main.TRUE
300 else:
301 return main.FALSE
302 else:
303 main.log.error( self.name + ": " + appName +
304 " application" + " is in " +
305 app.get( 'state' ) + " state" )
306 retCode = main.FALSE
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700307 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700308 main.log.warn( self.name + ": Skipping " + appName +
309 "application check" )
310 return main.TRUE
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700311 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700312 main.log.error( "Error with REST request, response was: %s: %s" %
313 ( response[ 0 ], response[ 1 ] ) )
314 retCode = main.FALSE
315 time.sleep( 30 )
316 return retCode
317 except ( ValueError ):
318 main.log.exception( self.name + ": Error parsing json" )
319 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700320 except ( AttributeError, TypeError ):
321 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700322 return None
Jon Halle401b092015-09-23 13:34:24 -0700323 except Exception:
324 main.log.exception( self.name + ": Uncaught exception!" )
Jon Hall3c0114c2020-08-11 15:07:42 -0700325 main.log.debug( self.name + ": " + response )
Devin Lim44075962017-08-11 10:56:37 -0700326 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700327
328 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
329 check=True ):
330 """
331 Required:
332 Deactivate an app that is already activated in ONOS
333 Optional:
334 bool check - If check is True, method will check the status of the
335 app after the command is issued
336 Returns:
337 Returns main.TRUE if the command was successfully sent
338 main.FALSE if the REST responded with an error or given
339 incorrect input; Returns None for exception
340 """
341 try:
342 output = None
343 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700344 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700345 ip = self.ip_address
346 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700347 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700348 "from topo file" )
349 port = self.port
350 query = "/" + str( appName ) + "/active"
Devin Limcde503f2017-09-11 17:23:30 -0700351 self.send( method="DELETE",
352 url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000353 ip = ip, port = port )
Devin Limcde503f2017-09-11 17:23:30 -0700354 response = self.getApp( appName, ip, port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700355 if response:
356 output = response[ 1 ]
Jon Hall6040bcf2017-08-14 11:15:41 -0700357 app = {} if output == "" else json.loads( output )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700358 if 200 <= response[ 0 ] <= 299:
359 if check:
360 if app.get( 'state' ) == 'INSTALLED':
361 main.log.info( self.name + ": " + appName +
362 " application" +
363 " is in INSTALLED state" )
364 return main.TRUE
365 else:
366 main.log.error( self.name + ": " + appName +
367 " application" + " is in " +
368 app.get( 'state' ) + " state" )
369 return main.FALSE
370 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700371 main.log.warn( self.name + ": Skipping " + appName +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700372 "application check" )
373 return main.TRUE
374 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700375 main.log.error( "Error with REST request, response was: %s: %s" %
376 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700377 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700378 except ( AttributeError, TypeError ):
379 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700380 return None
Jon Halle401b092015-09-23 13:34:24 -0700381 except Exception:
382 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700383 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700384
Devin Limcde503f2017-09-11 17:23:30 -0700385 def getApp( self, appName, ip="DEFAULT",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700386 port="DEFAULT" ):
387 """
388 Decription:
389 Gets the informaion of the given application
390 Required:
391 str name - Name of onos application
392 Returns:
393 Returns a dictionary of information ONOS application in string type;
394 Returns main.FALSE if error on requests; Returns None for exception
395 """
396 try:
Jon Hall3c0114c2020-08-11 15:07:42 -0700397 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700398 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700399 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700400 ip = self.ip_address
401 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700402 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700403 "from topo file" )
404 port = self.port
Devin Limcde503f2017-09-11 17:23:30 -0700405 query = "/" + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700406 response = self.send( url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000407 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700408 if response:
409 if 200 <= response[ 0 ] <= 299:
Devin Limcde503f2017-09-11 17:23:30 -0700410 return response
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700411 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700412 main.log.error( "Error with REST request, response was: %s: %s" %
413 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700414 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700415 except ( AttributeError, TypeError ):
416 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700417 return None
Jon Halle401b092015-09-23 13:34:24 -0700418 except Exception:
419 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700420 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700421
Jon Hall3c0114c2020-08-11 15:07:42 -0700422 def getAppHealth( self, appName, ip="DEFAULT",
423 port="DEFAULT" ):
424 """
425 Decription:
426 Gets the health of the given application
427 Required:
428 str name - Name of onos application
429 Returns:
430 Returns a dictionary of information ONOS application in string type;
431 Returns main.FALSE if error on requests; Returns None for exception
432 """
433 try:
434 response = None
435 if ip == "DEFAULT":
436 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
437 ip = self.ip_address
438 if port == "DEFAULT":
439 main.log.warn( self.name + ": No port given, reverting to port " +
440 "from topo file" )
441 port = self.port
442 response = self.send( url="/applications/%s/health" % str( appName ),
443 ip = ip, port = port )
444 if response:
445 if 200 <= response[ 0 ] <= 299:
446 return response
447 else:
448 main.log.error( "Error with REST request, response was: %s: %s" %
449 ( response[ 0 ], response[ 1 ] ) )
450 return main.FALSE
451 except ( AttributeError, TypeError ):
452 main.log.exception( self.name + ": Object not as expected" )
453 return None
454 except Exception:
455 main.log.exception( self.name + ": Uncaught exception!" )
456 main.cleanAndExit()
457
458 def getAllAppHealth( self, retries=1, wait=30 , ip="DEFAULT",
459 port="DEFAULT" ):
460 """
461 Description:
462 Gets the health of all activated apps
463 Required:
464 Optional:
465 retries - The number of tries to return before returning
466 wait - Time to wait in between retries
467 """
468 try:
469 responses = main.TRUE
470 if ip == "DEFAULT":
471 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
472 ip = self.ip_address
473 if port == "DEFAULT":
474 main.log.warn( self.name + ": No port given, reverting to port " +
475 "from topo file" )
476 port = self.port
477 apps = self.apps()
478 for app in json.loads(apps):
479 appName = app.get( "name" )
480 response = self.getAppHealth( appName=appName, ip=ip, port=port )
481 responses = main.TRUE and "ready" == json.loads( response[1] ).get( "message" )
482 return responses
483 except ( AttributeError, TypeError ):
484 main.log.exception( self.name + ": Object not as expected" )
485 return None
486 except Exception:
487 main.log.exception( self.name + ": Uncaught exception!" )
488 main.cleanAndExit()
489
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700490 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700491 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700492 """
493 Description:
494 Adds a host-to-host intent ( bidirectional ) by
495 specifying the two hosts.
496 Required:
497 * hostIdOne: ONOS host id for host1
498 * hostIdTwo: ONOS host id for host2
499 Optional:
500 str appId - Application name of intent identifier
501 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700502 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700503 error on requests; Returns None for exceptions
504 """
505 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700506 intentJson = { "two": str( hostIdTwo ),
507 "selector": { "criteria": [] }, "priority": 7,
508 "treatment": { "deferred": [], "instructions": [] },
509 "appId": appId, "one": str( hostIdOne ),
510 "type": "HostToHostIntent",
511 "constraints": [ { "type": "LinkTypeConstraint",
512 "types": [ "OPTICAL" ],
513 "inclusive": 'false' } ] }
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700514 if vlanId:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700515 intentJson[ 'selector' ][ 'criteria' ].append( { "type": "VLAN_VID",
516 "vlanId": vlanId } )
Jon Hall3c0114c2020-08-11 15:07:42 -0700517 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700518 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700519 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700520 ip = self.ip_address
521 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700522 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700523 "from topo file" )
524 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700525 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000526 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700527 data=json.dumps( intentJson ) )
528 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700529 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700530 main.log.info( self.name + ": Successfully POST host" +
531 " intent between host: " + hostIdOne +
532 " and host: " + hostIdTwo )
533 return main.TRUE
534 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700535 main.log.error( "Error with REST request, response was: %s: %s" %
536 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700537 return main.FALSE
538
Jon Halle401b092015-09-23 13:34:24 -0700539 except ( AttributeError, TypeError ):
540 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700541 return None
Jon Halle401b092015-09-23 13:34:24 -0700542 except Exception:
543 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700544 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700545
kelvin-onlabb50074f2015-07-27 16:18:32 -0700546 def addPointIntent( self,
547 ingressDevice,
548 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700549 appId='org.onosproject.cli',
550 ingressPort="",
551 egressPort="",
552 ethType="",
553 ethSrc="",
554 ethDst="",
555 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800556 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700557 lambdaAlloc=False,
558 ipProto="",
559 ipSrc="",
560 ipDst="",
561 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700562 tcpDst="",
563 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700564 port="DEFAULT",
565 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700566 """
567 Description:
568 Adds a point-to-point intent ( uni-directional ) by
569 specifying device id's and optional fields
570 Required:
571 * ingressDevice: device id of ingress device
572 * egressDevice: device id of egress device
573 Optional:
574 * ethType: specify ethType
575 * ethSrc: specify ethSrc ( i.e. src mac addr )
576 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000577 * bandwidth: specify bandwidth capacity of link (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700578 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000579 for the specified intent (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700580 * ipProto: specify ip protocol
581 * ipSrc: specify ip source address with mask eg. ip#/24
582 * ipDst: specify ip destination address eg. ip#/24
583 * tcpSrc: specify tcp source port
584 * tcpDst: specify tcp destination port
585 Returns:
586 Returns main.TRUE for successful requests; Returns main.FALSE if
587 no ingress|egress port found and if error on requests;
588 Returns None for exceptions
589 NOTE:
590 The ip and port option are for the requests input's ip and port
591 of the ONOS node
592 """
593 try:
594 if "/" in ingressDevice:
595 if not ingressPort:
596 ingressPort = ingressDevice.split( "/" )[ 1 ]
597 ingressDevice = ingressDevice.split( "/" )[ 0 ]
598 else:
599 if not ingressPort:
600 main.log.debug( self.name + ": Ingress port not specified" )
601 return main.FALSE
602
603 if "/" in egressDevice:
604 if not egressPort:
605 egressPort = egressDevice.split( "/" )[ 1 ]
606 egressDevice = egressDevice.split( "/" )[ 0 ]
607 else:
608 if not egressPort:
609 main.log.debug( self.name + ": Egress port not specified" )
610 return main.FALSE
611
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700612 intentJson = { "ingressPoint": { "device": ingressDevice,
613 "port": ingressPort },
614 "selector": { "criteria": [] },
615 "priority": 55,
616 "treatment": { "deferred": [],
617 "instructions": [] },
618 "egressPoint": { "device": egressDevice,
619 "port": egressPort },
620 "appId": appId,
621 "type": "PointToPointIntent",
622 "constraints": [ { "type": "LinkTypeConstraint",
623 "types": [ "OPTICAL" ],
624 "inclusive": "false" } ] }
kelvin-onlabb50074f2015-07-27 16:18:32 -0700625
626 if ethType == "IPV4":
627 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700628 "type": "ETH_TYPE",
629 "ethType": 2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700630 elif ethType:
631 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700632 "type": "ETH_TYPE",
633 "ethType": ethType } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700634
kelvin-onlabb50074f2015-07-27 16:18:32 -0700635 if ethSrc:
636 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700637 { "type": "ETH_SRC",
638 "mac": ethSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700639 if ethDst:
640 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700641 { "type": "ETH_DST",
642 "mac": ethDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700643 if ipSrc:
644 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700645 { "type": "IPV4_SRC",
646 "ip": ipSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700647 if ipDst:
648 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700649 { "type": "IPV4_DST",
650 "ip": ipDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700651 if tcpSrc:
652 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700653 { "type": "TCP_SRC",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700654 "tcpPort": tcpSrc } )
655 if tcpDst:
656 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700657 { "type": "TCP_DST",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700658 "tcpPort": tcpDst } )
659 if ipProto:
660 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700661 { "type": "IP_PROTO",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700662 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700663 if vlanId:
664 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700665 { "type": "VLAN_VID",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700666 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700667
668 # TODO: Bandwidth and Lambda will be implemented if needed
669
Jon Hall3c0114c2020-08-11 15:07:42 -0700670 response = None
kelvin-onlabb50074f2015-07-27 16:18:32 -0700671 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700672 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700673 ip = self.ip_address
674 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700675 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlabb50074f2015-07-27 16:18:32 -0700676 "from topo file" )
677 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700678 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000679 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700680 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700681
682 main.log.debug( intentJson )
683
kelvin-onlabb50074f2015-07-27 16:18:32 -0700684 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700685 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700686 main.log.info( self.name + ": Successfully POST point" +
687 " intent between ingress: " + ingressDevice +
688 " and egress: " + egressDevice + " devices" )
689 return main.TRUE
690 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700691 main.log.error( "Error with REST request, response was: %s: %s" %
692 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700693 return main.FALSE
694
Jon Halle401b092015-09-23 13:34:24 -0700695 except ( AttributeError, TypeError ):
696 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700697 return None
Jon Halle401b092015-09-23 13:34:24 -0700698 except Exception:
699 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700700 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700701
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700702 def addSinglepointToMultipointIntent( self,
703 ingressDevice,
704 egressDeviceList,
705 portEgressList,
706 appId='org.onosproject.cli',
707 portIngress="",
708 ethType="",
709 ethSrc="",
710 ethDst="",
711 bandwidth="",
712 lambdaAlloc=False,
713 ipProto="",
714 ipSrc="",
715 ipDst="",
716 tcpSrc="",
717 tcpDst="",
718 partial=False,
719 ip="DEFAULT",
720 port="DEFAULT",
721 vlanId="" ):
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700722 """
723 Description:
724 Adds a point-to-multi point intent ( uni-directional ) by
725 specifying device id's and optional fields
726 Required:
727 * ingressDevice: device id of ingress device
728 * egressDevice: device id of egress device
729 * portEgressList: a list of port id of egress device
730
731 Optional:
732 * portIngress: port id of ingress device
733 * ethType: specify ethType
734 * ethSrc: specify ethSrc ( i.e. src mac addr )
735 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000736 * bandwidth: specify bandwidth capacity of link (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700737 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000738 for the specified intent (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700739 * ipProto: specify ip protocol
740 * ipSrc: specify ip source address with mask eg. ip#/24
741 * ipDst: specify ip destination address eg. ip#/24
742 * tcpSrc: specify tcp source port
743 * tcpDst: specify tcp destination port
744 Returns:
745 Returns main.TRUE for successful requests; Returns main.FALSE if
746 no ingress|egress port found and if error on requests;
747 Returns None for exceptions
748 NOTE:
749 The ip and port option are for the requests input's ip and port
750 of the ONOS node
751 """
752 try:
753
754 if "/" in ingressDevice:
755 if not portIngress:
756 ingressPort = ingressDevice.split( "/" )[ 1 ]
757 ingressDevice = ingressDevice.split( "/" )[ 0 ]
758 else:
759 if not portIngress:
760 main.log.debug( self.name + ": Ingress port not specified" )
761 return main.FALSE
762 index = 0
763 for egressDevice in egressDeviceList:
764 if "/" in egressDevice:
765 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
766 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
767 else:
768 if not portEgressList:
769 main.log.debug( self.name + ": Egress port not specified" )
770 return main.FALSE
771 index = index + 1
772
773 intentJson = { "ingressPoint": { "device": ingressDevice,
774 "port": ingressPort },
775 "selector": { "criteria": [] },
776 "priority": 55,
777 "treatment": { "deferred": [],
778 "instructions": [] },
779 "egressPoint": { "connectPoints": [] },
780 "appId": appId,
781 "type": "SinglePointToMultiPointIntent",
782 "constraints": [ { "type": "LinkTypeConstraint",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700783 "types": [ "OPTICAL" ],
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700784 "inclusive": "false" } ] }
785
786 index = 0
787 for ep in portEgressList:
788 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
789 { "device": egressDeviceList[ index ],
790 "port": ep } )
791 index += 1
792
793 if ethType == "IPV4":
794 intentJson[ 'selector' ][ 'criteria' ].append(
795 { "type": "ETH_TYPE",
796 "ethType": 2048 } )
797 elif ethType:
798 intentJson[ 'selector' ][ 'criteria' ].append(
799 { "type": "ETH_TYPE",
800 "ethType": ethType } )
801
802 if ethSrc:
803 intentJson[ 'selector' ][ 'criteria' ].append(
804 { "type": "ETH_SRC",
805 "mac": ethSrc } )
806
807 if ethDst:
808 for dst in ethDst:
809 if dst:
810 intentJson[ 'selector' ][ 'criteria' ].append(
811 { "type": "ETH_DST",
812 "mac": dst } )
813 if tcpSrc:
814 intentJson[ 'selector' ][ 'criteria' ].append(
815 { "type": "TCP_SRC",
816 "tcpPort": tcpSrc } )
817 if tcpDst:
818 intentJson[ 'selector' ][ 'criteria' ].append(
819 { "type": "TCP_DST",
820 "tcpPort": tcpDst } )
821 if ipProto:
822 intentJson[ 'selector' ][ 'criteria' ].append(
823 { "type": "IP_PROTO",
824 "protocol": ipProto } )
825 if vlanId:
826 intentJson[ 'selector' ][ 'criteria' ].append(
827 { "type": "VLAN_VID",
828 "vlanId": vlanId } )
829
830 # TODO: Bandwidth and Lambda will be implemented if needed
831
Jon Hall3c0114c2020-08-11 15:07:42 -0700832 response = None
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700833 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700834 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700835 ip = self.ip_address
836 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700837 main.log.warn( self.name + ": No port given, reverting to port " +
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700838 "from topo file" )
839 port = self.port
840 response = self.send( method="POST",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700841 url="/intents", ip=ip, port=port,
842 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700843
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700844 main.log.debug( intentJson )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700845
846 if response:
847 if "201" in str( response[ 0 ] ):
848 main.log.info( self.name + ": Successfully POST point" +
849 " intent between ingress: " + ingressDevice +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700850 " and egress: " + str( egressDeviceList ) + " devices" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700851 return main.TRUE
852 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700853 main.log.error( "Error with REST request, response was: %s: %s" % ( response[ 0 ], response[ 1 ] ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700854 return main.FALSE
855 else:
856 main.log.error( "REST request has no response." )
857 return main.FALSE
858
859 except ( AttributeError, TypeError ):
860 main.log.exception( self.name + ": Object not as expected" )
861 return None
862 except Exception:
863 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700864 main.cleanAndExit()
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700865
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700866 def removeIntent( self, intentId, appId='org.onosproject.cli',
867 ip="DEFAULT", port="DEFAULT" ):
868 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700869 Remove intent for specified application id and intent id;
870 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700871 """
872 try:
Jon Hall3c0114c2020-08-11 15:07:42 -0700873 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700874 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700875 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700876 ip = self.ip_address
877 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700878 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700879 "from topo file" )
880 port = self.port
881 # NOTE: REST url requires the intent id to be in decimal form
882 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700883 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000884 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700885 if response:
886 if 200 <= response[ 0 ] <= 299:
887 return main.TRUE
888 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700889 main.log.error( "Error with REST request, response was: %s: %s" %
890 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700891 return main.FALSE
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!" )
Devin Lim44075962017-08-11 10:56:37 -0700897 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700898
Jon Hall3c0114c2020-08-11 15:07:42 -0700899 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700900 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700901 Description:
902 Gets all intents ID using intents function
903 Returns:
904 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700905 """
906 try:
907 intentIdList = []
Jon Hall3c0114c2020-08-11 15:07:42 -0700908 intentsJson = json.loads( self.intents( ip=ip, port=port ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700909 for intent in intentsJson:
910 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700911 if not intentIdList:
Jon Hall3c0114c2020-08-11 15:07:42 -0700912 main.log.debug( self.name + ": Cannot find any intents" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700913 return main.FALSE
914 else:
915 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700916 except ( AttributeError, TypeError ):
917 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700918 return None
Jon Halle401b092015-09-23 13:34:24 -0700919 except Exception:
920 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700921 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700922
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700923 def removeAllIntents( self, intentIdList ='ALL', appId='org.onosproject.cli',
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700924 ip="DEFAULT", port="DEFAULT", delay=5 ):
925 """
926 Description:
927 Remove all the intents
928 Returns:
929 Returns main.TRUE if all intents are removed, otherwise returns
930 main.FALSE; Returns None for exception
931 """
932 try:
933 results = []
934 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700935 # intentIdList = self.getIntentsId( ip=ip, port=port )
936 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700937
938 main.log.info( self.name + ": Removing intents " +
939 str( intentIdList ) )
940
941 if isinstance( intentIdList, types.ListType ):
942 for intent in intentIdList:
943 results.append( self.removeIntent( intentId=intent,
944 appId=appId,
945 ip=ip,
946 port=port ) )
947 # Check for remaining intents
948 # NOTE: Noticing some delay on Deleting the intents so i put
949 # this time out
950 import time
951 time.sleep( delay )
952 intentRemain = len( json.loads( self.intents() ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700953 if all( result == main.TRUE for result in results ) and \
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700954 intentRemain == 0:
955 main.log.info( self.name + ": All intents are removed " )
956 return main.TRUE
957 else:
958 main.log.error( self.name + ": Did not removed all intents,"
959 + " there are " + str( intentRemain )
960 + " intents remaining" )
961 return main.FALSE
962 else:
963 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700964 except ( AttributeError, TypeError ):
965 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700966 return None
Jon Halle401b092015-09-23 13:34:24 -0700967 except Exception:
968 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700969 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700970
971 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
972 """
973 Description:
974 Get a list of dictionary of all discovered hosts
975 Returns:
976 Returns a list of dictionary of information of the hosts currently
977 discovered by ONOS; Returns main.FALSE if error on requests;
978 Returns None for exception
979 """
980 try:
981 output = None
982 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700983 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700984 ip = self.ip_address
985 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700986 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -0700987 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700988 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000989 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700990 if response:
991 if 200 <= response[ 0 ] <= 299:
992 output = response[ 1 ]
993 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700994 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700995 b = json.dumps( a )
996 return b
997 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700998 main.log.error( "Error with REST request, response was: %s: %s" %
999 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001000 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001001 except ( AttributeError, AssertionError, TypeError ):
1002 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001003 return None
Jon Halle401b092015-09-23 13:34:24 -07001004 except Exception:
1005 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001006 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001007
1008 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
1009 """
1010 Description:
1011 Gets the information from the given host
1012 Required:
1013 str mac - MAC address of the host
1014 Optional:
1015 str vlan - VLAN tag of the host, defaults to -1
1016 Returns:
1017 Return the host id from the hosts/mac/vlan output in REST api
1018 whose 'id' contains mac/vlan; Returns None for exception;
1019 Returns main.FALSE if error on requests
1020
1021 NOTE:
1022 Not sure what this function should do, any suggestion?
1023 """
1024 try:
1025 output = None
1026 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001027 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001028 ip = self.ip_address
1029 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001030 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001031 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001032 port = self.port
1033 query = "/" + mac + "/" + vlan
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001034 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001035 if response:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001036 # NOTE: What if the person wants other values? would it be better
1037 # to have a function that gets a key and return a value instead?
1038 # This function requires mac and vlan and returns an ID which
1039 # makes this current function useless
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001040 if 200 <= response[ 0 ] <= 299:
1041 output = response[ 1 ]
1042 hostId = json.loads( output ).get( 'id' )
1043 return hostId
1044 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001045 main.log.error( "Error with REST request, response was: %s: %s" %
1046 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001047 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001048 except ( AttributeError, TypeError ):
1049 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001050 return None
Jon Halle401b092015-09-23 13:34:24 -07001051 except Exception:
1052 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001053 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001054
1055 def topology( self, ip="DEFAULT", port="DEFAULT" ):
1056 """
1057 Description:
1058 Gets the overview of network topology
1059 Returns:
1060 Returns a dictionary containing information about network topology;
1061 Returns None for exception
1062 """
1063 try:
1064 output = None
1065 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001066 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001067 ip = self.ip_address
1068 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001069 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001070 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001071 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001072 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001073 if response:
1074 if 200 <= response[ 0 ] <= 299:
1075 output = response[ 1 ]
1076 a = json.loads( output )
1077 b = json.dumps( a )
1078 return b
1079 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001080 main.log.error( "Error with REST request, response was: %s: %s" %
1081 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001082 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001083 except ( AttributeError, TypeError ):
1084 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001085 return None
Jon Halle401b092015-09-23 13:34:24 -07001086 except Exception:
1087 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001088 main.cleanAndExit()
Jon Halle401b092015-09-23 13:34:24 -07001089
1090 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1091 """
1092 Description:
1093 Get the devices discovered by ONOS is json string format
1094 Returns:
1095 a json string of the devices currently discovered by ONOS OR
1096 main.FALSE if there is an error in the request OR
1097 Returns None for exception
1098 """
1099 try:
1100 output = None
1101 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001102 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Halle401b092015-09-23 13:34:24 -07001103 ip = self.ip_address
1104 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001105 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Halle401b092015-09-23 13:34:24 -07001106 "from topo file" )
1107 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001108 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001109 if response:
1110 if 200 <= response[ 0 ] <= 299:
1111 output = response[ 1 ]
1112 a = json.loads( output ).get( 'devices' )
1113 assert a is not None, "Error parsing json object"
1114 b = json.dumps( a )
1115 return b
1116 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001117 main.log.error( "Error with REST request, response was: %s: %s" %
1118 ( response[ 0 ], response[ 1 ] ) )
Jon Halle401b092015-09-23 13:34:24 -07001119 return main.FALSE
1120 except ( AttributeError, AssertionError, TypeError ):
1121 main.log.exception( self.name + ": Object not as expected" )
1122 return None
1123 except Exception:
1124 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001125 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001126
1127 def getIntentState( self, intentsId, intentsJson=None,
1128 ip="DEFAULT", port="DEFAULT" ):
1129 """
1130 Description:
1131 Get intent state.
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001132 Accepts a single intent ID (string type) or a list of intent IDs.
1133 Returns the state(string type) of the id if a single intent ID is
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001134 accepted.
1135 Required:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001136 intentId: intent ID (string type)
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001137 intentsJson: parsed json object from the onos:intents api
1138 Returns:
1139 Returns a dictionary with intent IDs as the key and its
1140 corresponding states as the values; Returns None for invalid IDs or
1141 Type error and any exceptions
1142 NOTE:
1143 An intent's state consist of INSTALLED,WITHDRAWN etc.
1144 """
1145 try:
1146 state = "State is Undefined"
1147 if not intentsJson:
1148 intentsJsonTemp = json.loads( self.intents() )
1149 else:
1150 intentsJsonTemp = json.loads( intentsJson )
1151 if isinstance( intentsId, types.StringType ):
1152 for intent in intentsJsonTemp:
1153 if intentsId == intent[ 'id' ]:
1154 state = intent[ 'state' ]
1155 return state
Jon Hall3c0114c2020-08-11 15:07:42 -07001156 main.log.info( self.name + ": Cannot find intent ID" + str( intentsId ) +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001157 " on the list" )
1158 return state
1159 elif isinstance( intentsId, types.ListType ):
1160 dictList = []
1161 for i in xrange( len( intentsId ) ):
1162 stateDict = {}
1163 for intents in intentsJsonTemp:
1164 if intentsId[ i ] == intents[ 'id' ]:
1165 stateDict[ 'state' ] = intents[ 'state' ]
1166 stateDict[ 'id' ] = intentsId[ i ]
1167 dictList.append( stateDict )
1168 break
1169 if len( intentsId ) != len( dictList ):
Jon Hall3c0114c2020-08-11 15:07:42 -07001170 main.log.info( self.name + ": Cannot find some of the intent ID state" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001171 return dictList
1172 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001173 main.log.info( self.name + ": Invalid intents ID entry" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001174 return None
1175
Jon Halle401b092015-09-23 13:34:24 -07001176 except ( AttributeError, TypeError ):
1177 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001178 return None
Jon Halle401b092015-09-23 13:34:24 -07001179 except Exception:
1180 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001181 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001182
1183 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001184 ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001185 """
1186 Description:
1187 Check intents state based on expected state which defaults to
1188 INSTALLED state
1189 Required:
1190 intentsId - List of intents ID to be checked
1191 Optional:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001192 expectedState - Check the expected state(s) of each intents
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001193 state in the list.
1194 *NOTE: You can pass in a list of expected state,
1195 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1196 Return:
1197 Returns main.TRUE only if all intent are the same as expected states
1198 , otherwise, returns main.FALSE; Returns None for general exception
1199 """
1200 try:
1201 # Generating a dictionary: intent id as a key and state as value
1202 returnValue = main.TRUE
1203 if intentsId == "ALL":
1204 intentsId = self.getIntentsId( ip=ip, port=port )
1205 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1206
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001207 if len( intentsId ) != len( intentsDict ):
1208 main.log.error( self.name + ": There is something wrong " +
1209 "getting intents state" )
1210 return main.FALSE
1211
1212 if isinstance( expectedState, types.StringType ):
1213 for intents in intentsDict:
1214 if intents.get( 'state' ) != expectedState:
Jon Hall3c0114c2020-08-11 15:07:42 -07001215 main.log.debug( self.name + ": Intent ID - " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001216 intents.get( 'id' ) +
1217 " actual state = " +
1218 intents.get( 'state' )
1219 + " does not equal expected state = "
1220 + expectedState )
1221 returnValue = main.FALSE
1222
1223 elif isinstance( expectedState, types.ListType ):
1224 for intents in intentsDict:
1225 if not any( state == intents.get( 'state' ) for state in
1226 expectedState ):
Jon Hall3c0114c2020-08-11 15:07:42 -07001227 main.log.debug( self.name + ": Intent ID - " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001228 intents.get( 'id' ) +
1229 " actual state = " +
1230 intents.get( 'state' ) +
1231 " does not equal expected states = "
1232 + str( expectedState ) )
1233 returnValue = main.FALSE
1234
1235 if returnValue == main.TRUE:
1236 main.log.info( self.name + ": All " +
1237 str( len( intentsDict ) ) +
1238 " intents are in " + str( expectedState ) +
1239 " state" )
1240 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001241 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001242 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001243 return None
Jon Halle401b092015-09-23 13:34:24 -07001244 except Exception:
1245 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001246 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001247
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001248 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001249 """
1250 Description:
1251 Get flows currently added to the system
1252 NOTE:
1253 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001254 the REST output
1255
1256 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001257 """
1258 try:
1259 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001260 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001261 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001262 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001263 ip = self.ip_address
1264 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001265 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001266 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001267 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001268 if subjectKey and not subjectClass:
1269 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1270 if subjectClass:
1271 url += "/" + subjectClass
1272 if subjectKey:
1273 url += "/" + subjectKey
1274 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001275 if response:
1276 if 200 <= response[ 0 ] <= 299:
1277 output = response[ 1 ]
1278 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001279 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001280 b = json.dumps( a )
1281 return b
1282 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001283 main.log.error( "Error with REST request, response was: %s: %s" %
1284 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001285 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001286 except ( AttributeError, AssertionError, TypeError ):
1287 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001288 return None
Jon Halle401b092015-09-23 13:34:24 -07001289 except Exception:
1290 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001291 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001292
Jon Halle401b092015-09-23 13:34:24 -07001293 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001294 """
1295 Description:
1296 Gets all the flows of the device or get a specific flow in the
1297 device by giving its flow ID
1298 Required:
Jon Halle401b092015-09-23 13:34:24 -07001299 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001300 Optional:
1301 int/hex flowId - ID of the flow
1302 """
1303 try:
1304 output = None
1305 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001306 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001307 ip = self.ip_address
1308 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001309 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001310 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001311 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001312 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001313 if flowId:
1314 url += "/" + str( int( flowId ) )
1315 print url
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001316 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001317 if response:
1318 if 200 <= response[ 0 ] <= 299:
1319 output = response[ 1 ]
1320 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001321 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001322 b = json.dumps( a )
1323 return b
1324 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001325 main.log.error( "Error with REST request, response was: %s: %s" %
1326 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001327 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001328 except ( AttributeError, AssertionError, TypeError ):
1329 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001330 return None
Jon Halle401b092015-09-23 13:34:24 -07001331 except Exception:
1332 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001333 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001334
GlennRC073e8bc2015-10-27 17:11:28 -07001335 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001336 """
1337 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001338 Sends a single flow to the specified device. This function exists
1339 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001340 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001341 * The flow in json
1342 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001343 Returns:
1344 Returns main.TRUE for successful requests; Returns main.FALSE
1345 if error on requests;
1346 Returns None for exceptions
1347 NOTE:
1348 The ip and port option are for the requests input's ip and port
1349 of the ONOS node
1350 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001351
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001352 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001353 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07001354 main.log.debug( self.name + ": Adding flow: " + self.pprint( flowJson ) )
1355 response = None
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001356 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001357 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001358 ip = self.ip_address
1359 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001360 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001361 "from topo file" )
1362 port = self.port
1363 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001364 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001365 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001366 data=json.dumps( flowJson ) )
1367 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001368 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001369 main.log.info( self.name + ": Successfully POST flow" +
1370 "in device: " + str( deviceId ) )
1371 return main.TRUE
1372 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001373 main.log.error( "Error with REST request, response was: %s: %s" %
1374 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001375 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001376 except NotImplementedError as e:
1377 raise e # Inform the caller
1378 except ( AttributeError, TypeError ):
1379 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001380 return None
Jon Halle401b092015-09-23 13:34:24 -07001381 except Exception:
1382 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001383 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001384
GlennRC073e8bc2015-10-27 17:11:28 -07001385 def addFlow( self,
1386 deviceId,
1387 appId=0,
1388 ingressPort="",
1389 egressPort="",
1390 ethType="",
1391 ethSrc="",
1392 ethDst="",
1393 vlan="",
1394 ipProto="",
1395 ipSrc=(),
1396 ipDst=(),
1397 tcpSrc="",
1398 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001399 udpDst="",
1400 udpSrc="",
1401 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001402 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301403 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001404 ip="DEFAULT",
1405 port="DEFAULT",
1406 debug=False ):
1407 """
1408 Description:
1409 Creates a single flow in the specified device
1410 Required:
1411 * deviceId: id of the device
1412 Optional:
1413 * ingressPort: port ingress device
1414 * egressPort: port of egress device
1415 * ethType: specify ethType
1416 * ethSrc: specify ethSrc ( i.e. src mac addr )
1417 * ethDst: specify ethDst ( i.e. dst mac addr )
1418 * ipProto: specify ip protocol
1419 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001420 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001421 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001422 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001423 * tcpSrc: specify tcp source port
1424 * tcpDst: specify tcp destination port
1425 Returns:
1426 Returns main.TRUE for successful requests; Returns main.FALSE
1427 if error on requests;
1428 Returns None for exceptions
1429 NOTE:
1430 The ip and port option are for the requests input's ip and port
1431 of the ONOS node
1432 """
1433 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001434 flowJson = { "priority": priority,
1435 "isPermanent": "true",
1436 "timeout": 0,
1437 "deviceId": deviceId,
1438 "treatment": { "instructions": [] },
1439 "selector": { "criteria": [] }}
GlennRC073e8bc2015-10-27 17:11:28 -07001440 if appId:
1441 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301442
1443 if groupId:
1444 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001445 "type": "GROUP",
1446 "groupId": groupId } )
kavitha Alagesan373e0552016-11-22 05:22:05 +05301447
GlennRC073e8bc2015-10-27 17:11:28 -07001448 if egressPort:
1449 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001450 "type": "OUTPUT",
1451 "port": egressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001452 if ingressPort:
1453 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001454 "type": "IN_PORT",
1455 "port": ingressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001456 if ethType:
1457 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001458 "type": "ETH_TYPE",
1459 "ethType": ethType } )
GlennRC073e8bc2015-10-27 17:11:28 -07001460 if ethSrc:
1461 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001462 "type": "ETH_SRC",
1463 "mac": ethSrc } )
GlennRC073e8bc2015-10-27 17:11:28 -07001464 if ethDst:
1465 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001466 "type": "ETH_DST",
1467 "mac": ethDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001468 if vlan:
1469 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001470 "type": "VLAN_VID",
1471 "vlanId": vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001472 if mpls:
1473 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001474 "type": "MPLS_LABEL",
1475 "label": mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001476 if ipSrc:
1477 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001478 "type": ipSrc[ 0 ],
1479 "ip": ipSrc[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001480 if ipDst:
1481 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001482 "type": ipDst[ 0 ],
1483 "ip": ipDst[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001484 if tcpSrc:
1485 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001486 "type": "TCP_SRC",
GlennRC073e8bc2015-10-27 17:11:28 -07001487 "tcpPort": tcpSrc } )
1488 if tcpDst:
1489 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001490 "type": "TCP_DST",
GlennRC073e8bc2015-10-27 17:11:28 -07001491 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001492 if udpSrc:
1493 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001494 "type": "UDP_SRC",
GlennRC956ea742015-11-05 16:14:15 -08001495 "udpPort": udpSrc } )
1496 if udpDst:
1497 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001498 "type": "UDP_DST",
GlennRC956ea742015-11-05 16:14:15 -08001499 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001500 if ipProto:
1501 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001502 "type": "IP_PROTO",
GlennRC073e8bc2015-10-27 17:11:28 -07001503 "protocol": ipProto } )
1504
Jeremy Ronquillo29260cd2017-10-13 13:30:54 -07001505 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug, ip=ip, port=port )
GlennRC073e8bc2015-10-27 17:11:28 -07001506
1507 except ( AttributeError, TypeError ):
1508 main.log.exception( self.name + ": Object not as expected" )
1509 return None
1510 except Exception:
1511 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001512 main.cleanAndExit()
GlennRC073e8bc2015-10-27 17:11:28 -07001513
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001514 def removeFlow( self, deviceId, flowId,
1515 ip="DEFAULT", port="DEFAULT" ):
1516 """
1517 Description:
1518 Remove specific device flow
1519 Required:
1520 str deviceId - id of the device
1521 str flowId - id of the flow
1522 Return:
1523 Returns main.TRUE if successfully deletes flows, otherwise
1524 Returns main.FALSE, Returns None on error
1525 """
1526 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001527 response = None
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001528 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001529 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001530 ip = self.ip_address
1531 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001532 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001533 "from topo file" )
1534 port = self.port
1535 # NOTE: REST url requires the intent id to be in decimal form
1536 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001537 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001538 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001539 if response:
1540 if 200 <= response[ 0 ] <= 299:
1541 return main.TRUE
1542 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001543 main.log.error( "Error with REST request, response was: %s: %s" %
1544 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001545 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001546 except ( AttributeError, TypeError ):
1547 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001548 return None
Jon Halle401b092015-09-23 13:34:24 -07001549 except Exception:
1550 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001551 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001552
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001553 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001554 """
1555 Description:
1556 Check if all the current flows are in ADDED state
1557 Return:
1558 returnValue - Returns main.TRUE only if all flows are in
1559 return main.FALSE otherwise;
1560 Returns None for exception
1561 """
1562 try:
1563 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1564 returnValue = main.TRUE
1565 for flow in tempFlows:
1566 if flow.get( 'state' ) != 'ADDED':
1567 main.log.info( self.name + ": flow Id: " +
1568 str( flow.get( 'groupId' ) ) +
1569 " | state:" +
1570 str( flow.get( 'state' ) ) )
1571 returnValue = main.FALSE
1572 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001573 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001574 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001575 return None
1576 except Exception:
1577 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001578 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001579
1580 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1581 subjectClass=None, subjectKey=None, configKey=None ):
1582 """
1583 Description:
1584 Get a json object with the ONOS network configurations
1585 Returns:
1586 A json object containing the network configuration in
1587 ONOS; Returns main.FALSE if error on requests;
1588 Returns None for exception
1589 """
1590 try:
1591 output = None
1592 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001593 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001594 ip = self.ip_address
1595 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001596 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001597 "from topo file" )
1598 port = self.port
1599 url = "/network/configuration"
1600 if subjectClass:
1601 url += "/" + subjectClass
1602 if subjectKey:
1603 url += "/" + subjectKey
1604 if configKey:
1605 url += "/" + configKey
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001606 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001607 if response:
1608 if 200 <= response[ 0 ] <= 299:
1609 output = response[ 1 ]
1610 a = json.loads( output )
1611 b = json.dumps( a )
1612 return b
1613 elif response[ 0 ] == 404:
1614 main.log.error( "Requested configuration doesn't exist: " +
Jon Hall3c0114c2020-08-11 15:07:42 -07001615 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001616 return {}
1617 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001618 main.log.error( "Error with REST request, response was: %s: %s" %
1619 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001620 return main.FALSE
1621 except ( AttributeError, TypeError ):
1622 main.log.exception( self.name + ": Object not as expected" )
1623 return None
1624 except Exception:
1625 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001626 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001627
1628 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1629 subjectClass=None, subjectKey=None, configKey=None ):
1630 """
1631 Description:
1632 Set a json object with the ONOS network configurations
1633 Returns:
1634 Returns main.TRUE for successful requests; Returns main.FALSE
1635 if error on requests;
1636 Returns None for exceptions
1637
1638 """
1639 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001640 response = None
Jon Hall66e001c2015-11-12 09:45:10 -08001641 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001642 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001643 ip = self.ip_address
1644 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001645 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001646 "from topo file" )
1647 port = self.port
1648 url = "/network/configuration"
1649 if subjectClass:
1650 url += "/" + subjectClass
1651 if subjectKey:
1652 url += "/" + subjectKey
1653 if configKey:
1654 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001655 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001656 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001657 data=json.dumps( cfgJson ) )
1658 if response:
1659 if 200 <= response[ 0 ] <= 299:
1660 main.log.info( self.name + ": Successfully POST cfg" )
1661 return main.TRUE
1662 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001663 main.log.error( "Error with REST request, response was: %s: %s" %
1664 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001665 return main.FALSE
1666 except ( AttributeError, TypeError ):
1667 main.log.exception( self.name + ": Object not as expected" )
1668 return None
1669 except Exception:
1670 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001671 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001672
1673 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1674 subjectClass=None, subjectKey=None, configKey=None ):
1675 """
1676 Description:
1677 Remove a json object from the ONOS network configurations
1678 Returns:
1679 Returns main.TRUE for successful requests; Returns main.FALSE
1680 if error on requests;
1681 Returns None for exceptions
1682
1683 """
1684 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001685 response = None
Jon Hall66e001c2015-11-12 09:45:10 -08001686 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001687 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001688 ip = self.ip_address
1689 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001690 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001691 "from topo file" )
1692 port = self.port
1693 url = "/network/configuration"
1694 if subjectClass:
1695 url += "/" + subjectClass
1696 if subjectKey:
1697 url += "/" + subjectKey
1698 if configKey:
1699 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001700 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001701 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001702 if response:
1703 if 200 <= response[ 0 ] <= 299:
1704 main.log.info( self.name + ": Successfully delete cfg" )
1705 return main.TRUE
1706 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001707 main.log.error( "Error with REST request, response was: %s: %s" %
1708 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001709 return main.FALSE
1710 except ( AttributeError, TypeError ):
1711 main.log.exception( self.name + ": Object not as expected" )
1712 return None
1713 except Exception:
1714 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001715 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001716
Jon Hall10e2ab82020-09-15 17:14:54 -07001717 def getXconnect( self, ip="DEFAULT", port="DEFAULT" ):
1718 """
1719 Description:
1720 Get xconnects
1721 Returns:
1722 Return xconnects json object
1723 Returns None for exceptions
1724
1725 """
1726 try:
1727 base = "/onos/segmentrouting"
1728 response = None
1729 if ip == "DEFAULT":
1730 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1731 ip = self.ip_address
1732 if port == "DEFAULT":
1733 main.log.warn( self.name + ": No port given, reverting to port " +
1734 "from topo file" )
1735 port = self.port
1736 url = "/xconnect"
1737 response = self.send( method="GET",
1738 base=base,
1739 url=url, ip = ip, port = port )
1740 if response:
1741 if 200 <= response[ 0 ] <= 299:
1742 main.log.info( self.name + ": Successfully POST cfg" )
1743 return main.TRUE
1744 else:
1745 main.log.error( "Error with REST request, response was: %s: %s" %
1746 ( response[ 0 ], response[ 1 ] ) )
1747 return main.FALSE
1748 except ( AttributeError, TypeError ):
1749 main.log.exception( self.name + ": Object not as expected" )
1750 return None
1751 except Exception:
1752 main.log.exception( self.name + ": Uncaught exception!" )
1753 main.cleanAndExit()
1754
1755 def setXconnect( self, deviceId, vlanId, port1, port2, ip="DEFAULT", port="DEFAULT" ):
1756 """
1757 Description:
1758 Set xconnects
1759 Returns:
1760 Returns main.TRUE for successful requests; Returns main.FALSE
1761 if error on requests;
1762 Returns None for exceptions
1763
1764 """
1765 try:
1766 cfgJson = json.loads( '{"deviceId": "%s", "vlanId": "%s", "endpoints":[%s,%s]}' %
1767 ( deviceId, vlanId, port1, port2 ) )
1768 response = self.setXconnectJson( cfgJson, ip=ip, port=port )
1769 except ( AttributeError, TypeError ):
1770 main.log.exception( self.name + ": Object not as expected" )
1771 return None
1772 except Exception:
1773 main.log.exception( self.name + ": Uncaught exception!" )
1774 main.cleanAndExit()
1775
1776 def setXconnectJson( self, cfgJson, ip="DEFAULT", port="DEFAULT" ):
1777 """
1778 Description:
1779 Set xconnects
1780 Returns:
1781 Returns main.TRUE for successful requests; Returns main.FALSE
1782 if error on requests;
1783 Returns None for exceptions
1784
1785 """
1786 try:
1787 base = "/onos/segmentrouting"
1788 response = None
1789 if ip == "DEFAULT":
1790 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1791 ip = self.ip_address
1792 if port == "DEFAULT":
1793 main.log.warn( self.name + ": No port given, reverting to port " +
1794 "from topo file" )
1795 port = self.port
1796 url = "/xconnect"
1797 response = self.send( method="POST",
1798 base=base,
1799 url=url, ip = ip, port = port,
1800 data=json.dumps( cfgJson ) )
1801 if response:
1802 if 200 <= response[ 0 ] <= 299:
1803 main.log.info( self.name + ": Successfully POST cfg" )
1804 return main.TRUE
1805 else:
1806 main.log.error( "Error with REST request, response was: %s: %s" %
1807 ( response[ 0 ], response[ 1 ] ) )
1808 return main.FALSE
1809 except ( AttributeError, TypeError ):
1810 main.log.exception( self.name + ": Object not as expected" )
1811 return None
1812 except Exception:
1813 main.log.exception( self.name + ": Uncaught exception!" )
1814 main.cleanAndExit()
1815
1816 def deleteXconnect( self, cfgJson, ip="DEFAULT", port="DEFAULT" ):
1817 """
1818 Description:
1819 Remove xconnects
1820 Returns:
1821 Returns main.TRUE for successful requests; Returns main.FALSE
1822 if error on requests;
1823 Returns None for exceptions
1824
1825 """
1826 try:
1827 base = "/onos/segmentrouting"
1828 response = None
1829 if ip == "DEFAULT":
1830 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1831 ip = self.ip_address
1832 if port == "DEFAULT":
1833 main.log.warn( self.name + ": No port given, reverting to port " +
1834 "from topo file" )
1835 port = self.port
1836 url = "/xconnect"
1837 response = self.send( method="DELETE",
1838 base=base,
1839 url=url, ip = ip, port = port,
1840 data=json.dumps( cfgJson ) )
1841 if response:
1842 if 200 <= response[ 0 ] <= 299:
1843 main.log.info( self.name + ": Successfully POST cfg" )
1844 return main.TRUE
1845 else:
1846 main.log.error( "Error with REST request, response was: %s: %s" %
1847 ( response[ 0 ], response[ 1 ] ) )
1848 return main.FALSE
1849 except ( AttributeError, TypeError ):
1850 main.log.exception( self.name + ": Object not as expected" )
1851 return None
1852 except Exception:
1853 main.log.exception( self.name + ": Uncaught exception!" )
1854 main.cleanAndExit()
1855
suibin zhang17308622016-04-14 15:45:30 -07001856 def createFlowBatch( self,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001857 numSw = 1,
1858 swIndex = 1,
1859 batchSize = 1,
1860 batchIndex = 1,
1861 deviceIdpreFix = "of:",
1862 appId=0,
1863 deviceID="",
1864 ingressPort="",
1865 egressPort="",
1866 ethType="",
1867 ethSrc="",
1868 ethDst="",
1869 vlan="",
1870 ipProto="",
1871 ipSrc=(),
1872 ipDst=(),
1873 tcpSrc="",
1874 tcpDst="",
1875 udpDst="",
1876 udpSrc="",
1877 mpls="",
1878 ip="DEFAULT",
1879 port="DEFAULT",
1880 debug=False ):
suibin zhang17308622016-04-14 15:45:30 -07001881 """
1882 Description:
1883 Creates batches of MAC-rule flows for POST.
1884 Predefined MAC: 2 MS Hex digit for iterating devices
1885 Next 5 Hex digit for iterating batch numbers
1886 Next 5 Hex digit for iterating flows within a batch
1887 Required:
1888 * deviceId: id of the device
1889 Optional:
1890 * ingressPort: port ingress device
1891 * egressPort: port of egress device
1892 * ethType: specify ethType
1893 * ethSrc: specify ethSrc ( i.e. src mac addr )
1894 * ethDst: specify ethDst ( i.e. dst mac addr )
1895 * ipProto: specify ip protocol
1896 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001897 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001898 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001899 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001900 * tcpSrc: specify tcp source port
1901 * tcpDst: specify tcp destination port
1902 Returns:
1903 Returns main.TRUE for successful requests; Returns main.FALSE
1904 if error on requests;
1905 Returns None for exceptions
1906 NOTE:
1907 The ip and port option are for the requests input's ip and port
1908 of the ONOS node
1909 """
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001910 # from pprint import pprint
suibin zhang17308622016-04-14 15:45:30 -07001911
1912 flowJsonList = []
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001913 flowJsonBatch = { "flows": flowJsonList }
suibin zhang17308622016-04-14 15:45:30 -07001914 dev = swIndex
1915
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001916 for fl in range( 1, batchSize + 1 ):
1917 flowJson = { "priority": 100,
1918 "deviceId": "",
1919 "isPermanent": "true",
1920 "timeout": 0,
1921 "treatment": { "instructions": [] },
1922 "selector": { "criteria": [] }}
suibin zhang17308622016-04-14 15:45:30 -07001923
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001924 # main.log.info("fl: " + str(fl))
suibin zhang17308622016-04-14 15:45:30 -07001925 if dev <= numSw:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001926 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1927 # print deviceId
1928 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001929 dev += 1
1930 else:
1931 dev = 1
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001932 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1933 # print deviceId
1934 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001935 dev += 1
1936
1937 # ethSrc starts with "0"; ethDst starts with "1"
1938 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001939 ethS = "%02X" % int( "0" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1940 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1941 ethSrc = ':'.join( ethS[ i: i+2 ] for i in range( 0, len( ethS ), 2 ) )
1942 ethD = "%02X" % int( "1" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1943 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1944 ethDst = ':'.join( ethD[ i: i+2 ] for i in range( 0, len( ethD ), 2 ) )
suibin zhang17308622016-04-14 15:45:30 -07001945
1946 if appId:
1947 flowJson[ "appId" ] = appId
1948
1949 if egressPort:
1950 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001951 "type": "OUTPUT",
1952 "port": egressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001953 if ingressPort:
1954 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001955 "type": "IN_PORT",
1956 "port": ingressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001957 if ethType:
1958 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001959 "type": "ETH_TYPE",
1960 "ethType": ethType } )
suibin zhang17308622016-04-14 15:45:30 -07001961 if ethSrc:
1962 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001963 "type": "ETH_SRC",
1964 "mac": ethSrc } )
suibin zhang17308622016-04-14 15:45:30 -07001965 if ethDst:
1966 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001967 "type": "ETH_DST",
1968 "mac": ethDst } )
suibin zhang17308622016-04-14 15:45:30 -07001969 if vlan:
1970 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001971 "type": "VLAN_VID",
1972 "vlanId": vlan } )
suibin zhang17308622016-04-14 15:45:30 -07001973 if mpls:
1974 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001975 "type": "MPLS_LABEL",
1976 "label": mpls } )
suibin zhang17308622016-04-14 15:45:30 -07001977 if ipSrc:
1978 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001979 "type": ipSrc[ 0 ],
1980 "ip": ipSrc[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07001981 if ipDst:
1982 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001983 "type": ipDst[ 0 ],
1984 "ip": ipDst[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07001985 if tcpSrc:
1986 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001987 "type": "TCP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001988 "tcpPort": tcpSrc } )
1989 if tcpDst:
1990 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001991 "type": "TCP_DST",
suibin zhang17308622016-04-14 15:45:30 -07001992 "tcpPort": tcpDst } )
1993 if udpSrc:
1994 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001995 "type": "UDP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001996 "udpPort": udpSrc } )
1997 if udpDst:
1998 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001999 "type": "UDP_DST",
suibin zhang17308622016-04-14 15:45:30 -07002000 "udpPort": udpDst } )
2001 if ipProto:
2002 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002003 "type": "IP_PROTO",
suibin zhang17308622016-04-14 15:45:30 -07002004 "protocol": ipProto } )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002005 # pprint(flowJson)
2006 flowJsonList.append( flowJson )
suibin zhang17308622016-04-14 15:45:30 -07002007
Jon Hall3c0114c2020-08-11 15:07:42 -07002008 main.log.info( self.name + ": Number of flows in batch: " + str( len( flowJsonList ) ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002009 flowJsonBatch[ 'flows' ] = flowJsonList
2010 # pprint(flowJsonBatch)
suibin zhang17308622016-04-14 15:45:30 -07002011
2012 return flowJsonBatch
2013
suibin zhang17308622016-04-14 15:45:30 -07002014 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
2015 """
2016 Description:
2017 Sends a single flow batch through /flows REST API.
2018 Required:
2019 * The batch of flows
2020 Returns:
2021 Returns main.TRUE for successful requests; Returns main.FALSE
2022 if error on requests;
2023 Returns None for exceptions
2024 NOTE:
2025 The ip and port option are for the requests input's ip and port
2026 of the ONOS node
2027 """
suibin zhang17308622016-04-14 15:45:30 -07002028
2029 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002030 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07002031 main.log.debug( self.name + ": Adding flow: " + self.pprint( batch ) )
2032 response = None
suibin zhang17308622016-04-14 15:45:30 -07002033 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002034 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
suibin zhang17308622016-04-14 15:45:30 -07002035 ip = self.ip_address
2036 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002037 main.log.warn( self.name + ": No port given, reverting to port " +
suibin zhang17308622016-04-14 15:45:30 -07002038 "from topo file" )
2039 port = self.port
2040 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07002041 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002042 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07002043 data=json.dumps( batch ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002044 # main.log.info("Post response is: ", str(response[0]))
2045 if response[ 0 ] == 200:
suibin zhang17308622016-04-14 15:45:30 -07002046 main.log.info( self.name + ": Successfully POST flow batch" )
2047 return main.TRUE, response
2048 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002049 main.log.error( "Error with REST request, response was: %s: %s" %
2050 ( response[ 0 ], response[ 1 ] ) )
You Wang7b5b2262016-11-10 13:54:56 -08002051 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07002052 except NotImplementedError as e:
2053 raise e # Inform the caller
2054 except ( AttributeError, TypeError ):
2055 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08002056 return None, None
suibin zhang17308622016-04-14 15:45:30 -07002057 except Exception:
2058 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002059 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07002060
2061 def removeFlowBatch( self, batch={},
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002062 ip="DEFAULT", port="DEFAULT" ):
suibin zhang17308622016-04-14 15:45:30 -07002063 """
2064 Description:
2065 Remove a batch of flows
2066 Required:
2067 flow batch
2068 Return:
2069 Returns main.TRUE if successfully deletes flows, otherwise
2070 Returns main.FALSE, Returns None on error
2071 """
2072 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07002073 response = None
suibin zhang17308622016-04-14 15:45:30 -07002074 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002075 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
suibin zhang17308622016-04-14 15:45:30 -07002076 ip = self.ip_address
2077 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002078 main.log.warn( self.name + ": No port given, reverting to port " +
suibin zhang17308622016-04-14 15:45:30 -07002079 "from topo file" )
2080 port = self.port
2081 # NOTE: REST url requires the intent id to be in decimal form
2082
suibin zhang116647a2016-05-06 16:30:09 -07002083 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002084 url="/flows/", ip = ip, port = port,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002085 data = json.dumps( batch ) )
suibin zhang17308622016-04-14 15:45:30 -07002086 if response:
2087 if 200 <= response[ 0 ] <= 299:
2088 return main.TRUE
2089 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002090 main.log.error( "Error with REST request, response was: %s: %s" %
2091 ( response[ 0 ], response[ 1 ] ) )
suibin zhang17308622016-04-14 15:45:30 -07002092 return main.FALSE
2093 except ( AttributeError, TypeError ):
2094 main.log.exception( self.name + ": Object not as expected" )
2095 return None
2096 except Exception:
2097 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002098 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002099
2100 def getTopology( self, topologyOutput ):
2101 """
2102 Definition:
2103 Loads a json topology output
2104 Return:
2105 topology = current ONOS topology
2106 """
2107 import json
2108 try:
2109 # either onos:topology or 'topology' will work in CLI
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002110 topology = json.loads( topologyOutput )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002111 main.log.debug( topology )
2112 return topology
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002113 except Exception:
2114 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002115 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002116
2117 def checkStatus(
2118 self,
Devin Lim142b5342017-07-20 15:22:39 -07002119 numswitch,
2120 numlink,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002121 logLevel="info" ):
2122 """
2123 Checks the number of switches & links that ONOS sees against the
2124 supplied values. By default this will report to main.log, but the
2125 log level can be specific.
2126
Devin Lim142b5342017-07-20 15:22:39 -07002127 Params: numswitch = expected number of switches
2128 numlink = expected number of links
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002129 logLevel = level to log to.
2130 Currently accepts 'info', 'warn' and 'report'
2131
2132 Returns: main.TRUE if the number of switches and links are correct,
2133 main.FALSE if the number of switches and links is incorrect,
2134 and main.ERROR otherwise
2135 """
2136 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07002137 topology = self.getTopology( self.topology() )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002138 # summary = self.summary()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002139 if topology == {}:
2140 return main.ERROR
2141 output = ""
2142 # Is the number of switches is what we expected
2143 devices = topology.get( 'devices', False )
2144 links = topology.get( 'links', False )
Devin Lim142b5342017-07-20 15:22:39 -07002145 if devices is False or links is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002146 return main.ERROR
Devin Lim142b5342017-07-20 15:22:39 -07002147 switchCheck = ( int( devices ) == int( numswitch ) )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002148 # Is the number of links is what we expected
Devin Lim142b5342017-07-20 15:22:39 -07002149 linkCheck = ( int( links ) == int( numlink ) )
2150 if switchCheck and linkCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002151 # We expected the correct numbers
2152 output = output + "The number of links and switches match "\
2153 + "what was expected"
2154 result = main.TRUE
2155 else:
2156 output = output + \
2157 "The number of links and switches does not match " + \
2158 "what was expected"
2159 result = main.FALSE
2160 output = output + "\n ONOS sees %i devices" % int( devices )
Devin Lim142b5342017-07-20 15:22:39 -07002161 output = output + " (%i expected) " % int( numswitch )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002162 output = output + "and %i links " % int( links )
Devin Lim142b5342017-07-20 15:22:39 -07002163 output = output + "(%i expected)" % int( numlink )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002164 if logLevel == "report":
2165 main.log.report( output )
2166 elif logLevel == "warn":
2167 main.log.warn( output )
2168 else:
2169 main.log.info( output )
2170 return result
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002171 except Exception:
2172 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002173 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302174
Jon Hall3c0114c2020-08-11 15:07:42 -07002175 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId,
2176 ip="DEFAULT", port="DEFAULT", debug=False ):
kavitha Alagesan373e0552016-11-22 05:22:05 +05302177 """
2178 Description:
2179 Creates a single Group for the specified device.
2180 Required:
2181 * deviceId: id of the device
2182 * type: Type of the Group
2183 * bucketList: Buckets to be added to the group
2184 * appCookie: Cookie for the Group
2185 * groupId: Id of the Group
2186 Returns:
2187 Returns main.TRUE for successful requests; Returns main.FALSE
2188 if error on requests;
2189 Returns None for exceptions
2190 Note:
2191 The ip and port option are for the requests input's ip and port
2192 of the ONOS node
2193 """
2194 try:
2195 groupJson = { "type": groupType,
2196 "appCookie": appCookie,
2197 "groupId": groupId,
2198 "buckets": bucketList
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002199 }
kavitha Alagesan373e0552016-11-22 05:22:05 +05302200 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
2201
2202 except ( AttributeError, TypeError ):
2203 main.log.exception( self.name + ": Object not as expected" )
2204 return None
2205 except Exception:
2206 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002207 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302208
2209 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
2210 """
2211 Description:
2212 Sends a single group to the specified device.
2213 Required:
2214 * deviceId: id of the device
2215 * groupJson: the group in json
2216 Returns:
2217 Returns main.TRUE for successful requests; Returns main.FALSE
2218 if error on requests;
2219 Returns None for exceptions
2220 NOTE:
2221 The ip and port option are for the requests input's ip and port
2222 of the ONOS node
2223 """
2224 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002225 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07002226 main.log.debug( self.name + ": Adding group: " + self.pprint( groupJson ) )
2227 response = None
kavitha Alagesan373e0552016-11-22 05:22:05 +05302228 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002229 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302230 ip = self.ip_address
2231 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002232 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302233 "from topo file" )
2234 port = self.port
2235 url = "/groups/" + deviceId
2236 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002237 url=url, ip = ip, port = port,
kavitha Alagesan373e0552016-11-22 05:22:05 +05302238 data=json.dumps( groupJson ) )
2239 if response:
2240 if "201" in str( response[ 0 ] ):
2241 main.log.info( self.name + ": Successfully POST group " +
2242 "in device: " + str( deviceId ) )
2243 return main.TRUE
2244 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002245 main.log.error( "Error with REST request, response was: %s: %s" %
2246 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302247 return main.FALSE
2248 except NotImplementedError as e:
2249 raise e # Inform the caller
2250 except ( AttributeError, TypeError ):
2251 main.log.exception( self.name + ": Object not as expected" )
2252 return None
2253 except Exception:
2254 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002255 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302256
2257 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2258 """
2259 Description:
2260 Get all the groups or get a specific group by giving the
2261 deviceId and appCookie
2262 Optional:
2263 * deviceId: id of the Device
2264 * appCookie: Cookie of the Group
2265 Returns:
2266 Returns Groups for successful requests; Returns main.FALSE
2267 if error on requests;
2268 Returns None for exceptions
2269 NOTE:
2270 The ip and port option are for the requests input's ip and port
2271 of the ONOS node
2272 """
2273 try:
2274 output = None
2275 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002276 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302277 ip = self.ip_address
2278 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002279 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302280 "from topo file" )
2281 port = self.port
2282 url = "/groups"
2283 if deviceId:
2284 url += "/" + deviceId
2285 if appCookie:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002286 url += "/" + appCookie
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002287 response = self.send( url=url, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302288 if response:
2289 if 200 <= response[ 0 ] <= 299:
2290 output = response[ 1 ]
2291 groupsJson = json.loads( output ).get( 'groups' )
2292 assert groupsJson is not None, "Error parsing json object"
2293 groups = json.dumps( groupsJson )
2294 return groups
2295 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002296 main.log.error( "Error with REST request, response was: %s: %s" %
2297 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302298 return main.FALSE
2299 except ( AttributeError, AssertionError, TypeError ):
2300 main.log.exception( self.name + ": Object not as expected" )
2301 return None
2302 except Exception:
2303 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002304 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302305
2306 def removeGroup( self, deviceId, appCookie,
2307 ip="DEFAULT", port="DEFAULT" ):
2308 """
2309 Description:
2310 Removes specific device group
2311 Required:
2312 * deviceId: id of the Device
2313 * appCookie: Cookie of the Group
2314 Returns:
2315 Returns main.TRUE for successful requests; Returns main.FALSE
2316 if error on requests;
2317 Returns None for exceptions
2318 NOTE:
2319 The ip and port option are for the requests input's ip and port
2320 of the ONOS node
2321
2322 """
2323 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07002324 response = None
kavitha Alagesan373e0552016-11-22 05:22:05 +05302325 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002326 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302327 ip = self.ip_address
2328 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002329 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302330 "from topo file" )
2331 port = self.port
2332 query = "/" + str( deviceId ) + "/" + str( appCookie )
2333 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002334 url="/groups" + query, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302335 if response:
2336 if 200 <= response[ 0 ] <= 299:
2337 return main.TRUE
2338 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002339 main.log.error( "Error with REST request, response was: %s: %s" %
2340 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302341 return main.FALSE
2342 except ( AttributeError, TypeError ):
2343 main.log.exception( self.name + ": Object not as expected" )
2344 return None
2345 except Exception:
2346 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002347 main.cleanAndExit()
Jon Hall50a00012021-03-08 11:06:11 -08002348
2349 def portstats( self, ip="DEFAULT", port="DEFAULT" ):
2350 """
2351 Description:
2352 Gets the portstats for each port in ONOS
2353 Returns:
2354 A list of dicts containing device id and a list of dicts containing the
2355 port statistics for each port.
2356 Returns main.FALSE if error on request;
2357 Returns None for exception
2358 """
2359 try:
2360 output = None
2361 if ip == "DEFAULT":
2362 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2363 ip = self.ip_address
2364 if port == "DEFAULT":
2365 main.log.warn( self.name + ": No port given, reverting to port " +
2366 "from topo file" )
2367 port = self.port
2368 response = self.send( url="/statistics/ports", ip = ip, port = port )
2369 if response:
2370 if 200 <= response[ 0 ] <= 299:
2371 output = response[ 1 ]
2372 a = json.loads( output ).get( 'statistics' )
2373 assert a is not None, "Error parsing json object"
2374 b = json.dumps( a )
2375 return b
2376 else:
2377 main.log.error( "Error with REST request, response was: %s: %s" %
2378 ( response[ 0 ], response[ 1 ] ) )
2379 return main.FALSE
2380 except ( AttributeError, AssertionError, TypeError ):
2381 main.log.exception( self.name + ": Object not as expected" )
2382 return None
2383 except Exception:
2384 main.log.exception( self.name + ": Uncaught exception!" )
2385 main.cleanAndExit()