blob: 312f1f1c8471897664db6387332131148b848666 [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 Ronquillo4d5f1d02017-10-13 20:23:57 +000051 if os.getenv( str( self.ip_address ) ) != 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 Ronquillo4d5f1d02017-10-13 20:23:57 +000077 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000102 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000105 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000112 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000115 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000276 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000417 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000426 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000523 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000542 "type":"ETH_TYPE",
543 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700544 elif ethType:
545 intentJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000546 "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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000551 { "type":"ETH_SRC",
552 "mac":ethSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700553 if ethDst:
554 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000555 { "type":"ETH_DST",
556 "mac":ethDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700557 if ipSrc:
558 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000559 { "type":"IPV4_SRC",
560 "ip":ipSrc } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700561 if ipDst:
562 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000563 { "type":"IPV4_DST",
564 "ip":ipDst } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700565 if tcpSrc:
566 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000567 { "type":"TCP_SRC",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700568 "tcpPort": tcpSrc } )
569 if tcpDst:
570 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000571 { "type":"TCP_DST",
kelvin-onlabb50074f2015-07-27 16:18:32 -0700572 "tcpPort": tcpDst } )
573 if ipProto:
574 intentJson[ 'selector' ][ 'criteria' ].append(
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000575 { "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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000579 { "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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000616 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000697 "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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000755 url="/intents", ip=ip, port=port,
756 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700757
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +0000758 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000764 " 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000837 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000867 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 Ronquillo4d5f1d02017-10-13 20:23:57 +0000950 # 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 Ronquillo4d5f1d02017-10-13 20:23:57 +00001098 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 Ronquillo4d5f1d02017-10-13 20:23:57 +00001267 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001268 output = None
1269 if ip == "DEFAULT":
1270 main.log.warn( "No ip given, reverting to ip from topo file" )
1271 ip = self.ip_address
1272 if port == "DEFAULT":
1273 main.log.warn( "No port given, reverting to port " +
1274 "from topo file" )
1275 port = self.port
1276 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001277 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001278 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001279 data=json.dumps( flowJson ) )
1280 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001281 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001282 main.log.info( self.name + ": Successfully POST flow" +
1283 "in device: " + str( deviceId ) )
1284 return main.TRUE
1285 else:
1286 main.log.error( "Error with REST request, response was: " +
1287 str( response ) )
1288 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001289 except NotImplementedError as e:
1290 raise e # Inform the caller
1291 except ( AttributeError, TypeError ):
1292 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001293 return None
Jon Halle401b092015-09-23 13:34:24 -07001294 except Exception:
1295 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001296 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001297
GlennRC073e8bc2015-10-27 17:11:28 -07001298 def addFlow( self,
1299 deviceId,
1300 appId=0,
1301 ingressPort="",
1302 egressPort="",
1303 ethType="",
1304 ethSrc="",
1305 ethDst="",
1306 vlan="",
1307 ipProto="",
1308 ipSrc=(),
1309 ipDst=(),
1310 tcpSrc="",
1311 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001312 udpDst="",
1313 udpSrc="",
1314 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001315 priority=100,
kavitha Alagesan373e0552016-11-22 05:22:05 +05301316 groupId="",
GlennRC073e8bc2015-10-27 17:11:28 -07001317 ip="DEFAULT",
1318 port="DEFAULT",
1319 debug=False ):
1320 """
1321 Description:
1322 Creates a single flow in the specified device
1323 Required:
1324 * deviceId: id of the device
1325 Optional:
1326 * ingressPort: port ingress device
1327 * egressPort: port of egress device
1328 * ethType: specify ethType
1329 * ethSrc: specify ethSrc ( i.e. src mac addr )
1330 * ethDst: specify ethDst ( i.e. dst mac addr )
1331 * ipProto: specify ip protocol
1332 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001333 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001334 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001335 as a tuple (type, ip#)
GlennRC073e8bc2015-10-27 17:11:28 -07001336 * tcpSrc: specify tcp source port
1337 * tcpDst: specify tcp destination port
1338 Returns:
1339 Returns main.TRUE for successful requests; Returns main.FALSE
1340 if error on requests;
1341 Returns None for exceptions
1342 NOTE:
1343 The ip and port option are for the requests input's ip and port
1344 of the ONOS node
1345 """
1346 try:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001347 flowJson = { "priority":priority,
1348 "isPermanent":"true",
1349 "timeout":0,
1350 "deviceId":deviceId,
1351 "treatment":{"instructions":[]},
1352 "selector": {"criteria":[]}}
GlennRC073e8bc2015-10-27 17:11:28 -07001353 if appId:
1354 flowJson[ "appId" ] = appId
kavitha Alagesan373e0552016-11-22 05:22:05 +05301355
1356 if groupId:
1357 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001358 "type":"GROUP",
1359 "groupId":groupId } )
kavitha Alagesan373e0552016-11-22 05:22:05 +05301360
GlennRC073e8bc2015-10-27 17:11:28 -07001361 if egressPort:
1362 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001363 "type":"OUTPUT",
1364 "port":egressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001365 if ingressPort:
1366 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001367 "type":"IN_PORT",
1368 "port":ingressPort } )
GlennRC073e8bc2015-10-27 17:11:28 -07001369 if ethType:
1370 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001371 "type":"ETH_TYPE",
1372 "ethType":ethType } )
GlennRC073e8bc2015-10-27 17:11:28 -07001373 if ethSrc:
1374 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001375 "type":"ETH_SRC",
1376 "mac":ethSrc } )
GlennRC073e8bc2015-10-27 17:11:28 -07001377 if ethDst:
1378 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001379 "type":"ETH_DST",
1380 "mac":ethDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001381 if vlan:
1382 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001383 "type":"VLAN_VID",
1384 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001385 if mpls:
1386 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001387 "type":"MPLS_LABEL",
1388 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001389 if ipSrc:
1390 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001391 "type":ipSrc[0],
1392 "ip":ipSrc[1] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001393 if ipDst:
1394 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001395 "type":ipDst[0],
1396 "ip":ipDst[1] } )
GlennRC073e8bc2015-10-27 17:11:28 -07001397 if tcpSrc:
1398 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001399 "type":"TCP_SRC",
GlennRC073e8bc2015-10-27 17:11:28 -07001400 "tcpPort": tcpSrc } )
1401 if tcpDst:
1402 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001403 "type":"TCP_DST",
GlennRC073e8bc2015-10-27 17:11:28 -07001404 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001405 if udpSrc:
1406 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001407 "type":"UDP_SRC",
GlennRC956ea742015-11-05 16:14:15 -08001408 "udpPort": udpSrc } )
1409 if udpDst:
1410 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001411 "type":"UDP_DST",
GlennRC956ea742015-11-05 16:14:15 -08001412 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001413 if ipProto:
1414 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001415 "type":"IP_PROTO",
GlennRC073e8bc2015-10-27 17:11:28 -07001416 "protocol": ipProto } )
1417
1418 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1419
1420 except ( AttributeError, TypeError ):
1421 main.log.exception( self.name + ": Object not as expected" )
1422 return None
1423 except Exception:
1424 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001425 main.cleanAndExit()
GlennRC073e8bc2015-10-27 17:11:28 -07001426
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001427 def removeFlow( self, deviceId, flowId,
1428 ip="DEFAULT", port="DEFAULT" ):
1429 """
1430 Description:
1431 Remove specific device flow
1432 Required:
1433 str deviceId - id of the device
1434 str flowId - id of the flow
1435 Return:
1436 Returns main.TRUE if successfully deletes flows, otherwise
1437 Returns main.FALSE, Returns None on error
1438 """
1439 try:
1440 output = None
1441 if ip == "DEFAULT":
1442 main.log.warn( "No ip given, reverting to ip from topo file" )
1443 ip = self.ip_address
1444 if port == "DEFAULT":
1445 main.log.warn( "No port given, reverting to port " +
1446 "from topo file" )
1447 port = self.port
1448 # NOTE: REST url requires the intent id to be in decimal form
1449 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001450 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001451 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001452 if response:
1453 if 200 <= response[ 0 ] <= 299:
1454 return main.TRUE
1455 else:
1456 main.log.error( "Error with REST request, response was: " +
1457 str( response ) )
1458 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001459 except ( AttributeError, TypeError ):
1460 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001461 return None
Jon Halle401b092015-09-23 13:34:24 -07001462 except Exception:
1463 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001464 main.cleanAndExit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001465
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001466 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001467 """
1468 Description:
1469 Check if all the current flows are in ADDED state
1470 Return:
1471 returnValue - Returns main.TRUE only if all flows are in
1472 return main.FALSE otherwise;
1473 Returns None for exception
1474 """
1475 try:
1476 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1477 returnValue = main.TRUE
1478 for flow in tempFlows:
1479 if flow.get( 'state' ) != 'ADDED':
1480 main.log.info( self.name + ": flow Id: " +
1481 str( flow.get( 'groupId' ) ) +
1482 " | state:" +
1483 str( flow.get( 'state' ) ) )
1484 returnValue = main.FALSE
1485 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001486 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001487 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001488 return None
1489 except Exception:
1490 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001491 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001492
1493 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1494 subjectClass=None, subjectKey=None, configKey=None ):
1495 """
1496 Description:
1497 Get a json object with the ONOS network configurations
1498 Returns:
1499 A json object containing the network configuration in
1500 ONOS; Returns main.FALSE if error on requests;
1501 Returns None for exception
1502 """
1503 try:
1504 output = None
1505 if ip == "DEFAULT":
1506 main.log.warn( "No ip given, reverting to ip from topo file" )
1507 ip = self.ip_address
1508 if port == "DEFAULT":
1509 main.log.warn( "No port given, reverting to port " +
1510 "from topo file" )
1511 port = self.port
1512 url = "/network/configuration"
1513 if subjectClass:
1514 url += "/" + subjectClass
1515 if subjectKey:
1516 url += "/" + subjectKey
1517 if configKey:
1518 url += "/" + configKey
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001519 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001520 if response:
1521 if 200 <= response[ 0 ] <= 299:
1522 output = response[ 1 ]
1523 a = json.loads( output )
1524 b = json.dumps( a )
1525 return b
1526 elif response[ 0 ] == 404:
1527 main.log.error( "Requested configuration doesn't exist: " +
1528 str( response ) )
1529 return {}
1530 else:
1531 main.log.error( "Error with REST request, response was: " +
1532 str( response ) )
1533 return main.FALSE
1534 except ( AttributeError, TypeError ):
1535 main.log.exception( self.name + ": Object not as expected" )
1536 return None
1537 except Exception:
1538 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001539 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001540
1541 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1542 subjectClass=None, subjectKey=None, configKey=None ):
1543 """
1544 Description:
1545 Set a json object with the ONOS network configurations
1546 Returns:
1547 Returns main.TRUE for successful requests; Returns main.FALSE
1548 if error on requests;
1549 Returns None for exceptions
1550
1551 """
1552 try:
1553 output = None
1554 if ip == "DEFAULT":
1555 main.log.warn( "No ip given, reverting to ip from topo file" )
1556 ip = self.ip_address
1557 if port == "DEFAULT":
1558 main.log.warn( "No port given, reverting to port " +
1559 "from topo file" )
1560 port = self.port
1561 url = "/network/configuration"
1562 if subjectClass:
1563 url += "/" + subjectClass
1564 if subjectKey:
1565 url += "/" + subjectKey
1566 if configKey:
1567 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001568 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001569 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001570 data=json.dumps( cfgJson ) )
1571 if response:
1572 if 200 <= response[ 0 ] <= 299:
1573 main.log.info( self.name + ": Successfully POST cfg" )
1574 return main.TRUE
1575 else:
1576 main.log.error( "Error with REST request, response was: " +
1577 str( response ) )
1578 return main.FALSE
1579 except ( AttributeError, TypeError ):
1580 main.log.exception( self.name + ": Object not as expected" )
1581 return None
1582 except Exception:
1583 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001584 main.cleanAndExit()
Jon Hall66e001c2015-11-12 09:45:10 -08001585
1586 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1587 subjectClass=None, subjectKey=None, configKey=None ):
1588 """
1589 Description:
1590 Remove a json object from the ONOS network configurations
1591 Returns:
1592 Returns main.TRUE for successful requests; Returns main.FALSE
1593 if error on requests;
1594 Returns None for exceptions
1595
1596 """
1597 try:
1598 output = None
1599 if ip == "DEFAULT":
1600 main.log.warn( "No ip given, reverting to ip from topo file" )
1601 ip = self.ip_address
1602 if port == "DEFAULT":
1603 main.log.warn( "No port given, reverting to port " +
1604 "from topo file" )
1605 port = self.port
1606 url = "/network/configuration"
1607 if subjectClass:
1608 url += "/" + subjectClass
1609 if subjectKey:
1610 url += "/" + subjectKey
1611 if configKey:
1612 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001613 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001614 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001615 if response:
1616 if 200 <= response[ 0 ] <= 299:
1617 main.log.info( self.name + ": Successfully delete cfg" )
1618 return main.TRUE
1619 else:
1620 main.log.error( "Error with REST request, response was: " +
1621 str( response ) )
1622 return main.FALSE
1623 except ( AttributeError, TypeError ):
1624 main.log.exception( self.name + ": Object not as expected" )
1625 return None
1626 except Exception:
1627 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001628 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001629
1630 def createFlowBatch( self,
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001631 numSw = 1,
1632 swIndex = 1,
1633 batchSize = 1,
1634 batchIndex = 1,
1635 deviceIdpreFix = "of:",
1636 appId=0,
1637 deviceID="",
1638 ingressPort="",
1639 egressPort="",
1640 ethType="",
1641 ethSrc="",
1642 ethDst="",
1643 vlan="",
1644 ipProto="",
1645 ipSrc=(),
1646 ipDst=(),
1647 tcpSrc="",
1648 tcpDst="",
1649 udpDst="",
1650 udpSrc="",
1651 mpls="",
1652 ip="DEFAULT",
1653 port="DEFAULT",
1654 debug=False ):
suibin zhang17308622016-04-14 15:45:30 -07001655 """
1656 Description:
1657 Creates batches of MAC-rule flows for POST.
1658 Predefined MAC: 2 MS Hex digit for iterating devices
1659 Next 5 Hex digit for iterating batch numbers
1660 Next 5 Hex digit for iterating flows within a batch
1661 Required:
1662 * deviceId: id of the device
1663 Optional:
1664 * ingressPort: port ingress device
1665 * egressPort: port of egress device
1666 * ethType: specify ethType
1667 * ethSrc: specify ethSrc ( i.e. src mac addr )
1668 * ethDst: specify ethDst ( i.e. dst mac addr )
1669 * ipProto: specify ip protocol
1670 * ipSrc: specify ip source address with mask eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001671 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001672 * ipDst: specify ip destination address eg. ip#/24
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001673 as a tuple (type, ip#)
suibin zhang17308622016-04-14 15:45:30 -07001674 * tcpSrc: specify tcp source port
1675 * tcpDst: specify tcp destination port
1676 Returns:
1677 Returns main.TRUE for successful requests; Returns main.FALSE
1678 if error on requests;
1679 Returns None for exceptions
1680 NOTE:
1681 The ip and port option are for the requests input's ip and port
1682 of the ONOS node
1683 """
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001684 #from pprint import pprint
suibin zhang17308622016-04-14 15:45:30 -07001685
1686 flowJsonList = []
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001687 flowJsonBatch = {"flows":flowJsonList}
suibin zhang17308622016-04-14 15:45:30 -07001688 dev = swIndex
1689
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001690 for fl in range(1, batchSize + 1):
1691 flowJson = { "priority":100,
1692 "deviceId":"",
1693 "isPermanent":"true",
1694 "timeout":0,
1695 "treatment":{"instructions":[]},
1696 "selector": {"criteria":[]}}
suibin zhang17308622016-04-14 15:45:30 -07001697
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001698 #main.log.info("fl: " + str(fl))
suibin zhang17308622016-04-14 15:45:30 -07001699 if dev <= numSw:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001700 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1701 #print deviceId
1702 flowJson['deviceId'] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001703 dev += 1
1704 else:
1705 dev = 1
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001706 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1707 #print deviceId
1708 flowJson['deviceId'] = deviceId
suibin zhang17308622016-04-14 15:45:30 -07001709 dev += 1
1710
1711 # ethSrc starts with "0"; ethDst starts with "1"
1712 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001713 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1714 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1715 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1716 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1717 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1718 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
suibin zhang17308622016-04-14 15:45:30 -07001719
1720 if appId:
1721 flowJson[ "appId" ] = appId
1722
1723 if egressPort:
1724 flowJson[ 'treatment' ][ 'instructions' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001725 "type":"OUTPUT",
1726 "port":egressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001727 if ingressPort:
1728 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001729 "type":"IN_PORT",
1730 "port":ingressPort } )
suibin zhang17308622016-04-14 15:45:30 -07001731 if ethType:
1732 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001733 "type":"ETH_TYPE",
1734 "ethType":ethType } )
suibin zhang17308622016-04-14 15:45:30 -07001735 if ethSrc:
1736 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001737 "type":"ETH_SRC",
1738 "mac":ethSrc } )
suibin zhang17308622016-04-14 15:45:30 -07001739 if ethDst:
1740 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001741 "type":"ETH_DST",
1742 "mac":ethDst } )
suibin zhang17308622016-04-14 15:45:30 -07001743 if vlan:
1744 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001745 "type":"VLAN_VID",
1746 "vlanId":vlan } )
suibin zhang17308622016-04-14 15:45:30 -07001747 if mpls:
1748 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001749 "type":"MPLS_LABEL",
1750 "label":mpls } )
suibin zhang17308622016-04-14 15:45:30 -07001751 if ipSrc:
1752 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001753 "type":ipSrc[0],
1754 "ip":ipSrc[1] } )
suibin zhang17308622016-04-14 15:45:30 -07001755 if ipDst:
1756 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001757 "type":ipDst[0],
1758 "ip":ipDst[1] } )
suibin zhang17308622016-04-14 15:45:30 -07001759 if tcpSrc:
1760 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001761 "type":"TCP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001762 "tcpPort": tcpSrc } )
1763 if tcpDst:
1764 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001765 "type":"TCP_DST",
suibin zhang17308622016-04-14 15:45:30 -07001766 "tcpPort": tcpDst } )
1767 if udpSrc:
1768 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001769 "type":"UDP_SRC",
suibin zhang17308622016-04-14 15:45:30 -07001770 "udpPort": udpSrc } )
1771 if udpDst:
1772 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001773 "type":"UDP_DST",
suibin zhang17308622016-04-14 15:45:30 -07001774 "udpPort": udpDst } )
1775 if ipProto:
1776 flowJson[ 'selector' ][ 'criteria' ].append( {
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001777 "type":"IP_PROTO",
suibin zhang17308622016-04-14 15:45:30 -07001778 "protocol": ipProto } )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001779 #pprint(flowJson)
1780 flowJsonList.append(flowJson)
suibin zhang17308622016-04-14 15:45:30 -07001781
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001782 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1783 flowJsonBatch['flows'] = flowJsonList
1784 #pprint(flowJsonBatch)
suibin zhang17308622016-04-14 15:45:30 -07001785
1786 return flowJsonBatch
1787
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001788
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 Ronquillo4d5f1d02017-10-13 20:23:57 +00001806 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
suibin zhang17308622016-04-14 15:45:30 -07001807 output = None
1808 if ip == "DEFAULT":
1809 main.log.warn( "No ip given, reverting to ip from topo file" )
1810 ip = self.ip_address
1811 if port == "DEFAULT":
1812 main.log.warn( "No port given, reverting to port " +
1813 "from topo file" )
1814 port = self.port
1815 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001816 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001817 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001818 data=json.dumps( batch ) )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001819 #main.log.info("Post response is: ", str(response[0]))
1820 if response[0] == 200:
suibin zhang17308622016-04-14 15:45:30 -07001821 main.log.info( self.name + ": Successfully POST flow batch" )
1822 return main.TRUE, response
1823 else:
1824 main.log.error( "Error with REST request, response was: " +
1825 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001826 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001827 except NotImplementedError as e:
1828 raise e # Inform the caller
1829 except ( AttributeError, TypeError ):
1830 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001831 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001832 except Exception:
1833 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001834 main.cleanAndExit()
suibin zhang17308622016-04-14 15:45:30 -07001835
1836 def removeFlowBatch( self, batch={},
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001837 ip="DEFAULT", port="DEFAULT" ):
suibin zhang17308622016-04-14 15:45:30 -07001838 """
1839 Description:
1840 Remove a batch of flows
1841 Required:
1842 flow batch
1843 Return:
1844 Returns main.TRUE if successfully deletes flows, otherwise
1845 Returns main.FALSE, Returns None on error
1846 """
1847 try:
1848 output = None
1849 if ip == "DEFAULT":
1850 main.log.warn( "No ip given, reverting to ip from topo file" )
1851 ip = self.ip_address
1852 if port == "DEFAULT":
1853 main.log.warn( "No port given, reverting to port " +
1854 "from topo file" )
1855 port = self.port
1856 # NOTE: REST url requires the intent id to be in decimal form
1857
suibin zhang116647a2016-05-06 16:30:09 -07001858 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001859 url="/flows/", ip = ip, port = port,
1860 data = json.dumps(batch) )
suibin zhang17308622016-04-14 15:45:30 -07001861 if response:
1862 if 200 <= response[ 0 ] <= 299:
1863 return main.TRUE
1864 else:
1865 main.log.error( "Error with REST request, response was: " +
1866 str( response ) )
1867 return main.FALSE
1868 except ( AttributeError, TypeError ):
1869 main.log.exception( self.name + ": Object not as expected" )
1870 return None
1871 except Exception:
1872 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001873 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001874
1875 def getTopology( self, topologyOutput ):
1876 """
1877 Definition:
1878 Loads a json topology output
1879 Return:
1880 topology = current ONOS topology
1881 """
1882 import json
1883 try:
1884 # either onos:topology or 'topology' will work in CLI
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001885 topology = json.loads(topologyOutput)
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001886 main.log.debug( topology )
1887 return topology
1888 except pexpect.EOF:
1889 main.log.error( self.name + ": EOF exception found" )
1890 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001891 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001892 except Exception:
1893 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001894 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001895
1896 def checkStatus(
1897 self,
Devin Lim142b5342017-07-20 15:22:39 -07001898 numswitch,
1899 numlink,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001900 logLevel="info" ):
1901 """
1902 Checks the number of switches & links that ONOS sees against the
1903 supplied values. By default this will report to main.log, but the
1904 log level can be specific.
1905
Devin Lim142b5342017-07-20 15:22:39 -07001906 Params: numswitch = expected number of switches
1907 numlink = expected number of links
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001908 logLevel = level to log to.
1909 Currently accepts 'info', 'warn' and 'report'
1910
1911 Returns: main.TRUE if the number of switches and links are correct,
1912 main.FALSE if the number of switches and links is incorrect,
1913 and main.ERROR otherwise
1914 """
1915 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001916 topology = self.getTopology( self.topology() )
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001917 #summary = self.summary()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001918 if topology == {}:
1919 return main.ERROR
1920 output = ""
1921 # Is the number of switches is what we expected
1922 devices = topology.get( 'devices', False )
1923 links = topology.get( 'links', False )
Devin Lim142b5342017-07-20 15:22:39 -07001924 if devices is False or links is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001925 return main.ERROR
Devin Lim142b5342017-07-20 15:22:39 -07001926 switchCheck = ( int( devices ) == int( numswitch ) )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001927 # Is the number of links is what we expected
Devin Lim142b5342017-07-20 15:22:39 -07001928 linkCheck = ( int( links ) == int( numlink ) )
1929 if switchCheck and linkCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001930 # We expected the correct numbers
1931 output = output + "The number of links and switches match "\
1932 + "what was expected"
1933 result = main.TRUE
1934 else:
1935 output = output + \
1936 "The number of links and switches does not match " + \
1937 "what was expected"
1938 result = main.FALSE
1939 output = output + "\n ONOS sees %i devices" % int( devices )
Devin Lim142b5342017-07-20 15:22:39 -07001940 output = output + " (%i expected) " % int( numswitch )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001941 output = output + "and %i links " % int( links )
Devin Lim142b5342017-07-20 15:22:39 -07001942 output = output + "(%i expected)" % int( numlink )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001943 if logLevel == "report":
1944 main.log.report( output )
1945 elif logLevel == "warn":
1946 main.log.warn( output )
1947 else:
1948 main.log.info( output )
1949 return result
1950 except pexpect.EOF:
1951 main.log.error( self.name + ": EOF exception found" )
1952 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -07001953 main.cleanAndExit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001954 except Exception:
1955 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001956 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301957
1958 def addGroup( self, deviceId, groupType, bucketList, appCookie, groupId, ip="DEFAULT", port="DEFAULT", debug=False ):
1959 """
1960 Description:
1961 Creates a single Group for the specified device.
1962 Required:
1963 * deviceId: id of the device
1964 * type: Type of the Group
1965 * bucketList: Buckets to be added to the group
1966 * appCookie: Cookie for the Group
1967 * groupId: Id of the Group
1968 Returns:
1969 Returns main.TRUE for successful requests; Returns main.FALSE
1970 if error on requests;
1971 Returns None for exceptions
1972 Note:
1973 The ip and port option are for the requests input's ip and port
1974 of the ONOS node
1975 """
1976 try:
1977 groupJson = { "type": groupType,
1978 "appCookie": appCookie,
1979 "groupId": groupId,
1980 "buckets": bucketList
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00001981 }
kavitha Alagesan373e0552016-11-22 05:22:05 +05301982 return self.sendGroup( deviceId=deviceId, groupJson=groupJson, ip="DEFAULT", port="DEFAULT", debug=False )
1983
1984 except ( AttributeError, TypeError ):
1985 main.log.exception( self.name + ": Object not as expected" )
1986 return None
1987 except Exception:
1988 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001989 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05301990
1991 def sendGroup( self, deviceId, groupJson, ip="DEFAULT", port="DEFAULT", debug=False ):
1992 """
1993 Description:
1994 Sends a single group to the specified device.
1995 Required:
1996 * deviceId: id of the device
1997 * groupJson: the group in json
1998 Returns:
1999 Returns main.TRUE for successful requests; Returns main.FALSE
2000 if error on requests;
2001 Returns None for exceptions
2002 NOTE:
2003 The ip and port option are for the requests input's ip and port
2004 of the ONOS node
2005 """
2006 try:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002007 if debug: main.log.debug( "Adding group: " + self.pprint( groupJson ) )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302008 output = None
2009 if ip == "DEFAULT":
2010 main.log.warn( "No ip given, reverting to ip from topo file" )
2011 ip = self.ip_address
2012 if port == "DEFAULT":
2013 main.log.warn( "No port given, reverting to port " +
2014 "from topo file" )
2015 port = self.port
2016 url = "/groups/" + deviceId
2017 response = self.send( method="POST",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002018 url=url, ip = ip, port = port,
kavitha Alagesan373e0552016-11-22 05:22:05 +05302019 data=json.dumps( groupJson ) )
2020 if response:
2021 if "201" in str( response[ 0 ] ):
2022 main.log.info( self.name + ": Successfully POST group " +
2023 "in device: " + str( deviceId ) )
2024 return main.TRUE
2025 else:
2026 main.log.error( "Error with REST request, response was: " +
2027 str( response ) )
2028 return main.FALSE
2029 except NotImplementedError as e:
2030 raise e # Inform the caller
2031 except ( AttributeError, TypeError ):
2032 main.log.exception( self.name + ": Object not as expected" )
2033 return None
2034 except Exception:
2035 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002036 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302037
2038 def getGroups( self, deviceId=None, appCookie=None, ip="DEFAULT", port="DEFAULT" ):
2039 """
2040 Description:
2041 Get all the groups or get a specific group by giving the
2042 deviceId and appCookie
2043 Optional:
2044 * deviceId: id of the Device
2045 * appCookie: Cookie of the Group
2046 Returns:
2047 Returns Groups for successful requests; Returns main.FALSE
2048 if error on requests;
2049 Returns None for exceptions
2050 NOTE:
2051 The ip and port option are for the requests input's ip and port
2052 of the ONOS node
2053 """
2054 try:
2055 output = None
2056 if ip == "DEFAULT":
2057 main.log.warn( "No ip given, reverting to ip from topo file" )
2058 ip = self.ip_address
2059 if port == "DEFAULT":
2060 main.log.warn( "No port given, reverting to port " +
2061 "from topo file" )
2062 port = self.port
2063 url = "/groups"
2064 if deviceId:
2065 url += "/" + deviceId
2066 if appCookie:
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002067 url += "/" + appCookie
2068 response = self.send( url=url, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302069 if response:
2070 if 200 <= response[ 0 ] <= 299:
2071 output = response[ 1 ]
2072 groupsJson = json.loads( output ).get( 'groups' )
2073 assert groupsJson is not None, "Error parsing json object"
2074 groups = json.dumps( groupsJson )
2075 return groups
2076 else:
2077 main.log.error( "Error with REST request, response was: " +
2078 str( response ) )
2079 return main.FALSE
2080 except ( AttributeError, AssertionError, TypeError ):
2081 main.log.exception( self.name + ": Object not as expected" )
2082 return None
2083 except Exception:
2084 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002085 main.cleanAndExit()
kavitha Alagesan373e0552016-11-22 05:22:05 +05302086
2087 def removeGroup( self, deviceId, appCookie,
2088 ip="DEFAULT", port="DEFAULT" ):
2089 """
2090 Description:
2091 Removes specific device group
2092 Required:
2093 * deviceId: id of the Device
2094 * appCookie: Cookie of the Group
2095 Returns:
2096 Returns main.TRUE for successful requests; Returns main.FALSE
2097 if error on requests;
2098 Returns None for exceptions
2099 NOTE:
2100 The ip and port option are for the requests input's ip and port
2101 of the ONOS node
2102
2103 """
2104 try:
2105 output = None
2106 if ip == "DEFAULT":
2107 main.log.warn( "No ip given, reverting to ip from topo file" )
2108 ip = self.ip_address
2109 if port == "DEFAULT":
2110 main.log.warn( "No port given, reverting to port " +
2111 "from topo file" )
2112 port = self.port
2113 query = "/" + str( deviceId ) + "/" + str( appCookie )
2114 response = self.send( method="DELETE",
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002115 url="/groups" + query, ip = ip, port = port )
kavitha Alagesan373e0552016-11-22 05:22:05 +05302116 if response:
2117 if 200 <= response[ 0 ] <= 299:
2118 return main.TRUE
2119 else:
2120 main.log.error( "Error with REST request, response was: " +
2121 str( response ) )
2122 return main.FALSE
2123 except ( AttributeError, TypeError ):
2124 main.log.exception( self.name + ": Object not as expected" )
2125 return None
2126 except Exception:
2127 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07002128 main.cleanAndExit()
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00002129