blob: cf3eec6f9fb7fce90a40678f952b2d9c6950b3f6 [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 Hallfc915882015-07-14 13:33:17 -070029
Jon Hallfc915882015-07-14 13:33:17 -070030from drivers.common.api.controllerdriver import Controller
31
32
33class OnosRestDriver( Controller ):
34
35 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070036 self.pwd = None
37 self.user_name = "user"
Devin Limdc78e202017-06-09 18:30:07 -070038 super( OnosRestDriver, self ).__init__()
Jon Hallfc915882015-07-14 13:33:17 -070039 self.ip_address = "localhost"
40 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070041 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070042
43 def connect( self, **connectargs ):
44 try:
45 for key in connectargs:
46 vars( self )[ key ] = connectargs[ key ]
47 self.name = self.options[ 'name' ]
48 except Exception as e:
49 main.log.exception( e )
50 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -070051 if os.getenv( str( self.ip_address ) ) is not None:
Jon Hallfc915882015-07-14 13:33:17 -070052 self.ip_address = os.getenv( str( self.ip_address ) )
53 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070054 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070055 except KeyError:
56 main.log.info( "Invalid host name," +
57 "defaulting to 'localhost' instead" )
58 self.ip_address = 'localhost'
59 except Exception as inst:
60 main.log.error( "Uncaught exception: " + str( inst ) )
61
Jon Hall6040bcf2017-08-14 11:15:41 -070062 return super( OnosRestDriver, self ).connect()
Jon Hallfc915882015-07-14 13:33:17 -070063
Jon Halle401b092015-09-23 13:34:24 -070064 def pprint( self, jsonObject ):
65 """
66 Pretty Prints a json object
67
68 arguments:
69 jsonObject - a parsed json object
70 returns:
71 A formatted string for printing or None on error
72 """
73 try:
74 if isinstance( jsonObject, str ):
75 jsonObject = json.loads( jsonObject )
76 return json.dumps( jsonObject, sort_keys=True,
Jeremy Ronquillo82705492017-10-18 14:19:55 -070077 indent=4, separators=( ',', ': ' ) )
Jon Halle401b092015-09-23 13:34:24 -070078 except ( TypeError, ValueError ):
79 main.log.exception( "Error parsing jsonObject" )
80 return None
81
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000082 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070083 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070084 """
85 Arguments:
86 str ip: ONOS IP Address
87 str port: ONOS REST Port
88 str url: ONOS REST url path.
89 NOTE that this is is only the relative path. IE "/devices"
90 str base: The base url for the given REST api. Applications could
91 potentially have their own base url
92 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070093 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070094 the request
95 dict data: Dictionary to be sent in the body of the request
96 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +000097 # TODO: Authentication - simple http (user,pass) tuple
Jon Hallfc915882015-07-14 13:33:17 -070098 # TODO: should we maybe just pass kwargs straight to response?
99 # TODO: Do we need to allow for other protocols besides http?
100 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700101 if ip == "DEFAULT":
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700102 main.log.warn( "No ip given, reverting to ip from topo file" )
103 ip = self.ip_address
suibin zhangd5b6fe42016-05-12 08:48:58 -0700104 if port == "DEFAULT":
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700105 main.log.warn( "No port given, reverting to port " +
106 "from topo file" )
107 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700108
Jon Hallfc915882015-07-14 13:33:17 -0700109 try:
110 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700111 if self.user_name and self.pwd:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700112 main.log.info( "user/passwd is: " + self.user_name + "/" + self.pwd )
113 auth = ( self.user_name, self.pwd )
Jon Hallf7234882015-08-28 13:16:31 -0700114 else:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700115 auth = None
Jon Hallfc915882015-07-14 13:33:17 -0700116 main.log.info( "Sending request " + path + " using " +
117 method.upper() + " method." )
118 response = requests.request( method.upper(),
119 path,
120 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700121 data=data,
122 auth=auth )
123 if debug:
124 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700125 return ( response.status_code, response.text.encode( 'utf8' ) )
126 except requests.exceptions:
127 main.log.exception( "Error sending request." )
128 return None
Jon Halle401b092015-09-23 13:34:24 -0700129 except Exception:
130 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700131 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700132
133 def intents( self, ip="DEFAULT", port="DEFAULT" ):
134 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700135 Description:
136 Gets a list of dictionary of all intents in the system
137 Returns:
138 A list of dictionary of intents in string type to match the cli
139 version for now; Returns main.FALSE if error on request;
140 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700141 """
142 try:
143 output = None
144 if ip == "DEFAULT":
145 main.log.warn( "No ip given, reverting to ip from topo file" )
146 ip = self.ip_address
147 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700148 main.log.warn( "No port given, reverting to port " +
149 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700150 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000151 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700152 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700153 if 200 <= response[ 0 ] <= 299:
154 output = response[ 1 ]
155 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700156 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700157 b = json.dumps( a )
158 return b
Jon Hallfc915882015-07-14 13:33:17 -0700159 else:
160 main.log.error( "Error with REST request, response was: " +
161 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700162 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700163 except ( AttributeError, AssertionError, TypeError ):
164 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700165 return None
Jon Halle401b092015-09-23 13:34:24 -0700166 except Exception:
167 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700168 main.cleanAndExit()
Jon Hallfc915882015-07-14 13:33:17 -0700169
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700170 def intent( self, intentId, appId="org.onosproject.cli",
171 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700172 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700173 Description:
174 Get the specific intent information of the given application ID and
175 intent ID
176 Required:
177 str intentId - Intent id in hexadecimal form
178 Optional:
179 str appId - application id of intent
180 Returns:
181 Returns an information dictionary of the given intent;
182 Returns main.FALSE if error on requests; Returns None for exception
183 NOTE:
184 The GET /intents REST api command accepts application id but the
185 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700186 """
187 try:
188 output = None
189 if ip == "DEFAULT":
190 main.log.warn( "No ip given, reverting to ip from topo file" )
191 ip = self.ip_address
192 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700193 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700194 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700195 port = self.port
196 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700197 query = "/" + str( appId ) + "/" + str( intentId )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000198 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700199 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700200 if 200 <= response[ 0 ] <= 299:
201 output = response[ 1 ]
202 a = json.loads( output )
203 return a
Jon Hallfc915882015-07-14 13:33:17 -0700204 else:
205 main.log.error( "Error with REST request, response was: " +
206 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700207 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700208 except ( AttributeError, TypeError ):
209 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700210 return None
Jon Halle401b092015-09-23 13:34:24 -0700211 except Exception:
212 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700213 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700214
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700215 def apps( self, ip="DEFAULT", port="DEFAULT" ):
216 """
217 Description:
218 Returns all the current application installed in the system
219 Returns:
220 List of dictionary of installed application; Returns main.FALSE for
221 error on request; Returns None for exception
222 """
223 try:
224 output = None
225 if ip == "DEFAULT":
226 main.log.warn( "No ip given, reverting to ip from topo file" )
227 ip = self.ip_address
228 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700229 main.log.warn( "No port given, reverting to port " +
230 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700231 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000232 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700233 if response:
234 if 200 <= response[ 0 ] <= 299:
235 output = response[ 1 ]
236 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700237 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700238 b = json.dumps( a )
239 return b
240 else:
241 main.log.error( "Error with REST request, response was: " +
242 str( response ) )
243 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700244 except ( AttributeError, AssertionError, TypeError ):
245 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -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
251 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
252 """
253 Decription:
254 Activate an app that is already installed in ONOS
255 Optional:
256 bool check - If check is True, method will check the status
257 of the app after the command is issued
258 Returns:
259 Returns main.TRUE if the command was successfully or main.FALSE
260 if the REST responded with an error or given incorrect input;
261 Returns None for exception
262
263 """
264 try:
265 output = None
266 if ip == "DEFAULT":
267 main.log.warn( "No ip given, reverting to ip from topo file" )
268 ip = self.ip_address
269 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700270 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700271 "from topo file" )
272 port = self.port
273 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700274 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700275 url="/applications" + query,
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700276 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700277 if response:
278 output = response[ 1 ]
279 app = json.loads( output )
280 if 200 <= response[ 0 ] <= 299:
281 if check:
282 if app.get( 'state' ) == 'ACTIVE':
283 main.log.info( self.name + ": " + appName +
284 " application" +
285 " is in ACTIVE state" )
286 return main.TRUE
287 else:
288 main.log.error( self.name + ": " + appName +
289 " application" + " is in " +
290 app.get( 'state' ) + " state" )
291 return main.FALSE
292 else:
293 main.log.warn( "Skipping " + appName +
294 "application check" )
295 return main.TRUE
296 else:
297 main.log.error( "Error with REST request, response was: " +
298 str( response ) )
299 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700300 except ( AttributeError, TypeError ):
301 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700302 return None
Jon Halle401b092015-09-23 13:34:24 -0700303 except Exception:
304 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700305 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700306
307 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
308 check=True ):
309 """
310 Required:
311 Deactivate an app that is already activated in ONOS
312 Optional:
313 bool check - If check is True, method will check the status of the
314 app after the command is issued
315 Returns:
316 Returns main.TRUE if the command was successfully sent
317 main.FALSE if the REST responded with an error or given
318 incorrect input; Returns None for exception
319 """
320 try:
321 output = None
322 if ip == "DEFAULT":
323 main.log.warn( "No ip given, reverting to ip from topo file" )
324 ip = self.ip_address
325 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700326 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700327 "from topo file" )
328 port = self.port
329 query = "/" + str( appName ) + "/active"
Devin Limcde503f2017-09-11 17:23:30 -0700330 self.send( method="DELETE",
331 url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000332 ip = ip, port = port )
Devin Limcde503f2017-09-11 17:23:30 -0700333 response = self.getApp( appName, ip, port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700334 if response:
335 output = response[ 1 ]
Jon Hall6040bcf2017-08-14 11:15:41 -0700336 app = {} if output == "" else json.loads( output )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700337 if 200 <= response[ 0 ] <= 299:
338 if check:
339 if app.get( 'state' ) == 'INSTALLED':
340 main.log.info( self.name + ": " + appName +
341 " application" +
342 " is in INSTALLED state" )
343 return main.TRUE
344 else:
345 main.log.error( self.name + ": " + appName +
346 " application" + " is in " +
347 app.get( 'state' ) + " state" )
348 return main.FALSE
349 else:
350 main.log.warn( "Skipping " + appName +
351 "application check" )
352 return main.TRUE
353 else:
354 main.log.error( "Error with REST request, response was: " +
355 str( response ) )
356 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700357 except ( AttributeError, TypeError ):
358 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700359 return None
Jon Halle401b092015-09-23 13:34:24 -0700360 except Exception:
361 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700362 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700363
Devin Limcde503f2017-09-11 17:23:30 -0700364 def getApp( self, appName, ip="DEFAULT",
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700365 port="DEFAULT" ):
366 """
367 Decription:
368 Gets the informaion of the given application
369 Required:
370 str name - Name of onos application
371 Returns:
372 Returns a dictionary of information ONOS application in string type;
373 Returns main.FALSE if error on requests; Returns None for exception
374 """
375 try:
376 output = None
377 if ip == "DEFAULT":
378 main.log.warn( "No ip given, reverting to ip from topo file" )
379 ip = self.ip_address
380 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700381 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700382 "from topo file" )
383 port = self.port
Devin Limcde503f2017-09-11 17:23:30 -0700384 query = "/" + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700385 response = self.send( url="/applications" + query,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000386 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700387 if response:
388 if 200 <= response[ 0 ] <= 299:
Devin Limcde503f2017-09-11 17:23:30 -0700389 return response
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700390 else:
391 main.log.error( "Error with REST request, response was: " +
392 str( response ) )
393 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700394 except ( AttributeError, TypeError ):
395 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700396 return None
Jon Halle401b092015-09-23 13:34:24 -0700397 except Exception:
398 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700399 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700400
401 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700402 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700403 """
404 Description:
405 Adds a host-to-host intent ( bidirectional ) by
406 specifying the two hosts.
407 Required:
408 * hostIdOne: ONOS host id for host1
409 * hostIdTwo: ONOS host id for host2
410 Optional:
411 str appId - Application name of intent identifier
412 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700413 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700414 error on requests; Returns None for exceptions
415 """
416 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700417 intentJson = { "two": str( hostIdTwo ),
418 "selector": { "criteria": [] }, "priority": 7,
419 "treatment": { "deferred": [], "instructions": [] },
420 "appId": appId, "one": str( hostIdOne ),
421 "type": "HostToHostIntent",
422 "constraints": [ { "type": "LinkTypeConstraint",
423 "types": [ "OPTICAL" ],
424 "inclusive": 'false' } ] }
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700425 if vlanId:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700426 intentJson[ 'selector' ][ 'criteria' ].append( { "type": "VLAN_VID",
427 "vlanId": vlanId } )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700428 output = None
429 if ip == "DEFAULT":
430 main.log.warn( "No ip given, reverting to ip from topo file" )
431 ip = self.ip_address
432 if port == "DEFAULT":
433 main.log.warn( "No port given, reverting to port " +
434 "from topo file" )
435 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700436 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000437 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700438 data=json.dumps( intentJson ) )
439 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700440 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700441 main.log.info( self.name + ": Successfully POST host" +
442 " intent between host: " + hostIdOne +
443 " and host: " + hostIdTwo )
444 return main.TRUE
445 else:
446 main.log.error( "Error with REST request, response was: " +
447 str( response ) )
448 return main.FALSE
449
Jon Halle401b092015-09-23 13:34:24 -0700450 except ( AttributeError, TypeError ):
451 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700452 return None
Jon Halle401b092015-09-23 13:34:24 -0700453 except Exception:
454 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700455 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700456
kelvin-onlabb50074f2015-07-27 16:18:32 -0700457 def addPointIntent( self,
458 ingressDevice,
459 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700460 appId='org.onosproject.cli',
461 ingressPort="",
462 egressPort="",
463 ethType="",
464 ethSrc="",
465 ethDst="",
466 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800467 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700468 lambdaAlloc=False,
469 ipProto="",
470 ipSrc="",
471 ipDst="",
472 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700473 tcpDst="",
474 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700475 port="DEFAULT",
476 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700477 """
478 Description:
479 Adds a point-to-point intent ( uni-directional ) by
480 specifying device id's and optional fields
481 Required:
482 * ingressDevice: device id of ingress device
483 * egressDevice: device id of egress device
484 Optional:
485 * ethType: specify ethType
486 * ethSrc: specify ethSrc ( i.e. src mac addr )
487 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000488 * bandwidth: specify bandwidth capacity of link (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700489 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000490 for the specified intent (TODO)
kelvin-onlabb50074f2015-07-27 16:18:32 -0700491 * ipProto: specify ip protocol
492 * ipSrc: specify ip source address with mask eg. ip#/24
493 * ipDst: specify ip destination address eg. ip#/24
494 * tcpSrc: specify tcp source port
495 * tcpDst: specify tcp destination port
496 Returns:
497 Returns main.TRUE for successful requests; Returns main.FALSE if
498 no ingress|egress port found and if error on requests;
499 Returns None for exceptions
500 NOTE:
501 The ip and port option are for the requests input's ip and port
502 of the ONOS node
503 """
504 try:
505 if "/" in ingressDevice:
506 if not ingressPort:
507 ingressPort = ingressDevice.split( "/" )[ 1 ]
508 ingressDevice = ingressDevice.split( "/" )[ 0 ]
509 else:
510 if not ingressPort:
511 main.log.debug( self.name + ": Ingress port not specified" )
512 return main.FALSE
513
514 if "/" in egressDevice:
515 if not egressPort:
516 egressPort = egressDevice.split( "/" )[ 1 ]
517 egressDevice = egressDevice.split( "/" )[ 0 ]
518 else:
519 if not egressPort:
520 main.log.debug( self.name + ": Egress port not specified" )
521 return main.FALSE
522
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700523 intentJson = { "ingressPoint": { "device": ingressDevice,
524 "port": ingressPort },
525 "selector": { "criteria": [] },
526 "priority": 55,
527 "treatment": { "deferred": [],
528 "instructions": [] },
529 "egressPoint": { "device": egressDevice,
530 "port": egressPort },
531 "appId": appId,
532 "type": "PointToPointIntent",
533 "constraints": [ { "type": "LinkTypeConstraint",
534 "types": [ "OPTICAL" ],
535 "inclusive": "false" } ] }
kelvin-onlabb50074f2015-07-27 16:18:32 -0700536
alisonda157272016-12-22 01:13:21 -0800537 # if protected:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000538 # intentJson['constraints'].append( { "type": "Protection", "types": ["Protection"], "inclusive": "true" } )
alisonda157272016-12-22 01:13:21 -0800539
kelvin-onlabb50074f2015-07-27 16:18:32 -0700540 if ethType == "IPV4":
541 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700542 "type": "ETH_TYPE",
543 "ethType": 2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700544 elif ethType:
545 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700546 "type": "ETH_TYPE",
547 "ethType": ethType } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700548
kelvin-onlabb50074f2015-07-27 16:18:32 -0700549 if ethSrc:
550 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700551 { "type": "ETH_SRC",
552 "mac": ethSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700553 if ethDst:
554 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700555 { "type": "ETH_DST",
556 "mac": ethDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700557 if ipSrc:
558 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700559 { "type": "IPV4_SRC",
560 "ip": ipSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700561 if ipDst:
562 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700563 { "type": "IPV4_DST",
564 "ip": ipDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700565 if tcpSrc:
566 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700567 { "type": "TCP_SRC",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700568 "tcpPort": tcpSrc } )
569 if tcpDst:
570 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700571 { "type": "TCP_DST",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700572 "tcpPort": tcpDst } )
573 if ipProto:
574 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700575 { "type": "IP_PROTO",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700576 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700577 if vlanId:
578 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700579 { "type": "VLAN_VID",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700580 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700581
582 # TODO: Bandwidth and Lambda will be implemented if needed
583
kelvin-onlabb50074f2015-07-27 16:18:32 -0700584 output = None
585 if ip == "DEFAULT":
586 main.log.warn( "No ip given, reverting to ip from topo file" )
587 ip = self.ip_address
588 if port == "DEFAULT":
589 main.log.warn( "No port given, reverting to port " +
590 "from topo file" )
591 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700592 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000593 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700594 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700595
596 main.log.debug( intentJson )
597
kelvin-onlabb50074f2015-07-27 16:18:32 -0700598 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700599 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700600 main.log.info( self.name + ": Successfully POST point" +
601 " intent between ingress: " + ingressDevice +
602 " and egress: " + egressDevice + " devices" )
603 return main.TRUE
604 else:
605 main.log.error( "Error with REST request, response was: " +
606 str( response ) )
607 return main.FALSE
608
Jon Halle401b092015-09-23 13:34:24 -0700609 except ( AttributeError, TypeError ):
610 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700611 return None
Jon Halle401b092015-09-23 13:34:24 -0700612 except Exception:
613 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700614 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700615
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700616 def addSinglepointToMultipointIntent( self,
617 ingressDevice,
618 egressDeviceList,
619 portEgressList,
620 appId='org.onosproject.cli',
621 portIngress="",
622 ethType="",
623 ethSrc="",
624 ethDst="",
625 bandwidth="",
626 lambdaAlloc=False,
627 ipProto="",
628 ipSrc="",
629 ipDst="",
630 tcpSrc="",
631 tcpDst="",
632 partial=False,
633 ip="DEFAULT",
634 port="DEFAULT",
635 vlanId="" ):
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700636 """
637 Description:
638 Adds a point-to-multi point intent ( uni-directional ) by
639 specifying device id's and optional fields
640 Required:
641 * ingressDevice: device id of ingress device
642 * egressDevice: device id of egress device
643 * portEgressList: a list of port id of egress device
644
645 Optional:
646 * portIngress: port id of ingress device
647 * ethType: specify ethType
648 * ethSrc: specify ethSrc ( i.e. src mac addr )
649 * ethDst: specify ethDst ( i.e. dst mac addr )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000650 * bandwidth: specify bandwidth capacity of link (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700651 * lambdaAlloc: if True, intent will allocate lambda
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000652 for the specified intent (TODO)
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700653 * ipProto: specify ip protocol
654 * ipSrc: specify ip source address with mask eg. ip#/24
655 * ipDst: specify ip destination address eg. ip#/24
656 * tcpSrc: specify tcp source port
657 * tcpDst: specify tcp destination port
658 Returns:
659 Returns main.TRUE for successful requests; Returns main.FALSE if
660 no ingress|egress port found and if error on requests;
661 Returns None for exceptions
662 NOTE:
663 The ip and port option are for the requests input's ip and port
664 of the ONOS node
665 """
666 try:
667
668 if "/" in ingressDevice:
669 if not portIngress:
670 ingressPort = ingressDevice.split( "/" )[ 1 ]
671 ingressDevice = ingressDevice.split( "/" )[ 0 ]
672 else:
673 if not portIngress:
674 main.log.debug( self.name + ": Ingress port not specified" )
675 return main.FALSE
676 index = 0
677 for egressDevice in egressDeviceList:
678 if "/" in egressDevice:
679 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
680 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
681 else:
682 if not portEgressList:
683 main.log.debug( self.name + ": Egress port not specified" )
684 return main.FALSE
685 index = index + 1
686
687 intentJson = { "ingressPoint": { "device": ingressDevice,
688 "port": ingressPort },
689 "selector": { "criteria": [] },
690 "priority": 55,
691 "treatment": { "deferred": [],
692 "instructions": [] },
693 "egressPoint": { "connectPoints": [] },
694 "appId": appId,
695 "type": "SinglePointToMultiPointIntent",
696 "constraints": [ { "type": "LinkTypeConstraint",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700697 "types": [ "OPTICAL" ],
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700698 "inclusive": "false" } ] }
699
700 index = 0
701 for ep in portEgressList:
702 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
703 { "device": egressDeviceList[ index ],
704 "port": ep } )
705 index += 1
706
707 if ethType == "IPV4":
708 intentJson[ 'selector' ][ 'criteria' ].append(
709 { "type": "ETH_TYPE",
710 "ethType": 2048 } )
711 elif ethType:
712 intentJson[ 'selector' ][ 'criteria' ].append(
713 { "type": "ETH_TYPE",
714 "ethType": ethType } )
715
716 if ethSrc:
717 intentJson[ 'selector' ][ 'criteria' ].append(
718 { "type": "ETH_SRC",
719 "mac": ethSrc } )
720
721 if ethDst:
722 for dst in ethDst:
723 if dst:
724 intentJson[ 'selector' ][ 'criteria' ].append(
725 { "type": "ETH_DST",
726 "mac": dst } )
727 if tcpSrc:
728 intentJson[ 'selector' ][ 'criteria' ].append(
729 { "type": "TCP_SRC",
730 "tcpPort": tcpSrc } )
731 if tcpDst:
732 intentJson[ 'selector' ][ 'criteria' ].append(
733 { "type": "TCP_DST",
734 "tcpPort": tcpDst } )
735 if ipProto:
736 intentJson[ 'selector' ][ 'criteria' ].append(
737 { "type": "IP_PROTO",
738 "protocol": ipProto } )
739 if vlanId:
740 intentJson[ 'selector' ][ 'criteria' ].append(
741 { "type": "VLAN_VID",
742 "vlanId": vlanId } )
743
744 # TODO: Bandwidth and Lambda will be implemented if needed
745
746 output = None
747 if ip == "DEFAULT":
748 main.log.warn( "No ip given, reverting to ip from topo file" )
749 ip = self.ip_address
750 if port == "DEFAULT":
751 main.log.warn( "No port given, reverting to port " +
752 "from topo file" )
753 port = self.port
754 response = self.send( method="POST",
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700755 url="/intents", ip=ip, port=port,
756 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700757
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700758 main.log.debug( intentJson )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700759
760 if response:
761 if "201" in str( response[ 0 ] ):
762 main.log.info( self.name + ": Successfully POST point" +
763 " intent between ingress: " + ingressDevice +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700764 " and egress: " + str( egressDeviceList ) + " devices" )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700765 return main.TRUE
766 else:
767 main.log.error( "Error with REST request, response was: " + str( response ) )
768 return main.FALSE
769 else:
770 main.log.error( "REST request has no response." )
771 return main.FALSE
772
773 except ( AttributeError, TypeError ):
774 main.log.exception( self.name + ": Object not as expected" )
775 return None
776 except Exception:
777 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700778 main.cleanAndExit()
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700779
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700780 def removeIntent( self, intentId, appId='org.onosproject.cli',
781 ip="DEFAULT", port="DEFAULT" ):
782 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700783 Remove intent for specified application id and intent id;
784 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700785 """
786 try:
787 output = None
788 if ip == "DEFAULT":
789 main.log.warn( "No ip given, reverting to ip from topo file" )
790 ip = self.ip_address
791 if port == "DEFAULT":
792 main.log.warn( "No port given, reverting to port " +
793 "from topo file" )
794 port = self.port
795 # NOTE: REST url requires the intent id to be in decimal form
796 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700797 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000798 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700799 if response:
800 if 200 <= response[ 0 ] <= 299:
801 return main.TRUE
802 else:
803 main.log.error( "Error with REST request, response was: " +
804 str( response ) )
805 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700806 except ( AttributeError, TypeError ):
807 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700808 return None
Jon Halle401b092015-09-23 13:34:24 -0700809 except Exception:
810 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700811 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700812
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700813 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700814 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700815 Description:
816 Gets all intents ID using intents function
817 Returns:
818 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700819 """
820 try:
821 intentIdList = []
822 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700823 for intent in intentsJson:
824 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700825 if not intentIdList:
826 main.log.debug( "Cannot find any intents" )
827 return main.FALSE
828 else:
829 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700830 except ( AttributeError, TypeError ):
831 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700832 return None
Jon Halle401b092015-09-23 13:34:24 -0700833 except Exception:
834 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700835 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700836
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700837 def removeAllIntents( self, intentIdList ='ALL', appId='org.onosproject.cli',
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700838 ip="DEFAULT", port="DEFAULT", delay=5 ):
839 """
840 Description:
841 Remove all the intents
842 Returns:
843 Returns main.TRUE if all intents are removed, otherwise returns
844 main.FALSE; Returns None for exception
845 """
846 try:
847 results = []
848 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700849 # intentIdList = self.getIntentsId( ip=ip, port=port )
850 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700851
852 main.log.info( self.name + ": Removing intents " +
853 str( intentIdList ) )
854
855 if isinstance( intentIdList, types.ListType ):
856 for intent in intentIdList:
857 results.append( self.removeIntent( intentId=intent,
858 appId=appId,
859 ip=ip,
860 port=port ) )
861 # Check for remaining intents
862 # NOTE: Noticing some delay on Deleting the intents so i put
863 # this time out
864 import time
865 time.sleep( delay )
866 intentRemain = len( json.loads( self.intents() ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700867 if all( result == main.TRUE for result in results ) and \
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700868 intentRemain == 0:
869 main.log.info( self.name + ": All intents are removed " )
870 return main.TRUE
871 else:
872 main.log.error( self.name + ": Did not removed all intents,"
873 + " there are " + str( intentRemain )
874 + " intents remaining" )
875 return main.FALSE
876 else:
877 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700878 except ( AttributeError, TypeError ):
879 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700880 return None
Jon Halle401b092015-09-23 13:34:24 -0700881 except Exception:
882 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700883 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700884
885 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
886 """
887 Description:
888 Get a list of dictionary of all discovered hosts
889 Returns:
890 Returns a list of dictionary of information of the hosts currently
891 discovered by ONOS; Returns main.FALSE if error on requests;
892 Returns None for exception
893 """
894 try:
895 output = None
896 if ip == "DEFAULT":
897 main.log.warn( "No ip given, reverting to ip from topo file" )
898 ip = self.ip_address
899 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700900 main.log.warn( "No port given, reverting to port " +
901 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700902 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000903 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700904 if response:
905 if 200 <= response[ 0 ] <= 299:
906 output = response[ 1 ]
907 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700908 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700909 b = json.dumps( a )
910 return b
911 else:
912 main.log.error( "Error with REST request, response was: " +
913 str( response ) )
914 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700915 except ( AttributeError, AssertionError, TypeError ):
916 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700917 return None
Jon Halle401b092015-09-23 13:34:24 -0700918 except Exception:
919 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700920 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700921
922 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
923 """
924 Description:
925 Gets the information from the given host
926 Required:
927 str mac - MAC address of the host
928 Optional:
929 str vlan - VLAN tag of the host, defaults to -1
930 Returns:
931 Return the host id from the hosts/mac/vlan output in REST api
932 whose 'id' contains mac/vlan; Returns None for exception;
933 Returns main.FALSE if error on requests
934
935 NOTE:
936 Not sure what this function should do, any suggestion?
937 """
938 try:
939 output = None
940 if ip == "DEFAULT":
941 main.log.warn( "No ip given, reverting to ip from topo file" )
942 ip = self.ip_address
943 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700944 main.log.warn( "No port given, reverting to port " +
945 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700946 port = self.port
947 query = "/" + mac + "/" + vlan
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000948 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700949 if response:
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700950 # NOTE: What if the person wants other values? would it be better
951 # to have a function that gets a key and return a value instead?
952 # This function requires mac and vlan and returns an ID which
953 # makes this current function useless
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700954 if 200 <= response[ 0 ] <= 299:
955 output = response[ 1 ]
956 hostId = json.loads( output ).get( 'id' )
957 return hostId
958 else:
959 main.log.error( "Error with REST request, response was: " +
960 str( response ) )
961 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700962 except ( AttributeError, TypeError ):
963 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700964 return None
Jon Halle401b092015-09-23 13:34:24 -0700965 except Exception:
966 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700967 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700968
969 def topology( self, ip="DEFAULT", port="DEFAULT" ):
970 """
971 Description:
972 Gets the overview of network topology
973 Returns:
974 Returns a dictionary containing information about network topology;
975 Returns None for exception
976 """
977 try:
978 output = None
979 if ip == "DEFAULT":
980 main.log.warn( "No ip given, reverting to ip from topo file" )
981 ip = self.ip_address
982 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700983 main.log.warn( "No port given, reverting to port " +
984 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700985 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000986 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700987 if response:
988 if 200 <= response[ 0 ] <= 299:
989 output = response[ 1 ]
990 a = json.loads( output )
991 b = json.dumps( a )
992 return b
993 else:
994 main.log.error( "Error with REST request, response was: " +
995 str( response ) )
996 return main.FALSE
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()
Jon Halle401b092015-09-23 13:34:24 -07001003
1004 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1005 """
1006 Description:
1007 Get the devices discovered by ONOS is json string format
1008 Returns:
1009 a json string of the devices currently discovered by ONOS OR
1010 main.FALSE if there is an error in the request OR
1011 Returns None for exception
1012 """
1013 try:
1014 output = None
1015 if ip == "DEFAULT":
1016 main.log.warn( "No ip given, reverting to ip from topo file" )
1017 ip = self.ip_address
1018 if port == "DEFAULT":
1019 main.log.warn( "No port given, reverting to port " +
1020 "from topo file" )
1021 port = self.port
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001022 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001023 if response:
1024 if 200 <= response[ 0 ] <= 299:
1025 output = response[ 1 ]
1026 a = json.loads( output ).get( 'devices' )
1027 assert a is not None, "Error parsing json object"
1028 b = json.dumps( a )
1029 return b
1030 else:
1031 main.log.error( "Error with REST request, response was: " +
1032 str( response ) )
1033 return main.FALSE
1034 except ( AttributeError, AssertionError, TypeError ):
1035 main.log.exception( self.name + ": Object not as expected" )
1036 return None
1037 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 getIntentState( self, intentsId, intentsJson=None,
1042 ip="DEFAULT", port="DEFAULT" ):
1043 """
1044 Description:
1045 Get intent state.
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001046 Accepts a single intent ID (string type) or a list of intent IDs.
1047 Returns the state(string type) of the id if a single intent ID is
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001048 accepted.
1049 Required:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001050 intentId: intent ID (string type)
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001051 intentsJson: parsed json object from the onos:intents api
1052 Returns:
1053 Returns a dictionary with intent IDs as the key and its
1054 corresponding states as the values; Returns None for invalid IDs or
1055 Type error and any exceptions
1056 NOTE:
1057 An intent's state consist of INSTALLED,WITHDRAWN etc.
1058 """
1059 try:
1060 state = "State is Undefined"
1061 if not intentsJson:
1062 intentsJsonTemp = json.loads( self.intents() )
1063 else:
1064 intentsJsonTemp = json.loads( intentsJson )
1065 if isinstance( intentsId, types.StringType ):
1066 for intent in intentsJsonTemp:
1067 if intentsId == intent[ 'id' ]:
1068 state = intent[ 'state' ]
1069 return state
1070 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1071 " on the list" )
1072 return state
1073 elif isinstance( intentsId, types.ListType ):
1074 dictList = []
1075 for i in xrange( len( intentsId ) ):
1076 stateDict = {}
1077 for intents in intentsJsonTemp:
1078 if intentsId[ i ] == intents[ 'id' ]:
1079 stateDict[ 'state' ] = intents[ 'state' ]
1080 stateDict[ 'id' ] = intentsId[ i ]
1081 dictList.append( stateDict )
1082 break
1083 if len( intentsId ) != len( dictList ):
1084 main.log.info( "Cannot find some of the intent ID state" )
1085 return dictList
1086 else:
1087 main.log.info( "Invalid intents ID entry" )
1088 return None
1089
Jon Halle401b092015-09-23 13:34:24 -07001090 except ( AttributeError, TypeError ):
1091 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001092 return None
Jon Halle401b092015-09-23 13:34:24 -07001093 except Exception:
1094 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001095 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001096
1097 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001098 ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001099 """
1100 Description:
1101 Check intents state based on expected state which defaults to
1102 INSTALLED state
1103 Required:
1104 intentsId - List of intents ID to be checked
1105 Optional:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001106 expectedState - Check the expected state(s) of each intents
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001107 state in the list.
1108 *NOTE: You can pass in a list of expected state,
1109 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1110 Return:
1111 Returns main.TRUE only if all intent are the same as expected states
1112 , otherwise, returns main.FALSE; Returns None for general exception
1113 """
1114 try:
1115 # Generating a dictionary: intent id as a key and state as value
1116 returnValue = main.TRUE
1117 if intentsId == "ALL":
1118 intentsId = self.getIntentsId( ip=ip, port=port )
1119 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1120
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001121 if len( intentsId ) != len( intentsDict ):
1122 main.log.error( self.name + ": There is something wrong " +
1123 "getting intents state" )
1124 return main.FALSE
1125
1126 if isinstance( expectedState, types.StringType ):
1127 for intents in intentsDict:
1128 if intents.get( 'state' ) != expectedState:
1129 main.log.debug( self.name + " : Intent ID - " +
1130 intents.get( 'id' ) +
1131 " actual state = " +
1132 intents.get( 'state' )
1133 + " does not equal expected state = "
1134 + expectedState )
1135 returnValue = main.FALSE
1136
1137 elif isinstance( expectedState, types.ListType ):
1138 for intents in intentsDict:
1139 if not any( state == intents.get( 'state' ) for state in
1140 expectedState ):
1141 main.log.debug( self.name + " : Intent ID - " +
1142 intents.get( 'id' ) +
1143 " actual state = " +
1144 intents.get( 'state' ) +
1145 " does not equal expected states = "
1146 + str( expectedState ) )
1147 returnValue = main.FALSE
1148
1149 if returnValue == main.TRUE:
1150 main.log.info( self.name + ": All " +
1151 str( len( intentsDict ) ) +
1152 " intents are in " + str( expectedState ) +
1153 " state" )
1154 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001155 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001156 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001157 return None
Jon Halle401b092015-09-23 13:34:24 -07001158 except Exception:
1159 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001160 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001161
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001162 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001163 """
1164 Description:
1165 Get flows currently added to the system
1166 NOTE:
1167 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001168 the REST output
1169
1170 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001171 """
1172 try:
1173 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001174 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001175 if ip == "DEFAULT":
1176 main.log.warn( "No ip given, reverting to ip from topo file" )
1177 ip = self.ip_address
1178 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001179 main.log.warn( "No port given, reverting to port " +
1180 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001181 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001182 if subjectKey and not subjectClass:
1183 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1184 if subjectClass:
1185 url += "/" + subjectClass
1186 if subjectKey:
1187 url += "/" + subjectKey
1188 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001189 if response:
1190 if 200 <= response[ 0 ] <= 299:
1191 output = response[ 1 ]
1192 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001193 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001194 b = json.dumps( a )
1195 return b
1196 else:
1197 main.log.error( "Error with REST request, response was: " +
1198 str( response ) )
1199 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001200 except ( AttributeError, AssertionError, TypeError ):
1201 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001202 return None
Jon Halle401b092015-09-23 13:34:24 -07001203 except Exception:
1204 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001205 main.cleanAndExit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001206
Jon Halle401b092015-09-23 13:34:24 -07001207 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001208 """
1209 Description:
1210 Gets all the flows of the device or get a specific flow in the
1211 device by giving its flow ID
1212 Required:
Jon Halle401b092015-09-23 13:34:24 -07001213 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001214 Optional:
1215 int/hex flowId - ID of the flow
1216 """
1217 try:
1218 output = None
1219 if ip == "DEFAULT":
1220 main.log.warn( "No ip given, reverting to ip from topo file" )
1221 ip = self.ip_address
1222 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001223 main.log.warn( "No port given, reverting to port " +
1224 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001225 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001226 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001227 if flowId:
1228 url += "/" + str( int( flowId ) )
1229 print url
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001230 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001231 if response:
1232 if 200 <= response[ 0 ] <= 299:
1233 output = response[ 1 ]
1234 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001235 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001236 b = json.dumps( a )
1237 return b
1238 else:
1239 main.log.error( "Error with REST request, response was: " +
1240 str( response ) )
1241 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001242 except ( AttributeError, AssertionError, TypeError ):
1243 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001244 return None
Jon Halle401b092015-09-23 13:34:24 -07001245 except Exception:
1246 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001247 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001248
GlennRC073e8bc2015-10-27 17:11:28 -07001249 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001250 """
1251 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001252 Sends a single flow to the specified device. This function exists
1253 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001254 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001255 * The flow in json
1256 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001257 Returns:
1258 Returns main.TRUE for successful requests; Returns main.FALSE
1259 if error on requests;
1260 Returns None for exceptions
1261 NOTE:
1262 The ip and port option are for the requests input's ip and port
1263 of the ONOS node
1264 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001265
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001266 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001267 if debug:
1268 main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001269 output = None
1270 if ip == "DEFAULT":
1271 main.log.warn( "No ip given, reverting to ip from topo file" )
1272 ip = self.ip_address
1273 if port == "DEFAULT":
1274 main.log.warn( "No port given, reverting to port " +
1275 "from topo file" )
1276 port = self.port
1277 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001278 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001279 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001280 data=json.dumps( flowJson ) )
1281 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001282 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001283 main.log.info( self.name + ": Successfully POST flow" +
1284 "in device: " + str( deviceId ) )
1285 return main.TRUE
1286 else:
1287 main.log.error( "Error with REST request, response was: " +
1288 str( response ) )
1289 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001290 except NotImplementedError as e:
1291 raise e # Inform the caller
1292 except ( AttributeError, TypeError ):
1293 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001294 return None
Jon Halle401b092015-09-23 13:34:24 -07001295 except Exception:
1296 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001297 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001298
GlennRC073e8bc2015-10-27 17:11:28 -07001299 def addFlow( self,
1300 deviceId,
1301 appId=0,
1302 ingressPort="",
1303 egressPort="",
1304 ethType="",
1305 ethSrc="",
1306 ethDst="",
1307 vlan="",
1308 ipProto="",
1309 ipSrc=(),
1310 ipDst=(),
1311 tcpSrc="",
1312 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001313 udpDst="",
1314 udpSrc="",
1315 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001316 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301317 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001318 ip="DEFAULT",
1319 port="DEFAULT",
1320 debug=False ):
1321 """
1322 Description:
1323 Creates a single flow in the specified device
1324 Required:
1325 * deviceId: id of the device
1326 Optional:
1327 * ingressPort: port ingress device
1328 * egressPort: port of egress device
1329 * ethType: specify ethType
1330 * ethSrc: specify ethSrc ( i.e. src mac addr )
1331 * ethDst: specify ethDst ( i.e. dst mac addr )
1332 * ipProto: specify ip protocol
1333 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001334 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001335 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001336 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001337 * tcpSrc: specify tcp source port
1338 * tcpDst: specify tcp destination port
1339 Returns:
1340 Returns main.TRUE for successful requests; Returns main.FALSE
1341 if error on requests;
1342 Returns None for exceptions
1343 NOTE:
1344 The ip and port option are for the requests input's ip and port
1345 of the ONOS node
1346 """
1347 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001348 flowJson = { "priority": priority,
1349 "isPermanent": "true",
1350 "timeout": 0,
1351 "deviceId": deviceId,
1352 "treatment": { "instructions": [] },
1353 "selector": { "criteria": [] }}
GlennRC073e8bc2015-10-27 17:11:28 -07001354 if appId:
1355 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301356
1357 if groupId:
1358 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001359 "type": "GROUP",
1360 "groupId": groupId } )
kavitha Alagesan373e0552016-11-22 05:22:05 +05301361
GlennRC073e8bc2015-10-27 17:11:28 -07001362 if egressPort:
1363 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001364 "type": "OUTPUT",
1365 "port": egressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001366 if ingressPort:
1367 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001368 "type": "IN_PORT",
1369 "port": ingressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001370 if ethType:
1371 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001372 "type": "ETH_TYPE",
1373 "ethType": ethType } )
GlennRC073e8bc2015-10-27 17:11:28 -07001374 if ethSrc:
1375 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001376 "type": "ETH_SRC",
1377 "mac": ethSrc } )
GlennRC073e8bc2015-10-27 17:11:28 -07001378 if ethDst:
1379 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001380 "type": "ETH_DST",
1381 "mac": ethDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001382 if vlan:
1383 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001384 "type": "VLAN_VID",
1385 "vlanId": vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001386 if mpls:
1387 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001388 "type": "MPLS_LABEL",
1389 "label": mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001390 if ipSrc:
1391 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001392 "type": ipSrc[ 0 ],
1393 "ip": ipSrc[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001394 if ipDst:
1395 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001396 "type": ipDst[ 0 ],
1397 "ip": ipDst[ 1 ] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001398 if tcpSrc:
1399 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001400 "type": "TCP_SRC",
GlennRC073e8bc2015-10-27 17:11:28 -07001401 "tcpPort": tcpSrc } )
1402 if tcpDst:
1403 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001404 "type": "TCP_DST",
GlennRC073e8bc2015-10-27 17:11:28 -07001405 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001406 if udpSrc:
1407 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001408 "type": "UDP_SRC",
GlennRC956ea742015-11-05 16:14:15 -08001409 "udpPort": udpSrc } )
1410 if udpDst:
1411 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001412 "type": "UDP_DST",
GlennRC956ea742015-11-05 16:14:15 -08001413 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001414 if ipProto:
1415 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001416 "type": "IP_PROTO",
GlennRC073e8bc2015-10-27 17:11:28 -07001417 "protocol": ipProto } )
1418
1419 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1420
1421 except ( AttributeError, TypeError ):
1422 main.log.exception( self.name + ": Object not as expected" )
1423 return None
1424 except Exception:
1425 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001426 main.cleanAndExit()
GlennRC073e8bc2015-10-27 17:11:28 -07001427
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001428 def removeFlow( self, deviceId, flowId,
1429 ip="DEFAULT", port="DEFAULT" ):
1430 """
1431 Description:
1432 Remove specific device flow
1433 Required:
1434 str deviceId - id of the device
1435 str flowId - id of the flow
1436 Return:
1437 Returns main.TRUE if successfully deletes flows, otherwise
1438 Returns main.FALSE, Returns None on error
1439 """
1440 try:
1441 output = None
1442 if ip == "DEFAULT":
1443 main.log.warn( "No ip given, reverting to ip from topo file" )
1444 ip = self.ip_address
1445 if port == "DEFAULT":
1446 main.log.warn( "No port given, reverting to port " +
1447 "from topo file" )
1448 port = self.port
1449 # NOTE: REST url requires the intent id to be in decimal form
1450 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001451 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001452 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001453 if response:
1454 if 200 <= response[ 0 ] <= 299:
1455 return main.TRUE
1456 else:
1457 main.log.error( "Error with REST request, response was: " +
1458 str( response ) )
1459 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001460 except ( AttributeError, TypeError ):
1461 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001462 return None
Jon Halle401b092015-09-23 13:34:24 -07001463 except Exception:
1464 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001465 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001466
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001467 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001468 """
1469 Description:
1470 Check if all the current flows are in ADDED state
1471 Return:
1472 returnValue - Returns main.TRUE only if all flows are in
1473 return main.FALSE otherwise;
1474 Returns None for exception
1475 """
1476 try:
1477 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1478 returnValue = main.TRUE
1479 for flow in tempFlows:
1480 if flow.get( 'state' ) != 'ADDED':
1481 main.log.info( self.name + ": flow Id: " +
1482 str( flow.get( 'groupId' ) ) +
1483 " | state:" +
1484 str( flow.get( 'state' ) ) )
1485 returnValue = main.FALSE
1486 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001487 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001488 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001489 return None
1490 except Exception:
1491 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001492 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001493
1494 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1495 subjectClass=None, subjectKey=None, configKey=None ):
1496 """
1497 Description:
1498 Get a json object with the ONOS network configurations
1499 Returns:
1500 A json object containing the network configuration in
1501 ONOS; Returns main.FALSE if error on requests;
1502 Returns None for exception
1503 """
1504 try:
1505 output = None
1506 if ip == "DEFAULT":
1507 main.log.warn( "No ip given, reverting to ip from topo file" )
1508 ip = self.ip_address
1509 if port == "DEFAULT":
1510 main.log.warn( "No port given, reverting to port " +
1511 "from topo file" )
1512 port = self.port
1513 url = "/network/configuration"
1514 if subjectClass:
1515 url += "/" + subjectClass
1516 if subjectKey:
1517 url += "/" + subjectKey
1518 if configKey:
1519 url += "/" + configKey
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001520 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001521 if response:
1522 if 200 <= response[ 0 ] <= 299:
1523 output = response[ 1 ]
1524 a = json.loads( output )
1525 b = json.dumps( a )
1526 return b
1527 elif response[ 0 ] == 404:
1528 main.log.error( "Requested configuration doesn't exist: " +
1529 str( response ) )
1530 return {}
1531 else:
1532 main.log.error( "Error with REST request, response was: " +
1533 str( response ) )
1534 return main.FALSE
1535 except ( AttributeError, TypeError ):
1536 main.log.exception( self.name + ": Object not as expected" )
1537 return None
1538 except Exception:
1539 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001540 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001541
1542 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1543 subjectClass=None, subjectKey=None, configKey=None ):
1544 """
1545 Description:
1546 Set a json object with the ONOS network configurations
1547 Returns:
1548 Returns main.TRUE for successful requests; Returns main.FALSE
1549 if error on requests;
1550 Returns None for exceptions
1551
1552 """
1553 try:
1554 output = None
1555 if ip == "DEFAULT":
1556 main.log.warn( "No ip given, reverting to ip from topo file" )
1557 ip = self.ip_address
1558 if port == "DEFAULT":
1559 main.log.warn( "No port given, reverting to port " +
1560 "from topo file" )
1561 port = self.port
1562 url = "/network/configuration"
1563 if subjectClass:
1564 url += "/" + subjectClass
1565 if subjectKey:
1566 url += "/" + subjectKey
1567 if configKey:
1568 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001569 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001570 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001571 data=json.dumps( cfgJson ) )
1572 if response:
1573 if 200 <= response[ 0 ] <= 299:
1574 main.log.info( self.name + ": Successfully POST cfg" )
1575 return main.TRUE
1576 else:
1577 main.log.error( "Error with REST request, response was: " +
1578 str( response ) )
1579 return main.FALSE
1580 except ( AttributeError, TypeError ):
1581 main.log.exception( self.name + ": Object not as expected" )
1582 return None
1583 except Exception:
1584 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001585 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001586
1587 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1588 subjectClass=None, subjectKey=None, configKey=None ):
1589 """
1590 Description:
1591 Remove a json object from the ONOS network configurations
1592 Returns:
1593 Returns main.TRUE for successful requests; Returns main.FALSE
1594 if error on requests;
1595 Returns None for exceptions
1596
1597 """
1598 try:
1599 output = None
1600 if ip == "DEFAULT":
1601 main.log.warn( "No ip given, reverting to ip from topo file" )
1602 ip = self.ip_address
1603 if port == "DEFAULT":
1604 main.log.warn( "No port given, reverting to port " +
1605 "from topo file" )
1606 port = self.port
1607 url = "/network/configuration"
1608 if subjectClass:
1609 url += "/" + subjectClass
1610 if subjectKey:
1611 url += "/" + subjectKey
1612 if configKey:
1613 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001614 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001615 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001616 if response:
1617 if 200 <= response[ 0 ] <= 299:
1618 main.log.info( self.name + ": Successfully delete cfg" )
1619 return main.TRUE
1620 else:
1621 main.log.error( "Error with REST request, response was: " +
1622 str( response ) )
1623 return main.FALSE
1624 except ( AttributeError, TypeError ):
1625 main.log.exception( self.name + ": Object not as expected" )
1626 return None
1627 except Exception:
1628 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001629 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001630
1631 def createFlowBatch( self,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001632 numSw = 1,
1633 swIndex = 1,
1634 batchSize = 1,
1635 batchIndex = 1,
1636 deviceIdpreFix = "of:",
1637 appId=0,
1638 deviceID="",
1639 ingressPort="",
1640 egressPort="",
1641 ethType="",
1642 ethSrc="",
1643 ethDst="",
1644 vlan="",
1645 ipProto="",
1646 ipSrc=(),
1647 ipDst=(),
1648 tcpSrc="",
1649 tcpDst="",
1650 udpDst="",
1651 udpSrc="",
1652 mpls="",
1653 ip="DEFAULT",
1654 port="DEFAULT",
1655 debug=False ):
suibin zhang17308622016-04-14 15:45:30 -07001656 """
1657 Description:
1658 Creates batches of MAC-rule flows for POST.
1659 Predefined MAC: 2 MS Hex digit for iterating devices
1660 Next 5 Hex digit for iterating batch numbers
1661 Next 5 Hex digit for iterating flows within a batch
1662 Required:
1663 * deviceId: id of the device
1664 Optional:
1665 * ingressPort: port ingress device
1666 * egressPort: port of egress device
1667 * ethType: specify ethType
1668 * ethSrc: specify ethSrc ( i.e. src mac addr )
1669 * ethDst: specify ethDst ( i.e. dst mac addr )
1670 * ipProto: specify ip protocol
1671 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001672 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001673 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001674 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001675 * tcpSrc: specify tcp source port
1676 * tcpDst: specify tcp destination port
1677 Returns:
1678 Returns main.TRUE for successful requests; Returns main.FALSE
1679 if error on requests;
1680 Returns None for exceptions
1681 NOTE:
1682 The ip and port option are for the requests input's ip and port
1683 of the ONOS node
1684 """
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001685 # from pprint import pprint
suibin zhang17308622016-04-14 15:45:30 -07001686
1687 flowJsonList = []
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001688 flowJsonBatch = { "flows": flowJsonList }
suibin zhang17308622016-04-14 15:45:30 -07001689 dev = swIndex
1690
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001691 for fl in range( 1, batchSize + 1 ):
1692 flowJson = { "priority": 100,
1693 "deviceId": "",
1694 "isPermanent": "true",
1695 "timeout": 0,
1696 "treatment": { "instructions": [] },
1697 "selector": { "criteria": [] }}
suibin zhang17308622016-04-14 15:45:30 -07001698
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001699 # main.log.info("fl: " + str(fl))
suibin zhang17308622016-04-14 15:45:30 -07001700 if dev <= numSw:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001701 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1702 # print deviceId
1703 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001704 dev += 1
1705 else:
1706 dev = 1
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001707 deviceId = deviceIdpreFix + "{0:0{1}x}".format( dev, 16 )
1708 # print deviceId
1709 flowJson[ 'deviceId' ] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001710 dev += 1
1711
1712 # ethSrc starts with "0"; ethDst starts with "1"
1713 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001714 ethS = "%02X" % int( "0" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1715 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1716 ethSrc = ':'.join( ethS[ i: i+2 ] for i in range( 0, len( ethS ), 2 ) )
1717 ethD = "%02X" % int( "1" + "{0:0{1}b}".format( dev, 7 ), 2 ) + \
1718 "{0:0{1}x}".format( batchIndex, 5 ) + "{0:0{1}x}".format( fl, 5 )
1719 ethDst = ':'.join( ethD[ i: i+2 ] for i in range( 0, len( ethD ), 2 ) )
suibin zhang17308622016-04-14 15:45:30 -07001720
1721 if appId:
1722 flowJson[ "appId" ] = appId
1723
1724 if egressPort:
1725 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001726 "type": "OUTPUT",
1727 "port": egressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001728 if ingressPort:
1729 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001730 "type": "IN_PORT",
1731 "port": ingressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001732 if ethType:
1733 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001734 "type": "ETH_TYPE",
1735 "ethType": ethType } )
suibin zhang17308622016-04-14 15:45:30 -07001736 if ethSrc:
1737 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001738 "type": "ETH_SRC",
1739 "mac": ethSrc } )
suibin zhang17308622016-04-14 15:45:30 -07001740 if ethDst:
1741 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001742 "type": "ETH_DST",
1743 "mac": ethDst } )
suibin zhang17308622016-04-14 15:45:30 -07001744 if vlan:
1745 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001746 "type": "VLAN_VID",
1747 "vlanId": vlan } )
suibin zhang17308622016-04-14 15:45:30 -07001748 if mpls:
1749 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001750 "type": "MPLS_LABEL",
1751 "label": mpls } )
suibin zhang17308622016-04-14 15:45:30 -07001752 if ipSrc:
1753 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001754 "type": ipSrc[ 0 ],
1755 "ip": ipSrc[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07001756 if ipDst:
1757 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001758 "type": ipDst[ 0 ],
1759 "ip": ipDst[ 1 ] } )
suibin zhang17308622016-04-14 15:45:30 -07001760 if tcpSrc:
1761 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001762 "type": "TCP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001763 "tcpPort": tcpSrc } )
1764 if tcpDst:
1765 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001766 "type": "TCP_DST",
suibin zhang17308622016-04-14 15:45:30 -07001767 "tcpPort": tcpDst } )
1768 if udpSrc:
1769 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001770 "type": "UDP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001771 "udpPort": udpSrc } )
1772 if udpDst:
1773 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001774 "type": "UDP_DST",
suibin zhang17308622016-04-14 15:45:30 -07001775 "udpPort": udpDst } )
1776 if ipProto:
1777 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001778 "type": "IP_PROTO",
suibin zhang17308622016-04-14 15:45:30 -07001779 "protocol": ipProto } )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001780 # pprint(flowJson)
1781 flowJsonList.append( flowJson )
suibin zhang17308622016-04-14 15:45:30 -07001782
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001783 main.log.info( "Number of flows in batch: " + str( len( flowJsonList ) ) )
1784 flowJsonBatch[ 'flows' ] = flowJsonList
1785 # pprint(flowJsonBatch)
suibin zhang17308622016-04-14 15:45:30 -07001786
1787 return flowJsonBatch
1788
suibin zhang17308622016-04-14 15:45:30 -07001789 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1790 """
1791 Description:
1792 Sends a single flow batch through /flows REST API.
1793 Required:
1794 * The batch of flows
1795 Returns:
1796 Returns main.TRUE for successful requests; Returns main.FALSE
1797 if error on requests;
1798 Returns None for exceptions
1799 NOTE:
1800 The ip and port option are for the requests input's ip and port
1801 of the ONOS node
1802 """
1803 import time
1804
1805 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001806 if debug:
1807 main.log.debug( "Adding flow: " + self.pprint( batch ) )
suibin zhang17308622016-04-14 15:45:30 -07001808 output = None
1809 if ip == "DEFAULT":
1810 main.log.warn( "No ip given, reverting to ip from topo file" )
1811 ip = self.ip_address
1812 if port == "DEFAULT":
1813 main.log.warn( "No port given, reverting to port " +
1814 "from topo file" )
1815 port = self.port
1816 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001817 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001818 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001819 data=json.dumps( batch ) )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001820 # main.log.info("Post response is: ", str(response[0]))
1821 if response[ 0 ] == 200:
suibin zhang17308622016-04-14 15:45:30 -07001822 main.log.info( self.name + ": Successfully POST flow batch" )
1823 return main.TRUE, response
1824 else:
1825 main.log.error( "Error with REST request, response was: " +
1826 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001827 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001828 except NotImplementedError as e:
1829 raise e # Inform the caller
1830 except ( AttributeError, TypeError ):
1831 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001832 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001833 except Exception:
1834 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001835 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001836
1837 def removeFlowBatch( self, batch={},
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001838 ip="DEFAULT", port="DEFAULT" ):
suibin zhang17308622016-04-14 15:45:30 -07001839 """
1840 Description:
1841 Remove a batch of flows
1842 Required:
1843 flow batch
1844 Return:
1845 Returns main.TRUE if successfully deletes flows, otherwise
1846 Returns main.FALSE, Returns None on error
1847 """
1848 try:
1849 output = None
1850 if ip == "DEFAULT":
1851 main.log.warn( "No ip given, reverting to ip from topo file" )
1852 ip = self.ip_address
1853 if port == "DEFAULT":
1854 main.log.warn( "No port given, reverting to port " +
1855 "from topo file" )
1856 port = self.port
1857 # NOTE: REST url requires the intent id to be in decimal form
1858
suibin zhang116647a2016-05-06 16:30:09 -07001859 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001860 url="/flows/", ip = ip, port = port,
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001861 data = json.dumps( batch ) )
suibin zhang17308622016-04-14 15:45:30 -07001862 if response:
1863 if 200 <= response[ 0 ] <= 299:
1864 return main.TRUE
1865 else:
1866 main.log.error( "Error with REST request, response was: " +
1867 str( response ) )
1868 return main.FALSE
1869 except ( AttributeError, TypeError ):
1870 main.log.exception( self.name + ": Object not as expected" )
1871 return None
1872 except Exception:
1873 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001874 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001875
1876 def getTopology( self, topologyOutput ):
1877 """
1878 Definition:
1879 Loads a json topology output
1880 Return:
1881 topology = current ONOS topology
1882 """
1883 import json
1884 try:
1885 # either onos:topology or 'topology' will work in CLI
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001886 topology = json.loads( topologyOutput )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001887 main.log.debug( topology )
1888 return topology
1889 except pexpect.EOF:
1890 main.log.error( self.name + ": EOF exception found" )
1891 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001892 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001893 except Exception:
1894 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001895 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001896
1897 def checkStatus(
1898 self,
Devin Lim142b5342017-07-20 15:22:39 -07001899 numswitch,
1900 numlink,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001901 logLevel="info" ):
1902 """
1903 Checks the number of switches & links that ONOS sees against the
1904 supplied values. By default this will report to main.log, but the
1905 log level can be specific.
1906
Devin Lim142b5342017-07-20 15:22:39 -07001907 Params: numswitch = expected number of switches
1908 numlink = expected number of links
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001909 logLevel = level to log to.
1910 Currently accepts 'info', 'warn' and 'report'
1911
1912 Returns: main.TRUE if the number of switches and links are correct,
1913 main.FALSE if the number of switches and links is incorrect,
1914 and main.ERROR otherwise
1915 """
1916 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001917 topology = self.getTopology( self.topology() )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001918 # summary = self.summary()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001919 if topology == {}:
1920 return main.ERROR
1921 output = ""
1922 # Is the number of switches is what we expected
1923 devices = topology.get( 'devices', False )
1924 links = topology.get( 'links', False )
Devin Lim142b5342017-07-20 15:22:39 -07001925 if devices is False or links is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001926 return main.ERROR
Devin Lim142b5342017-07-20 15:22:39 -07001927 switchCheck = ( int( devices ) == int( numswitch ) )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001928 # Is the number of links is what we expected
Devin Lim142b5342017-07-20 15:22:39 -07001929 linkCheck = ( int( links ) == int( numlink ) )
1930 if switchCheck and linkCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001931 # We expected the correct numbers
1932 output = output + "The number of links and switches match "\
1933 + "what was expected"
1934 result = main.TRUE
1935 else:
1936 output = output + \
1937 "The number of links and switches does not match " + \
1938 "what was expected"
1939 result = main.FALSE
1940 output = output + "\n ONOS sees %i devices" % int( devices )
Devin Lim142b5342017-07-20 15:22:39 -07001941 output = output + " (%i expected) " % int( numswitch )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001942 output = output + "and %i links " % int( links )
Devin Lim142b5342017-07-20 15:22:39 -07001943 output = output + "(%i expected)" % int( numlink )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001944 if logLevel == "report":
1945 main.log.report( output )
1946 elif logLevel == "warn":
1947 main.log.warn( output )
1948 else:
1949 main.log.info( output )
1950 return result
1951 except pexpect.EOF:
1952 main.log.error( self.name + ": EOF exception found" )
1953 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001954 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001955 except Exception:
1956 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001957 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301958
1959 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
1960 """
1961 Description:
1962 Creates a single Group for the specified device.
1963 Required:
1964 * deviceId: id of the device
1965 * type: Type of the Group
1966 * bucketList: Buckets to be added to the group
1967 * appCookie: Cookie for the Group
1968 * groupId: Id of the Group
1969 Returns:
1970 Returns main.TRUE for successful requests; Returns main.FALSE
1971 if error on requests;
1972 Returns None for exceptions
1973 Note:
1974 The ip and port option are for the requests input's ip and port
1975 of the ONOS node
1976 """
1977 try:
1978 groupJson = { "type": groupType,
1979 "appCookie": appCookie,
1980 "groupId": groupId,
1981 "buckets": bucketList
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001982 }
kavitha Alagesan373e0552016-11-22 05:22:05 +05301983 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
1984
1985 except ( AttributeError, TypeError ):
1986 main.log.exception( self.name + ": Object not as expected" )
1987 return None
1988 except Exception:
1989 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001990 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301991
1992 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
1993 """
1994 Description:
1995 Sends a single group to the specified device.
1996 Required:
1997 * deviceId: id of the device
1998 * groupJson: the group in json
1999 Returns:
2000 Returns main.TRUE for successful requests; Returns main.FALSE
2001 if error on requests;
2002 Returns None for exceptions
2003 NOTE:
2004 The ip and port option are for the requests input's ip and port
2005 of the ONOS node
2006 """
2007 try:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002008 if debug:
2009 main.log.debug( "Adding group: " + self.pprint( groupJson ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302010 output = None
2011 if ip == "DEFAULT":
2012 main.log.warn( "No ip given, reverting to ip from topo file" )
2013 ip = self.ip_address
2014 if port == "DEFAULT":
2015 main.log.warn( "No port given, reverting to port " +
2016 "from topo file" )
2017 port = self.port
2018 url = "/groups/" + deviceId
2019 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002020 url=url, ip = ip, port = port,
kavitha Alagesan373e0552016-11-22 05:22:05 +05302021 data=json.dumps( groupJson ) )
2022 if response:
2023 if "201" in str( response[ 0 ] ):
2024 main.log.info( self.name + ": Successfully POST group " +
2025 "in device: " + str( deviceId ) )
2026 return main.TRUE
2027 else:
2028 main.log.error( "Error with REST request, response was: " +
2029 str( response ) )
2030 return main.FALSE
2031 except NotImplementedError as e:
2032 raise e # Inform the caller
2033 except ( AttributeError, TypeError ):
2034 main.log.exception( self.name + ": Object not as expected" )
2035 return None
2036 except Exception:
2037 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002038 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302039
2040 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2041 """
2042 Description:
2043 Get all the groups or get a specific group by giving the
2044 deviceId and appCookie
2045 Optional:
2046 * deviceId: id of the Device
2047 * appCookie: Cookie of the Group
2048 Returns:
2049 Returns Groups for successful requests; Returns main.FALSE
2050 if error on requests;
2051 Returns None for exceptions
2052 NOTE:
2053 The ip and port option are for the requests input's ip and port
2054 of the ONOS node
2055 """
2056 try:
2057 output = None
2058 if ip == "DEFAULT":
2059 main.log.warn( "No ip given, reverting to ip from topo file" )
2060 ip = self.ip_address
2061 if port == "DEFAULT":
2062 main.log.warn( "No port given, reverting to port " +
2063 "from topo file" )
2064 port = self.port
2065 url = "/groups"
2066 if deviceId:
2067 url += "/" + deviceId
2068 if appCookie:
Jeremy Ronquillo82705492017-10-18 14:19:55 -07002069 url += "/" + appCookie
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002070 response = self.send( url=url, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302071 if response:
2072 if 200 <= response[ 0 ] <= 299:
2073 output = response[ 1 ]
2074 groupsJson = json.loads( output ).get( 'groups' )
2075 assert groupsJson is not None, "Error parsing json object"
2076 groups = json.dumps( groupsJson )
2077 return groups
2078 else:
2079 main.log.error( "Error with REST request, response was: " +
2080 str( response ) )
2081 return main.FALSE
2082 except ( AttributeError, AssertionError, TypeError ):
2083 main.log.exception( self.name + ": Object not as expected" )
2084 return None
2085 except Exception:
2086 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002087 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302088
2089 def removeGroup( self, deviceId, appCookie,
2090 ip="DEFAULT", port="DEFAULT" ):
2091 """
2092 Description:
2093 Removes specific device group
2094 Required:
2095 * deviceId: id of the Device
2096 * appCookie: Cookie of the Group
2097 Returns:
2098 Returns main.TRUE for successful requests; Returns main.FALSE
2099 if error on requests;
2100 Returns None for exceptions
2101 NOTE:
2102 The ip and port option are for the requests input's ip and port
2103 of the ONOS node
2104
2105 """
2106 try:
2107 output = None
2108 if ip == "DEFAULT":
2109 main.log.warn( "No ip given, reverting to ip from topo file" )
2110 ip = self.ip_address
2111 if port == "DEFAULT":
2112 main.log.warn( "No port given, reverting to port " +
2113 "from topo file" )
2114 port = self.port
2115 query = "/" + str( deviceId ) + "/" + str( appCookie )
2116 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002117 url="/groups" + query, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302118 if response:
2119 if 200 <= response[ 0 ] <= 299:
2120 return main.TRUE
2121 else:
2122 main.log.error( "Error with REST request, response was: " +
2123 str( response ) )
2124 return main.FALSE
2125 except ( AttributeError, TypeError ):
2126 main.log.exception( self.name + ": Object not as expected" )
2127 return None
2128 except Exception:
2129 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002130 main.cleanAndExit()