blob: 2a60ba11ab24ce2172c59a1f2b032b88ddcfce5c [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:
Siddeshd9840842021-08-06 19:26:05 +000075 if not jsonObject:
76 return None
Jon Halle401b092015-09-23 13:34:24 -070077 if isinstance( jsonObject, str ):
78 jsonObject = json.loads( jsonObject )
79 return json.dumps( jsonObject, sort_keys=True,
Jeremy Ronquillo82705492017-10-18 14:19:55 -070080 indent=4, separators=( ',', ': ' ) )
Jon Halle401b092015-09-23 13:34:24 -070081 except ( TypeError, ValueError ):
Siddeshd9840842021-08-06 19:26:05 +000082 main.log.exception( "Error parsing jsonObject %s" % repr(jsonObject) )
Jon Halle401b092015-09-23 13:34:24 -070083 return None
84
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000085 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070086 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070087 """
88 Arguments:
89 str ip: ONOS IP Address
90 str port: ONOS REST Port
91 str url: ONOS REST url path.
92 NOTE that this is is only the relative path. IE "/devices"
93 str base: The base url for the given REST api. Applications could
94 potentially have their own base url
95 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070096 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070097 the request
98 dict data: Dictionary to be sent in the body of the request
99 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000100 # TODO: Authentication - simple http (user,pass) tuple
Jon Hallfc915882015-07-14 13:33:17 -0700101 # TODO: should we maybe just pass kwargs straight to response?
102 # TODO: Do we need to allow for other protocols besides http?
103 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700104 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700105 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700106 ip = self.ip_address
suibin zhangd5b6fe42016-05-12 08:48:58 -0700107 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700108 main.log.warn( self.name + ": No port given, reverting to port " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700109 "from topo file" )
110 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700111
Jon Hallfc915882015-07-14 13:33:17 -0700112 try:
113 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700114 if self.user_name and self.pwd:
Jon Hall3c0114c2020-08-11 15:07:42 -0700115 main.log.info( self.name + ": user/passwd is: " + self.user_name + "/" + self.pwd )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700116 auth = ( self.user_name, self.pwd )
Jon Hallf7234882015-08-28 13:16:31 -0700117 else:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700118 auth = None
Jon Hall3c0114c2020-08-11 15:07:42 -0700119 main.log.info( self.name + ": Sending request " + path + " using " +
Jon Hallfc915882015-07-14 13:33:17 -0700120 method.upper() + " method." )
Jon Hallf69e3162020-09-01 09:08:44 -0700121 if debug:
122 main.log.debug( self.name + ": request data: " + str( data ) )
Jon Hall8e93b412021-08-10 14:42:42 -0700123 self.log( "Sending %s to %s with %s\n" % ( method.upper(),
124 path,
125 self.pprint( data ) ) )
Jon Hallf6b1a372022-02-11 14:10:31 -0800126 try:
127 response = requests.request( method.upper(),
128 path,
129 params=query,
130 data=data,
131 auth=auth )
132 except requests.ConnectionError as e:
133 # FIXME: This isn't really the correct place for this, but it works for now
134 # Check if port-forward session is still up first
135 if hasattr( main, "Cluster"):
136 main.log.warn( self.name + ": Error sending request, checking port-forwarding status" )
137 ctrl = None
138 for c in main.Cluster.controllers:
139 if c.REST is self:
140 ctrl = c
141 break
142 if not ctrl:
143 main.log.warn( self.name + ": Could not find this node in Cluster. Can't check port-forward status" )
144 raise
145 elif ctrl.k8s:
146 ctrl.k8s.checkPortForward( ctrl.k8s.podName,
147 kubeconfig=ctrl.k8s.kubeConfig,
148 namespace=main.params[ 'kubernetes' ][ 'namespace' ] )
149 main.log.debug( self.name + ": Resending message" )
150 response = requests.request( method.upper(),
151 path,
152 params=query,
153 data=data,
154 auth=auth )
155 else:
156 raise
Jon Hall8e93b412021-08-10 14:42:42 -0700157 self.log( "Received %s code with body: %s\n" % ( response.status_code,
158 self.pprint( response.text.encode( 'utf8' ) ) ) )
Jon Hallf7234882015-08-28 13:16:31 -0700159 if debug:
160 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700161 return ( response.status_code, response.text.encode( 'utf8' ) )
You Wang7880b372019-02-27 16:50:47 -0800162 except requests.ConnectionError:
Jon Hallfc915882015-07-14 13:33:17 -0700163 main.log.exception( "Error sending request." )
You Wang7880b372019-02-27 16:50:47 -0800164 return ( None, None )
Jon Halle401b092015-09-23 13:34:24 -0700165 except Exception:
166 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700167 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700168
169 def intents( self, ip="DEFAULT", port="DEFAULT" ):
170 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700171 Description:
172 Gets a list of dictionary of all intents in the system
173 Returns:
174 A list of dictionary of intents in string type to match the cli
175 version for now; Returns main.FALSE if error on request;
176 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700177 """
178 try:
179 output = None
180 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700181 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700182 ip = self.ip_address
183 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700184 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700185 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700186 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000187 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700188 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700189 if 200 <= response[ 0 ] <= 299:
190 output = response[ 1 ]
191 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700192 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700193 b = json.dumps( a )
194 return b
Jon Hallfc915882015-07-14 13:33:17 -0700195 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700196 main.log.error( "Error with REST request, response was: %s: %s" %
197 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700198 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700199 except ( AttributeError, AssertionError, TypeError ):
200 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700201 return None
Jon Halle401b092015-09-23 13:34:24 -0700202 except Exception:
203 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700204 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700205
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700206 def intent( self, intentId, appId="org.onosproject.cli",
207 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700208 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700209 Description:
210 Get the specific intent information of the given application ID and
211 intent ID
212 Required:
213 str intentId - Intent id in hexadecimal form
214 Optional:
215 str appId - application id of intent
216 Returns:
217 Returns an information dictionary of the given intent;
218 Returns main.FALSE if error on requests; Returns None for exception
219 NOTE:
220 The GET /intents REST api command accepts application id but the
221 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700222 """
223 try:
224 output = None
225 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700226 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700227 ip = self.ip_address
228 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700229 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700230 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700231 port = self.port
232 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700233 query = "/" + str( appId ) + "/" + str( intentId )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000234 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700235 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700236 if 200 <= response[ 0 ] <= 299:
237 output = response[ 1 ]
238 a = json.loads( output )
239 return a
Jon Hallfc915882015-07-14 13:33:17 -0700240 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700241 main.log.error( "Error with REST request, response was: %s: %s" %
242 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700243 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700244 except ( AttributeError, TypeError ):
245 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700246 return None
Jon Halle401b092015-09-23 13:34:24 -0700247 except Exception:
248 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700249 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700250
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700251 def apps( self, ip="DEFAULT", port="DEFAULT" ):
252 """
253 Description:
254 Returns all the current application installed in the system
255 Returns:
256 List of dictionary of installed application; Returns main.FALSE for
257 error on request; Returns None for exception
258 """
259 try:
260 output = None
261 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700262 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700263 ip = self.ip_address
264 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700265 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -0700266 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700267 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000268 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700269 if response:
270 if 200 <= response[ 0 ] <= 299:
271 output = response[ 1 ]
272 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700273 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700274 b = json.dumps( a )
275 return b
276 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700277 main.log.error( "Error with REST request, response was: %s: %s" %
278 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700279 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700280 except ( AttributeError, AssertionError, TypeError ):
281 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700282 return None
Jon Halle401b092015-09-23 13:34:24 -0700283 except Exception:
284 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700285 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700286
287 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
288 """
289 Decription:
290 Activate an app that is already installed in ONOS
291 Optional:
292 bool check - If check is True, method will check the status
293 of the app after the command is issued
294 Returns:
295 Returns main.TRUE if the command was successfully or main.FALSE
296 if the REST responded with an error or given incorrect input;
297 Returns None for exception
298
299 """
300 try:
301 output = None
302 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700303 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700304 ip = self.ip_address
305 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700306 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700307 "from topo file" )
308 port = self.port
309 query = "/" + str( appName ) + "/active"
Jon Hall3c0114c2020-08-11 15:07:42 -0700310 retry = 0
311 retCode = main.TRUE
312 while retry < 50:
313 if retry > 0:
314 main.log.warn( self.name + ": Retrying " + query + " for the " + str( retry ) + " time" )
315
316 retry += 1
317 response = self.send( method="POST",
318 debug=True,
319 url="/applications" + query,
320 ip = ip, port = port )
321 if response:
322 output = response[ 1 ]
323 if 200 <= response[ 0 ] <= 299:
324 if check:
325 app = json.loads( output )
326 if app.get( 'state' ) == 'ACTIVE':
327 main.log.info( self.name + ": " + appName +
328 " application" +
329 " is in ACTIVE state" )
330 appHealth = self.getAppHealth( appName=appName, ip=ip, port=port )
331 if "ready" == json.loads( appHealth[1] ).get( 'message' ):
332 return main.TRUE
333 else:
334 return main.FALSE
335 else:
336 main.log.error( self.name + ": " + appName +
337 " application" + " is in " +
338 app.get( 'state' ) + " state" )
339 retCode = main.FALSE
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700340 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700341 main.log.warn( self.name + ": Skipping " + appName +
342 "application check" )
343 return main.TRUE
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700344 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700345 main.log.error( "Error with REST request, response was: %s: %s" %
346 ( response[ 0 ], response[ 1 ] ) )
347 retCode = main.FALSE
348 time.sleep( 30 )
349 return retCode
350 except ( ValueError ):
351 main.log.exception( self.name + ": Error parsing json" )
352 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700353 except ( AttributeError, TypeError ):
354 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700355 return None
Jon Halle401b092015-09-23 13:34:24 -0700356 except Exception:
357 main.log.exception( self.name + ": Uncaught exception!" )
Jon Hall3c0114c2020-08-11 15:07:42 -0700358 main.log.debug( self.name + ": " + response )
Devin Lim44075962017-08-11 10:56:37 -0700359 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700360
361 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
362 check=True ):
363 """
364 Required:
365 Deactivate an app that is already activated in ONOS
366 Optional:
367 bool check - If check is True, method will check the status of the
368 app after the command is issued
369 Returns:
370 Returns main.TRUE if the command was successfully sent
371 main.FALSE if the REST responded with an error or given
372 incorrect input; Returns None for exception
373 """
374 try:
375 output = None
376 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700377 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700378 ip = self.ip_address
379 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700380 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700381 "from topo file" )
382 port = self.port
383 query = "/" + str( appName ) + "/active"
Devin Limcde503f2017-09-11 17:23:30 -0700384 self.send( method="DELETE",
385 url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000386 ip = ip, port = port )
Devin Limcde503f2017-09-11 17:23:30 -0700387 response = self.getApp( appName, ip, port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700388 if response:
389 output = response[ 1 ]
Jon Hall6040bcf2017-08-14 11:15:41 -0700390 app = {} if output == "" else json.loads( output )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700391 if 200 <= response[ 0 ] <= 299:
392 if check:
393 if app.get( 'state' ) == 'INSTALLED':
394 main.log.info( self.name + ": " + appName +
395 " application" +
396 " is in INSTALLED state" )
397 return main.TRUE
398 else:
399 main.log.error( self.name + ": " + appName +
400 " application" + " is in " +
401 app.get( 'state' ) + " state" )
402 return main.FALSE
403 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700404 main.log.warn( self.name + ": Skipping " + appName +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700405 "application check" )
406 return main.TRUE
407 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700408 main.log.error( "Error with REST request, response was: %s: %s" %
409 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700410 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700411 except ( AttributeError, TypeError ):
412 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700413 return None
Jon Halle401b092015-09-23 13:34:24 -0700414 except Exception:
415 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700416 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700417
Devin Limcde503f2017-09-11 17:23:30 -0700418 def getApp( self, appName, ip="DEFAULT",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700419 port="DEFAULT" ):
420 """
421 Decription:
422 Gets the informaion of the given application
423 Required:
424 str name - Name of onos application
425 Returns:
426 Returns a dictionary of information ONOS application in string type;
427 Returns main.FALSE if error on requests; Returns None for exception
428 """
429 try:
Jon Hall3c0114c2020-08-11 15:07:42 -0700430 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700431 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700432 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700433 ip = self.ip_address
434 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700435 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700436 "from topo file" )
437 port = self.port
Devin Limcde503f2017-09-11 17:23:30 -0700438 query = "/" + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700439 response = self.send( url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000440 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700441 if response:
442 if 200 <= response[ 0 ] <= 299:
Devin Limcde503f2017-09-11 17:23:30 -0700443 return response
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700444 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700445 main.log.error( "Error with REST request, response was: %s: %s" %
446 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700447 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700448 except ( AttributeError, TypeError ):
449 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700450 return None
Jon Halle401b092015-09-23 13:34:24 -0700451 except Exception:
452 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700453 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700454
Jon Hall3c0114c2020-08-11 15:07:42 -0700455 def getAppHealth( self, appName, ip="DEFAULT",
456 port="DEFAULT" ):
457 """
458 Decription:
459 Gets the health of the given application
460 Required:
461 str name - Name of onos application
462 Returns:
463 Returns a dictionary of information ONOS application in string type;
464 Returns main.FALSE if error on requests; Returns None for exception
465 """
466 try:
467 response = None
468 if ip == "DEFAULT":
469 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
470 ip = self.ip_address
471 if port == "DEFAULT":
472 main.log.warn( self.name + ": No port given, reverting to port " +
473 "from topo file" )
474 port = self.port
475 response = self.send( url="/applications/%s/health" % str( appName ),
476 ip = ip, port = port )
477 if response:
478 if 200 <= response[ 0 ] <= 299:
479 return response
480 else:
481 main.log.error( "Error with REST request, response was: %s: %s" %
482 ( response[ 0 ], response[ 1 ] ) )
483 return main.FALSE
484 except ( AttributeError, TypeError ):
485 main.log.exception( self.name + ": Object not as expected" )
486 return None
487 except Exception:
488 main.log.exception( self.name + ": Uncaught exception!" )
489 main.cleanAndExit()
490
491 def getAllAppHealth( self, retries=1, wait=30 , ip="DEFAULT",
492 port="DEFAULT" ):
493 """
494 Description:
495 Gets the health of all activated apps
496 Required:
497 Optional:
498 retries - The number of tries to return before returning
499 wait - Time to wait in between retries
500 """
501 try:
502 responses = main.TRUE
503 if ip == "DEFAULT":
504 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
505 ip = self.ip_address
506 if port == "DEFAULT":
507 main.log.warn( self.name + ": No port given, reverting to port " +
508 "from topo file" )
509 port = self.port
510 apps = self.apps()
511 for app in json.loads(apps):
512 appName = app.get( "name" )
513 response = self.getAppHealth( appName=appName, ip=ip, port=port )
514 responses = main.TRUE and "ready" == json.loads( response[1] ).get( "message" )
515 return responses
516 except ( AttributeError, TypeError ):
517 main.log.exception( self.name + ": Object not as expected" )
518 return None
519 except Exception:
520 main.log.exception( self.name + ": Uncaught exception!" )
521 main.cleanAndExit()
522
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700523 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700524 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700525 """
526 Description:
527 Adds a host-to-host intent ( bidirectional ) by
528 specifying the two hosts.
529 Required:
530 * hostIdOne: ONOS host id for host1
531 * hostIdTwo: ONOS host id for host2
532 Optional:
533 str appId - Application name of intent identifier
534 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700535 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700536 error on requests; Returns None for exceptions
537 """
538 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700539 intentJson = { "two": str( hostIdTwo ),
540 "selector": { "criteria": [] }, "priority": 7,
541 "treatment": { "deferred": [], "instructions": [] },
542 "appId": appId, "one": str( hostIdOne ),
543 "type": "HostToHostIntent",
544 "constraints": [ { "type": "LinkTypeConstraint",
545 "types": [ "OPTICAL" ],
546 "inclusive": 'false' } ] }
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700547 if vlanId:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700548 intentJson[ 'selector' ][ 'criteria' ].append( { "type": "VLAN_VID",
549 "vlanId": vlanId } )
Jon Hall3c0114c2020-08-11 15:07:42 -0700550 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700551 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700552 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700553 ip = self.ip_address
554 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700555 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700556 "from topo file" )
557 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700558 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000559 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700560 data=json.dumps( intentJson ) )
561 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700562 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700563 main.log.info( self.name + ": Successfully POST host" +
564 " intent between host: " + hostIdOne +
565 " and host: " + hostIdTwo )
566 return main.TRUE
567 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700568 main.log.error( "Error with REST request, response was: %s: %s" %
569 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700570 return main.FALSE
571
Jon Halle401b092015-09-23 13:34:24 -0700572 except ( AttributeError, TypeError ):
573 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700574 return None
Jon Halle401b092015-09-23 13:34:24 -0700575 except Exception:
576 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700577 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700578
kelvin-onlabb50074f2015-07-27 16:18:32 -0700579 def addPointIntent( self,
580 ingressDevice,
581 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700582 appId='org.onosproject.cli',
583 ingressPort="",
584 egressPort="",
585 ethType="",
586 ethSrc="",
587 ethDst="",
588 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800589 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700590 lambdaAlloc=False,
591 ipProto="",
592 ipSrc="",
593 ipDst="",
594 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700595 tcpDst="",
596 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700597 port="DEFAULT",
598 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700599 """
600 Description:
601 Adds a point-to-point intent ( uni-directional ) by
602 specifying device id's and optional fields
603 Required:
604 * ingressDevice: device id of ingress device
605 * egressDevice: device id of egress device
606 Optional:
607 * ethType: specify ethType
608 * ethSrc: specify ethSrc ( i.e. src mac addr )
609 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000610 * bandwidth: specify bandwidth capacity of link (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700611 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000612 for the specified intent (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700613 * ipProto: specify ip protocol
614 * ipSrc: specify ip source address with mask eg. ip#/24
615 * ipDst: specify ip destination address eg. ip#/24
616 * tcpSrc: specify tcp source port
617 * tcpDst: specify tcp destination port
618 Returns:
619 Returns main.TRUE for successful requests; Returns main.FALSE if
620 no ingress|egress port found and if error on requests;
621 Returns None for exceptions
622 NOTE:
623 The ip and port option are for the requests input's ip and port
624 of the ONOS node
625 """
626 try:
627 if "/" in ingressDevice:
628 if not ingressPort:
629 ingressPort = ingressDevice.split( "/" )[ 1 ]
630 ingressDevice = ingressDevice.split( "/" )[ 0 ]
631 else:
632 if not ingressPort:
633 main.log.debug( self.name + ": Ingress port not specified" )
634 return main.FALSE
635
636 if "/" in egressDevice:
637 if not egressPort:
638 egressPort = egressDevice.split( "/" )[ 1 ]
639 egressDevice = egressDevice.split( "/" )[ 0 ]
640 else:
641 if not egressPort:
642 main.log.debug( self.name + ": Egress port not specified" )
643 return main.FALSE
644
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700645 intentJson = { "ingressPoint": { "device": ingressDevice,
646 "port": ingressPort },
647 "selector": { "criteria": [] },
648 "priority": 55,
649 "treatment": { "deferred": [],
650 "instructions": [] },
651 "egressPoint": { "device": egressDevice,
652 "port": egressPort },
653 "appId": appId,
654 "type": "PointToPointIntent",
655 "constraints": [ { "type": "LinkTypeConstraint",
656 "types": [ "OPTICAL" ],
657 "inclusive": "false" } ] }
kelvin-onlabb50074f2015-07-27 16:18:32 -0700658
659 if ethType == "IPV4":
660 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700661 "type": "ETH_TYPE",
662 "ethType": 2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700663 elif ethType:
664 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700665 "type": "ETH_TYPE",
666 "ethType": ethType } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700667
kelvin-onlabb50074f2015-07-27 16:18:32 -0700668 if ethSrc:
669 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700670 { "type": "ETH_SRC",
671 "mac": ethSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700672 if ethDst:
673 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700674 { "type": "ETH_DST",
675 "mac": ethDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700676 if ipSrc:
677 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700678 { "type": "IPV4_SRC",
679 "ip": ipSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700680 if ipDst:
681 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700682 { "type": "IPV4_DST",
683 "ip": ipDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700684 if tcpSrc:
685 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700686 { "type": "TCP_SRC",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700687 "tcpPort": tcpSrc } )
688 if tcpDst:
689 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700690 { "type": "TCP_DST",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700691 "tcpPort": tcpDst } )
692 if ipProto:
693 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700694 { "type": "IP_PROTO",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700695 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700696 if vlanId:
697 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700698 { "type": "VLAN_VID",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700699 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700700
701 # TODO: Bandwidth and Lambda will be implemented if needed
702
Jon Hall3c0114c2020-08-11 15:07:42 -0700703 response = None
kelvin-onlabb50074f2015-07-27 16:18:32 -0700704 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700705 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700706 ip = self.ip_address
707 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700708 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlabb50074f2015-07-27 16:18:32 -0700709 "from topo file" )
710 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700711 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000712 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700713 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700714
715 main.log.debug( intentJson )
716
kelvin-onlabb50074f2015-07-27 16:18:32 -0700717 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700718 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700719 main.log.info( self.name + ": Successfully POST point" +
720 " intent between ingress: " + ingressDevice +
721 " and egress: " + egressDevice + " devices" )
722 return main.TRUE
723 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700724 main.log.error( "Error with REST request, response was: %s: %s" %
725 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700726 return main.FALSE
727
Jon Halle401b092015-09-23 13:34:24 -0700728 except ( AttributeError, TypeError ):
729 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700730 return None
Jon Halle401b092015-09-23 13:34:24 -0700731 except Exception:
732 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700733 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700734
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700735 def addSinglepointToMultipointIntent( self,
736 ingressDevice,
737 egressDeviceList,
738 portEgressList,
739 appId='org.onosproject.cli',
740 portIngress="",
741 ethType="",
742 ethSrc="",
743 ethDst="",
744 bandwidth="",
745 lambdaAlloc=False,
746 ipProto="",
747 ipSrc="",
748 ipDst="",
749 tcpSrc="",
750 tcpDst="",
751 partial=False,
752 ip="DEFAULT",
753 port="DEFAULT",
754 vlanId="" ):
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700755 """
756 Description:
757 Adds a point-to-multi point intent ( uni-directional ) by
758 specifying device id's and optional fields
759 Required:
760 * ingressDevice: device id of ingress device
761 * egressDevice: device id of egress device
762 * portEgressList: a list of port id of egress device
763
764 Optional:
765 * portIngress: port id of ingress device
766 * ethType: specify ethType
767 * ethSrc: specify ethSrc ( i.e. src mac addr )
768 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000769 * bandwidth: specify bandwidth capacity of link (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700770 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000771 for the specified intent (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700772 * ipProto: specify ip protocol
773 * ipSrc: specify ip source address with mask eg. ip#/24
774 * ipDst: specify ip destination address eg. ip#/24
775 * tcpSrc: specify tcp source port
776 * tcpDst: specify tcp destination port
777 Returns:
778 Returns main.TRUE for successful requests; Returns main.FALSE if
779 no ingress|egress port found and if error on requests;
780 Returns None for exceptions
781 NOTE:
782 The ip and port option are for the requests input's ip and port
783 of the ONOS node
784 """
785 try:
786
787 if "/" in ingressDevice:
788 if not portIngress:
789 ingressPort = ingressDevice.split( "/" )[ 1 ]
790 ingressDevice = ingressDevice.split( "/" )[ 0 ]
791 else:
792 if not portIngress:
793 main.log.debug( self.name + ": Ingress port not specified" )
794 return main.FALSE
795 index = 0
796 for egressDevice in egressDeviceList:
797 if "/" in egressDevice:
798 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
799 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
800 else:
801 if not portEgressList:
802 main.log.debug( self.name + ": Egress port not specified" )
803 return main.FALSE
804 index = index + 1
805
806 intentJson = { "ingressPoint": { "device": ingressDevice,
807 "port": ingressPort },
808 "selector": { "criteria": [] },
809 "priority": 55,
810 "treatment": { "deferred": [],
811 "instructions": [] },
812 "egressPoint": { "connectPoints": [] },
813 "appId": appId,
814 "type": "SinglePointToMultiPointIntent",
815 "constraints": [ { "type": "LinkTypeConstraint",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700816 "types": [ "OPTICAL" ],
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700817 "inclusive": "false" } ] }
818
819 index = 0
820 for ep in portEgressList:
821 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
822 { "device": egressDeviceList[ index ],
823 "port": ep } )
824 index += 1
825
826 if ethType == "IPV4":
827 intentJson[ 'selector' ][ 'criteria' ].append(
828 { "type": "ETH_TYPE",
829 "ethType": 2048 } )
830 elif ethType:
831 intentJson[ 'selector' ][ 'criteria' ].append(
832 { "type": "ETH_TYPE",
833 "ethType": ethType } )
834
835 if ethSrc:
836 intentJson[ 'selector' ][ 'criteria' ].append(
837 { "type": "ETH_SRC",
838 "mac": ethSrc } )
839
840 if ethDst:
841 for dst in ethDst:
842 if dst:
843 intentJson[ 'selector' ][ 'criteria' ].append(
844 { "type": "ETH_DST",
845 "mac": dst } )
846 if tcpSrc:
847 intentJson[ 'selector' ][ 'criteria' ].append(
848 { "type": "TCP_SRC",
849 "tcpPort": tcpSrc } )
850 if tcpDst:
851 intentJson[ 'selector' ][ 'criteria' ].append(
852 { "type": "TCP_DST",
853 "tcpPort": tcpDst } )
854 if ipProto:
855 intentJson[ 'selector' ][ 'criteria' ].append(
856 { "type": "IP_PROTO",
857 "protocol": ipProto } )
858 if vlanId:
859 intentJson[ 'selector' ][ 'criteria' ].append(
860 { "type": "VLAN_VID",
861 "vlanId": vlanId } )
862
863 # TODO: Bandwidth and Lambda will be implemented if needed
864
Jon Hall3c0114c2020-08-11 15:07:42 -0700865 response = None
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700866 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700867 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700868 ip = self.ip_address
869 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700870 main.log.warn( self.name + ": No port given, reverting to port " +
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700871 "from topo file" )
872 port = self.port
873 response = self.send( method="POST",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700874 url="/intents", ip=ip, port=port,
875 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700876
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700877 main.log.debug( intentJson )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700878
879 if response:
880 if "201" in str( response[ 0 ] ):
881 main.log.info( self.name + ": Successfully POST point" +
882 " intent between ingress: " + ingressDevice +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700883 " and egress: " + str( egressDeviceList ) + " devices" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700884 return main.TRUE
885 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700886 main.log.error( "Error with REST request, response was: %s: %s" % ( response[ 0 ], response[ 1 ] ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700887 return main.FALSE
888 else:
889 main.log.error( "REST request has no response." )
890 return main.FALSE
891
892 except ( AttributeError, TypeError ):
893 main.log.exception( self.name + ": Object not as expected" )
894 return None
895 except Exception:
896 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700897 main.cleanAndExit()
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700898
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700899 def removeIntent( self, intentId, appId='org.onosproject.cli',
900 ip="DEFAULT", port="DEFAULT" ):
901 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700902 Remove intent for specified application id and intent id;
903 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700904 """
905 try:
Jon Hall3c0114c2020-08-11 15:07:42 -0700906 response = None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700907 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700908 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700909 ip = self.ip_address
910 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -0700911 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700912 "from topo file" )
913 port = self.port
914 # NOTE: REST url requires the intent id to be in decimal form
915 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700916 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000917 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700918 if response:
919 if 200 <= response[ 0 ] <= 299:
920 return main.TRUE
921 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700922 main.log.error( "Error with REST request, response was: %s: %s" %
923 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700924 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700925 except ( AttributeError, TypeError ):
926 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700927 return None
Jon Halle401b092015-09-23 13:34:24 -0700928 except Exception:
929 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700930 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700931
Jon Hall3c0114c2020-08-11 15:07:42 -0700932 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700933 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700934 Description:
935 Gets all intents ID using intents function
936 Returns:
937 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700938 """
939 try:
940 intentIdList = []
Jon Hall3c0114c2020-08-11 15:07:42 -0700941 intentsJson = json.loads( self.intents( ip=ip, port=port ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700942 for intent in intentsJson:
943 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700944 if not intentIdList:
Jon Hall3c0114c2020-08-11 15:07:42 -0700945 main.log.debug( self.name + ": Cannot find any intents" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700946 return main.FALSE
947 else:
948 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700949 except ( AttributeError, TypeError ):
950 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700951 return None
Jon Halle401b092015-09-23 13:34:24 -0700952 except Exception:
953 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700954 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700955
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700956 def removeAllIntents( self, intentIdList ='ALL', appId='org.onosproject.cli',
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700957 ip="DEFAULT", port="DEFAULT", delay=5 ):
958 """
959 Description:
960 Remove all the intents
961 Returns:
962 Returns main.TRUE if all intents are removed, otherwise returns
963 main.FALSE; Returns None for exception
964 """
965 try:
966 results = []
967 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700968 # intentIdList = self.getIntentsId( ip=ip, port=port )
969 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700970
971 main.log.info( self.name + ": Removing intents " +
972 str( intentIdList ) )
973
974 if isinstance( intentIdList, types.ListType ):
975 for intent in intentIdList:
976 results.append( self.removeIntent( intentId=intent,
977 appId=appId,
978 ip=ip,
979 port=port ) )
980 # Check for remaining intents
981 # NOTE: Noticing some delay on Deleting the intents so i put
982 # this time out
983 import time
984 time.sleep( delay )
985 intentRemain = len( json.loads( self.intents() ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700986 if all( result == main.TRUE for result in results ) and \
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700987 intentRemain == 0:
988 main.log.info( self.name + ": All intents are removed " )
989 return main.TRUE
990 else:
991 main.log.error( self.name + ": Did not removed all intents,"
992 + " there are " + str( intentRemain )
993 + " intents remaining" )
994 return main.FALSE
995 else:
996 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700997 except ( AttributeError, TypeError ):
998 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700999 return None
Jon Halle401b092015-09-23 13:34:24 -07001000 except Exception:
1001 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001002 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001003
1004 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
1005 """
1006 Description:
1007 Get a list of dictionary of all discovered hosts
1008 Returns:
1009 Returns a list of dictionary of information of the hosts currently
1010 discovered by ONOS; Returns main.FALSE if error on requests;
1011 Returns None for exception
1012 """
1013 try:
1014 output = None
1015 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001016 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001017 ip = self.ip_address
1018 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001019 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001020 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001021 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001022 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001023 if response:
1024 if 200 <= response[ 0 ] <= 299:
1025 output = response[ 1 ]
1026 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -07001027 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001028 b = json.dumps( a )
1029 return b
1030 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001031 main.log.error( "Error with REST request, response was: %s: %s" %
1032 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001033 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001034 except ( AttributeError, AssertionError, TypeError ):
1035 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001036 return None
Jon Halle401b092015-09-23 13:34:24 -07001037 except Exception:
1038 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001039 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001040
1041 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
1042 """
1043 Description:
1044 Gets the information from the given host
1045 Required:
1046 str mac - MAC address of the host
1047 Optional:
1048 str vlan - VLAN tag of the host, defaults to -1
1049 Returns:
1050 Return the host id from the hosts/mac/vlan output in REST api
1051 whose 'id' contains mac/vlan; Returns None for exception;
1052 Returns main.FALSE if error on requests
1053
1054 NOTE:
1055 Not sure what this function should do, any suggestion?
1056 """
1057 try:
1058 output = None
1059 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001060 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001061 ip = self.ip_address
1062 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001063 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001064 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001065 port = self.port
1066 query = "/" + mac + "/" + vlan
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001067 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001068 if response:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001069 # NOTE: What if the person wants other values? would it be better
1070 # to have a function that gets a key and return a value instead?
1071 # This function requires mac and vlan and returns an ID which
1072 # makes this current function useless
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001073 if 200 <= response[ 0 ] <= 299:
1074 output = response[ 1 ]
1075 hostId = json.loads( output ).get( 'id' )
1076 return hostId
1077 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001078 main.log.error( "Error with REST request, response was: %s: %s" %
1079 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001080 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001081 except ( AttributeError, TypeError ):
1082 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001083 return None
Jon Halle401b092015-09-23 13:34:24 -07001084 except Exception:
1085 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001086 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001087
1088 def topology( self, ip="DEFAULT", port="DEFAULT" ):
1089 """
1090 Description:
1091 Gets the overview of network topology
1092 Returns:
1093 Returns a dictionary containing information about network topology;
1094 Returns None for exception
1095 """
1096 try:
1097 output = None
1098 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001099 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001100 ip = self.ip_address
1101 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001102 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001103 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001104 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001105 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001106 if response:
1107 if 200 <= response[ 0 ] <= 299:
1108 output = response[ 1 ]
1109 a = json.loads( output )
1110 b = json.dumps( a )
1111 return b
1112 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001113 main.log.error( "Error with REST request, response was: %s: %s" %
1114 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001115 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001116 except ( AttributeError, TypeError ):
1117 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001118 return None
Jon Halle401b092015-09-23 13:34:24 -07001119 except Exception:
1120 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001121 main.cleanAndExit()
Jon Halle401b092015-09-23 13:34:24 -07001122
1123 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1124 """
1125 Description:
1126 Get the devices discovered by ONOS is json string format
1127 Returns:
1128 a json string of the devices currently discovered by ONOS OR
1129 main.FALSE if there is an error in the request OR
1130 Returns None for exception
1131 """
1132 try:
1133 output = None
1134 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001135 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Halle401b092015-09-23 13:34:24 -07001136 ip = self.ip_address
1137 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001138 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Halle401b092015-09-23 13:34:24 -07001139 "from topo file" )
1140 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001141 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001142 if response:
1143 if 200 <= response[ 0 ] <= 299:
1144 output = response[ 1 ]
1145 a = json.loads( output ).get( 'devices' )
1146 assert a is not None, "Error parsing json object"
1147 b = json.dumps( a )
1148 return b
1149 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001150 main.log.error( "Error with REST request, response was: %s: %s" %
1151 ( response[ 0 ], response[ 1 ] ) )
Jon Halle401b092015-09-23 13:34:24 -07001152 return main.FALSE
1153 except ( AttributeError, AssertionError, TypeError ):
1154 main.log.exception( self.name + ": Object not as expected" )
1155 return None
1156 except Exception:
1157 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001158 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001159
1160 def getIntentState( self, intentsId, intentsJson=None,
1161 ip="DEFAULT", port="DEFAULT" ):
1162 """
1163 Description:
1164 Get intent state.
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001165 Accepts a single intent ID (string type) or a list of intent IDs.
1166 Returns the state(string type) of the id if a single intent ID is
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001167 accepted.
1168 Required:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001169 intentId: intent ID (string type)
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001170 intentsJson: parsed json object from the onos:intents api
1171 Returns:
1172 Returns a dictionary with intent IDs as the key and its
1173 corresponding states as the values; Returns None for invalid IDs or
1174 Type error and any exceptions
1175 NOTE:
1176 An intent's state consist of INSTALLED,WITHDRAWN etc.
1177 """
1178 try:
1179 state = "State is Undefined"
1180 if not intentsJson:
1181 intentsJsonTemp = json.loads( self.intents() )
1182 else:
1183 intentsJsonTemp = json.loads( intentsJson )
1184 if isinstance( intentsId, types.StringType ):
1185 for intent in intentsJsonTemp:
1186 if intentsId == intent[ 'id' ]:
1187 state = intent[ 'state' ]
1188 return state
Jon Hall3c0114c2020-08-11 15:07:42 -07001189 main.log.info( self.name + ": Cannot find intent ID" + str( intentsId ) +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001190 " on the list" )
1191 return state
1192 elif isinstance( intentsId, types.ListType ):
1193 dictList = []
1194 for i in xrange( len( intentsId ) ):
1195 stateDict = {}
1196 for intents in intentsJsonTemp:
1197 if intentsId[ i ] == intents[ 'id' ]:
1198 stateDict[ 'state' ] = intents[ 'state' ]
1199 stateDict[ 'id' ] = intentsId[ i ]
1200 dictList.append( stateDict )
1201 break
1202 if len( intentsId ) != len( dictList ):
Jon Hall3c0114c2020-08-11 15:07:42 -07001203 main.log.info( self.name + ": Cannot find some of the intent ID state" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001204 return dictList
1205 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001206 main.log.info( self.name + ": Invalid intents ID entry" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001207 return None
1208
Jon Halle401b092015-09-23 13:34:24 -07001209 except ( AttributeError, TypeError ):
1210 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001211 return None
Jon Halle401b092015-09-23 13:34:24 -07001212 except Exception:
1213 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001214 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001215
1216 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001217 ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001218 """
1219 Description:
1220 Check intents state based on expected state which defaults to
1221 INSTALLED state
1222 Required:
1223 intentsId - List of intents ID to be checked
1224 Optional:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001225 expectedState - Check the expected state(s) of each intents
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001226 state in the list.
1227 *NOTE: You can pass in a list of expected state,
1228 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1229 Return:
1230 Returns main.TRUE only if all intent are the same as expected states
1231 , otherwise, returns main.FALSE; Returns None for general exception
1232 """
1233 try:
1234 # Generating a dictionary: intent id as a key and state as value
1235 returnValue = main.TRUE
1236 if intentsId == "ALL":
1237 intentsId = self.getIntentsId( ip=ip, port=port )
1238 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1239
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001240 if len( intentsId ) != len( intentsDict ):
1241 main.log.error( self.name + ": There is something wrong " +
1242 "getting intents state" )
1243 return main.FALSE
1244
1245 if isinstance( expectedState, types.StringType ):
1246 for intents in intentsDict:
1247 if intents.get( 'state' ) != expectedState:
Jon Hall3c0114c2020-08-11 15:07:42 -07001248 main.log.debug( self.name + ": Intent ID - " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001249 intents.get( 'id' ) +
1250 " actual state = " +
1251 intents.get( 'state' )
1252 + " does not equal expected state = "
1253 + expectedState )
1254 returnValue = main.FALSE
1255
1256 elif isinstance( expectedState, types.ListType ):
1257 for intents in intentsDict:
1258 if not any( state == intents.get( 'state' ) for state in
1259 expectedState ):
Jon Hall3c0114c2020-08-11 15:07:42 -07001260 main.log.debug( self.name + ": Intent ID - " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001261 intents.get( 'id' ) +
1262 " actual state = " +
1263 intents.get( 'state' ) +
1264 " does not equal expected states = "
1265 + str( expectedState ) )
1266 returnValue = main.FALSE
1267
1268 if returnValue == main.TRUE:
1269 main.log.info( self.name + ": All " +
1270 str( len( intentsDict ) ) +
1271 " intents are in " + str( expectedState ) +
1272 " state" )
1273 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001274 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001275 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001276 return None
Jon Halle401b092015-09-23 13:34:24 -07001277 except Exception:
1278 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001279 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001280
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001281 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001282 """
1283 Description:
1284 Get flows currently added to the system
1285 NOTE:
1286 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001287 the REST output
1288
1289 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001290 """
1291 try:
1292 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001293 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001294 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001295 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001296 ip = self.ip_address
1297 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001298 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001299 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001300 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001301 if subjectKey and not subjectClass:
1302 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1303 if subjectClass:
1304 url += "/" + subjectClass
1305 if subjectKey:
1306 url += "/" + subjectKey
1307 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001308 if response:
1309 if 200 <= response[ 0 ] <= 299:
1310 output = response[ 1 ]
1311 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001312 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001313 b = json.dumps( a )
1314 return b
1315 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001316 main.log.error( "Error with REST request, response was: %s: %s" %
1317 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001318 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001319 except ( AttributeError, AssertionError, TypeError ):
1320 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001321 return None
Jon Halle401b092015-09-23 13:34:24 -07001322 except Exception:
1323 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001324 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001325
Jon Halle401b092015-09-23 13:34:24 -07001326 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001327 """
1328 Description:
1329 Gets all the flows of the device or get a specific flow in the
1330 device by giving its flow ID
1331 Required:
Jon Halle401b092015-09-23 13:34:24 -07001332 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001333 Optional:
1334 int/hex flowId - ID of the flow
1335 """
1336 try:
1337 output = None
1338 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001339 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001340 ip = self.ip_address
1341 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001342 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hallf7234882015-08-28 13:16:31 -07001343 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001344 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001345 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001346 if flowId:
1347 url += "/" + str( int( flowId ) )
1348 print url
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001349 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001350 if response:
1351 if 200 <= response[ 0 ] <= 299:
1352 output = response[ 1 ]
1353 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001354 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001355 b = json.dumps( a )
1356 return b
1357 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001358 main.log.error( "Error with REST request, response was: %s: %s" %
1359 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001360 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001361 except ( AttributeError, AssertionError, TypeError ):
1362 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001363 return None
Jon Halle401b092015-09-23 13:34:24 -07001364 except Exception:
1365 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001366 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001367
GlennRC073e8bc2015-10-27 17:11:28 -07001368 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001369 """
1370 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001371 Sends a single flow to the specified device. This function exists
1372 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001373 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001374 * The flow in json
1375 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001376 Returns:
1377 Returns main.TRUE for successful requests; Returns main.FALSE
1378 if error on requests;
1379 Returns None for exceptions
1380 NOTE:
1381 The ip and port option are for the requests input's ip and port
1382 of the ONOS node
1383 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001384
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001385 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001386 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07001387 main.log.debug( self.name + ": Adding flow: " + self.pprint( flowJson ) )
1388 response = None
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001389 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001390 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001391 ip = self.ip_address
1392 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001393 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001394 "from topo file" )
1395 port = self.port
1396 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001397 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001398 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001399 data=json.dumps( flowJson ) )
1400 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001401 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001402 main.log.info( self.name + ": Successfully POST flow" +
1403 "in device: " + str( deviceId ) )
1404 return main.TRUE
1405 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001406 main.log.error( "Error with REST request, response was: %s: %s" %
1407 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001408 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001409 except NotImplementedError as e:
1410 raise e # Inform the caller
1411 except ( AttributeError, TypeError ):
1412 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001413 return None
Jon Halle401b092015-09-23 13:34:24 -07001414 except Exception:
1415 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001416 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001417
GlennRC073e8bc2015-10-27 17:11:28 -07001418 def addFlow( self,
1419 deviceId,
1420 appId=0,
1421 ingressPort="",
1422 egressPort="",
1423 ethType="",
1424 ethSrc="",
1425 ethDst="",
1426 vlan="",
1427 ipProto="",
1428 ipSrc=(),
1429 ipDst=(),
1430 tcpSrc="",
1431 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001432 udpDst="",
1433 udpSrc="",
1434 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001435 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301436 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001437 ip="DEFAULT",
1438 port="DEFAULT",
1439 debug=False ):
1440 """
1441 Description:
1442 Creates a single flow in the specified device
1443 Required:
1444 * deviceId: id of the device
1445 Optional:
1446 * ingressPort: port ingress device
1447 * egressPort: port of egress device
1448 * ethType: specify ethType
1449 * ethSrc: specify ethSrc ( i.e. src mac addr )
1450 * ethDst: specify ethDst ( i.e. dst mac addr )
1451 * ipProto: specify ip protocol
1452 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001453 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001454 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001455 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001456 * tcpSrc: specify tcp source port
1457 * tcpDst: specify tcp destination port
1458 Returns:
1459 Returns main.TRUE for successful requests; Returns main.FALSE
1460 if error on requests;
1461 Returns None for exceptions
1462 NOTE:
1463 The ip and port option are for the requests input's ip and port
1464 of the ONOS node
1465 """
1466 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001467 flowJson = { "priority": priority,
1468 "isPermanent": "true",
1469 "timeout": 0,
1470 "deviceId": deviceId,
1471 "treatment": { "instructions": [] },
1472 "selector": { "criteria": [] }}
GlennRC073e8bc2015-10-27 17:11:28 -07001473 if appId:
1474 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301475
1476 if groupId:
1477 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001478 "type": "GROUP",
1479 "groupId": groupId } )
kavitha Alagesan373e0552016-11-22 05:22:05 +05301480
GlennRC073e8bc2015-10-27 17:11:28 -07001481 if egressPort:
1482 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001483 "type": "OUTPUT",
1484 "port": egressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001485 if ingressPort:
1486 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001487 "type": "IN_PORT",
1488 "port": ingressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001489 if ethType:
1490 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001491 "type": "ETH_TYPE",
1492 "ethType": ethType } )
GlennRC073e8bc2015-10-27 17:11:28 -07001493 if ethSrc:
1494 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001495 "type": "ETH_SRC",
1496 "mac": ethSrc } )
GlennRC073e8bc2015-10-27 17:11:28 -07001497 if ethDst:
1498 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001499 "type": "ETH_DST",
1500 "mac": ethDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001501 if vlan:
1502 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001503 "type": "VLAN_VID",
1504 "vlanId": vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001505 if mpls:
1506 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001507 "type": "MPLS_LABEL",
1508 "label": mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001509 if ipSrc:
1510 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001511 "type": ipSrc[ 0 ],
1512 "ip": ipSrc[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001513 if ipDst:
1514 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001515 "type": ipDst[ 0 ],
1516 "ip": ipDst[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001517 if tcpSrc:
1518 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001519 "type": "TCP_SRC",
GlennRC073e8bc2015-10-27 17:11:28 -07001520 "tcpPort": tcpSrc } )
1521 if tcpDst:
1522 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001523 "type": "TCP_DST",
GlennRC073e8bc2015-10-27 17:11:28 -07001524 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001525 if udpSrc:
1526 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001527 "type": "UDP_SRC",
GlennRC956ea742015-11-05 16:14:15 -08001528 "udpPort": udpSrc } )
1529 if udpDst:
1530 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001531 "type": "UDP_DST",
GlennRC956ea742015-11-05 16:14:15 -08001532 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001533 if ipProto:
1534 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001535 "type": "IP_PROTO",
GlennRC073e8bc2015-10-27 17:11:28 -07001536 "protocol": ipProto } )
1537
Jeremy Ronquillo29260cd2017-10-13 13:30:54 -07001538 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug, ip=ip, port=port )
GlennRC073e8bc2015-10-27 17:11:28 -07001539
1540 except ( AttributeError, TypeError ):
1541 main.log.exception( self.name + ": Object not as expected" )
1542 return None
1543 except Exception:
1544 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001545 main.cleanAndExit()
GlennRC073e8bc2015-10-27 17:11:28 -07001546
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001547 def removeFlow( self, deviceId, flowId,
1548 ip="DEFAULT", port="DEFAULT" ):
1549 """
1550 Description:
1551 Remove specific device flow
1552 Required:
1553 str deviceId - id of the device
1554 str flowId - id of the flow
1555 Return:
1556 Returns main.TRUE if successfully deletes flows, otherwise
1557 Returns main.FALSE, Returns None on error
1558 """
1559 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001560 response = None
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001561 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001562 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001563 ip = self.ip_address
1564 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001565 main.log.warn( self.name + ": No port given, reverting to port " +
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001566 "from topo file" )
1567 port = self.port
1568 # NOTE: REST url requires the intent id to be in decimal form
1569 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001570 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001571 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001572 if response:
1573 if 200 <= response[ 0 ] <= 299:
1574 return main.TRUE
1575 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001576 main.log.error( "Error with REST request, response was: %s: %s" %
1577 ( response[ 0 ], response[ 1 ] ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001578 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001579 except ( AttributeError, TypeError ):
1580 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001581 return None
Jon Halle401b092015-09-23 13:34:24 -07001582 except Exception:
1583 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001584 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001585
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001586 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001587 """
1588 Description:
1589 Check if all the current flows are in ADDED state
1590 Return:
1591 returnValue - Returns main.TRUE only if all flows are in
1592 return main.FALSE otherwise;
1593 Returns None for exception
1594 """
1595 try:
1596 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1597 returnValue = main.TRUE
1598 for flow in tempFlows:
1599 if flow.get( 'state' ) != 'ADDED':
1600 main.log.info( self.name + ": flow Id: " +
1601 str( flow.get( 'groupId' ) ) +
1602 " | state:" +
1603 str( flow.get( 'state' ) ) )
1604 returnValue = main.FALSE
1605 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001606 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001607 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001608 return None
1609 except Exception:
1610 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001611 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001612
1613 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1614 subjectClass=None, subjectKey=None, configKey=None ):
1615 """
1616 Description:
1617 Get a json object with the ONOS network configurations
1618 Returns:
1619 A json object containing the network configuration in
1620 ONOS; Returns main.FALSE if error on requests;
1621 Returns None for exception
1622 """
1623 try:
1624 output = None
1625 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001626 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001627 ip = self.ip_address
1628 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001629 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001630 "from topo file" )
1631 port = self.port
1632 url = "/network/configuration"
1633 if subjectClass:
Siddesh606bd872021-06-29 23:42:36 +00001634 url += "/" + self.checkRegex( subjectClass )
Jon Hall66e001c2015-11-12 09:45:10 -08001635 if subjectKey:
Siddesh606bd872021-06-29 23:42:36 +00001636 url += "/" + self.checkRegex( subjectKey )
Jon Hall66e001c2015-11-12 09:45:10 -08001637 if configKey:
Siddesh606bd872021-06-29 23:42:36 +00001638 url += "/" + self.checkRegex( configKey )
1639 url = requests.utils.quote( url, safe="%/" )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001640 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001641 if response:
1642 if 200 <= response[ 0 ] <= 299:
1643 output = response[ 1 ]
1644 a = json.loads( output )
1645 b = json.dumps( a )
1646 return b
1647 elif response[ 0 ] == 404:
1648 main.log.error( "Requested configuration doesn't exist: " +
Jon Hall3c0114c2020-08-11 15:07:42 -07001649 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001650 return {}
1651 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001652 main.log.error( "Error with REST request, response was: %s: %s" %
1653 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001654 return main.FALSE
1655 except ( AttributeError, TypeError ):
1656 main.log.exception( self.name + ": Object not as expected" )
1657 return None
1658 except Exception:
1659 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001660 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001661
Siddesh606bd872021-06-29 23:42:36 +00001662 def checkRegex( self, inputStr ):
1663 res = inputStr.replace('/', '%2f')
1664 return res
1665
Jon Hall66e001c2015-11-12 09:45:10 -08001666 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1667 subjectClass=None, subjectKey=None, configKey=None ):
1668 """
1669 Description:
1670 Set a json object with the ONOS network configurations
1671 Returns:
1672 Returns main.TRUE for successful requests; Returns main.FALSE
1673 if error on requests;
1674 Returns None for exceptions
1675
1676 """
1677 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001678 response = None
Jon Hall66e001c2015-11-12 09:45:10 -08001679 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001680 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001681 ip = self.ip_address
1682 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001683 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001684 "from topo file" )
1685 port = self.port
1686 url = "/network/configuration"
1687 if subjectClass:
Siddesh606bd872021-06-29 23:42:36 +00001688 url += "/" + self.checkRegex( subjectClass )
Jon Hall66e001c2015-11-12 09:45:10 -08001689 if subjectKey:
Siddesh606bd872021-06-29 23:42:36 +00001690 url += "/" + self.checkRegex( subjectKey )
Jon Hall66e001c2015-11-12 09:45:10 -08001691 if configKey:
Siddesh606bd872021-06-29 23:42:36 +00001692 url += "/" + self.checkRegex( configKey )
1693 url = requests.utils.quote( url, safe="%/" )
suibin zhang116647a2016-05-06 16:30:09 -07001694 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001695 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001696 data=json.dumps( cfgJson ) )
1697 if response:
1698 if 200 <= response[ 0 ] <= 299:
1699 main.log.info( self.name + ": Successfully POST cfg" )
1700 return main.TRUE
1701 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001702 main.log.error( "Error with REST request, response was: %s: %s" %
1703 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001704 return main.FALSE
1705 except ( AttributeError, TypeError ):
1706 main.log.exception( self.name + ": Object not as expected" )
1707 return None
1708 except Exception:
1709 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001710 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001711
1712 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1713 subjectClass=None, subjectKey=None, configKey=None ):
1714 """
1715 Description:
1716 Remove a json object from the ONOS network configurations
1717 Returns:
1718 Returns main.TRUE for successful requests; Returns main.FALSE
1719 if error on requests;
1720 Returns None for exceptions
1721
1722 """
1723 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07001724 response = None
Jon Hall66e001c2015-11-12 09:45:10 -08001725 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001726 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
Jon Hall66e001c2015-11-12 09:45:10 -08001727 ip = self.ip_address
1728 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07001729 main.log.warn( self.name + ": No port given, reverting to port " +
Jon Hall66e001c2015-11-12 09:45:10 -08001730 "from topo file" )
1731 port = self.port
1732 url = "/network/configuration"
1733 if subjectClass:
1734 url += "/" + subjectClass
1735 if subjectKey:
1736 url += "/" + subjectKey
1737 if configKey:
1738 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001739 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001740 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001741 if response:
1742 if 200 <= response[ 0 ] <= 299:
1743 main.log.info( self.name + ": Successfully delete cfg" )
1744 return main.TRUE
1745 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07001746 main.log.error( "Error with REST request, response was: %s: %s" %
1747 ( response[ 0 ], response[ 1 ] ) )
Jon Hall66e001c2015-11-12 09:45:10 -08001748 return main.FALSE
1749 except ( AttributeError, TypeError ):
1750 main.log.exception( self.name + ": Object not as expected" )
1751 return None
1752 except Exception:
1753 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001754 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001755
Jon Hall10e2ab82020-09-15 17:14:54 -07001756 def getXconnect( self, ip="DEFAULT", port="DEFAULT" ):
1757 """
1758 Description:
1759 Get xconnects
1760 Returns:
1761 Return xconnects json object
1762 Returns None for exceptions
1763
1764 """
1765 try:
1766 base = "/onos/segmentrouting"
1767 response = None
1768 if ip == "DEFAULT":
1769 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1770 ip = self.ip_address
1771 if port == "DEFAULT":
1772 main.log.warn( self.name + ": No port given, reverting to port " +
1773 "from topo file" )
1774 port = self.port
1775 url = "/xconnect"
1776 response = self.send( method="GET",
1777 base=base,
1778 url=url, ip = ip, port = port )
1779 if response:
1780 if 200 <= response[ 0 ] <= 299:
1781 main.log.info( self.name + ": Successfully POST cfg" )
1782 return main.TRUE
1783 else:
1784 main.log.error( "Error with REST request, response was: %s: %s" %
1785 ( response[ 0 ], response[ 1 ] ) )
1786 return main.FALSE
1787 except ( AttributeError, TypeError ):
1788 main.log.exception( self.name + ": Object not as expected" )
1789 return None
1790 except Exception:
1791 main.log.exception( self.name + ": Uncaught exception!" )
1792 main.cleanAndExit()
1793
1794 def setXconnect( self, deviceId, vlanId, port1, port2, ip="DEFAULT", port="DEFAULT" ):
1795 """
1796 Description:
1797 Set xconnects
1798 Returns:
1799 Returns main.TRUE for successful requests; Returns main.FALSE
1800 if error on requests;
1801 Returns None for exceptions
1802
1803 """
1804 try:
1805 cfgJson = json.loads( '{"deviceId": "%s", "vlanId": "%s", "endpoints":[%s,%s]}' %
1806 ( deviceId, vlanId, port1, port2 ) )
1807 response = self.setXconnectJson( cfgJson, ip=ip, port=port )
1808 except ( AttributeError, TypeError ):
1809 main.log.exception( self.name + ": Object not as expected" )
1810 return None
1811 except Exception:
1812 main.log.exception( self.name + ": Uncaught exception!" )
1813 main.cleanAndExit()
1814
1815 def setXconnectJson( self, cfgJson, ip="DEFAULT", port="DEFAULT" ):
1816 """
1817 Description:
1818 Set xconnects
1819 Returns:
1820 Returns main.TRUE for successful requests; Returns main.FALSE
1821 if error on requests;
1822 Returns None for exceptions
1823
1824 """
1825 try:
1826 base = "/onos/segmentrouting"
1827 response = None
1828 if ip == "DEFAULT":
1829 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1830 ip = self.ip_address
1831 if port == "DEFAULT":
1832 main.log.warn( self.name + ": No port given, reverting to port " +
1833 "from topo file" )
1834 port = self.port
1835 url = "/xconnect"
1836 response = self.send( method="POST",
1837 base=base,
1838 url=url, ip = ip, port = port,
1839 data=json.dumps( cfgJson ) )
1840 if response:
1841 if 200 <= response[ 0 ] <= 299:
1842 main.log.info( self.name + ": Successfully POST cfg" )
1843 return main.TRUE
1844 else:
1845 main.log.error( "Error with REST request, response was: %s: %s" %
1846 ( response[ 0 ], response[ 1 ] ) )
1847 return main.FALSE
1848 except ( AttributeError, TypeError ):
1849 main.log.exception( self.name + ": Object not as expected" )
1850 return None
1851 except Exception:
1852 main.log.exception( self.name + ": Uncaught exception!" )
1853 main.cleanAndExit()
1854
1855 def deleteXconnect( self, cfgJson, ip="DEFAULT", port="DEFAULT" ):
1856 """
1857 Description:
1858 Remove xconnects
1859 Returns:
1860 Returns main.TRUE for successful requests; Returns main.FALSE
1861 if error on requests;
1862 Returns None for exceptions
1863
1864 """
1865 try:
1866 base = "/onos/segmentrouting"
1867 response = None
1868 if ip == "DEFAULT":
1869 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
1870 ip = self.ip_address
1871 if port == "DEFAULT":
1872 main.log.warn( self.name + ": No port given, reverting to port " +
1873 "from topo file" )
1874 port = self.port
1875 url = "/xconnect"
1876 response = self.send( method="DELETE",
1877 base=base,
1878 url=url, ip = ip, port = port,
1879 data=json.dumps( cfgJson ) )
1880 if response:
1881 if 200 <= response[ 0 ] <= 299:
1882 main.log.info( self.name + ": Successfully POST cfg" )
1883 return main.TRUE
1884 else:
1885 main.log.error( "Error with REST request, response was: %s: %s" %
1886 ( response[ 0 ], response[ 1 ] ) )
1887 return main.FALSE
1888 except ( AttributeError, TypeError ):
1889 main.log.exception( self.name + ": Object not as expected" )
1890 return None
1891 except Exception:
1892 main.log.exception( self.name + ": Uncaught exception!" )
1893 main.cleanAndExit()
1894
suibin zhang17308622016-04-14 15:45:30 -07001895 def createFlowBatch( self,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001896 numSw = 1,
1897 swIndex = 1,
1898 batchSize = 1,
1899 batchIndex = 1,
1900 deviceIdpreFix = "of:",
1901 appId=0,
1902 deviceID="",
1903 ingressPort="",
1904 egressPort="",
1905 ethType="",
1906 ethSrc="",
1907 ethDst="",
1908 vlan="",
1909 ipProto="",
1910 ipSrc=(),
1911 ipDst=(),
1912 tcpSrc="",
1913 tcpDst="",
1914 udpDst="",
1915 udpSrc="",
1916 mpls="",
1917 ip="DEFAULT",
1918 port="DEFAULT",
1919 debug=False ):
suibin zhang17308622016-04-14 15:45:30 -07001920 """
1921 Description:
1922 Creates batches of MAC-rule flows for POST.
1923 Predefined MAC: 2 MS Hex digit for iterating devices
1924 Next 5 Hex digit for iterating batch numbers
1925 Next 5 Hex digit for iterating flows within a batch
1926 Required:
1927 * deviceId: id of the device
1928 Optional:
1929 * ingressPort: port ingress device
1930 * egressPort: port of egress device
1931 * ethType: specify ethType
1932 * ethSrc: specify ethSrc ( i.e. src mac addr )
1933 * ethDst: specify ethDst ( i.e. dst mac addr )
1934 * ipProto: specify ip protocol
1935 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001936 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001937 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001938 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001939 * tcpSrc: specify tcp source port
1940 * tcpDst: specify tcp destination port
1941 Returns:
1942 Returns main.TRUE for successful requests; Returns main.FALSE
1943 if error on requests;
1944 Returns None for exceptions
1945 NOTE:
1946 The ip and port option are for the requests input's ip and port
1947 of the ONOS node
1948 """
suibin zhang17308622016-04-14 15:45:30 -07001949
1950 flowJsonList = []
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001951 flowJsonBatch = { "flows": flowJsonList }
suibin zhang17308622016-04-14 15:45:30 -07001952 dev = swIndex
1953
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001954 for fl in range( 1, batchSize + 1 ):
1955 flowJson = { "priority": 100,
1956 "deviceId": "",
1957 "isPermanent": "true",
1958 "timeout": 0,
1959 "treatment": { "instructions": [] },
1960 "selector": { "criteria": [] }}
suibin zhang17308622016-04-14 15:45:30 -07001961
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001962 # main.log.info("fl: " + str(fl))
suibin zhang17308622016-04-14 15:45:30 -07001963 if dev <= numSw:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001964 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1965 # print deviceId
1966 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001967 dev += 1
1968 else:
1969 dev = 1
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001970 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1971 # print deviceId
1972 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001973 dev += 1
1974
1975 # ethSrc starts with "0"; ethDst starts with "1"
1976 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001977 ethS = "%02X" % int( "0" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1978 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1979 ethSrc = ':'.join( ethS[ i: i+2 ] for i in range( 0, len( ethS ), 2 ) )
1980 ethD = "%02X" % int( "1" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1981 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1982 ethDst = ':'.join( ethD[ i: i+2 ] for i in range( 0, len( ethD ), 2 ) )
suibin zhang17308622016-04-14 15:45:30 -07001983
1984 if appId:
1985 flowJson[ "appId" ] = appId
1986
1987 if egressPort:
1988 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001989 "type": "OUTPUT",
1990 "port": egressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001991 if ingressPort:
1992 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001993 "type": "IN_PORT",
1994 "port": ingressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001995 if ethType:
1996 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001997 "type": "ETH_TYPE",
1998 "ethType": ethType } )
suibin zhang17308622016-04-14 15:45:30 -07001999 if ethSrc:
2000 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002001 "type": "ETH_SRC",
2002 "mac": ethSrc } )
suibin zhang17308622016-04-14 15:45:30 -07002003 if ethDst:
2004 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002005 "type": "ETH_DST",
2006 "mac": ethDst } )
suibin zhang17308622016-04-14 15:45:30 -07002007 if vlan:
2008 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002009 "type": "VLAN_VID",
2010 "vlanId": vlan } )
suibin zhang17308622016-04-14 15:45:30 -07002011 if mpls:
2012 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002013 "type": "MPLS_LABEL",
2014 "label": mpls } )
suibin zhang17308622016-04-14 15:45:30 -07002015 if ipSrc:
2016 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002017 "type": ipSrc[ 0 ],
2018 "ip": ipSrc[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07002019 if ipDst:
2020 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002021 "type": ipDst[ 0 ],
2022 "ip": ipDst[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07002023 if tcpSrc:
2024 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002025 "type": "TCP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07002026 "tcpPort": tcpSrc } )
2027 if tcpDst:
2028 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002029 "type": "TCP_DST",
suibin zhang17308622016-04-14 15:45:30 -07002030 "tcpPort": tcpDst } )
2031 if udpSrc:
2032 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002033 "type": "UDP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07002034 "udpPort": udpSrc } )
2035 if udpDst:
2036 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002037 "type": "UDP_DST",
suibin zhang17308622016-04-14 15:45:30 -07002038 "udpPort": udpDst } )
2039 if ipProto:
2040 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002041 "type": "IP_PROTO",
suibin zhang17308622016-04-14 15:45:30 -07002042 "protocol": ipProto } )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002043 flowJsonList.append( flowJson )
suibin zhang17308622016-04-14 15:45:30 -07002044
Jon Hall3c0114c2020-08-11 15:07:42 -07002045 main.log.info( self.name + ": Number of flows in batch: " + str( len( flowJsonList ) ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002046 flowJsonBatch[ 'flows' ] = flowJsonList
suibin zhang17308622016-04-14 15:45:30 -07002047
2048 return flowJsonBatch
2049
suibin zhang17308622016-04-14 15:45:30 -07002050 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
2051 """
2052 Description:
2053 Sends a single flow batch through /flows REST API.
2054 Required:
2055 * The batch of flows
2056 Returns:
2057 Returns main.TRUE for successful requests; Returns main.FALSE
2058 if error on requests;
2059 Returns None for exceptions
2060 NOTE:
2061 The ip and port option are for the requests input's ip and port
2062 of the ONOS node
2063 """
suibin zhang17308622016-04-14 15:45:30 -07002064
2065 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002066 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07002067 main.log.debug( self.name + ": Adding flow: " + self.pprint( batch ) )
2068 response = None
suibin zhang17308622016-04-14 15:45:30 -07002069 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002070 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
suibin zhang17308622016-04-14 15:45:30 -07002071 ip = self.ip_address
2072 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002073 main.log.warn( self.name + ": No port given, reverting to port " +
suibin zhang17308622016-04-14 15:45:30 -07002074 "from topo file" )
2075 port = self.port
2076 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07002077 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002078 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07002079 data=json.dumps( batch ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002080 # main.log.info("Post response is: ", str(response[0]))
2081 if response[ 0 ] == 200:
suibin zhang17308622016-04-14 15:45:30 -07002082 main.log.info( self.name + ": Successfully POST flow batch" )
2083 return main.TRUE, response
2084 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002085 main.log.error( "Error with REST request, response was: %s: %s" %
2086 ( response[ 0 ], response[ 1 ] ) )
You Wang7b5b2262016-11-10 13:54:56 -08002087 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07002088 except NotImplementedError as e:
2089 raise e # Inform the caller
2090 except ( AttributeError, TypeError ):
2091 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08002092 return None, None
suibin zhang17308622016-04-14 15:45:30 -07002093 except Exception:
2094 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002095 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07002096
2097 def removeFlowBatch( self, batch={},
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002098 ip="DEFAULT", port="DEFAULT" ):
suibin zhang17308622016-04-14 15:45:30 -07002099 """
2100 Description:
2101 Remove a batch of flows
2102 Required:
2103 flow batch
2104 Return:
2105 Returns main.TRUE if successfully deletes flows, otherwise
2106 Returns main.FALSE, Returns None on error
2107 """
2108 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07002109 response = None
suibin zhang17308622016-04-14 15:45:30 -07002110 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002111 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
suibin zhang17308622016-04-14 15:45:30 -07002112 ip = self.ip_address
2113 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002114 main.log.warn( self.name + ": No port given, reverting to port " +
suibin zhang17308622016-04-14 15:45:30 -07002115 "from topo file" )
2116 port = self.port
2117 # NOTE: REST url requires the intent id to be in decimal form
2118
suibin zhang116647a2016-05-06 16:30:09 -07002119 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002120 url="/flows/", ip = ip, port = port,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002121 data = json.dumps( batch ) )
suibin zhang17308622016-04-14 15:45:30 -07002122 if response:
2123 if 200 <= response[ 0 ] <= 299:
2124 return main.TRUE
2125 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002126 main.log.error( "Error with REST request, response was: %s: %s" %
2127 ( response[ 0 ], response[ 1 ] ) )
suibin zhang17308622016-04-14 15:45:30 -07002128 return main.FALSE
2129 except ( AttributeError, TypeError ):
2130 main.log.exception( self.name + ": Object not as expected" )
2131 return None
2132 except Exception:
2133 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002134 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002135
2136 def getTopology( self, topologyOutput ):
2137 """
2138 Definition:
2139 Loads a json topology output
2140 Return:
2141 topology = current ONOS topology
2142 """
2143 import json
2144 try:
2145 # either onos:topology or 'topology' will work in CLI
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002146 topology = json.loads( topologyOutput )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002147 main.log.debug( topology )
2148 return topology
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002149 except Exception:
2150 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002151 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002152
2153 def checkStatus(
2154 self,
Devin Lim142b5342017-07-20 15:22:39 -07002155 numswitch,
2156 numlink,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002157 logLevel="info" ):
2158 """
2159 Checks the number of switches & links that ONOS sees against the
2160 supplied values. By default this will report to main.log, but the
2161 log level can be specific.
2162
Devin Lim142b5342017-07-20 15:22:39 -07002163 Params: numswitch = expected number of switches
2164 numlink = expected number of links
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002165 logLevel = level to log to.
2166 Currently accepts 'info', 'warn' and 'report'
2167
2168 Returns: main.TRUE if the number of switches and links are correct,
2169 main.FALSE if the number of switches and links is incorrect,
2170 and main.ERROR otherwise
2171 """
2172 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07002173 topology = self.getTopology( self.topology() )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002174 # summary = self.summary()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002175 if topology == {}:
2176 return main.ERROR
2177 output = ""
2178 # Is the number of switches is what we expected
2179 devices = topology.get( 'devices', False )
2180 links = topology.get( 'links', False )
Devin Lim142b5342017-07-20 15:22:39 -07002181 if devices is False or links is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002182 return main.ERROR
Devin Lim142b5342017-07-20 15:22:39 -07002183 switchCheck = ( int( devices ) == int( numswitch ) )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002184 # Is the number of links is what we expected
Devin Lim142b5342017-07-20 15:22:39 -07002185 linkCheck = ( int( links ) == int( numlink ) )
2186 if switchCheck and linkCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002187 # We expected the correct numbers
2188 output = output + "The number of links and switches match "\
2189 + "what was expected"
2190 result = main.TRUE
2191 else:
2192 output = output + \
2193 "The number of links and switches does not match " + \
2194 "what was expected"
2195 result = main.FALSE
2196 output = output + "\n ONOS sees %i devices" % int( devices )
Devin Lim142b5342017-07-20 15:22:39 -07002197 output = output + " (%i expected) " % int( numswitch )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002198 output = output + "and %i links " % int( links )
Devin Lim142b5342017-07-20 15:22:39 -07002199 output = output + "(%i expected)" % int( numlink )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002200 if logLevel == "report":
2201 main.log.report( output )
2202 elif logLevel == "warn":
2203 main.log.warn( output )
2204 else:
2205 main.log.info( output )
2206 return result
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07002207 except Exception:
2208 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002209 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302210
Jon Hall3c0114c2020-08-11 15:07:42 -07002211 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId,
2212 ip="DEFAULT", port="DEFAULT", debug=False ):
kavitha Alagesan373e0552016-11-22 05:22:05 +05302213 """
2214 Description:
2215 Creates a single Group for the specified device.
2216 Required:
2217 * deviceId: id of the device
2218 * type: Type of the Group
2219 * bucketList: Buckets to be added to the group
2220 * appCookie: Cookie for the Group
2221 * groupId: Id of the Group
2222 Returns:
2223 Returns main.TRUE for successful requests; Returns main.FALSE
2224 if error on requests;
2225 Returns None for exceptions
2226 Note:
2227 The ip and port option are for the requests input's ip and port
2228 of the ONOS node
2229 """
2230 try:
2231 groupJson = { "type": groupType,
2232 "appCookie": appCookie,
2233 "groupId": groupId,
2234 "buckets": bucketList
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002235 }
kavitha Alagesan373e0552016-11-22 05:22:05 +05302236 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
2237
2238 except ( AttributeError, TypeError ):
2239 main.log.exception( self.name + ": Object not as expected" )
2240 return None
2241 except Exception:
2242 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002243 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302244
2245 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
2246 """
2247 Description:
2248 Sends a single group to the specified device.
2249 Required:
2250 * deviceId: id of the device
2251 * groupJson: the group in json
2252 Returns:
2253 Returns main.TRUE for successful requests; Returns main.FALSE
2254 if error on requests;
2255 Returns None for exceptions
2256 NOTE:
2257 The ip and port option are for the requests input's ip and port
2258 of the ONOS node
2259 """
2260 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002261 if debug:
Jon Hall3c0114c2020-08-11 15:07:42 -07002262 main.log.debug( self.name + ": Adding group: " + self.pprint( groupJson ) )
2263 response = None
kavitha Alagesan373e0552016-11-22 05:22:05 +05302264 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002265 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302266 ip = self.ip_address
2267 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002268 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302269 "from topo file" )
2270 port = self.port
2271 url = "/groups/" + deviceId
2272 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002273 url=url, ip = ip, port = port,
kavitha Alagesan373e0552016-11-22 05:22:05 +05302274 data=json.dumps( groupJson ) )
2275 if response:
2276 if "201" in str( response[ 0 ] ):
2277 main.log.info( self.name + ": Successfully POST group " +
2278 "in device: " + str( deviceId ) )
2279 return main.TRUE
2280 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002281 main.log.error( "Error with REST request, response was: %s: %s" %
2282 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302283 return main.FALSE
2284 except NotImplementedError as e:
2285 raise e # Inform the caller
2286 except ( AttributeError, TypeError ):
2287 main.log.exception( self.name + ": Object not as expected" )
2288 return None
2289 except Exception:
2290 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002291 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302292
2293 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2294 """
2295 Description:
2296 Get all the groups or get a specific group by giving the
2297 deviceId and appCookie
2298 Optional:
2299 * deviceId: id of the Device
2300 * appCookie: Cookie of the Group
2301 Returns:
2302 Returns Groups for successful requests; Returns main.FALSE
2303 if error on requests;
2304 Returns None for exceptions
2305 NOTE:
2306 The ip and port option are for the requests input's ip and port
2307 of the ONOS node
2308 """
2309 try:
2310 output = None
2311 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002312 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302313 ip = self.ip_address
2314 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002315 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302316 "from topo file" )
2317 port = self.port
2318 url = "/groups"
2319 if deviceId:
2320 url += "/" + deviceId
2321 if appCookie:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002322 url += "/" + appCookie
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002323 response = self.send( url=url, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302324 if response:
2325 if 200 <= response[ 0 ] <= 299:
2326 output = response[ 1 ]
2327 groupsJson = json.loads( output ).get( 'groups' )
2328 assert groupsJson is not None, "Error parsing json object"
2329 groups = json.dumps( groupsJson )
2330 return groups
2331 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002332 main.log.error( "Error with REST request, response was: %s: %s" %
2333 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302334 return main.FALSE
2335 except ( AttributeError, AssertionError, TypeError ):
2336 main.log.exception( self.name + ": Object not as expected" )
2337 return None
2338 except Exception:
2339 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002340 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302341
2342 def removeGroup( self, deviceId, appCookie,
2343 ip="DEFAULT", port="DEFAULT" ):
2344 """
2345 Description:
2346 Removes specific device group
2347 Required:
2348 * deviceId: id of the Device
2349 * appCookie: Cookie of the Group
2350 Returns:
2351 Returns main.TRUE for successful requests; Returns main.FALSE
2352 if error on requests;
2353 Returns None for exceptions
2354 NOTE:
2355 The ip and port option are for the requests input's ip and port
2356 of the ONOS node
2357
2358 """
2359 try:
Jon Hall3c0114c2020-08-11 15:07:42 -07002360 response = None
kavitha Alagesan373e0552016-11-22 05:22:05 +05302361 if ip == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002362 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302363 ip = self.ip_address
2364 if port == "DEFAULT":
Jon Hall3c0114c2020-08-11 15:07:42 -07002365 main.log.warn( self.name + ": No port given, reverting to port " +
kavitha Alagesan373e0552016-11-22 05:22:05 +05302366 "from topo file" )
2367 port = self.port
2368 query = "/" + str( deviceId ) + "/" + str( appCookie )
2369 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002370 url="/groups" + query, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302371 if response:
2372 if 200 <= response[ 0 ] <= 299:
2373 return main.TRUE
2374 else:
Jon Hall3c0114c2020-08-11 15:07:42 -07002375 main.log.error( "Error with REST request, response was: %s: %s" %
2376 ( response[ 0 ], response[ 1 ] ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302377 return main.FALSE
2378 except ( AttributeError, TypeError ):
2379 main.log.exception( self.name + ": Object not as expected" )
2380 return None
2381 except Exception:
2382 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002383 main.cleanAndExit()
Jon Hall50a00012021-03-08 11:06:11 -08002384
2385 def portstats( self, ip="DEFAULT", port="DEFAULT" ):
2386 """
2387 Description:
2388 Gets the portstats for each port in ONOS
2389 Returns:
2390 A list of dicts containing device id and a list of dicts containing the
2391 port statistics for each port.
2392 Returns main.FALSE if error on request;
2393 Returns None for exception
2394 """
2395 try:
2396 output = None
2397 if ip == "DEFAULT":
2398 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2399 ip = self.ip_address
2400 if port == "DEFAULT":
2401 main.log.warn( self.name + ": No port given, reverting to port " +
2402 "from topo file" )
2403 port = self.port
2404 response = self.send( url="/statistics/ports", ip = ip, port = port )
2405 if response:
2406 if 200 <= response[ 0 ] <= 299:
2407 output = response[ 1 ]
2408 a = json.loads( output ).get( 'statistics' )
2409 assert a is not None, "Error parsing json object"
2410 b = json.dumps( a )
2411 return b
2412 else:
2413 main.log.error( "Error with REST request, response was: %s: %s" %
2414 ( response[ 0 ], response[ 1 ] ) )
2415 return main.FALSE
2416 except ( AttributeError, AssertionError, TypeError ):
2417 main.log.exception( self.name + ": Object not as expected" )
2418 return None
2419 except Exception:
2420 main.log.exception( self.name + ": Uncaught exception!" )
2421 main.cleanAndExit()
Daniele Moro04a62d12021-10-06 17:37:36 +02002422
2423 def getSlices( self, ip="DEFAULT", port="DEFAULT", debug=False ):
2424 try:
2425 output = None
2426 if ip == "DEFAULT":
2427 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2428 ip = self.ip_address
2429 if port == "DEFAULT":
2430 main.log.warn( self.name + ": No port given, reverting to port " +
2431 "from topo file" )
2432 port = self.port
2433 url = "/fabrictna/slicing/slice"
2434 response = self.send( url=url, ip = ip, port = port, base="/onos" )
2435 if response:
2436 if 200 <= response[ 0 ] <= 299:
2437 output = response[ 1 ]
2438 if debug:
2439 main.log.debug(self.name + ": read: " + output)
2440 slices = json.loads( output ).get( 'Slices' )
2441 assert slices is not None, "Error parsing json object"
2442 return json.dumps( slices )
2443 else:
2444 main.log.error( "Error with REST request, response was: %s: %s" %
2445 ( response[ 0 ], response[ 1 ] ) )
2446 return main.FALSE
2447 except ( AttributeError, AssertionError, TypeError ):
2448 main.log.exception( self.name + ": Object not as expected" )
2449 return None
2450 except Exception:
2451 main.log.exception( self.name + ": Uncaught exception!" )
2452 main.cleanAndExit()
2453
Daniele Moro04a62d12021-10-06 17:37:36 +02002454 def getTrafficClasses( self, slice_id, ip="DEFAULT", port="DEFAULT", debug=False ):
2455 try:
2456 if ip == "DEFAULT":
2457 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2458 ip = self.ip_address
2459 if port == "DEFAULT":
2460 main.log.warn( self.name + ": No port given, reverting to port " +
2461 "from topo file" )
2462 port = self.port
2463 url = "/fabrictna/slicing/tc/%d" % slice_id
2464 response = self.send( url=url, ip = ip, port = port, base="/onos" )
2465 if response:
2466 if 200 <= response[ 0 ] <= 299:
2467 output = response[ 1 ]
2468 if debug:
2469 main.log.debug(self.name + ": read: " + output)
2470 traffic_classes = json.loads( output ).get( 'TrafficClasses' )
2471 assert traffic_classes is not None, "Error parsing json object"
2472 return json.dumps( traffic_classes )
2473 else:
2474 main.log.error( "Error with REST request, response was: %s: %s" %
2475 ( response[ 0 ], response[ 1 ] ) )
2476 return main.FALSE
2477 except ( AttributeError, AssertionError, TypeError ):
2478 main.log.exception( self.name + ": Object not as expected" )
2479 return None
2480 except Exception:
2481 main.log.exception( self.name + ": Uncaught exception!" )
2482 main.cleanAndExit()
2483
Daniele Moro04a62d12021-10-06 17:37:36 +02002484 def addSlicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
2485 ip="DEFAULT", port="DEFAULT", debug=False ):
2486 self.__slicingClassifierFlow( slice_id, traffic_class, traffic_selector,
2487 ip, port, debug, method="POST" )
2488
2489 def removeSlicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
2490 ip="DEFAULT", port="DEFAULT", debug=False ):
2491 self.__slicingClassifierFlow( slice_id, traffic_class, traffic_selector,
2492 ip, port, debug, method="DELETE" )
2493
Carmelo Cascone11bd4422022-02-07 18:22:24 -08002494 def getSlicingClassifierFlows( self, slice_id, traffic_class, ip="DEFAULT",
Daniele Moro04a62d12021-10-06 17:37:36 +02002495 port="DEFAULT", debug=False ):
2496 try:
2497 if ip == "DEFAULT":
2498 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2499 ip = self.ip_address
2500 if port == "DEFAULT":
2501 main.log.warn( self.name + ": No port given, reverting to port " +
2502 "from topo file" )
2503 port = self.port
2504 url = "/fabrictna/slicing/flow/%d/%s" % ( slice_id, traffic_class )
2505 response = self.send( url=url, ip = ip, port = port, base="/onos" )
2506 if response:
2507 if 200 <= response[ 0 ] <= 299:
2508 output = response[ 1 ]
2509 if debug:
2510 main.log.debug(self.name + ": read: " + output)
Carmelo Cascone11bd4422022-02-07 18:22:24 -08002511 # FIXME: use plural in slicing service API
2512 # TrafficSelector actually points to an array of selectors.
2513 traffic_selectors = json.loads( output ).get( 'TrafficSelector' )
2514 assert traffic_selectors is not None, "Error parsing json object"
2515 return json.dumps( traffic_selectors )
Daniele Moro04a62d12021-10-06 17:37:36 +02002516 else:
2517 main.log.error( "Error with REST request, response was: %s: %s" %
2518 ( response[ 0 ], response[ 1 ] ) )
2519 return main.FALSE
2520 except ( AttributeError, AssertionError, TypeError ):
2521 main.log.exception( self.name + ": Object not as expected" )
2522 return None
2523 except Exception:
2524 main.log.exception( self.name + ": Uncaught exception!" )
2525 main.cleanAndExit()
2526
Daniele Moro04a62d12021-10-06 17:37:36 +02002527 def __slicingClassifierFlow( self, slice_id, traffic_class, traffic_selector,
2528 ip="DEFAULT", port="DEFAULT", debug=False,
2529 method="POST" ):
2530 try:
2531 if debug:
2532 main.log.debug( self.name + ": %s Slicing Classifier Flow" % method )
2533 main.log.debug( self.name + ": Slice ID: %d, Traffic Class: %s, Traffic Selector: %s" % (
2534 slice_id, traffic_class, self.pprint( traffic_selector ) ) )
2535 if ip == "DEFAULT":
2536 main.log.warn( self.name + ": No ip given, reverting to ip from topo file" )
2537 ip = self.ip_address
2538 if port == "DEFAULT":
2539 main.log.warn( self.name + ": No port given, reverting to port " +
2540 "from topo file" )
2541 port = self.port
2542 url = "/fabrictna/slicing/flow/%d/%s" % ( slice_id, traffic_class )
2543 response = self.send( method=method,
2544 url=url, ip = ip, port = port,
2545 data=json.dumps( traffic_selector ),
2546 base="/onos" )
2547 if response:
2548 if "200" in str( response[ 0 ] ):
2549 main.log.info( self.name + ": Successfully %s Slicing Classifier Flow for " % method +
2550 "Slice ID: %d, Traffic Class: %s" % ( slice_id, traffic_class ) )
2551 return main.TRUE
2552 else:
2553 main.log.error( "Error with REST request, response was: %s: %s" %
2554 ( response[ 0 ], response[ 1 ] ) )
2555 return main.FALSE
2556 except NotImplementedError as e:
2557 raise # Inform the caller
2558 except ( AttributeError, TypeError ):
2559 main.log.exception( self.name + ": Object not as expected" )
2560 return None
2561 except Exception:
2562 main.log.exception( self.name + ": Uncaught exception!" )
2563 main.cleanAndExit()