blob: 5cf327460a6499cbd5fab8611b9fab457aae79fc [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
5
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jon Hallfc915882015-07-14 13:33:17 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 ( at your option ) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23"""
24import json
25import os
26import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070027import types
Jon Halle401b092015-09-23 13:34:24 -070028import sys
Jon Hallfc915882015-07-14 13:33:17 -070029
Jon Hallfc915882015-07-14 13:33:17 -070030from drivers.common.api.controllerdriver import Controller
31
32
33class OnosRestDriver( Controller ):
34
35 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070036 self.pwd = None
37 self.user_name = "user"
Jon Hallfc915882015-07-14 13:33:17 -070038 super( Controller, self ).__init__()
39 self.ip_address = "localhost"
40 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070041 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070042
43 def connect( self, **connectargs ):
44 try:
45 for key in connectargs:
46 vars( self )[ key ] = connectargs[ key ]
47 self.name = self.options[ 'name' ]
48 except Exception as e:
49 main.log.exception( e )
50 try:
51 if os.getenv( str( self.ip_address ) ) != None:
52 self.ip_address = os.getenv( str( self.ip_address ) )
53 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070054 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070055 except KeyError:
56 main.log.info( "Invalid host name," +
57 "defaulting to 'localhost' instead" )
58 self.ip_address = 'localhost'
59 except Exception as inst:
60 main.log.error( "Uncaught exception: " + str( inst ) )
61
62 self.handle = super( OnosRestDriver, self ).connect()
63 return self.handle
64
Jon Halle401b092015-09-23 13:34:24 -070065 def pprint( self, jsonObject ):
66 """
67 Pretty Prints a json object
68
69 arguments:
70 jsonObject - a parsed json object
71 returns:
72 A formatted string for printing or None on error
73 """
74 try:
75 if isinstance( jsonObject, str ):
76 jsonObject = json.loads( jsonObject )
77 return json.dumps( jsonObject, sort_keys=True,
78 indent=4, separators=(',', ': '))
79 except ( TypeError, ValueError ):
80 main.log.exception( "Error parsing jsonObject" )
81 return None
82
suibin zhangd5b6fe42016-05-12 08:48:58 -070083 def send( self, url, ip = "DEFAULT", port = "DEFAULT", base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070084 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070085 """
86 Arguments:
87 str ip: ONOS IP Address
88 str port: ONOS REST Port
89 str url: ONOS REST url path.
90 NOTE that this is is only the relative path. IE "/devices"
91 str base: The base url for the given REST api. Applications could
92 potentially have their own base url
93 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070094 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070095 the request
96 dict data: Dictionary to be sent in the body of the request
97 """
98 # TODO: Authentication - simple http (user,pass) tuple
99 # TODO: should we maybe just pass kwargs straight to response?
100 # TODO: Do we need to allow for other protocols besides http?
101 # ANSWER: Not yet, but potentially https with certificates
suibin zhangd5b6fe42016-05-12 08:48:58 -0700102 if ip == "DEFAULT":
103 main.log.warn( "No ip given, reverting to ip from topo file" )
104 ip = self.ip_address
105 if port == "DEFAULT":
106 main.log.warn( "No port given, reverting to port " +
107 "from topo file" )
108 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700109
Jon Hallfc915882015-07-14 13:33:17 -0700110 try:
111 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -0700112 if self.user_name and self.pwd:
suibin zhangeb121c02015-11-04 12:06:38 -0800113 main.log.info("user/passwd is: " + self.user_name + "/" + self.pwd)
Jon Hallf7234882015-08-28 13:16:31 -0700114 auth = (self.user_name, self.pwd)
115 else:
116 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700117 main.log.info( "Sending request " + path + " using " +
118 method.upper() + " method." )
119 response = requests.request( method.upper(),
120 path,
121 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700122 data=data,
123 auth=auth )
124 if debug:
125 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700126 return ( response.status_code, response.text.encode( 'utf8' ) )
127 except requests.exceptions:
128 main.log.exception( "Error sending request." )
129 return None
Jon Halle401b092015-09-23 13:34:24 -0700130 except Exception:
131 main.log.exception( self.name + ": Uncaught exception!" )
132 main.cleanup()
133 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700134
135 def intents( self, ip="DEFAULT", port="DEFAULT" ):
136 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700137 Description:
138 Gets a list of dictionary of all intents in the system
139 Returns:
140 A list of dictionary of intents in string type to match the cli
141 version for now; Returns main.FALSE if error on request;
142 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700143 """
144 try:
145 output = None
146 if ip == "DEFAULT":
147 main.log.warn( "No ip given, reverting to ip from topo file" )
148 ip = self.ip_address
149 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700150 main.log.warn( "No port given, reverting to port " +
151 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700152 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700153 response = self.send( url="/intents", ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700154 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700155 if 200 <= response[ 0 ] <= 299:
156 output = response[ 1 ]
157 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700158 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700159 b = json.dumps( a )
160 return b
Jon Hallfc915882015-07-14 13:33:17 -0700161 else:
162 main.log.error( "Error with REST request, response was: " +
163 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700164 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700165 except ( AttributeError, AssertionError, TypeError ):
166 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700167 return None
Jon Halle401b092015-09-23 13:34:24 -0700168 except Exception:
169 main.log.exception( self.name + ": Uncaught exception!" )
170 main.cleanup()
171 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700172
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700173 def intent( self, intentId, appId="org.onosproject.cli",
174 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700175 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700176 Description:
177 Get the specific intent information of the given application ID and
178 intent ID
179 Required:
180 str intentId - Intent id in hexadecimal form
181 Optional:
182 str appId - application id of intent
183 Returns:
184 Returns an information dictionary of the given intent;
185 Returns main.FALSE if error on requests; Returns None for exception
186 NOTE:
187 The GET /intents REST api command accepts application id but the
188 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700189 """
190 try:
191 output = None
192 if ip == "DEFAULT":
193 main.log.warn( "No ip given, reverting to ip from topo file" )
194 ip = self.ip_address
195 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700196 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700197 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700198 port = self.port
199 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700200 query = "/" + str( appId ) + "/" + str( intentId )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700201 response = self.send( url="/intents" + query, ip = ip, port = port )
Jon Hallfc915882015-07-14 13:33:17 -0700202 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700203 if 200 <= response[ 0 ] <= 299:
204 output = response[ 1 ]
205 a = json.loads( output )
206 return a
Jon Hallfc915882015-07-14 13:33:17 -0700207 else:
208 main.log.error( "Error with REST request, response was: " +
209 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700210 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700211 except ( AttributeError, TypeError ):
212 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700213 return None
Jon Halle401b092015-09-23 13:34:24 -0700214 except Exception:
215 main.log.exception( self.name + ": Uncaught exception!" )
216 main.cleanup()
217 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700218
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700219 def apps( self, ip="DEFAULT", port="DEFAULT" ):
220 """
221 Description:
222 Returns all the current application installed in the system
223 Returns:
224 List of dictionary of installed application; Returns main.FALSE for
225 error on request; Returns None for exception
226 """
227 try:
228 output = None
229 if ip == "DEFAULT":
230 main.log.warn( "No ip given, reverting to ip from topo file" )
231 ip = self.ip_address
232 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700233 main.log.warn( "No port given, reverting to port " +
234 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700235 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700236 response = self.send( url="/applications", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700237 if response:
238 if 200 <= response[ 0 ] <= 299:
239 output = response[ 1 ]
240 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700241 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700242 b = json.dumps( a )
243 return b
244 else:
245 main.log.error( "Error with REST request, response was: " +
246 str( response ) )
247 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700248 except ( AttributeError, AssertionError, TypeError ):
249 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700250 return None
Jon Halle401b092015-09-23 13:34:24 -0700251 except Exception:
252 main.log.exception( self.name + ": Uncaught exception!" )
253 main.cleanup()
254 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700255
256 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
257 """
258 Decription:
259 Activate an app that is already installed in ONOS
260 Optional:
261 bool check - If check is True, method will check the status
262 of the app after the command is issued
263 Returns:
264 Returns main.TRUE if the command was successfully or main.FALSE
265 if the REST responded with an error or given incorrect input;
266 Returns None for exception
267
268 """
269 try:
270 output = None
271 if ip == "DEFAULT":
272 main.log.warn( "No ip given, reverting to ip from topo file" )
273 ip = self.ip_address
274 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700275 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700276 "from topo file" )
277 port = self.port
278 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700279 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700280 url="/applications" + query,
281 ip = ip, port = port)
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700282 if response:
283 output = response[ 1 ]
284 app = json.loads( output )
285 if 200 <= response[ 0 ] <= 299:
286 if check:
287 if app.get( 'state' ) == 'ACTIVE':
288 main.log.info( self.name + ": " + appName +
289 " application" +
290 " is in ACTIVE state" )
291 return main.TRUE
292 else:
293 main.log.error( self.name + ": " + appName +
294 " application" + " is in " +
295 app.get( 'state' ) + " state" )
296 return main.FALSE
297 else:
298 main.log.warn( "Skipping " + appName +
299 "application check" )
300 return main.TRUE
301 else:
302 main.log.error( "Error with REST request, response was: " +
303 str( response ) )
304 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700305 except ( AttributeError, TypeError ):
306 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700307 return None
Jon Halle401b092015-09-23 13:34:24 -0700308 except Exception:
309 main.log.exception( self.name + ": Uncaught exception!" )
310 main.cleanup()
311 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700312
313 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
314 check=True ):
315 """
316 Required:
317 Deactivate an app that is already activated in ONOS
318 Optional:
319 bool check - If check is True, method will check the status of the
320 app after the command is issued
321 Returns:
322 Returns main.TRUE if the command was successfully sent
323 main.FALSE if the REST responded with an error or given
324 incorrect input; Returns None for exception
325 """
326 try:
327 output = None
328 if ip == "DEFAULT":
329 main.log.warn( "No ip given, reverting to ip from topo file" )
330 ip = self.ip_address
331 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700332 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700333 "from topo file" )
334 port = self.port
335 query = "/" + str( appName ) + "/active"
suibin zhang116647a2016-05-06 16:30:09 -0700336 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700337 url="/applications" + query,
338 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700339 if response:
340 output = response[ 1 ]
341 app = json.loads( output )
342 if 200 <= response[ 0 ] <= 299:
343 if check:
344 if app.get( 'state' ) == 'INSTALLED':
345 main.log.info( self.name + ": " + appName +
346 " application" +
347 " is in INSTALLED state" )
348 return main.TRUE
349 else:
350 main.log.error( self.name + ": " + appName +
351 " application" + " is in " +
352 app.get( 'state' ) + " state" )
353 return main.FALSE
354 else:
355 main.log.warn( "Skipping " + appName +
356 "application check" )
357 return main.TRUE
358 else:
359 main.log.error( "Error with REST request, response was: " +
360 str( response ) )
361 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700362 except ( AttributeError, TypeError ):
363 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700364 return None
Jon Halle401b092015-09-23 13:34:24 -0700365 except Exception:
366 main.log.exception( self.name + ": Uncaught exception!" )
367 main.cleanup()
368 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700369
370 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
371 port="DEFAULT" ):
372 """
373 Decription:
374 Gets the informaion of the given application
375 Required:
376 str name - Name of onos application
377 Returns:
378 Returns a dictionary of information ONOS application in string type;
379 Returns main.FALSE if error on requests; Returns None for exception
380 """
381 try:
382 output = None
383 if ip == "DEFAULT":
384 main.log.warn( "No ip given, reverting to ip from topo file" )
385 ip = self.ip_address
386 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700387 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700388 "from topo file" )
389 port = self.port
390 query = "/" + project + str( appName )
suibin zhangd5b6fe42016-05-12 08:48:58 -0700391 response = self.send( url="/applications" + query,
392 ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700393 if response:
394 if 200 <= response[ 0 ] <= 299:
395 output = response[ 1 ]
396 a = json.loads( output )
397 return a
398 else:
399 main.log.error( "Error with REST request, response was: " +
400 str( response ) )
401 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700402 except ( AttributeError, TypeError ):
403 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700404 return None
Jon Halle401b092015-09-23 13:34:24 -0700405 except Exception:
406 main.log.exception( self.name + ": Uncaught exception!" )
407 main.cleanup()
408 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700409
410 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700411 ip="DEFAULT", port="DEFAULT", vlanId="" ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700412 """
413 Description:
414 Adds a host-to-host intent ( bidirectional ) by
415 specifying the two hosts.
416 Required:
417 * hostIdOne: ONOS host id for host1
418 * hostIdTwo: ONOS host id for host2
419 Optional:
420 str appId - Application name of intent identifier
421 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700422 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700423 error on requests; Returns None for exceptions
424 """
425 try:
426 intentJson = {"two": str( hostIdTwo ),
427 "selector": {"criteria": []}, "priority": 7,
428 "treatment": {"deferred": [], "instructions": []},
429 "appId": appId, "one": str( hostIdOne ),
430 "type": "HostToHostIntent",
431 "constraints": [{"type": "LinkTypeConstraint",
432 "types": ["OPTICAL"],
433 "inclusive": 'false' }]}
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700434 if vlanId:
435 intentJson[ 'selector' ][ 'criteria' ].append( { "type":"VLAN_VID",
436 "vlanId":vlanId } )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700437 output = None
438 if ip == "DEFAULT":
439 main.log.warn( "No ip given, reverting to ip from topo file" )
440 ip = self.ip_address
441 if port == "DEFAULT":
442 main.log.warn( "No port given, reverting to port " +
443 "from topo file" )
444 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700445 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700446 url="/intents", ip = ip, port = port,
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700447 data=json.dumps( intentJson ) )
448 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700449 if "201" in str( response[ 0 ] ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700450 main.log.info( self.name + ": Successfully POST host" +
451 " intent between host: " + hostIdOne +
452 " and host: " + hostIdTwo )
453 return main.TRUE
454 else:
455 main.log.error( "Error with REST request, response was: " +
456 str( response ) )
457 return main.FALSE
458
Jon Halle401b092015-09-23 13:34:24 -0700459 except ( AttributeError, TypeError ):
460 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700461 return None
Jon Halle401b092015-09-23 13:34:24 -0700462 except Exception:
463 main.log.exception( self.name + ": Uncaught exception!" )
464 main.cleanup()
465 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700466
kelvin-onlabb50074f2015-07-27 16:18:32 -0700467 def addPointIntent( self,
468 ingressDevice,
469 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700470 appId='org.onosproject.cli',
471 ingressPort="",
472 egressPort="",
473 ethType="",
474 ethSrc="",
475 ethDst="",
476 bandwidth="",
alisonda157272016-12-22 01:13:21 -0800477 protected=False,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700478 lambdaAlloc=False,
479 ipProto="",
480 ipSrc="",
481 ipDst="",
482 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700483 tcpDst="",
484 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700485 port="DEFAULT",
486 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700487 """
488 Description:
489 Adds a point-to-point intent ( uni-directional ) by
490 specifying device id's and optional fields
491 Required:
492 * ingressDevice: device id of ingress device
493 * egressDevice: device id of egress device
494 Optional:
495 * ethType: specify ethType
496 * ethSrc: specify ethSrc ( i.e. src mac addr )
497 * ethDst: specify ethDst ( i.e. dst mac addr )
498 * bandwidth: specify bandwidth capacity of link (TODO)
499 * lambdaAlloc: if True, intent will allocate lambda
500 for the specified intent (TODO)
501 * ipProto: specify ip protocol
502 * ipSrc: specify ip source address with mask eg. ip#/24
503 * ipDst: specify ip destination address eg. ip#/24
504 * tcpSrc: specify tcp source port
505 * tcpDst: specify tcp destination port
506 Returns:
507 Returns main.TRUE for successful requests; Returns main.FALSE if
508 no ingress|egress port found and if error on requests;
509 Returns None for exceptions
510 NOTE:
511 The ip and port option are for the requests input's ip and port
512 of the ONOS node
513 """
514 try:
515 if "/" in ingressDevice:
516 if not ingressPort:
517 ingressPort = ingressDevice.split( "/" )[ 1 ]
518 ingressDevice = ingressDevice.split( "/" )[ 0 ]
519 else:
520 if not ingressPort:
521 main.log.debug( self.name + ": Ingress port not specified" )
522 return main.FALSE
523
524 if "/" in egressDevice:
525 if not egressPort:
526 egressPort = egressDevice.split( "/" )[ 1 ]
527 egressDevice = egressDevice.split( "/" )[ 0 ]
528 else:
529 if not egressPort:
530 main.log.debug( self.name + ": Egress port not specified" )
531 return main.FALSE
532
533 intentJson ={ "ingressPoint": { "device": ingressDevice,
534 "port": ingressPort },
535 "selector": { "criteria": [] },
536 "priority": 55,
537 "treatment": { "deferred": [],
538 "instructions": [] },
539 "egressPoint": { "device": egressDevice,
540 "port": egressPort },
541 "appId": appId,
542 "type": "PointToPointIntent",
543 "constraints": [ { "type": "LinkTypeConstraint",
544 "types": [ "OPTICAL" ],
545 "inclusive": "false" } ] }
546
alisonda157272016-12-22 01:13:21 -0800547 # if protected:
548 # intentJson['constraints'].append( { "type": "Protection", "types": ["Protection"], "inclusive": "true" } )
549
kelvin-onlabb50074f2015-07-27 16:18:32 -0700550 if ethType == "IPV4":
551 intentJson[ 'selector' ][ 'criteria' ].append( {
552 "type":"ETH_TYPE",
553 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700554 elif ethType:
555 intentJson[ 'selector' ][ 'criteria' ].append( {
556 "type":"ETH_TYPE",
557 "ethType":ethType } )
558
kelvin-onlabb50074f2015-07-27 16:18:32 -0700559 if ethSrc:
560 intentJson[ 'selector' ][ 'criteria' ].append(
561 { "type":"ETH_SRC",
562 "mac":ethSrc } )
563 if ethDst:
564 intentJson[ 'selector' ][ 'criteria' ].append(
565 { "type":"ETH_DST",
566 "mac":ethDst } )
567 if ipSrc:
568 intentJson[ 'selector' ][ 'criteria' ].append(
569 { "type":"IPV4_SRC",
570 "ip":ipSrc } )
571 if ipDst:
572 intentJson[ 'selector' ][ 'criteria' ].append(
573 { "type":"IPV4_DST",
574 "ip":ipDst } )
575 if tcpSrc:
576 intentJson[ 'selector' ][ 'criteria' ].append(
577 { "type":"TCP_SRC",
578 "tcpPort": tcpSrc } )
579 if tcpDst:
580 intentJson[ 'selector' ][ 'criteria' ].append(
581 { "type":"TCP_DST",
582 "tcpPort": tcpDst } )
583 if ipProto:
584 intentJson[ 'selector' ][ 'criteria' ].append(
585 { "type":"IP_PROTO",
586 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700587 if vlanId:
588 intentJson[ 'selector' ][ 'criteria' ].append(
589 { "type":"VLAN_VID",
590 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700591
592 # TODO: Bandwidth and Lambda will be implemented if needed
593
kelvin-onlabb50074f2015-07-27 16:18:32 -0700594 output = None
595 if ip == "DEFAULT":
596 main.log.warn( "No ip given, reverting to ip from topo file" )
597 ip = self.ip_address
598 if port == "DEFAULT":
599 main.log.warn( "No port given, reverting to port " +
600 "from topo file" )
601 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700602 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700603 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700604 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700605
606 main.log.debug( intentJson )
607
kelvin-onlabb50074f2015-07-27 16:18:32 -0700608 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700609 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700610 main.log.info( self.name + ": Successfully POST point" +
611 " intent between ingress: " + ingressDevice +
612 " and egress: " + egressDevice + " devices" )
613 return main.TRUE
614 else:
615 main.log.error( "Error with REST request, response was: " +
616 str( response ) )
617 return main.FALSE
618
Jon Halle401b092015-09-23 13:34:24 -0700619 except ( AttributeError, TypeError ):
620 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700621 return None
Jon Halle401b092015-09-23 13:34:24 -0700622 except Exception:
623 main.log.exception( self.name + ": Uncaught exception!" )
624 main.cleanup()
625 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700626
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700627 def addSinglepointToMultipointIntent(self,
628 ingressDevice,
629 egressDeviceList,
630 portEgressList,
631 appId='org.onosproject.cli',
632 portIngress="",
633 ethType="",
634 ethSrc="",
635 ethDst="",
636 bandwidth="",
637 lambdaAlloc=False,
638 ipProto="",
639 ipSrc="",
640 ipDst="",
641 tcpSrc="",
642 tcpDst="",
643 partial=False,
644 ip="DEFAULT",
645 port="DEFAULT",
646 vlanId="" ):
647 """
648 Description:
649 Adds a point-to-multi point intent ( uni-directional ) by
650 specifying device id's and optional fields
651 Required:
652 * ingressDevice: device id of ingress device
653 * egressDevice: device id of egress device
654 * portEgressList: a list of port id of egress device
655
656 Optional:
657 * portIngress: port id of ingress device
658 * ethType: specify ethType
659 * ethSrc: specify ethSrc ( i.e. src mac addr )
660 * ethDst: specify ethDst ( i.e. dst mac addr )
661 * bandwidth: specify bandwidth capacity of link (TODO)
662 * lambdaAlloc: if True, intent will allocate lambda
663 for the specified intent (TODO)
664 * ipProto: specify ip protocol
665 * ipSrc: specify ip source address with mask eg. ip#/24
666 * ipDst: specify ip destination address eg. ip#/24
667 * tcpSrc: specify tcp source port
668 * tcpDst: specify tcp destination port
669 Returns:
670 Returns main.TRUE for successful requests; Returns main.FALSE if
671 no ingress|egress port found and if error on requests;
672 Returns None for exceptions
673 NOTE:
674 The ip and port option are for the requests input's ip and port
675 of the ONOS node
676 """
677 try:
678
679 if "/" in ingressDevice:
680 if not portIngress:
681 ingressPort = ingressDevice.split( "/" )[ 1 ]
682 ingressDevice = ingressDevice.split( "/" )[ 0 ]
683 else:
684 if not portIngress:
685 main.log.debug( self.name + ": Ingress port not specified" )
686 return main.FALSE
687 index = 0
688 for egressDevice in egressDeviceList:
689 if "/" in egressDevice:
690 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
691 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
692 else:
693 if not portEgressList:
694 main.log.debug( self.name + ": Egress port not specified" )
695 return main.FALSE
696 index = index + 1
697
698 intentJson = { "ingressPoint": { "device": ingressDevice,
699 "port": ingressPort },
700 "selector": { "criteria": [] },
701 "priority": 55,
702 "treatment": { "deferred": [],
703 "instructions": [] },
704 "egressPoint": { "connectPoints": [] },
705 "appId": appId,
706 "type": "SinglePointToMultiPointIntent",
707 "constraints": [ { "type": "LinkTypeConstraint",
708 "types": ["OPTICAL"],
709 "inclusive": "false" } ] }
710
711 index = 0
712 for ep in portEgressList:
713 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
714 { "device": egressDeviceList[ index ],
715 "port": ep } )
716 index += 1
717
718 if ethType == "IPV4":
719 intentJson[ 'selector' ][ 'criteria' ].append(
720 { "type": "ETH_TYPE",
721 "ethType": 2048 } )
722 elif ethType:
723 intentJson[ 'selector' ][ 'criteria' ].append(
724 { "type": "ETH_TYPE",
725 "ethType": ethType } )
726
727 if ethSrc:
728 intentJson[ 'selector' ][ 'criteria' ].append(
729 { "type": "ETH_SRC",
730 "mac": ethSrc } )
731
732 if ethDst:
733 for dst in ethDst:
734 if dst:
735 intentJson[ 'selector' ][ 'criteria' ].append(
736 { "type": "ETH_DST",
737 "mac": dst } )
738 if tcpSrc:
739 intentJson[ 'selector' ][ 'criteria' ].append(
740 { "type": "TCP_SRC",
741 "tcpPort": tcpSrc } )
742 if tcpDst:
743 intentJson[ 'selector' ][ 'criteria' ].append(
744 { "type": "TCP_DST",
745 "tcpPort": tcpDst } )
746 if ipProto:
747 intentJson[ 'selector' ][ 'criteria' ].append(
748 { "type": "IP_PROTO",
749 "protocol": ipProto } )
750 if vlanId:
751 intentJson[ 'selector' ][ 'criteria' ].append(
752 { "type": "VLAN_VID",
753 "vlanId": vlanId } )
754
755 # TODO: Bandwidth and Lambda will be implemented if needed
756
757 output = None
758 if ip == "DEFAULT":
759 main.log.warn( "No ip given, reverting to ip from topo file" )
760 ip = self.ip_address
761 if port == "DEFAULT":
762 main.log.warn( "No port given, reverting to port " +
763 "from topo file" )
764 port = self.port
765 response = self.send( method="POST",
766 url="/intents", ip=ip, port=port,
767 data=json.dumps( intentJson ) )
768
769 main.log.debug(intentJson)
770
771 if response:
772 if "201" in str( response[ 0 ] ):
773 main.log.info( self.name + ": Successfully POST point" +
774 " intent between ingress: " + ingressDevice +
775 " and egress: " + str(egressDeviceList) + " devices" )
776 return main.TRUE
777 else:
778 main.log.error( "Error with REST request, response was: " + str( response ) )
779 return main.FALSE
780 else:
781 main.log.error( "REST request has no response." )
782 return main.FALSE
783
784 except ( AttributeError, TypeError ):
785 main.log.exception( self.name + ": Object not as expected" )
786 return None
787 except Exception:
788 main.log.exception( self.name + ": Uncaught exception!" )
789 main.cleanup()
790 main.exit()
791
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700792 def removeIntent( self, intentId, appId='org.onosproject.cli',
793 ip="DEFAULT", port="DEFAULT" ):
794 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700795 Remove intent for specified application id and intent id;
796 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700797 """
798 try:
799 output = None
800 if ip == "DEFAULT":
801 main.log.warn( "No ip given, reverting to ip from topo file" )
802 ip = self.ip_address
803 if port == "DEFAULT":
804 main.log.warn( "No port given, reverting to port " +
805 "from topo file" )
806 port = self.port
807 # NOTE: REST url requires the intent id to be in decimal form
808 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700809 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700810 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700811 if response:
812 if 200 <= response[ 0 ] <= 299:
813 return main.TRUE
814 else:
815 main.log.error( "Error with REST request, response was: " +
816 str( response ) )
817 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700818 except ( AttributeError, TypeError ):
819 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700820 return None
Jon Halle401b092015-09-23 13:34:24 -0700821 except Exception:
822 main.log.exception( self.name + ": Uncaught exception!" )
823 main.cleanup()
824 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700825
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700826 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700827 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700828 Description:
829 Gets all intents ID using intents function
830 Returns:
831 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700832 """
833 try:
834 intentIdList = []
835 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700836 for intent in intentsJson:
837 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700838 if not intentIdList:
839 main.log.debug( "Cannot find any intents" )
840 return main.FALSE
841 else:
842 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700843 except ( AttributeError, TypeError ):
844 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700845 return None
Jon Halle401b092015-09-23 13:34:24 -0700846 except Exception:
847 main.log.exception( self.name + ": Uncaught exception!" )
848 main.cleanup()
849 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700850
851 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
852 ip="DEFAULT", port="DEFAULT", delay=5 ):
853 """
854 Description:
855 Remove all the intents
856 Returns:
857 Returns main.TRUE if all intents are removed, otherwise returns
858 main.FALSE; Returns None for exception
859 """
860 try:
861 results = []
862 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700863 # intentIdList = self.getIntentsId( ip=ip, port=port )
864 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700865
866 main.log.info( self.name + ": Removing intents " +
867 str( intentIdList ) )
868
869 if isinstance( intentIdList, types.ListType ):
870 for intent in intentIdList:
871 results.append( self.removeIntent( intentId=intent,
872 appId=appId,
873 ip=ip,
874 port=port ) )
875 # Check for remaining intents
876 # NOTE: Noticing some delay on Deleting the intents so i put
877 # this time out
878 import time
879 time.sleep( delay )
880 intentRemain = len( json.loads( self.intents() ) )
881 if all( result==main.TRUE for result in results ) and \
882 intentRemain == 0:
883 main.log.info( self.name + ": All intents are removed " )
884 return main.TRUE
885 else:
886 main.log.error( self.name + ": Did not removed all intents,"
887 + " there are " + str( intentRemain )
888 + " intents remaining" )
889 return main.FALSE
890 else:
891 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700892 except ( AttributeError, TypeError ):
893 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700894 return None
Jon Halle401b092015-09-23 13:34:24 -0700895 except Exception:
896 main.log.exception( self.name + ": Uncaught exception!" )
897 main.cleanup()
898 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700899
900 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
901 """
902 Description:
903 Get a list of dictionary of all discovered hosts
904 Returns:
905 Returns a list of dictionary of information of the hosts currently
906 discovered by ONOS; Returns main.FALSE if error on requests;
907 Returns None for exception
908 """
909 try:
910 output = None
911 if ip == "DEFAULT":
912 main.log.warn( "No ip given, reverting to ip from topo file" )
913 ip = self.ip_address
914 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700915 main.log.warn( "No port given, reverting to port " +
916 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700917 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700918 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700919 if response:
920 if 200 <= response[ 0 ] <= 299:
921 output = response[ 1 ]
922 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700923 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700924 b = json.dumps( a )
925 return b
926 else:
927 main.log.error( "Error with REST request, response was: " +
928 str( response ) )
929 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700930 except ( AttributeError, AssertionError, TypeError ):
931 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700932 return None
Jon Halle401b092015-09-23 13:34:24 -0700933 except Exception:
934 main.log.exception( self.name + ": Uncaught exception!" )
935 main.cleanup()
936 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700937
938 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
939 """
940 Description:
941 Gets the information from the given host
942 Required:
943 str mac - MAC address of the host
944 Optional:
945 str vlan - VLAN tag of the host, defaults to -1
946 Returns:
947 Return the host id from the hosts/mac/vlan output in REST api
948 whose 'id' contains mac/vlan; Returns None for exception;
949 Returns main.FALSE if error on requests
950
951 NOTE:
952 Not sure what this function should do, any suggestion?
953 """
954 try:
955 output = None
956 if ip == "DEFAULT":
957 main.log.warn( "No ip given, reverting to ip from topo file" )
958 ip = self.ip_address
959 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700960 main.log.warn( "No port given, reverting to port " +
961 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700962 port = self.port
963 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700964 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700965 if response:
966 # NOTE: What if the person wants other values? would it be better
967 # to have a function that gets a key and return a value instead?
968 # This function requires mac and vlan and returns an ID which
969 # makes this current function useless
970 if 200 <= response[ 0 ] <= 299:
971 output = response[ 1 ]
972 hostId = json.loads( output ).get( 'id' )
973 return hostId
974 else:
975 main.log.error( "Error with REST request, response was: " +
976 str( response ) )
977 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700978 except ( AttributeError, TypeError ):
979 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700980 return None
Jon Halle401b092015-09-23 13:34:24 -0700981 except Exception:
982 main.log.exception( self.name + ": Uncaught exception!" )
983 main.cleanup()
984 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700985
986 def topology( self, ip="DEFAULT", port="DEFAULT" ):
987 """
988 Description:
989 Gets the overview of network topology
990 Returns:
991 Returns a dictionary containing information about network topology;
992 Returns None for exception
993 """
994 try:
995 output = None
996 if ip == "DEFAULT":
997 main.log.warn( "No ip given, reverting to ip from topo file" )
998 ip = self.ip_address
999 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001000 main.log.warn( "No port given, reverting to port " +
1001 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001002 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001003 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001004 if response:
1005 if 200 <= response[ 0 ] <= 299:
1006 output = response[ 1 ]
1007 a = json.loads( output )
1008 b = json.dumps( a )
1009 return b
1010 else:
1011 main.log.error( "Error with REST request, response was: " +
1012 str( response ) )
1013 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001014 except ( AttributeError, TypeError ):
1015 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001016 return None
Jon Halle401b092015-09-23 13:34:24 -07001017 except Exception:
1018 main.log.exception( self.name + ": Uncaught exception!" )
1019 main.cleanup()
1020 main.exit()
1021
1022 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1023 """
1024 Description:
1025 Get the devices discovered by ONOS is json string format
1026 Returns:
1027 a json string of the devices currently discovered by ONOS OR
1028 main.FALSE if there is an error in the request OR
1029 Returns None for exception
1030 """
1031 try:
1032 output = None
1033 if ip == "DEFAULT":
1034 main.log.warn( "No ip given, reverting to ip from topo file" )
1035 ip = self.ip_address
1036 if port == "DEFAULT":
1037 main.log.warn( "No port given, reverting to port " +
1038 "from topo file" )
1039 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001040 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001041 if response:
1042 if 200 <= response[ 0 ] <= 299:
1043 output = response[ 1 ]
1044 a = json.loads( output ).get( 'devices' )
1045 assert a is not None, "Error parsing json object"
1046 b = json.dumps( a )
1047 return b
1048 else:
1049 main.log.error( "Error with REST request, response was: " +
1050 str( response ) )
1051 return main.FALSE
1052 except ( AttributeError, AssertionError, TypeError ):
1053 main.log.exception( self.name + ": Object not as expected" )
1054 return None
1055 except Exception:
1056 main.log.exception( self.name + ": Uncaught exception!" )
1057 main.cleanup()
1058 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001059
1060 def getIntentState( self, intentsId, intentsJson=None,
1061 ip="DEFAULT", port="DEFAULT" ):
1062 """
1063 Description:
1064 Get intent state.
1065 Accepts a single intent ID (string type) or a list of intent IDs.
1066 Returns the state(string type) of the id if a single intent ID is
1067 accepted.
1068 Required:
1069 intentId: intent ID (string type)
1070 intentsJson: parsed json object from the onos:intents api
1071 Returns:
1072 Returns a dictionary with intent IDs as the key and its
1073 corresponding states as the values; Returns None for invalid IDs or
1074 Type error and any exceptions
1075 NOTE:
1076 An intent's state consist of INSTALLED,WITHDRAWN etc.
1077 """
1078 try:
1079 state = "State is Undefined"
1080 if not intentsJson:
1081 intentsJsonTemp = json.loads( self.intents() )
1082 else:
1083 intentsJsonTemp = json.loads( intentsJson )
1084 if isinstance( intentsId, types.StringType ):
1085 for intent in intentsJsonTemp:
1086 if intentsId == intent[ 'id' ]:
1087 state = intent[ 'state' ]
1088 return state
1089 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1090 " on the list" )
1091 return state
1092 elif isinstance( intentsId, types.ListType ):
1093 dictList = []
1094 for i in xrange( len( intentsId ) ):
1095 stateDict = {}
1096 for intents in intentsJsonTemp:
1097 if intentsId[ i ] == intents[ 'id' ]:
1098 stateDict[ 'state' ] = intents[ 'state' ]
1099 stateDict[ 'id' ] = intentsId[ i ]
1100 dictList.append( stateDict )
1101 break
1102 if len( intentsId ) != len( dictList ):
1103 main.log.info( "Cannot find some of the intent ID state" )
1104 return dictList
1105 else:
1106 main.log.info( "Invalid intents ID entry" )
1107 return None
1108
Jon Halle401b092015-09-23 13:34:24 -07001109 except ( AttributeError, TypeError ):
1110 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001111 return None
Jon Halle401b092015-09-23 13:34:24 -07001112 except Exception:
1113 main.log.exception( self.name + ": Uncaught exception!" )
1114 main.cleanup()
1115 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001116
1117 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
1118 ip="DEFAULT", port="DEFAULT"):
1119 """
1120 Description:
1121 Check intents state based on expected state which defaults to
1122 INSTALLED state
1123 Required:
1124 intentsId - List of intents ID to be checked
1125 Optional:
1126 expectedState - Check the expected state(s) of each intents
1127 state in the list.
1128 *NOTE: You can pass in a list of expected state,
1129 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1130 Return:
1131 Returns main.TRUE only if all intent are the same as expected states
1132 , otherwise, returns main.FALSE; Returns None for general exception
1133 """
1134 try:
1135 # Generating a dictionary: intent id as a key and state as value
1136 returnValue = main.TRUE
1137 if intentsId == "ALL":
1138 intentsId = self.getIntentsId( ip=ip, port=port )
1139 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1140
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001141 if len( intentsId ) != len( intentsDict ):
1142 main.log.error( self.name + ": There is something wrong " +
1143 "getting intents state" )
1144 return main.FALSE
1145
1146 if isinstance( expectedState, types.StringType ):
1147 for intents in intentsDict:
1148 if intents.get( 'state' ) != expectedState:
1149 main.log.debug( self.name + " : Intent ID - " +
1150 intents.get( 'id' ) +
1151 " actual state = " +
1152 intents.get( 'state' )
1153 + " does not equal expected state = "
1154 + expectedState )
1155 returnValue = main.FALSE
1156
1157 elif isinstance( expectedState, types.ListType ):
1158 for intents in intentsDict:
1159 if not any( state == intents.get( 'state' ) for state in
1160 expectedState ):
1161 main.log.debug( self.name + " : Intent ID - " +
1162 intents.get( 'id' ) +
1163 " actual state = " +
1164 intents.get( 'state' ) +
1165 " does not equal expected states = "
1166 + str( expectedState ) )
1167 returnValue = main.FALSE
1168
1169 if returnValue == main.TRUE:
1170 main.log.info( self.name + ": All " +
1171 str( len( intentsDict ) ) +
1172 " intents are in " + str( expectedState ) +
1173 " state" )
1174 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001175 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001176 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001177 return None
Jon Halle401b092015-09-23 13:34:24 -07001178 except Exception:
1179 main.log.exception( self.name + ": Uncaught exception!" )
1180 main.cleanup()
1181 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001182
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001183 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001184 """
1185 Description:
1186 Get flows currently added to the system
1187 NOTE:
1188 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001189 the REST output
1190
1191 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001192 """
1193 try:
1194 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001195 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001196 if ip == "DEFAULT":
1197 main.log.warn( "No ip given, reverting to ip from topo file" )
1198 ip = self.ip_address
1199 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001200 main.log.warn( "No port given, reverting to port " +
1201 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001202 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001203 if subjectKey and not subjectClass:
1204 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1205 if subjectClass:
1206 url += "/" + subjectClass
1207 if subjectKey:
1208 url += "/" + subjectKey
1209 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001210 if response:
1211 if 200 <= response[ 0 ] <= 299:
1212 output = response[ 1 ]
1213 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001214 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001215 b = json.dumps( a )
1216 return b
1217 else:
1218 main.log.error( "Error with REST request, response was: " +
1219 str( response ) )
1220 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001221 except ( AttributeError, AssertionError, TypeError ):
1222 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001223 return None
Jon Halle401b092015-09-23 13:34:24 -07001224 except Exception:
1225 main.log.exception( self.name + ": Uncaught exception!" )
1226 main.cleanup()
1227 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001228
Jon Halle401b092015-09-23 13:34:24 -07001229 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001230 """
1231 Description:
1232 Gets all the flows of the device or get a specific flow in the
1233 device by giving its flow ID
1234 Required:
Jon Halle401b092015-09-23 13:34:24 -07001235 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001236 Optional:
1237 int/hex flowId - ID of the flow
1238 """
1239 try:
1240 output = None
1241 if ip == "DEFAULT":
1242 main.log.warn( "No ip given, reverting to ip from topo file" )
1243 ip = self.ip_address
1244 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001245 main.log.warn( "No port given, reverting to port " +
1246 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001247 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001248 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001249 if flowId:
1250 url += "/" + str( int( flowId ) )
1251 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001252 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001253 if response:
1254 if 200 <= response[ 0 ] <= 299:
1255 output = response[ 1 ]
1256 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001257 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001258 b = json.dumps( a )
1259 return b
1260 else:
1261 main.log.error( "Error with REST request, response was: " +
1262 str( response ) )
1263 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001264 except ( AttributeError, AssertionError, TypeError ):
1265 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001266 return None
Jon Halle401b092015-09-23 13:34:24 -07001267 except Exception:
1268 main.log.exception( self.name + ": Uncaught exception!" )
1269 main.cleanup()
1270 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001271
GlennRC073e8bc2015-10-27 17:11:28 -07001272 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001273 """
1274 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001275 Sends a single flow to the specified device. This function exists
1276 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001277 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001278 * The flow in json
1279 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001280 Returns:
1281 Returns main.TRUE for successful requests; Returns main.FALSE
1282 if error on requests;
1283 Returns None for exceptions
1284 NOTE:
1285 The ip and port option are for the requests input's ip and port
1286 of the ONOS node
1287 """
GlennRC073e8bc2015-10-27 17:11:28 -07001288
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001289 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001290 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001291 output = None
1292 if ip == "DEFAULT":
1293 main.log.warn( "No ip given, reverting to ip from topo file" )
1294 ip = self.ip_address
1295 if port == "DEFAULT":
1296 main.log.warn( "No port given, reverting to port " +
1297 "from topo file" )
1298 port = self.port
1299 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001300 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001301 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001302 data=json.dumps( flowJson ) )
1303 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001304 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001305 main.log.info( self.name + ": Successfully POST flow" +
1306 "in device: " + str( deviceId ) )
1307 return main.TRUE
1308 else:
1309 main.log.error( "Error with REST request, response was: " +
1310 str( response ) )
1311 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001312 except NotImplementedError as e:
1313 raise e # Inform the caller
1314 except ( AttributeError, TypeError ):
1315 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001316 return None
Jon Halle401b092015-09-23 13:34:24 -07001317 except Exception:
1318 main.log.exception( self.name + ": Uncaught exception!" )
1319 main.cleanup()
1320 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001321
GlennRC073e8bc2015-10-27 17:11:28 -07001322 def addFlow( self,
1323 deviceId,
1324 appId=0,
1325 ingressPort="",
1326 egressPort="",
1327 ethType="",
1328 ethSrc="",
1329 ethDst="",
1330 vlan="",
1331 ipProto="",
1332 ipSrc=(),
1333 ipDst=(),
1334 tcpSrc="",
1335 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001336 udpDst="",
1337 udpSrc="",
1338 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001339 priority=100,
GlennRC073e8bc2015-10-27 17:11:28 -07001340 ip="DEFAULT",
1341 port="DEFAULT",
1342 debug=False ):
1343 """
1344 Description:
1345 Creates a single flow in the specified device
1346 Required:
1347 * deviceId: id of the device
1348 Optional:
1349 * ingressPort: port ingress device
1350 * egressPort: port of egress device
1351 * ethType: specify ethType
1352 * ethSrc: specify ethSrc ( i.e. src mac addr )
1353 * ethDst: specify ethDst ( i.e. dst mac addr )
1354 * ipProto: specify ip protocol
1355 * ipSrc: specify ip source address with mask eg. ip#/24
1356 as a tuple (type, ip#)
1357 * ipDst: specify ip destination address eg. ip#/24
1358 as a tuple (type, ip#)
1359 * tcpSrc: specify tcp source port
1360 * tcpDst: specify tcp destination port
1361 Returns:
1362 Returns main.TRUE for successful requests; Returns main.FALSE
1363 if error on requests;
1364 Returns None for exceptions
1365 NOTE:
1366 The ip and port option are for the requests input's ip and port
1367 of the ONOS node
1368 """
1369 try:
alisone14d7b02016-07-06 10:31:51 -07001370 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001371 "isPermanent":"true",
1372 "timeout":0,
1373 "deviceId":deviceId,
1374 "treatment":{"instructions":[]},
1375 "selector": {"criteria":[]}}
1376 if appId:
1377 flowJson[ "appId" ] = appId
1378 if egressPort:
1379 flowJson[ 'treatment' ][ 'instructions' ].append( {
1380 "type":"OUTPUT",
1381 "port":egressPort } )
1382 if ingressPort:
1383 flowJson[ 'selector' ][ 'criteria' ].append( {
1384 "type":"IN_PORT",
1385 "port":ingressPort } )
1386 if ethType:
1387 flowJson[ 'selector' ][ 'criteria' ].append( {
1388 "type":"ETH_TYPE",
1389 "ethType":ethType } )
1390 if ethSrc:
1391 flowJson[ 'selector' ][ 'criteria' ].append( {
1392 "type":"ETH_SRC",
1393 "mac":ethSrc } )
1394 if ethDst:
1395 flowJson[ 'selector' ][ 'criteria' ].append( {
1396 "type":"ETH_DST",
1397 "mac":ethDst } )
1398 if vlan:
1399 flowJson[ 'selector' ][ 'criteria' ].append( {
1400 "type":"VLAN_VID",
1401 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001402 if mpls:
1403 flowJson[ 'selector' ][ 'criteria' ].append( {
1404 "type":"MPLS_LABEL",
1405 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001406 if ipSrc:
1407 flowJson[ 'selector' ][ 'criteria' ].append( {
1408 "type":ipSrc[0],
1409 "ip":ipSrc[1] } )
1410 if ipDst:
1411 flowJson[ 'selector' ][ 'criteria' ].append( {
1412 "type":ipDst[0],
1413 "ip":ipDst[1] } )
1414 if tcpSrc:
1415 flowJson[ 'selector' ][ 'criteria' ].append( {
1416 "type":"TCP_SRC",
1417 "tcpPort": tcpSrc } )
1418 if tcpDst:
1419 flowJson[ 'selector' ][ 'criteria' ].append( {
1420 "type":"TCP_DST",
1421 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001422 if udpSrc:
1423 flowJson[ 'selector' ][ 'criteria' ].append( {
1424 "type":"UDP_SRC",
1425 "udpPort": udpSrc } )
1426 if udpDst:
1427 flowJson[ 'selector' ][ 'criteria' ].append( {
1428 "type":"UDP_DST",
1429 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001430 if ipProto:
1431 flowJson[ 'selector' ][ 'criteria' ].append( {
1432 "type":"IP_PROTO",
1433 "protocol": ipProto } )
1434
1435 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1436
1437 except ( AttributeError, TypeError ):
1438 main.log.exception( self.name + ": Object not as expected" )
1439 return None
1440 except Exception:
1441 main.log.exception( self.name + ": Uncaught exception!" )
1442 main.cleanup()
1443 main.exit()
1444
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001445 def removeFlow( self, deviceId, flowId,
1446 ip="DEFAULT", port="DEFAULT" ):
1447 """
1448 Description:
1449 Remove specific device flow
1450 Required:
1451 str deviceId - id of the device
1452 str flowId - id of the flow
1453 Return:
1454 Returns main.TRUE if successfully deletes flows, otherwise
1455 Returns main.FALSE, Returns None on error
1456 """
1457 try:
1458 output = None
1459 if ip == "DEFAULT":
1460 main.log.warn( "No ip given, reverting to ip from topo file" )
1461 ip = self.ip_address
1462 if port == "DEFAULT":
1463 main.log.warn( "No port given, reverting to port " +
1464 "from topo file" )
1465 port = self.port
1466 # NOTE: REST url requires the intent id to be in decimal form
1467 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001468 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001469 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001470 if response:
1471 if 200 <= response[ 0 ] <= 299:
1472 return main.TRUE
1473 else:
1474 main.log.error( "Error with REST request, response was: " +
1475 str( response ) )
1476 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001477 except ( AttributeError, TypeError ):
1478 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001479 return None
Jon Halle401b092015-09-23 13:34:24 -07001480 except Exception:
1481 main.log.exception( self.name + ": Uncaught exception!" )
1482 main.cleanup()
1483 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001484
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001485 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1486 """
1487 Description:
1488 Check if all the current flows are in ADDED state
1489 Return:
1490 returnValue - Returns main.TRUE only if all flows are in
1491 return main.FALSE otherwise;
1492 Returns None for exception
1493 """
1494 try:
1495 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1496 returnValue = main.TRUE
1497 for flow in tempFlows:
1498 if flow.get( 'state' ) != 'ADDED':
1499 main.log.info( self.name + ": flow Id: " +
1500 str( flow.get( 'groupId' ) ) +
1501 " | state:" +
1502 str( flow.get( 'state' ) ) )
1503 returnValue = main.FALSE
1504 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001505 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001506 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001507 return None
1508 except Exception:
1509 main.log.exception( self.name + ": Uncaught exception!" )
1510 main.cleanup()
1511 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001512
1513 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1514 subjectClass=None, subjectKey=None, configKey=None ):
1515 """
1516 Description:
1517 Get a json object with the ONOS network configurations
1518 Returns:
1519 A json object containing the network configuration in
1520 ONOS; Returns main.FALSE if error on requests;
1521 Returns None for exception
1522 """
1523 try:
1524 output = None
1525 if ip == "DEFAULT":
1526 main.log.warn( "No ip given, reverting to ip from topo file" )
1527 ip = self.ip_address
1528 if port == "DEFAULT":
1529 main.log.warn( "No port given, reverting to port " +
1530 "from topo file" )
1531 port = self.port
1532 url = "/network/configuration"
1533 if subjectClass:
1534 url += "/" + subjectClass
1535 if subjectKey:
1536 url += "/" + subjectKey
1537 if configKey:
1538 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001539 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001540 if response:
1541 if 200 <= response[ 0 ] <= 299:
1542 output = response[ 1 ]
1543 a = json.loads( output )
1544 b = json.dumps( a )
1545 return b
1546 elif response[ 0 ] == 404:
1547 main.log.error( "Requested configuration doesn't exist: " +
1548 str( response ) )
1549 return {}
1550 else:
1551 main.log.error( "Error with REST request, response was: " +
1552 str( response ) )
1553 return main.FALSE
1554 except ( AttributeError, TypeError ):
1555 main.log.exception( self.name + ": Object not as expected" )
1556 return None
1557 except Exception:
1558 main.log.exception( self.name + ": Uncaught exception!" )
1559 main.cleanup()
1560 main.exit()
1561
1562 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1563 subjectClass=None, subjectKey=None, configKey=None ):
1564 """
1565 Description:
1566 Set a json object with the ONOS network configurations
1567 Returns:
1568 Returns main.TRUE for successful requests; Returns main.FALSE
1569 if error on requests;
1570 Returns None for exceptions
1571
1572 """
1573 try:
1574 output = None
1575 if ip == "DEFAULT":
1576 main.log.warn( "No ip given, reverting to ip from topo file" )
1577 ip = self.ip_address
1578 if port == "DEFAULT":
1579 main.log.warn( "No port given, reverting to port " +
1580 "from topo file" )
1581 port = self.port
1582 url = "/network/configuration"
1583 if subjectClass:
1584 url += "/" + subjectClass
1585 if subjectKey:
1586 url += "/" + subjectKey
1587 if configKey:
1588 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001589 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001590 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001591 data=json.dumps( cfgJson ) )
1592 if response:
1593 if 200 <= response[ 0 ] <= 299:
1594 main.log.info( self.name + ": Successfully POST cfg" )
1595 return main.TRUE
1596 else:
1597 main.log.error( "Error with REST request, response was: " +
1598 str( response ) )
1599 return main.FALSE
1600 except ( AttributeError, TypeError ):
1601 main.log.exception( self.name + ": Object not as expected" )
1602 return None
1603 except Exception:
1604 main.log.exception( self.name + ": Uncaught exception!" )
1605 main.cleanup()
1606 main.exit()
1607
1608 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1609 subjectClass=None, subjectKey=None, configKey=None ):
1610 """
1611 Description:
1612 Remove a json object from the ONOS network configurations
1613 Returns:
1614 Returns main.TRUE for successful requests; Returns main.FALSE
1615 if error on requests;
1616 Returns None for exceptions
1617
1618 """
1619 try:
1620 output = None
1621 if ip == "DEFAULT":
1622 main.log.warn( "No ip given, reverting to ip from topo file" )
1623 ip = self.ip_address
1624 if port == "DEFAULT":
1625 main.log.warn( "No port given, reverting to port " +
1626 "from topo file" )
1627 port = self.port
1628 url = "/network/configuration"
1629 if subjectClass:
1630 url += "/" + subjectClass
1631 if subjectKey:
1632 url += "/" + subjectKey
1633 if configKey:
1634 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001635 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001636 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001637 if response:
1638 if 200 <= response[ 0 ] <= 299:
1639 main.log.info( self.name + ": Successfully delete cfg" )
1640 return main.TRUE
1641 else:
1642 main.log.error( "Error with REST request, response was: " +
1643 str( response ) )
1644 return main.FALSE
1645 except ( AttributeError, TypeError ):
1646 main.log.exception( self.name + ": Object not as expected" )
1647 return None
1648 except Exception:
1649 main.log.exception( self.name + ": Uncaught exception!" )
1650 main.cleanup()
1651 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001652
1653 def createFlowBatch( self,
1654 numSw = 1,
1655 swIndex = 1,
1656 batchSize = 1,
1657 batchIndex = 1,
1658 deviceIdpreFix = "of:",
1659 appId=0,
1660 deviceID="",
1661 ingressPort="",
1662 egressPort="",
1663 ethType="",
1664 ethSrc="",
1665 ethDst="",
1666 vlan="",
1667 ipProto="",
1668 ipSrc=(),
1669 ipDst=(),
1670 tcpSrc="",
1671 tcpDst="",
1672 udpDst="",
1673 udpSrc="",
1674 mpls="",
1675 ip="DEFAULT",
1676 port="DEFAULT",
1677 debug=False ):
1678 """
1679 Description:
1680 Creates batches of MAC-rule flows for POST.
1681 Predefined MAC: 2 MS Hex digit for iterating devices
1682 Next 5 Hex digit for iterating batch numbers
1683 Next 5 Hex digit for iterating flows within a batch
1684 Required:
1685 * deviceId: id of the device
1686 Optional:
1687 * ingressPort: port ingress device
1688 * egressPort: port of egress device
1689 * ethType: specify ethType
1690 * ethSrc: specify ethSrc ( i.e. src mac addr )
1691 * ethDst: specify ethDst ( i.e. dst mac addr )
1692 * ipProto: specify ip protocol
1693 * ipSrc: specify ip source address with mask eg. ip#/24
1694 as a tuple (type, ip#)
1695 * ipDst: specify ip destination address eg. ip#/24
1696 as a tuple (type, ip#)
1697 * tcpSrc: specify tcp source port
1698 * tcpDst: specify tcp destination port
1699 Returns:
1700 Returns main.TRUE for successful requests; Returns main.FALSE
1701 if error on requests;
1702 Returns None for exceptions
1703 NOTE:
1704 The ip and port option are for the requests input's ip and port
1705 of the ONOS node
1706 """
1707 #from pprint import pprint
1708
1709 flowJsonList = []
1710 flowJsonBatch = {"flows":flowJsonList}
1711 dev = swIndex
1712
1713 for fl in range(1, batchSize + 1):
1714 flowJson = { "priority":100,
1715 "deviceId":"",
1716 "isPermanent":"true",
1717 "timeout":0,
1718 "treatment":{"instructions":[]},
1719 "selector": {"criteria":[]}}
1720
1721 #main.log.info("fl: " + str(fl))
1722 if dev <= numSw:
1723 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1724 #print deviceId
1725 flowJson['deviceId'] = deviceId
1726 dev += 1
1727 else:
1728 dev = 1
1729 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1730 #print deviceId
1731 flowJson['deviceId'] = deviceId
1732 dev += 1
1733
1734 # ethSrc starts with "0"; ethDst starts with "1"
1735 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1736 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1737 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1738 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1739 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1740 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1741 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1742
1743 if appId:
1744 flowJson[ "appId" ] = appId
1745
1746 if egressPort:
1747 flowJson[ 'treatment' ][ 'instructions' ].append( {
1748 "type":"OUTPUT",
1749 "port":egressPort } )
1750 if ingressPort:
1751 flowJson[ 'selector' ][ 'criteria' ].append( {
1752 "type":"IN_PORT",
1753 "port":ingressPort } )
1754 if ethType:
1755 flowJson[ 'selector' ][ 'criteria' ].append( {
1756 "type":"ETH_TYPE",
1757 "ethType":ethType } )
1758 if ethSrc:
1759 flowJson[ 'selector' ][ 'criteria' ].append( {
1760 "type":"ETH_SRC",
1761 "mac":ethSrc } )
1762 if ethDst:
1763 flowJson[ 'selector' ][ 'criteria' ].append( {
1764 "type":"ETH_DST",
1765 "mac":ethDst } )
1766 if vlan:
1767 flowJson[ 'selector' ][ 'criteria' ].append( {
1768 "type":"VLAN_VID",
1769 "vlanId":vlan } )
1770 if mpls:
1771 flowJson[ 'selector' ][ 'criteria' ].append( {
1772 "type":"MPLS_LABEL",
1773 "label":mpls } )
1774 if ipSrc:
1775 flowJson[ 'selector' ][ 'criteria' ].append( {
1776 "type":ipSrc[0],
1777 "ip":ipSrc[1] } )
1778 if ipDst:
1779 flowJson[ 'selector' ][ 'criteria' ].append( {
1780 "type":ipDst[0],
1781 "ip":ipDst[1] } )
1782 if tcpSrc:
1783 flowJson[ 'selector' ][ 'criteria' ].append( {
1784 "type":"TCP_SRC",
1785 "tcpPort": tcpSrc } )
1786 if tcpDst:
1787 flowJson[ 'selector' ][ 'criteria' ].append( {
1788 "type":"TCP_DST",
1789 "tcpPort": tcpDst } )
1790 if udpSrc:
1791 flowJson[ 'selector' ][ 'criteria' ].append( {
1792 "type":"UDP_SRC",
1793 "udpPort": udpSrc } )
1794 if udpDst:
1795 flowJson[ 'selector' ][ 'criteria' ].append( {
1796 "type":"UDP_DST",
1797 "udpPort": udpDst } )
1798 if ipProto:
1799 flowJson[ 'selector' ][ 'criteria' ].append( {
1800 "type":"IP_PROTO",
1801 "protocol": ipProto } )
1802 #pprint(flowJson)
1803 flowJsonList.append(flowJson)
1804
1805 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1806 flowJsonBatch['flows'] = flowJsonList
1807 #pprint(flowJsonBatch)
1808
1809 return flowJsonBatch
1810
1811
1812 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1813 """
1814 Description:
1815 Sends a single flow batch through /flows REST API.
1816 Required:
1817 * The batch of flows
1818 Returns:
1819 Returns main.TRUE for successful requests; Returns main.FALSE
1820 if error on requests;
1821 Returns None for exceptions
1822 NOTE:
1823 The ip and port option are for the requests input's ip and port
1824 of the ONOS node
1825 """
1826 import time
1827
1828 try:
1829 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1830 output = None
1831 if ip == "DEFAULT":
1832 main.log.warn( "No ip given, reverting to ip from topo file" )
1833 ip = self.ip_address
1834 if port == "DEFAULT":
1835 main.log.warn( "No port given, reverting to port " +
1836 "from topo file" )
1837 port = self.port
1838 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001839 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001840 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001841 data=json.dumps( batch ) )
1842 #main.log.info("Post response is: ", str(response[0]))
1843 if response[0] == 200:
1844 main.log.info( self.name + ": Successfully POST flow batch" )
1845 return main.TRUE, response
1846 else:
1847 main.log.error( "Error with REST request, response was: " +
1848 str( response ) )
You Wang7b5b2262016-11-10 13:54:56 -08001849 return main.FALSE, response
suibin zhang17308622016-04-14 15:45:30 -07001850 except NotImplementedError as e:
1851 raise e # Inform the caller
1852 except ( AttributeError, TypeError ):
1853 main.log.exception( self.name + ": Object not as expected" )
You Wang7b5b2262016-11-10 13:54:56 -08001854 return None, None
suibin zhang17308622016-04-14 15:45:30 -07001855 except Exception:
1856 main.log.exception( self.name + ": Uncaught exception!" )
1857 main.cleanup()
1858 main.exit()
1859
1860 def removeFlowBatch( self, batch={},
1861 ip="DEFAULT", port="DEFAULT" ):
1862 """
1863 Description:
1864 Remove a batch of flows
1865 Required:
1866 flow batch
1867 Return:
1868 Returns main.TRUE if successfully deletes flows, otherwise
1869 Returns main.FALSE, Returns None on error
1870 """
1871 try:
1872 output = None
1873 if ip == "DEFAULT":
1874 main.log.warn( "No ip given, reverting to ip from topo file" )
1875 ip = self.ip_address
1876 if port == "DEFAULT":
1877 main.log.warn( "No port given, reverting to port " +
1878 "from topo file" )
1879 port = self.port
1880 # NOTE: REST url requires the intent id to be in decimal form
1881
suibin zhang116647a2016-05-06 16:30:09 -07001882 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001883 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001884 data = json.dumps(batch) )
1885 if response:
1886 if 200 <= response[ 0 ] <= 299:
1887 return main.TRUE
1888 else:
1889 main.log.error( "Error with REST request, response was: " +
1890 str( response ) )
1891 return main.FALSE
1892 except ( AttributeError, TypeError ):
1893 main.log.exception( self.name + ": Object not as expected" )
1894 return None
1895 except Exception:
1896 main.log.exception( self.name + ": Uncaught exception!" )
1897 main.cleanup()
1898 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001899
1900 def getTopology( self, topologyOutput ):
1901 """
1902 Definition:
1903 Loads a json topology output
1904 Return:
1905 topology = current ONOS topology
1906 """
1907 import json
1908 try:
1909 # either onos:topology or 'topology' will work in CLI
1910 topology = json.loads(topologyOutput)
1911 main.log.debug( topology )
1912 return topology
1913 except pexpect.EOF:
1914 main.log.error( self.name + ": EOF exception found" )
1915 main.log.error( self.name + ": " + self.handle.before )
1916 main.cleanup()
1917 main.exit()
1918 except Exception:
1919 main.log.exception( self.name + ": Uncaught exception!" )
1920 main.cleanup()
1921 main.exit()
1922
1923 def checkStatus(
1924 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001925 numoswitch,
1926 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001927 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001928 logLevel="info" ):
1929 """
1930 Checks the number of switches & links that ONOS sees against the
1931 supplied values. By default this will report to main.log, but the
1932 log level can be specific.
1933
Flavio Castro82ee2f62016-06-07 15:04:12 -07001934 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001935 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001936 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001937 logLevel = level to log to.
1938 Currently accepts 'info', 'warn' and 'report'
1939
1940 Returns: main.TRUE if the number of switches and links are correct,
1941 main.FALSE if the number of switches and links is incorrect,
1942 and main.ERROR otherwise
1943 """
1944 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001945 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001946 if topology == {}:
1947 return main.ERROR
1948 output = ""
1949 # Is the number of switches is what we expected
1950 devices = topology.get( 'devices', False )
1951 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001952 nodes = topology.get( 'nodes' , False )
1953 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001954 return main.ERROR
1955 switchCheck = ( int( devices ) == int( numoswitch ) )
1956 # Is the number of links is what we expected
1957 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001958 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1959 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001960 # We expected the correct numbers
1961 output = output + "The number of links and switches match "\
1962 + "what was expected"
1963 result = main.TRUE
1964 else:
1965 output = output + \
1966 "The number of links and switches does not match " + \
1967 "what was expected"
1968 result = main.FALSE
1969 output = output + "\n ONOS sees %i devices" % int( devices )
1970 output = output + " (%i expected) " % int( numoswitch )
1971 output = output + "and %i links " % int( links )
1972 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001973 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001974 output = output + "and %i controllers " % int( nodes )
1975 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001976 if logLevel == "report":
1977 main.log.report( output )
1978 elif logLevel == "warn":
1979 main.log.warn( output )
1980 else:
1981 main.log.info( output )
1982 return result
1983 except pexpect.EOF:
1984 main.log.error( self.name + ": EOF exception found" )
1985 main.log.error( self.name + ": " + self.handle.before )
1986 main.cleanup()
1987 main.exit()
1988 except Exception:
1989 main.log.exception( self.name + ": Uncaught exception!" )
1990 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001991 main.exit()