blob: 082ab1799ab34f6f00a33fe0a56277a46d295ed6 [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="",
477 lambdaAlloc=False,
478 ipProto="",
479 ipSrc="",
480 ipDst="",
481 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700482 tcpDst="",
483 ip="DEFAULT",
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700484 port="DEFAULT",
485 vlanId="" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700486 """
487 Description:
488 Adds a point-to-point intent ( uni-directional ) by
489 specifying device id's and optional fields
490 Required:
491 * ingressDevice: device id of ingress device
492 * egressDevice: device id of egress device
493 Optional:
494 * ethType: specify ethType
495 * ethSrc: specify ethSrc ( i.e. src mac addr )
496 * ethDst: specify ethDst ( i.e. dst mac addr )
497 * bandwidth: specify bandwidth capacity of link (TODO)
498 * lambdaAlloc: if True, intent will allocate lambda
499 for the specified intent (TODO)
500 * ipProto: specify ip protocol
501 * ipSrc: specify ip source address with mask eg. ip#/24
502 * ipDst: specify ip destination address eg. ip#/24
503 * tcpSrc: specify tcp source port
504 * tcpDst: specify tcp destination port
505 Returns:
506 Returns main.TRUE for successful requests; Returns main.FALSE if
507 no ingress|egress port found and if error on requests;
508 Returns None for exceptions
509 NOTE:
510 The ip and port option are for the requests input's ip and port
511 of the ONOS node
512 """
513 try:
514 if "/" in ingressDevice:
515 if not ingressPort:
516 ingressPort = ingressDevice.split( "/" )[ 1 ]
517 ingressDevice = ingressDevice.split( "/" )[ 0 ]
518 else:
519 if not ingressPort:
520 main.log.debug( self.name + ": Ingress port not specified" )
521 return main.FALSE
522
523 if "/" in egressDevice:
524 if not egressPort:
525 egressPort = egressDevice.split( "/" )[ 1 ]
526 egressDevice = egressDevice.split( "/" )[ 0 ]
527 else:
528 if not egressPort:
529 main.log.debug( self.name + ": Egress port not specified" )
530 return main.FALSE
531
532 intentJson ={ "ingressPoint": { "device": ingressDevice,
533 "port": ingressPort },
534 "selector": { "criteria": [] },
535 "priority": 55,
536 "treatment": { "deferred": [],
537 "instructions": [] },
538 "egressPoint": { "device": egressDevice,
539 "port": egressPort },
540 "appId": appId,
541 "type": "PointToPointIntent",
542 "constraints": [ { "type": "LinkTypeConstraint",
543 "types": [ "OPTICAL" ],
544 "inclusive": "false" } ] }
545
546 if ethType == "IPV4":
547 intentJson[ 'selector' ][ 'criteria' ].append( {
548 "type":"ETH_TYPE",
549 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700550 elif ethType:
551 intentJson[ 'selector' ][ 'criteria' ].append( {
552 "type":"ETH_TYPE",
553 "ethType":ethType } )
554
kelvin-onlabb50074f2015-07-27 16:18:32 -0700555 if ethSrc:
556 intentJson[ 'selector' ][ 'criteria' ].append(
557 { "type":"ETH_SRC",
558 "mac":ethSrc } )
559 if ethDst:
560 intentJson[ 'selector' ][ 'criteria' ].append(
561 { "type":"ETH_DST",
562 "mac":ethDst } )
563 if ipSrc:
564 intentJson[ 'selector' ][ 'criteria' ].append(
565 { "type":"IPV4_SRC",
566 "ip":ipSrc } )
567 if ipDst:
568 intentJson[ 'selector' ][ 'criteria' ].append(
569 { "type":"IPV4_DST",
570 "ip":ipDst } )
571 if tcpSrc:
572 intentJson[ 'selector' ][ 'criteria' ].append(
573 { "type":"TCP_SRC",
574 "tcpPort": tcpSrc } )
575 if tcpDst:
576 intentJson[ 'selector' ][ 'criteria' ].append(
577 { "type":"TCP_DST",
578 "tcpPort": tcpDst } )
579 if ipProto:
580 intentJson[ 'selector' ][ 'criteria' ].append(
581 { "type":"IP_PROTO",
582 "protocol": ipProto } )
Jeremy Songsterae2dd452016-05-17 16:44:35 -0700583 if vlanId:
584 intentJson[ 'selector' ][ 'criteria' ].append(
585 { "type":"VLAN_VID",
586 "vlanId": vlanId } )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700587
588 # TODO: Bandwidth and Lambda will be implemented if needed
589
kelvin-onlabb50074f2015-07-27 16:18:32 -0700590 output = None
591 if ip == "DEFAULT":
592 main.log.warn( "No ip given, reverting to ip from topo file" )
593 ip = self.ip_address
594 if port == "DEFAULT":
595 main.log.warn( "No port given, reverting to port " +
596 "from topo file" )
597 port = self.port
suibin zhang116647a2016-05-06 16:30:09 -0700598 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700599 url="/intents", ip = ip, port = port,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700600 data=json.dumps( intentJson ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700601
602 main.log.debug( intentJson )
603
kelvin-onlabb50074f2015-07-27 16:18:32 -0700604 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700605 if "201" in str( response[ 0 ] ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700606 main.log.info( self.name + ": Successfully POST point" +
607 " intent between ingress: " + ingressDevice +
608 " and egress: " + egressDevice + " devices" )
609 return main.TRUE
610 else:
611 main.log.error( "Error with REST request, response was: " +
612 str( response ) )
613 return main.FALSE
614
Jon Halle401b092015-09-23 13:34:24 -0700615 except ( AttributeError, TypeError ):
616 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700617 return None
Jon Halle401b092015-09-23 13:34:24 -0700618 except Exception:
619 main.log.exception( self.name + ": Uncaught exception!" )
620 main.cleanup()
621 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700622
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700623 def addSinglepointToMultipointIntent(self,
624 ingressDevice,
625 egressDeviceList,
626 portEgressList,
627 appId='org.onosproject.cli',
628 portIngress="",
629 ethType="",
630 ethSrc="",
631 ethDst="",
632 bandwidth="",
633 lambdaAlloc=False,
634 ipProto="",
635 ipSrc="",
636 ipDst="",
637 tcpSrc="",
638 tcpDst="",
639 partial=False,
640 ip="DEFAULT",
641 port="DEFAULT",
642 vlanId="" ):
643 """
644 Description:
645 Adds a point-to-multi point intent ( uni-directional ) by
646 specifying device id's and optional fields
647 Required:
648 * ingressDevice: device id of ingress device
649 * egressDevice: device id of egress device
650 * portEgressList: a list of port id of egress device
651
652 Optional:
653 * portIngress: port id of ingress device
654 * ethType: specify ethType
655 * ethSrc: specify ethSrc ( i.e. src mac addr )
656 * ethDst: specify ethDst ( i.e. dst mac addr )
657 * bandwidth: specify bandwidth capacity of link (TODO)
658 * lambdaAlloc: if True, intent will allocate lambda
659 for the specified intent (TODO)
660 * ipProto: specify ip protocol
661 * ipSrc: specify ip source address with mask eg. ip#/24
662 * ipDst: specify ip destination address eg. ip#/24
663 * tcpSrc: specify tcp source port
664 * tcpDst: specify tcp destination port
665 Returns:
666 Returns main.TRUE for successful requests; Returns main.FALSE if
667 no ingress|egress port found and if error on requests;
668 Returns None for exceptions
669 NOTE:
670 The ip and port option are for the requests input's ip and port
671 of the ONOS node
672 """
673 try:
674
675 if "/" in ingressDevice:
676 if not portIngress:
677 ingressPort = ingressDevice.split( "/" )[ 1 ]
678 ingressDevice = ingressDevice.split( "/" )[ 0 ]
679 else:
680 if not portIngress:
681 main.log.debug( self.name + ": Ingress port not specified" )
682 return main.FALSE
683 index = 0
684 for egressDevice in egressDeviceList:
685 if "/" in egressDevice:
686 portEgressList.append( egressDevice.split( "/" )[ 1 ] )
687 egressDeviceList[ index ] = egressDevice.split( "/" )[ 0 ]
688 else:
689 if not portEgressList:
690 main.log.debug( self.name + ": Egress port not specified" )
691 return main.FALSE
692 index = index + 1
693
694 intentJson = { "ingressPoint": { "device": ingressDevice,
695 "port": ingressPort },
696 "selector": { "criteria": [] },
697 "priority": 55,
698 "treatment": { "deferred": [],
699 "instructions": [] },
700 "egressPoint": { "connectPoints": [] },
701 "appId": appId,
702 "type": "SinglePointToMultiPointIntent",
703 "constraints": [ { "type": "LinkTypeConstraint",
704 "types": ["OPTICAL"],
705 "inclusive": "false" } ] }
706
707 index = 0
708 for ep in portEgressList:
709 intentJson[ 'egressPoint' ][ 'connectPoints' ].append(
710 { "device": egressDeviceList[ index ],
711 "port": ep } )
712 index += 1
713
714 if ethType == "IPV4":
715 intentJson[ 'selector' ][ 'criteria' ].append(
716 { "type": "ETH_TYPE",
717 "ethType": 2048 } )
718 elif ethType:
719 intentJson[ 'selector' ][ 'criteria' ].append(
720 { "type": "ETH_TYPE",
721 "ethType": ethType } )
722
723 if ethSrc:
724 intentJson[ 'selector' ][ 'criteria' ].append(
725 { "type": "ETH_SRC",
726 "mac": ethSrc } )
727
728 if ethDst:
729 for dst in ethDst:
730 if dst:
731 intentJson[ 'selector' ][ 'criteria' ].append(
732 { "type": "ETH_DST",
733 "mac": dst } )
734 if tcpSrc:
735 intentJson[ 'selector' ][ 'criteria' ].append(
736 { "type": "TCP_SRC",
737 "tcpPort": tcpSrc } )
738 if tcpDst:
739 intentJson[ 'selector' ][ 'criteria' ].append(
740 { "type": "TCP_DST",
741 "tcpPort": tcpDst } )
742 if ipProto:
743 intentJson[ 'selector' ][ 'criteria' ].append(
744 { "type": "IP_PROTO",
745 "protocol": ipProto } )
746 if vlanId:
747 intentJson[ 'selector' ][ 'criteria' ].append(
748 { "type": "VLAN_VID",
749 "vlanId": vlanId } )
750
751 # TODO: Bandwidth and Lambda will be implemented if needed
752
753 output = None
754 if ip == "DEFAULT":
755 main.log.warn( "No ip given, reverting to ip from topo file" )
756 ip = self.ip_address
757 if port == "DEFAULT":
758 main.log.warn( "No port given, reverting to port " +
759 "from topo file" )
760 port = self.port
761 response = self.send( method="POST",
762 url="/intents", ip=ip, port=port,
763 data=json.dumps( intentJson ) )
764
765 main.log.debug(intentJson)
766
767 if response:
768 if "201" in str( response[ 0 ] ):
769 main.log.info( self.name + ": Successfully POST point" +
770 " intent between ingress: " + ingressDevice +
771 " and egress: " + str(egressDeviceList) + " devices" )
772 return main.TRUE
773 else:
774 main.log.error( "Error with REST request, response was: " + str( response ) )
775 return main.FALSE
776 else:
777 main.log.error( "REST request has no response." )
778 return main.FALSE
779
780 except ( AttributeError, TypeError ):
781 main.log.exception( self.name + ": Object not as expected" )
782 return None
783 except Exception:
784 main.log.exception( self.name + ": Uncaught exception!" )
785 main.cleanup()
786 main.exit()
787
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700788 def removeIntent( self, intentId, appId='org.onosproject.cli',
789 ip="DEFAULT", port="DEFAULT" ):
790 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700791 Remove intent for specified application id and intent id;
792 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700793 """
794 try:
795 output = None
796 if ip == "DEFAULT":
797 main.log.warn( "No ip given, reverting to ip from topo file" )
798 ip = self.ip_address
799 if port == "DEFAULT":
800 main.log.warn( "No port given, reverting to port " +
801 "from topo file" )
802 port = self.port
803 # NOTE: REST url requires the intent id to be in decimal form
804 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
suibin zhang116647a2016-05-06 16:30:09 -0700805 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -0700806 url="/intents" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700807 if response:
808 if 200 <= response[ 0 ] <= 299:
809 return main.TRUE
810 else:
811 main.log.error( "Error with REST request, response was: " +
812 str( response ) )
813 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700814 except ( AttributeError, TypeError ):
815 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700816 return None
Jon Halle401b092015-09-23 13:34:24 -0700817 except Exception:
818 main.log.exception( self.name + ": Uncaught exception!" )
819 main.cleanup()
820 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700821
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700822 def getIntentsId( self ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700823 """
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700824 Description:
825 Gets all intents ID using intents function
826 Returns:
827 List of intents ID if found any intents; Returns main.FALSE for other exceptions
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700828 """
829 try:
830 intentIdList = []
831 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700832 for intent in intentsJson:
833 intentIdList.append( intent.get( 'id' ) )
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700834 if not intentIdList:
835 main.log.debug( "Cannot find any intents" )
836 return main.FALSE
837 else:
838 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700839 except ( AttributeError, TypeError ):
840 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700841 return None
Jon Halle401b092015-09-23 13:34:24 -0700842 except Exception:
843 main.log.exception( self.name + ": Uncaught exception!" )
844 main.cleanup()
845 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700846
847 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
848 ip="DEFAULT", port="DEFAULT", delay=5 ):
849 """
850 Description:
851 Remove all the intents
852 Returns:
853 Returns main.TRUE if all intents are removed, otherwise returns
854 main.FALSE; Returns None for exception
855 """
856 try:
857 results = []
858 if intentIdList == 'ALL':
Ming Yan Shuab2f7f52016-08-03 15:21:24 -0700859 # intentIdList = self.getIntentsId( ip=ip, port=port )
860 intentIdList = self.getIntentsId()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700861
862 main.log.info( self.name + ": Removing intents " +
863 str( intentIdList ) )
864
865 if isinstance( intentIdList, types.ListType ):
866 for intent in intentIdList:
867 results.append( self.removeIntent( intentId=intent,
868 appId=appId,
869 ip=ip,
870 port=port ) )
871 # Check for remaining intents
872 # NOTE: Noticing some delay on Deleting the intents so i put
873 # this time out
874 import time
875 time.sleep( delay )
876 intentRemain = len( json.loads( self.intents() ) )
877 if all( result==main.TRUE for result in results ) and \
878 intentRemain == 0:
879 main.log.info( self.name + ": All intents are removed " )
880 return main.TRUE
881 else:
882 main.log.error( self.name + ": Did not removed all intents,"
883 + " there are " + str( intentRemain )
884 + " intents remaining" )
885 return main.FALSE
886 else:
887 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700888 except ( AttributeError, TypeError ):
889 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700890 return None
Jon Halle401b092015-09-23 13:34:24 -0700891 except Exception:
892 main.log.exception( self.name + ": Uncaught exception!" )
893 main.cleanup()
894 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700895
896 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
897 """
898 Description:
899 Get a list of dictionary of all discovered hosts
900 Returns:
901 Returns a list of dictionary of information of the hosts currently
902 discovered by ONOS; Returns main.FALSE if error on requests;
903 Returns None for exception
904 """
905 try:
906 output = None
907 if ip == "DEFAULT":
908 main.log.warn( "No ip given, reverting to ip from topo file" )
909 ip = self.ip_address
910 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700911 main.log.warn( "No port given, reverting to port " +
912 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700913 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700914 response = self.send( url="/hosts", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700915 if response:
916 if 200 <= response[ 0 ] <= 299:
917 output = response[ 1 ]
918 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700919 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700920 b = json.dumps( a )
921 return b
922 else:
923 main.log.error( "Error with REST request, response was: " +
924 str( response ) )
925 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700926 except ( AttributeError, AssertionError, TypeError ):
927 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700928 return None
Jon Halle401b092015-09-23 13:34:24 -0700929 except Exception:
930 main.log.exception( self.name + ": Uncaught exception!" )
931 main.cleanup()
932 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700933
934 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
935 """
936 Description:
937 Gets the information from the given host
938 Required:
939 str mac - MAC address of the host
940 Optional:
941 str vlan - VLAN tag of the host, defaults to -1
942 Returns:
943 Return the host id from the hosts/mac/vlan output in REST api
944 whose 'id' contains mac/vlan; Returns None for exception;
945 Returns main.FALSE if error on requests
946
947 NOTE:
948 Not sure what this function should do, any suggestion?
949 """
950 try:
951 output = None
952 if ip == "DEFAULT":
953 main.log.warn( "No ip given, reverting to ip from topo file" )
954 ip = self.ip_address
955 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700956 main.log.warn( "No port given, reverting to port " +
957 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700958 port = self.port
959 query = "/" + mac + "/" + vlan
suibin zhangd5b6fe42016-05-12 08:48:58 -0700960 response = self.send( url="/hosts" + query, ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700961 if response:
962 # NOTE: What if the person wants other values? would it be better
963 # to have a function that gets a key and return a value instead?
964 # This function requires mac and vlan and returns an ID which
965 # makes this current function useless
966 if 200 <= response[ 0 ] <= 299:
967 output = response[ 1 ]
968 hostId = json.loads( output ).get( 'id' )
969 return hostId
970 else:
971 main.log.error( "Error with REST request, response was: " +
972 str( response ) )
973 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700974 except ( AttributeError, TypeError ):
975 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700976 return None
Jon Halle401b092015-09-23 13:34:24 -0700977 except Exception:
978 main.log.exception( self.name + ": Uncaught exception!" )
979 main.cleanup()
980 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700981
982 def topology( self, ip="DEFAULT", port="DEFAULT" ):
983 """
984 Description:
985 Gets the overview of network topology
986 Returns:
987 Returns a dictionary containing information about network topology;
988 Returns None for exception
989 """
990 try:
991 output = None
992 if ip == "DEFAULT":
993 main.log.warn( "No ip given, reverting to ip from topo file" )
994 ip = self.ip_address
995 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700996 main.log.warn( "No port given, reverting to port " +
997 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700998 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -0700999 response = self.send( url="/topology", ip = ip, port = port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001000 if response:
1001 if 200 <= response[ 0 ] <= 299:
1002 output = response[ 1 ]
1003 a = json.loads( output )
1004 b = json.dumps( a )
1005 return b
1006 else:
1007 main.log.error( "Error with REST request, response was: " +
1008 str( response ) )
1009 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001010 except ( AttributeError, TypeError ):
1011 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001012 return None
Jon Halle401b092015-09-23 13:34:24 -07001013 except Exception:
1014 main.log.exception( self.name + ": Uncaught exception!" )
1015 main.cleanup()
1016 main.exit()
1017
1018 def devices( self, ip="DEFAULT", port="DEFAULT" ):
1019 """
1020 Description:
1021 Get the devices discovered by ONOS is json string format
1022 Returns:
1023 a json string of the devices currently discovered by ONOS OR
1024 main.FALSE if there is an error in the request OR
1025 Returns None for exception
1026 """
1027 try:
1028 output = None
1029 if ip == "DEFAULT":
1030 main.log.warn( "No ip given, reverting to ip from topo file" )
1031 ip = self.ip_address
1032 if port == "DEFAULT":
1033 main.log.warn( "No port given, reverting to port " +
1034 "from topo file" )
1035 port = self.port
suibin zhangd5b6fe42016-05-12 08:48:58 -07001036 response = self.send( url="/devices", ip = ip, port = port )
Jon Halle401b092015-09-23 13:34:24 -07001037 if response:
1038 if 200 <= response[ 0 ] <= 299:
1039 output = response[ 1 ]
1040 a = json.loads( output ).get( 'devices' )
1041 assert a is not None, "Error parsing json object"
1042 b = json.dumps( a )
1043 return b
1044 else:
1045 main.log.error( "Error with REST request, response was: " +
1046 str( response ) )
1047 return main.FALSE
1048 except ( AttributeError, AssertionError, TypeError ):
1049 main.log.exception( self.name + ": Object not as expected" )
1050 return None
1051 except Exception:
1052 main.log.exception( self.name + ": Uncaught exception!" )
1053 main.cleanup()
1054 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001055
1056 def getIntentState( self, intentsId, intentsJson=None,
1057 ip="DEFAULT", port="DEFAULT" ):
1058 """
1059 Description:
1060 Get intent state.
1061 Accepts a single intent ID (string type) or a list of intent IDs.
1062 Returns the state(string type) of the id if a single intent ID is
1063 accepted.
1064 Required:
1065 intentId: intent ID (string type)
1066 intentsJson: parsed json object from the onos:intents api
1067 Returns:
1068 Returns a dictionary with intent IDs as the key and its
1069 corresponding states as the values; Returns None for invalid IDs or
1070 Type error and any exceptions
1071 NOTE:
1072 An intent's state consist of INSTALLED,WITHDRAWN etc.
1073 """
1074 try:
1075 state = "State is Undefined"
1076 if not intentsJson:
1077 intentsJsonTemp = json.loads( self.intents() )
1078 else:
1079 intentsJsonTemp = json.loads( intentsJson )
1080 if isinstance( intentsId, types.StringType ):
1081 for intent in intentsJsonTemp:
1082 if intentsId == intent[ 'id' ]:
1083 state = intent[ 'state' ]
1084 return state
1085 main.log.info( "Cannot find intent ID" + str( intentsId ) +
1086 " on the list" )
1087 return state
1088 elif isinstance( intentsId, types.ListType ):
1089 dictList = []
1090 for i in xrange( len( intentsId ) ):
1091 stateDict = {}
1092 for intents in intentsJsonTemp:
1093 if intentsId[ i ] == intents[ 'id' ]:
1094 stateDict[ 'state' ] = intents[ 'state' ]
1095 stateDict[ 'id' ] = intentsId[ i ]
1096 dictList.append( stateDict )
1097 break
1098 if len( intentsId ) != len( dictList ):
1099 main.log.info( "Cannot find some of the intent ID state" )
1100 return dictList
1101 else:
1102 main.log.info( "Invalid intents ID entry" )
1103 return None
1104
Jon Halle401b092015-09-23 13:34:24 -07001105 except ( AttributeError, TypeError ):
1106 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001107 return None
Jon Halle401b092015-09-23 13:34:24 -07001108 except Exception:
1109 main.log.exception( self.name + ": Uncaught exception!" )
1110 main.cleanup()
1111 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001112
1113 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
1114 ip="DEFAULT", port="DEFAULT"):
1115 """
1116 Description:
1117 Check intents state based on expected state which defaults to
1118 INSTALLED state
1119 Required:
1120 intentsId - List of intents ID to be checked
1121 Optional:
1122 expectedState - Check the expected state(s) of each intents
1123 state in the list.
1124 *NOTE: You can pass in a list of expected state,
1125 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
1126 Return:
1127 Returns main.TRUE only if all intent are the same as expected states
1128 , otherwise, returns main.FALSE; Returns None for general exception
1129 """
1130 try:
1131 # Generating a dictionary: intent id as a key and state as value
1132 returnValue = main.TRUE
1133 if intentsId == "ALL":
1134 intentsId = self.getIntentsId( ip=ip, port=port )
1135 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
1136
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001137 if len( intentsId ) != len( intentsDict ):
1138 main.log.error( self.name + ": There is something wrong " +
1139 "getting intents state" )
1140 return main.FALSE
1141
1142 if isinstance( expectedState, types.StringType ):
1143 for intents in intentsDict:
1144 if intents.get( 'state' ) != expectedState:
1145 main.log.debug( self.name + " : Intent ID - " +
1146 intents.get( 'id' ) +
1147 " actual state = " +
1148 intents.get( 'state' )
1149 + " does not equal expected state = "
1150 + expectedState )
1151 returnValue = main.FALSE
1152
1153 elif isinstance( expectedState, types.ListType ):
1154 for intents in intentsDict:
1155 if not any( state == intents.get( 'state' ) for state in
1156 expectedState ):
1157 main.log.debug( self.name + " : Intent ID - " +
1158 intents.get( 'id' ) +
1159 " actual state = " +
1160 intents.get( 'state' ) +
1161 " does not equal expected states = "
1162 + str( expectedState ) )
1163 returnValue = main.FALSE
1164
1165 if returnValue == main.TRUE:
1166 main.log.info( self.name + ": All " +
1167 str( len( intentsDict ) ) +
1168 " intents are in " + str( expectedState ) +
1169 " state" )
1170 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001171 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001172 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001173 return None
Jon Halle401b092015-09-23 13:34:24 -07001174 except Exception:
1175 main.log.exception( self.name + ": Uncaught exception!" )
1176 main.cleanup()
1177 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001178
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001179 def flows( self, ip="DEFAULT", port="DEFAULT", subjectClass=None, subjectKey=None ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001180 """
1181 Description:
1182 Get flows currently added to the system
1183 NOTE:
1184 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001185 the REST output
1186
1187 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001188 """
1189 try:
1190 output = None
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001191 url = "/flows"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001192 if ip == "DEFAULT":
1193 main.log.warn( "No ip given, reverting to ip from topo file" )
1194 ip = self.ip_address
1195 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001196 main.log.warn( "No port given, reverting to port " +
1197 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001198 port = self.port
Jeremy Songster306ed7a2016-07-19 10:59:07 -07001199 if subjectKey and not subjectClass:
1200 main.log.warning( "Subject Key provided without Subject Class. Ignoring Subject Key" )
1201 if subjectClass:
1202 url += "/" + subjectClass
1203 if subjectKey:
1204 url += "/" + subjectKey
1205 response = self.send( url=url, ip=ip, port=port )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001206 if response:
1207 if 200 <= response[ 0 ] <= 299:
1208 output = response[ 1 ]
1209 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001210 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001211 b = json.dumps( a )
1212 return b
1213 else:
1214 main.log.error( "Error with REST request, response was: " +
1215 str( response ) )
1216 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001217 except ( AttributeError, AssertionError, TypeError ):
1218 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001219 return None
Jon Halle401b092015-09-23 13:34:24 -07001220 except Exception:
1221 main.log.exception( self.name + ": Uncaught exception!" )
1222 main.cleanup()
1223 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001224
Jon Halle401b092015-09-23 13:34:24 -07001225 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001226 """
1227 Description:
1228 Gets all the flows of the device or get a specific flow in the
1229 device by giving its flow ID
1230 Required:
Jon Halle401b092015-09-23 13:34:24 -07001231 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001232 Optional:
1233 int/hex flowId - ID of the flow
1234 """
1235 try:
1236 output = None
1237 if ip == "DEFAULT":
1238 main.log.warn( "No ip given, reverting to ip from topo file" )
1239 ip = self.ip_address
1240 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001241 main.log.warn( "No port given, reverting to port " +
1242 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001243 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001244 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001245 if flowId:
1246 url += "/" + str( int( flowId ) )
1247 print url
suibin zhangd5b6fe42016-05-12 08:48:58 -07001248 response = self.send( url=url, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001249 if response:
1250 if 200 <= response[ 0 ] <= 299:
1251 output = response[ 1 ]
1252 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001253 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001254 b = json.dumps( a )
1255 return b
1256 else:
1257 main.log.error( "Error with REST request, response was: " +
1258 str( response ) )
1259 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001260 except ( AttributeError, AssertionError, TypeError ):
1261 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001262 return None
Jon Halle401b092015-09-23 13:34:24 -07001263 except Exception:
1264 main.log.exception( self.name + ": Uncaught exception!" )
1265 main.cleanup()
1266 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001267
GlennRC073e8bc2015-10-27 17:11:28 -07001268 def sendFlow( self, deviceId, flowJson, ip="DEFAULT", port="DEFAULT", debug=False ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001269 """
1270 Description:
GlennRC073e8bc2015-10-27 17:11:28 -07001271 Sends a single flow to the specified device. This function exists
1272 so you can bypass the addFLow driver and send your own custom flow.
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001273 Required:
GlennRC073e8bc2015-10-27 17:11:28 -07001274 * The flow in json
1275 * the device id to add the flow to
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001276 Returns:
1277 Returns main.TRUE for successful requests; Returns main.FALSE
1278 if error on requests;
1279 Returns None for exceptions
1280 NOTE:
1281 The ip and port option are for the requests input's ip and port
1282 of the ONOS node
1283 """
GlennRC073e8bc2015-10-27 17:11:28 -07001284
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001285 try:
GlennRC073e8bc2015-10-27 17:11:28 -07001286 if debug: main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001287 output = None
1288 if ip == "DEFAULT":
1289 main.log.warn( "No ip given, reverting to ip from topo file" )
1290 ip = self.ip_address
1291 if port == "DEFAULT":
1292 main.log.warn( "No port given, reverting to port " +
1293 "from topo file" )
1294 port = self.port
1295 url = "/flows/" + deviceId
suibin zhang116647a2016-05-06 16:30:09 -07001296 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001297 url=url, ip = ip, port = port,
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001298 data=json.dumps( flowJson ) )
1299 if response:
Ming Yan Shuab2f7f52016-08-03 15:21:24 -07001300 if "201" in str( response[ 0 ] ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001301 main.log.info( self.name + ": Successfully POST flow" +
1302 "in device: " + str( deviceId ) )
1303 return main.TRUE
1304 else:
1305 main.log.error( "Error with REST request, response was: " +
1306 str( response ) )
1307 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001308 except NotImplementedError as e:
1309 raise e # Inform the caller
1310 except ( AttributeError, TypeError ):
1311 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001312 return None
Jon Halle401b092015-09-23 13:34:24 -07001313 except Exception:
1314 main.log.exception( self.name + ": Uncaught exception!" )
1315 main.cleanup()
1316 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001317
GlennRC073e8bc2015-10-27 17:11:28 -07001318 def addFlow( self,
1319 deviceId,
1320 appId=0,
1321 ingressPort="",
1322 egressPort="",
1323 ethType="",
1324 ethSrc="",
1325 ethDst="",
1326 vlan="",
1327 ipProto="",
1328 ipSrc=(),
1329 ipDst=(),
1330 tcpSrc="",
1331 tcpDst="",
GlennRC956ea742015-11-05 16:14:15 -08001332 udpDst="",
1333 udpSrc="",
1334 mpls="",
alisone14d7b02016-07-06 10:31:51 -07001335 priority=100,
GlennRC073e8bc2015-10-27 17:11:28 -07001336 ip="DEFAULT",
1337 port="DEFAULT",
1338 debug=False ):
1339 """
1340 Description:
1341 Creates a single flow in the specified device
1342 Required:
1343 * deviceId: id of the device
1344 Optional:
1345 * ingressPort: port ingress device
1346 * egressPort: port of egress device
1347 * ethType: specify ethType
1348 * ethSrc: specify ethSrc ( i.e. src mac addr )
1349 * ethDst: specify ethDst ( i.e. dst mac addr )
1350 * ipProto: specify ip protocol
1351 * ipSrc: specify ip source address with mask eg. ip#/24
1352 as a tuple (type, ip#)
1353 * ipDst: specify ip destination address eg. ip#/24
1354 as a tuple (type, ip#)
1355 * tcpSrc: specify tcp source port
1356 * tcpDst: specify tcp destination port
1357 Returns:
1358 Returns main.TRUE for successful requests; Returns main.FALSE
1359 if error on requests;
1360 Returns None for exceptions
1361 NOTE:
1362 The ip and port option are for the requests input's ip and port
1363 of the ONOS node
1364 """
1365 try:
alisone14d7b02016-07-06 10:31:51 -07001366 flowJson = { "priority":priority,
GlennRC073e8bc2015-10-27 17:11:28 -07001367 "isPermanent":"true",
1368 "timeout":0,
1369 "deviceId":deviceId,
1370 "treatment":{"instructions":[]},
1371 "selector": {"criteria":[]}}
1372 if appId:
1373 flowJson[ "appId" ] = appId
1374 if egressPort:
1375 flowJson[ 'treatment' ][ 'instructions' ].append( {
1376 "type":"OUTPUT",
1377 "port":egressPort } )
1378 if ingressPort:
1379 flowJson[ 'selector' ][ 'criteria' ].append( {
1380 "type":"IN_PORT",
1381 "port":ingressPort } )
1382 if ethType:
1383 flowJson[ 'selector' ][ 'criteria' ].append( {
1384 "type":"ETH_TYPE",
1385 "ethType":ethType } )
1386 if ethSrc:
1387 flowJson[ 'selector' ][ 'criteria' ].append( {
1388 "type":"ETH_SRC",
1389 "mac":ethSrc } )
1390 if ethDst:
1391 flowJson[ 'selector' ][ 'criteria' ].append( {
1392 "type":"ETH_DST",
1393 "mac":ethDst } )
1394 if vlan:
1395 flowJson[ 'selector' ][ 'criteria' ].append( {
1396 "type":"VLAN_VID",
1397 "vlanId":vlan } )
GlennRC956ea742015-11-05 16:14:15 -08001398 if mpls:
1399 flowJson[ 'selector' ][ 'criteria' ].append( {
1400 "type":"MPLS_LABEL",
1401 "label":mpls } )
GlennRC073e8bc2015-10-27 17:11:28 -07001402 if ipSrc:
1403 flowJson[ 'selector' ][ 'criteria' ].append( {
1404 "type":ipSrc[0],
1405 "ip":ipSrc[1] } )
1406 if ipDst:
1407 flowJson[ 'selector' ][ 'criteria' ].append( {
1408 "type":ipDst[0],
1409 "ip":ipDst[1] } )
1410 if tcpSrc:
1411 flowJson[ 'selector' ][ 'criteria' ].append( {
1412 "type":"TCP_SRC",
1413 "tcpPort": tcpSrc } )
1414 if tcpDst:
1415 flowJson[ 'selector' ][ 'criteria' ].append( {
1416 "type":"TCP_DST",
1417 "tcpPort": tcpDst } )
GlennRC956ea742015-11-05 16:14:15 -08001418 if udpSrc:
1419 flowJson[ 'selector' ][ 'criteria' ].append( {
1420 "type":"UDP_SRC",
1421 "udpPort": udpSrc } )
1422 if udpDst:
1423 flowJson[ 'selector' ][ 'criteria' ].append( {
1424 "type":"UDP_DST",
1425 "udpPort": udpDst } )
GlennRC073e8bc2015-10-27 17:11:28 -07001426 if ipProto:
1427 flowJson[ 'selector' ][ 'criteria' ].append( {
1428 "type":"IP_PROTO",
1429 "protocol": ipProto } )
1430
1431 return self.sendFlow( deviceId=deviceId, flowJson=flowJson, debug=debug )
1432
1433 except ( AttributeError, TypeError ):
1434 main.log.exception( self.name + ": Object not as expected" )
1435 return None
1436 except Exception:
1437 main.log.exception( self.name + ": Uncaught exception!" )
1438 main.cleanup()
1439 main.exit()
1440
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001441 def removeFlow( self, deviceId, flowId,
1442 ip="DEFAULT", port="DEFAULT" ):
1443 """
1444 Description:
1445 Remove specific device flow
1446 Required:
1447 str deviceId - id of the device
1448 str flowId - id of the flow
1449 Return:
1450 Returns main.TRUE if successfully deletes flows, otherwise
1451 Returns main.FALSE, Returns None on error
1452 """
1453 try:
1454 output = None
1455 if ip == "DEFAULT":
1456 main.log.warn( "No ip given, reverting to ip from topo file" )
1457 ip = self.ip_address
1458 if port == "DEFAULT":
1459 main.log.warn( "No port given, reverting to port " +
1460 "from topo file" )
1461 port = self.port
1462 # NOTE: REST url requires the intent id to be in decimal form
1463 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
suibin zhang116647a2016-05-06 16:30:09 -07001464 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001465 url="/flows" + query, ip = ip, port = port )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001466 if response:
1467 if 200 <= response[ 0 ] <= 299:
1468 return main.TRUE
1469 else:
1470 main.log.error( "Error with REST request, response was: " +
1471 str( response ) )
1472 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001473 except ( AttributeError, TypeError ):
1474 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001475 return None
Jon Halle401b092015-09-23 13:34:24 -07001476 except Exception:
1477 main.log.exception( self.name + ": Uncaught exception!" )
1478 main.cleanup()
1479 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001480
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001481 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1482 """
1483 Description:
1484 Check if all the current flows are in ADDED state
1485 Return:
1486 returnValue - Returns main.TRUE only if all flows are in
1487 return main.FALSE otherwise;
1488 Returns None for exception
1489 """
1490 try:
1491 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1492 returnValue = main.TRUE
1493 for flow in tempFlows:
1494 if flow.get( 'state' ) != 'ADDED':
1495 main.log.info( self.name + ": flow Id: " +
1496 str( flow.get( 'groupId' ) ) +
1497 " | state:" +
1498 str( flow.get( 'state' ) ) )
1499 returnValue = main.FALSE
1500 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001501 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001502 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001503 return None
1504 except Exception:
1505 main.log.exception( self.name + ": Uncaught exception!" )
1506 main.cleanup()
1507 main.exit()
Jon Hall66e001c2015-11-12 09:45:10 -08001508
1509 def getNetCfg( self, ip="DEFAULT", port="DEFAULT",
1510 subjectClass=None, subjectKey=None, configKey=None ):
1511 """
1512 Description:
1513 Get a json object with the ONOS network configurations
1514 Returns:
1515 A json object containing the network configuration in
1516 ONOS; Returns main.FALSE if error on requests;
1517 Returns None for exception
1518 """
1519 try:
1520 output = None
1521 if ip == "DEFAULT":
1522 main.log.warn( "No ip given, reverting to ip from topo file" )
1523 ip = self.ip_address
1524 if port == "DEFAULT":
1525 main.log.warn( "No port given, reverting to port " +
1526 "from topo file" )
1527 port = self.port
1528 url = "/network/configuration"
1529 if subjectClass:
1530 url += "/" + subjectClass
1531 if subjectKey:
1532 url += "/" + subjectKey
1533 if configKey:
1534 url += "/" + configKey
suibin zhangd5b6fe42016-05-12 08:48:58 -07001535 response = self.send( url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001536 if response:
1537 if 200 <= response[ 0 ] <= 299:
1538 output = response[ 1 ]
1539 a = json.loads( output )
1540 b = json.dumps( a )
1541 return b
1542 elif response[ 0 ] == 404:
1543 main.log.error( "Requested configuration doesn't exist: " +
1544 str( response ) )
1545 return {}
1546 else:
1547 main.log.error( "Error with REST request, response was: " +
1548 str( response ) )
1549 return main.FALSE
1550 except ( AttributeError, TypeError ):
1551 main.log.exception( self.name + ": Object not as expected" )
1552 return None
1553 except Exception:
1554 main.log.exception( self.name + ": Uncaught exception!" )
1555 main.cleanup()
1556 main.exit()
1557
1558 def setNetCfg( self, cfgJson, ip="DEFAULT", port="DEFAULT",
1559 subjectClass=None, subjectKey=None, configKey=None ):
1560 """
1561 Description:
1562 Set a json object with the ONOS network configurations
1563 Returns:
1564 Returns main.TRUE for successful requests; Returns main.FALSE
1565 if error on requests;
1566 Returns None for exceptions
1567
1568 """
1569 try:
1570 output = None
1571 if ip == "DEFAULT":
1572 main.log.warn( "No ip given, reverting to ip from topo file" )
1573 ip = self.ip_address
1574 if port == "DEFAULT":
1575 main.log.warn( "No port given, reverting to port " +
1576 "from topo file" )
1577 port = self.port
1578 url = "/network/configuration"
1579 if subjectClass:
1580 url += "/" + subjectClass
1581 if subjectKey:
1582 url += "/" + subjectKey
1583 if configKey:
1584 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001585 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001586 url=url, ip = ip, port = port,
Jon Hall66e001c2015-11-12 09:45:10 -08001587 data=json.dumps( cfgJson ) )
1588 if response:
1589 if 200 <= response[ 0 ] <= 299:
1590 main.log.info( self.name + ": Successfully POST cfg" )
1591 return main.TRUE
1592 else:
1593 main.log.error( "Error with REST request, response was: " +
1594 str( response ) )
1595 return main.FALSE
1596 except ( AttributeError, TypeError ):
1597 main.log.exception( self.name + ": Object not as expected" )
1598 return None
1599 except Exception:
1600 main.log.exception( self.name + ": Uncaught exception!" )
1601 main.cleanup()
1602 main.exit()
1603
1604 def removeNetCfg( self, ip="DEFAULT", port="DEFAULT",
1605 subjectClass=None, subjectKey=None, configKey=None ):
1606 """
1607 Description:
1608 Remove a json object from the ONOS network configurations
1609 Returns:
1610 Returns main.TRUE for successful requests; Returns main.FALSE
1611 if error on requests;
1612 Returns None for exceptions
1613
1614 """
1615 try:
1616 output = None
1617 if ip == "DEFAULT":
1618 main.log.warn( "No ip given, reverting to ip from topo file" )
1619 ip = self.ip_address
1620 if port == "DEFAULT":
1621 main.log.warn( "No port given, reverting to port " +
1622 "from topo file" )
1623 port = self.port
1624 url = "/network/configuration"
1625 if subjectClass:
1626 url += "/" + subjectClass
1627 if subjectKey:
1628 url += "/" + subjectKey
1629 if configKey:
1630 url += "/" + configKey
suibin zhang116647a2016-05-06 16:30:09 -07001631 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001632 url=url, ip = ip, port = port )
Jon Hall66e001c2015-11-12 09:45:10 -08001633 if response:
1634 if 200 <= response[ 0 ] <= 299:
1635 main.log.info( self.name + ": Successfully delete cfg" )
1636 return main.TRUE
1637 else:
1638 main.log.error( "Error with REST request, response was: " +
1639 str( response ) )
1640 return main.FALSE
1641 except ( AttributeError, TypeError ):
1642 main.log.exception( self.name + ": Object not as expected" )
1643 return None
1644 except Exception:
1645 main.log.exception( self.name + ": Uncaught exception!" )
1646 main.cleanup()
1647 main.exit()
suibin zhang17308622016-04-14 15:45:30 -07001648
1649 def createFlowBatch( self,
1650 numSw = 1,
1651 swIndex = 1,
1652 batchSize = 1,
1653 batchIndex = 1,
1654 deviceIdpreFix = "of:",
1655 appId=0,
1656 deviceID="",
1657 ingressPort="",
1658 egressPort="",
1659 ethType="",
1660 ethSrc="",
1661 ethDst="",
1662 vlan="",
1663 ipProto="",
1664 ipSrc=(),
1665 ipDst=(),
1666 tcpSrc="",
1667 tcpDst="",
1668 udpDst="",
1669 udpSrc="",
1670 mpls="",
1671 ip="DEFAULT",
1672 port="DEFAULT",
1673 debug=False ):
1674 """
1675 Description:
1676 Creates batches of MAC-rule flows for POST.
1677 Predefined MAC: 2 MS Hex digit for iterating devices
1678 Next 5 Hex digit for iterating batch numbers
1679 Next 5 Hex digit for iterating flows within a batch
1680 Required:
1681 * deviceId: id of the device
1682 Optional:
1683 * ingressPort: port ingress device
1684 * egressPort: port of egress device
1685 * ethType: specify ethType
1686 * ethSrc: specify ethSrc ( i.e. src mac addr )
1687 * ethDst: specify ethDst ( i.e. dst mac addr )
1688 * ipProto: specify ip protocol
1689 * ipSrc: specify ip source address with mask eg. ip#/24
1690 as a tuple (type, ip#)
1691 * ipDst: specify ip destination address eg. ip#/24
1692 as a tuple (type, ip#)
1693 * tcpSrc: specify tcp source port
1694 * tcpDst: specify tcp destination port
1695 Returns:
1696 Returns main.TRUE for successful requests; Returns main.FALSE
1697 if error on requests;
1698 Returns None for exceptions
1699 NOTE:
1700 The ip and port option are for the requests input's ip and port
1701 of the ONOS node
1702 """
1703 #from pprint import pprint
1704
1705 flowJsonList = []
1706 flowJsonBatch = {"flows":flowJsonList}
1707 dev = swIndex
1708
1709 for fl in range(1, batchSize + 1):
1710 flowJson = { "priority":100,
1711 "deviceId":"",
1712 "isPermanent":"true",
1713 "timeout":0,
1714 "treatment":{"instructions":[]},
1715 "selector": {"criteria":[]}}
1716
1717 #main.log.info("fl: " + str(fl))
1718 if dev <= numSw:
1719 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1720 #print deviceId
1721 flowJson['deviceId'] = deviceId
1722 dev += 1
1723 else:
1724 dev = 1
1725 deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
1726 #print deviceId
1727 flowJson['deviceId'] = deviceId
1728 dev += 1
1729
1730 # ethSrc starts with "0"; ethDst starts with "1"
1731 # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
1732 ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
1733 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1734 ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
1735 ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
1736 "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
1737 ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
1738
1739 if appId:
1740 flowJson[ "appId" ] = appId
1741
1742 if egressPort:
1743 flowJson[ 'treatment' ][ 'instructions' ].append( {
1744 "type":"OUTPUT",
1745 "port":egressPort } )
1746 if ingressPort:
1747 flowJson[ 'selector' ][ 'criteria' ].append( {
1748 "type":"IN_PORT",
1749 "port":ingressPort } )
1750 if ethType:
1751 flowJson[ 'selector' ][ 'criteria' ].append( {
1752 "type":"ETH_TYPE",
1753 "ethType":ethType } )
1754 if ethSrc:
1755 flowJson[ 'selector' ][ 'criteria' ].append( {
1756 "type":"ETH_SRC",
1757 "mac":ethSrc } )
1758 if ethDst:
1759 flowJson[ 'selector' ][ 'criteria' ].append( {
1760 "type":"ETH_DST",
1761 "mac":ethDst } )
1762 if vlan:
1763 flowJson[ 'selector' ][ 'criteria' ].append( {
1764 "type":"VLAN_VID",
1765 "vlanId":vlan } )
1766 if mpls:
1767 flowJson[ 'selector' ][ 'criteria' ].append( {
1768 "type":"MPLS_LABEL",
1769 "label":mpls } )
1770 if ipSrc:
1771 flowJson[ 'selector' ][ 'criteria' ].append( {
1772 "type":ipSrc[0],
1773 "ip":ipSrc[1] } )
1774 if ipDst:
1775 flowJson[ 'selector' ][ 'criteria' ].append( {
1776 "type":ipDst[0],
1777 "ip":ipDst[1] } )
1778 if tcpSrc:
1779 flowJson[ 'selector' ][ 'criteria' ].append( {
1780 "type":"TCP_SRC",
1781 "tcpPort": tcpSrc } )
1782 if tcpDst:
1783 flowJson[ 'selector' ][ 'criteria' ].append( {
1784 "type":"TCP_DST",
1785 "tcpPort": tcpDst } )
1786 if udpSrc:
1787 flowJson[ 'selector' ][ 'criteria' ].append( {
1788 "type":"UDP_SRC",
1789 "udpPort": udpSrc } )
1790 if udpDst:
1791 flowJson[ 'selector' ][ 'criteria' ].append( {
1792 "type":"UDP_DST",
1793 "udpPort": udpDst } )
1794 if ipProto:
1795 flowJson[ 'selector' ][ 'criteria' ].append( {
1796 "type":"IP_PROTO",
1797 "protocol": ipProto } )
1798 #pprint(flowJson)
1799 flowJsonList.append(flowJson)
1800
1801 main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
1802 flowJsonBatch['flows'] = flowJsonList
1803 #pprint(flowJsonBatch)
1804
1805 return flowJsonBatch
1806
1807
1808 def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
1809 """
1810 Description:
1811 Sends a single flow batch through /flows REST API.
1812 Required:
1813 * The batch of flows
1814 Returns:
1815 Returns main.TRUE for successful requests; Returns main.FALSE
1816 if error on requests;
1817 Returns None for exceptions
1818 NOTE:
1819 The ip and port option are for the requests input's ip and port
1820 of the ONOS node
1821 """
1822 import time
1823
1824 try:
1825 if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
1826 output = None
1827 if ip == "DEFAULT":
1828 main.log.warn( "No ip given, reverting to ip from topo file" )
1829 ip = self.ip_address
1830 if port == "DEFAULT":
1831 main.log.warn( "No port given, reverting to port " +
1832 "from topo file" )
1833 port = self.port
1834 url = "/flows/"
suibin zhang116647a2016-05-06 16:30:09 -07001835 response = self.send( method="POST",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001836 url=url, ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001837 data=json.dumps( batch ) )
1838 #main.log.info("Post response is: ", str(response[0]))
1839 if response[0] == 200:
1840 main.log.info( self.name + ": Successfully POST flow batch" )
1841 return main.TRUE, response
1842 else:
1843 main.log.error( "Error with REST request, response was: " +
1844 str( response ) )
1845 return main.FALSE
1846 except NotImplementedError as e:
1847 raise e # Inform the caller
1848 except ( AttributeError, TypeError ):
1849 main.log.exception( self.name + ": Object not as expected" )
1850 return None
1851 except Exception:
1852 main.log.exception( self.name + ": Uncaught exception!" )
1853 main.cleanup()
1854 main.exit()
1855
1856 def removeFlowBatch( self, batch={},
1857 ip="DEFAULT", port="DEFAULT" ):
1858 """
1859 Description:
1860 Remove a batch of flows
1861 Required:
1862 flow batch
1863 Return:
1864 Returns main.TRUE if successfully deletes flows, otherwise
1865 Returns main.FALSE, Returns None on error
1866 """
1867 try:
1868 output = None
1869 if ip == "DEFAULT":
1870 main.log.warn( "No ip given, reverting to ip from topo file" )
1871 ip = self.ip_address
1872 if port == "DEFAULT":
1873 main.log.warn( "No port given, reverting to port " +
1874 "from topo file" )
1875 port = self.port
1876 # NOTE: REST url requires the intent id to be in decimal form
1877
suibin zhang116647a2016-05-06 16:30:09 -07001878 response = self.send( method="DELETE",
suibin zhangd5b6fe42016-05-12 08:48:58 -07001879 url="/flows/", ip = ip, port = port,
suibin zhang17308622016-04-14 15:45:30 -07001880 data = json.dumps(batch) )
1881 if response:
1882 if 200 <= response[ 0 ] <= 299:
1883 return main.TRUE
1884 else:
1885 main.log.error( "Error with REST request, response was: " +
1886 str( response ) )
1887 return main.FALSE
1888 except ( AttributeError, TypeError ):
1889 main.log.exception( self.name + ": Object not as expected" )
1890 return None
1891 except Exception:
1892 main.log.exception( self.name + ": Uncaught exception!" )
1893 main.cleanup()
1894 main.exit()
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001895
1896 def getTopology( self, topologyOutput ):
1897 """
1898 Definition:
1899 Loads a json topology output
1900 Return:
1901 topology = current ONOS topology
1902 """
1903 import json
1904 try:
1905 # either onos:topology or 'topology' will work in CLI
1906 topology = json.loads(topologyOutput)
1907 main.log.debug( topology )
1908 return topology
1909 except pexpect.EOF:
1910 main.log.error( self.name + ": EOF exception found" )
1911 main.log.error( self.name + ": " + self.handle.before )
1912 main.cleanup()
1913 main.exit()
1914 except Exception:
1915 main.log.exception( self.name + ": Uncaught exception!" )
1916 main.cleanup()
1917 main.exit()
1918
1919 def checkStatus(
1920 self,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001921 numoswitch,
1922 numolink,
Flavio Castro82ee2f62016-06-07 15:04:12 -07001923 numoctrl = -1,
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001924 logLevel="info" ):
1925 """
1926 Checks the number of switches & links that ONOS sees against the
1927 supplied values. By default this will report to main.log, but the
1928 log level can be specific.
1929
Flavio Castro82ee2f62016-06-07 15:04:12 -07001930 Params: numoswitch = expected number of switches
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001931 numolink = expected number of links
Flavio Castro82ee2f62016-06-07 15:04:12 -07001932 numoctrl = expected number of controllers
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001933 logLevel = level to log to.
1934 Currently accepts 'info', 'warn' and 'report'
1935
1936 Returns: main.TRUE if the number of switches and links are correct,
1937 main.FALSE if the number of switches and links is incorrect,
1938 and main.ERROR otherwise
1939 """
1940 try:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001941 topology = self.getTopology( self.topology() )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001942 if topology == {}:
1943 return main.ERROR
1944 output = ""
1945 # Is the number of switches is what we expected
1946 devices = topology.get( 'devices', False )
1947 links = topology.get( 'links', False )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001948 nodes = topology.get( 'nodes' , False )
1949 if devices is False or links is False or nodes is False:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001950 return main.ERROR
1951 switchCheck = ( int( devices ) == int( numoswitch ) )
1952 # Is the number of links is what we expected
1953 linkCheck = ( int( links ) == int( numolink ) )
Flavio Castro82ee2f62016-06-07 15:04:12 -07001954 nodeCheck = ( int(nodes) == int(numoctrl) )or int(numoctrl) == -1
1955 if switchCheck and linkCheck and nodeCheck:
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001956 # We expected the correct numbers
1957 output = output + "The number of links and switches match "\
1958 + "what was expected"
1959 result = main.TRUE
1960 else:
1961 output = output + \
1962 "The number of links and switches does not match " + \
1963 "what was expected"
1964 result = main.FALSE
1965 output = output + "\n ONOS sees %i devices" % int( devices )
1966 output = output + " (%i expected) " % int( numoswitch )
1967 output = output + "and %i links " % int( links )
1968 output = output + "(%i expected)" % int( numolink )
Flavio Castrodd0f3982016-06-17 15:50:57 -07001969 if int( numoctrl ) > 0:
Flavio Castro82ee2f62016-06-07 15:04:12 -07001970 output = output + "and %i controllers " % int( nodes )
1971 output = output + "(%i expected)" % int( numoctrl )
Jeremy Songsterbc2d8ac2016-05-04 11:25:42 -07001972 if logLevel == "report":
1973 main.log.report( output )
1974 elif logLevel == "warn":
1975 main.log.warn( output )
1976 else:
1977 main.log.info( output )
1978 return result
1979 except pexpect.EOF:
1980 main.log.error( self.name + ": EOF exception found" )
1981 main.log.error( self.name + ": " + self.handle.before )
1982 main.cleanup()
1983 main.exit()
1984 except Exception:
1985 main.log.exception( self.name + ": Uncaught exception!" )
1986 main.cleanup()
Flavio Castro82ee2f62016-06-07 15:04:12 -07001987 main.exit()