blob: 3ff43c7d6c4388eaea4b66a06ee9bbee689d5768 [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
4
5 TestON is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 ( at your option ) any later version.
9
10 TestON is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TestON. If not, see <http://www.gnu.org/licenses/>.
17
18"""
19import json
20import os
21import requests
kelvin-onlab03eb88d2015-07-22 10:29:02 -070022import types
Jon Halle401b092015-09-23 13:34:24 -070023import sys
Jon Hallfc915882015-07-14 13:33:17 -070024
Jon Hallfc915882015-07-14 13:33:17 -070025from drivers.common.api.controllerdriver import Controller
26
27
28class OnosRestDriver( Controller ):
29
30 def __init__( self ):
Jon Hallf7234882015-08-28 13:16:31 -070031 self.pwd = None
32 self.user_name = "user"
Jon Hallfc915882015-07-14 13:33:17 -070033 super( Controller, self ).__init__()
34 self.ip_address = "localhost"
35 self.port = "8080"
Jon Halle401b092015-09-23 13:34:24 -070036 self.wrapped = sys.modules[ __name__ ]
Jon Hallfc915882015-07-14 13:33:17 -070037
38 def connect( self, **connectargs ):
39 try:
40 for key in connectargs:
41 vars( self )[ key ] = connectargs[ key ]
42 self.name = self.options[ 'name' ]
43 except Exception as e:
44 main.log.exception( e )
45 try:
46 if os.getenv( str( self.ip_address ) ) != None:
47 self.ip_address = os.getenv( str( self.ip_address ) )
48 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070049 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070050 except KeyError:
51 main.log.info( "Invalid host name," +
52 "defaulting to 'localhost' instead" )
53 self.ip_address = 'localhost'
54 except Exception as inst:
55 main.log.error( "Uncaught exception: " + str( inst ) )
56
57 self.handle = super( OnosRestDriver, self ).connect()
58 return self.handle
59
Jon Halle401b092015-09-23 13:34:24 -070060 def pprint( self, jsonObject ):
61 """
62 Pretty Prints a json object
63
64 arguments:
65 jsonObject - a parsed json object
66 returns:
67 A formatted string for printing or None on error
68 """
69 try:
70 if isinstance( jsonObject, str ):
71 jsonObject = json.loads( jsonObject )
72 return json.dumps( jsonObject, sort_keys=True,
73 indent=4, separators=(',', ': '))
74 except ( TypeError, ValueError ):
75 main.log.exception( "Error parsing jsonObject" )
76 return None
77
Jon Hallfc915882015-07-14 13:33:17 -070078 def send( self, ip, port, url, base="/onos/v1", method="GET",
Jon Hallf7234882015-08-28 13:16:31 -070079 query=None, data=None, debug=False ):
Jon Hallfc915882015-07-14 13:33:17 -070080 """
81 Arguments:
82 str ip: ONOS IP Address
83 str port: ONOS REST Port
84 str url: ONOS REST url path.
85 NOTE that this is is only the relative path. IE "/devices"
86 str base: The base url for the given REST api. Applications could
87 potentially have their own base url
88 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070089 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070090 the request
91 dict data: Dictionary to be sent in the body of the request
92 """
93 # TODO: Authentication - simple http (user,pass) tuple
94 # TODO: should we maybe just pass kwargs straight to response?
95 # TODO: Do we need to allow for other protocols besides http?
96 # ANSWER: Not yet, but potentially https with certificates
97 try:
98 path = "http://" + str( ip ) + ":" + str( port ) + base + url
Jon Hallf7234882015-08-28 13:16:31 -070099 if self.user_name and self.pwd:
100 auth = (self.user_name, self.pwd)
101 else:
102 auth=None
Jon Hallfc915882015-07-14 13:33:17 -0700103 main.log.info( "Sending request " + path + " using " +
104 method.upper() + " method." )
105 response = requests.request( method.upper(),
106 path,
107 params=query,
Jon Hallf7234882015-08-28 13:16:31 -0700108 data=data,
109 auth=auth )
110 if debug:
111 main.log.debug( response )
Jon Hallfc915882015-07-14 13:33:17 -0700112 return ( response.status_code, response.text.encode( 'utf8' ) )
113 except requests.exceptions:
114 main.log.exception( "Error sending request." )
115 return None
Jon Halle401b092015-09-23 13:34:24 -0700116 except Exception:
117 main.log.exception( self.name + ": Uncaught exception!" )
118 main.cleanup()
119 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700120
121 def intents( self, ip="DEFAULT", port="DEFAULT" ):
122 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700123 Description:
124 Gets a list of dictionary of all intents in the system
125 Returns:
126 A list of dictionary of intents in string type to match the cli
127 version for now; Returns main.FALSE if error on request;
128 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700129 """
130 try:
131 output = None
132 if ip == "DEFAULT":
133 main.log.warn( "No ip given, reverting to ip from topo file" )
134 ip = self.ip_address
135 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700136 main.log.warn( "No port given, reverting to port " +
137 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700138 port = self.port
139 response = self.send( ip, port, url="/intents" )
140 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700141 if 200 <= response[ 0 ] <= 299:
142 output = response[ 1 ]
143 a = json.loads( output ).get( 'intents' )
Jon Halle401b092015-09-23 13:34:24 -0700144 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700145 b = json.dumps( a )
146 return b
Jon Hallfc915882015-07-14 13:33:17 -0700147 else:
148 main.log.error( "Error with REST request, response was: " +
149 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700150 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700151 except ( AttributeError, AssertionError, TypeError ):
152 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700153 return None
Jon Halle401b092015-09-23 13:34:24 -0700154 except Exception:
155 main.log.exception( self.name + ": Uncaught exception!" )
156 main.cleanup()
157 main.exit()
Jon Hallfc915882015-07-14 13:33:17 -0700158
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700159 def intent( self, intentId, appId="org.onosproject.cli",
160 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700161 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700162 Description:
163 Get the specific intent information of the given application ID and
164 intent ID
165 Required:
166 str intentId - Intent id in hexadecimal form
167 Optional:
168 str appId - application id of intent
169 Returns:
170 Returns an information dictionary of the given intent;
171 Returns main.FALSE if error on requests; Returns None for exception
172 NOTE:
173 The GET /intents REST api command accepts application id but the
174 api will get updated to accept application name instead
Jon Hallfc915882015-07-14 13:33:17 -0700175 """
176 try:
177 output = None
178 if ip == "DEFAULT":
179 main.log.warn( "No ip given, reverting to ip from topo file" )
180 ip = self.ip_address
181 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700182 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700183 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700184 port = self.port
185 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700186 query = "/" + str( appId ) + "/" + str( intentId )
Jon Hallfc915882015-07-14 13:33:17 -0700187 response = self.send( ip, port, url="/intents" + query )
188 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700189 if 200 <= response[ 0 ] <= 299:
190 output = response[ 1 ]
191 a = json.loads( output )
192 return a
Jon Hallfc915882015-07-14 13:33:17 -0700193 else:
194 main.log.error( "Error with REST request, response was: " +
195 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700196 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700197 except ( AttributeError, TypeError ):
198 main.log.exception( self.name + ": Object not as expected" )
Jon Hallfc915882015-07-14 13:33:17 -0700199 return None
Jon Halle401b092015-09-23 13:34:24 -0700200 except Exception:
201 main.log.exception( self.name + ": Uncaught exception!" )
202 main.cleanup()
203 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700204
205 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
206 """
207 Description:
208 Gets all intents ID using intents function
209 Returns:
210 List of intents ID;Returns None for exception; Returns None for
211 exception; Returns None for exception
212 """
213 try:
214 intentsDict = {}
215 intentsIdList = []
216 intentsDict = json.loads( self.intents( ip=ip, port=port ) )
217 for intent in intentsDict:
218 intentsIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700219 if not intentsIdList:
220 main.log.debug( "Cannot find any intents" )
221 return main.FALSE
222 else:
223 main.log.info( "Found intents: " + str( intentsIdList ) )
224 return main.TRUE
Jon Halle401b092015-09-23 13:34:24 -0700225 except ( AttributeError, TypeError ):
226 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700227 return None
Jon Halle401b092015-09-23 13:34:24 -0700228 except Exception:
229 main.log.exception( self.name + ": Uncaught exception!" )
230 main.cleanup()
231 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700232
233 def apps( self, ip="DEFAULT", port="DEFAULT" ):
234 """
235 Description:
236 Returns all the current application installed in the system
237 Returns:
238 List of dictionary of installed application; Returns main.FALSE for
239 error on request; Returns None for exception
240 """
241 try:
242 output = None
243 if ip == "DEFAULT":
244 main.log.warn( "No ip given, reverting to ip from topo file" )
245 ip = self.ip_address
246 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700247 main.log.warn( "No port given, reverting to port " +
248 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700249 port = self.port
250 response = self.send( ip, port, url="/applications" )
251 if response:
252 if 200 <= response[ 0 ] <= 299:
253 output = response[ 1 ]
254 a = json.loads( output ).get( 'applications' )
Jon Halle401b092015-09-23 13:34:24 -0700255 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700256 b = json.dumps( a )
257 return b
258 else:
259 main.log.error( "Error with REST request, response was: " +
260 str( response ) )
261 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700262 except ( AttributeError, AssertionError, TypeError ):
263 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700264 return None
Jon Halle401b092015-09-23 13:34:24 -0700265 except Exception:
266 main.log.exception( self.name + ": Uncaught exception!" )
267 main.cleanup()
268 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700269
270 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
271 """
272 Decription:
273 Activate an app that is already installed in ONOS
274 Optional:
275 bool check - If check is True, method will check the status
276 of the app after the command is issued
277 Returns:
278 Returns main.TRUE if the command was successfully or main.FALSE
279 if the REST responded with an error or given incorrect input;
280 Returns None for exception
281
282 """
283 try:
284 output = None
285 if ip == "DEFAULT":
286 main.log.warn( "No ip given, reverting to ip from topo file" )
287 ip = self.ip_address
288 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700289 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700290 "from topo file" )
291 port = self.port
292 query = "/" + str( appName ) + "/active"
293 response = self.send( ip, port, method="POST",
294 url="/applications" + query )
295 if response:
296 output = response[ 1 ]
297 app = json.loads( output )
298 if 200 <= response[ 0 ] <= 299:
299 if check:
300 if app.get( 'state' ) == 'ACTIVE':
301 main.log.info( self.name + ": " + appName +
302 " application" +
303 " is in ACTIVE state" )
304 return main.TRUE
305 else:
306 main.log.error( self.name + ": " + appName +
307 " application" + " is in " +
308 app.get( 'state' ) + " state" )
309 return main.FALSE
310 else:
311 main.log.warn( "Skipping " + appName +
312 "application check" )
313 return main.TRUE
314 else:
315 main.log.error( "Error with REST request, response was: " +
316 str( response ) )
317 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700318 except ( AttributeError, TypeError ):
319 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700320 return None
Jon Halle401b092015-09-23 13:34:24 -0700321 except Exception:
322 main.log.exception( self.name + ": Uncaught exception!" )
323 main.cleanup()
324 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700325
326 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
327 check=True ):
328 """
329 Required:
330 Deactivate an app that is already activated in ONOS
331 Optional:
332 bool check - If check is True, method will check the status of the
333 app after the command is issued
334 Returns:
335 Returns main.TRUE if the command was successfully sent
336 main.FALSE if the REST responded with an error or given
337 incorrect input; Returns None for exception
338 """
339 try:
340 output = None
341 if ip == "DEFAULT":
342 main.log.warn( "No ip given, reverting to ip from topo file" )
343 ip = self.ip_address
344 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700345 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700346 "from topo file" )
347 port = self.port
348 query = "/" + str( appName ) + "/active"
349 response = self.send( ip, port, method="DELETE",
350 url="/applications" + query )
351 if response:
352 output = response[ 1 ]
353 app = json.loads( output )
354 if 200 <= response[ 0 ] <= 299:
355 if check:
356 if app.get( 'state' ) == 'INSTALLED':
357 main.log.info( self.name + ": " + appName +
358 " application" +
359 " is in INSTALLED state" )
360 return main.TRUE
361 else:
362 main.log.error( self.name + ": " + appName +
363 " application" + " is in " +
364 app.get( 'state' ) + " state" )
365 return main.FALSE
366 else:
367 main.log.warn( "Skipping " + appName +
368 "application check" )
369 return main.TRUE
370 else:
371 main.log.error( "Error with REST request, response was: " +
372 str( response ) )
373 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700374 except ( AttributeError, TypeError ):
375 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700376 return None
Jon Halle401b092015-09-23 13:34:24 -0700377 except Exception:
378 main.log.exception( self.name + ": Uncaught exception!" )
379 main.cleanup()
380 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700381
382 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
383 port="DEFAULT" ):
384 """
385 Decription:
386 Gets the informaion of the given application
387 Required:
388 str name - Name of onos application
389 Returns:
390 Returns a dictionary of information ONOS application in string type;
391 Returns main.FALSE if error on requests; Returns None for exception
392 """
393 try:
394 output = None
395 if ip == "DEFAULT":
396 main.log.warn( "No ip given, reverting to ip from topo file" )
397 ip = self.ip_address
398 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700399 main.log.warn( "No port given, reverting to port " +
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700400 "from topo file" )
401 port = self.port
402 query = "/" + project + str( appName )
403 response = self.send( ip, port, url="/applications" + query )
404 if response:
405 if 200 <= response[ 0 ] <= 299:
406 output = response[ 1 ]
407 a = json.loads( output )
408 return a
409 else:
410 main.log.error( "Error with REST request, response was: " +
411 str( response ) )
412 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700413 except ( AttributeError, TypeError ):
414 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700415 return None
Jon Halle401b092015-09-23 13:34:24 -0700416 except Exception:
417 main.log.exception( self.name + ": Uncaught exception!" )
418 main.cleanup()
419 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700420
421 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
422 ip="DEFAULT", port="DEFAULT" ):
423 """
424 Description:
425 Adds a host-to-host intent ( bidirectional ) by
426 specifying the two hosts.
427 Required:
428 * hostIdOne: ONOS host id for host1
429 * hostIdTwo: ONOS host id for host2
430 Optional:
431 str appId - Application name of intent identifier
432 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700433 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700434 error on requests; Returns None for exceptions
435 """
436 try:
437 intentJson = {"two": str( hostIdTwo ),
438 "selector": {"criteria": []}, "priority": 7,
439 "treatment": {"deferred": [], "instructions": []},
440 "appId": appId, "one": str( hostIdOne ),
441 "type": "HostToHostIntent",
442 "constraints": [{"type": "LinkTypeConstraint",
443 "types": ["OPTICAL"],
444 "inclusive": 'false' }]}
445 output = None
446 if ip == "DEFAULT":
447 main.log.warn( "No ip given, reverting to ip from topo file" )
448 ip = self.ip_address
449 if port == "DEFAULT":
450 main.log.warn( "No port given, reverting to port " +
451 "from topo file" )
452 port = self.port
453 response = self.send( ip,
454 port,
455 method="POST",
456 url="/intents",
457 data=json.dumps( intentJson ) )
458 if response:
459 if 201:
460 main.log.info( self.name + ": Successfully POST host" +
461 " intent between host: " + hostIdOne +
462 " and host: " + hostIdTwo )
463 return main.TRUE
464 else:
465 main.log.error( "Error with REST request, response was: " +
466 str( response ) )
467 return main.FALSE
468
Jon Halle401b092015-09-23 13:34:24 -0700469 except ( AttributeError, TypeError ):
470 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700471 return None
Jon Halle401b092015-09-23 13:34:24 -0700472 except Exception:
473 main.log.exception( self.name + ": Uncaught exception!" )
474 main.cleanup()
475 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700476
kelvin-onlabb50074f2015-07-27 16:18:32 -0700477 def addPointIntent( self,
478 ingressDevice,
479 egressDevice,
kelvin-onlabb50074f2015-07-27 16:18:32 -0700480 appId='org.onosproject.cli',
481 ingressPort="",
482 egressPort="",
483 ethType="",
484 ethSrc="",
485 ethDst="",
486 bandwidth="",
487 lambdaAlloc=False,
488 ipProto="",
489 ipSrc="",
490 ipDst="",
491 tcpSrc="",
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700492 tcpDst="",
493 ip="DEFAULT",
494 port="DEFAULT" ):
kelvin-onlabb50074f2015-07-27 16:18:32 -0700495 """
496 Description:
497 Adds a point-to-point intent ( uni-directional ) by
498 specifying device id's and optional fields
499 Required:
500 * ingressDevice: device id of ingress device
501 * egressDevice: device id of egress device
502 Optional:
503 * ethType: specify ethType
504 * ethSrc: specify ethSrc ( i.e. src mac addr )
505 * ethDst: specify ethDst ( i.e. dst mac addr )
506 * bandwidth: specify bandwidth capacity of link (TODO)
507 * lambdaAlloc: if True, intent will allocate lambda
508 for the specified intent (TODO)
509 * ipProto: specify ip protocol
510 * ipSrc: specify ip source address with mask eg. ip#/24
511 * ipDst: specify ip destination address eg. ip#/24
512 * tcpSrc: specify tcp source port
513 * tcpDst: specify tcp destination port
514 Returns:
515 Returns main.TRUE for successful requests; Returns main.FALSE if
516 no ingress|egress port found and if error on requests;
517 Returns None for exceptions
518 NOTE:
519 The ip and port option are for the requests input's ip and port
520 of the ONOS node
521 """
522 try:
523 if "/" in ingressDevice:
524 if not ingressPort:
525 ingressPort = ingressDevice.split( "/" )[ 1 ]
526 ingressDevice = ingressDevice.split( "/" )[ 0 ]
527 else:
528 if not ingressPort:
529 main.log.debug( self.name + ": Ingress port not specified" )
530 return main.FALSE
531
532 if "/" in egressDevice:
533 if not egressPort:
534 egressPort = egressDevice.split( "/" )[ 1 ]
535 egressDevice = egressDevice.split( "/" )[ 0 ]
536 else:
537 if not egressPort:
538 main.log.debug( self.name + ": Egress port not specified" )
539 return main.FALSE
540
541 intentJson ={ "ingressPoint": { "device": ingressDevice,
542 "port": ingressPort },
543 "selector": { "criteria": [] },
544 "priority": 55,
545 "treatment": { "deferred": [],
546 "instructions": [] },
547 "egressPoint": { "device": egressDevice,
548 "port": egressPort },
549 "appId": appId,
550 "type": "PointToPointIntent",
551 "constraints": [ { "type": "LinkTypeConstraint",
552 "types": [ "OPTICAL" ],
553 "inclusive": "false" } ] }
554
555 if ethType == "IPV4":
556 intentJson[ 'selector' ][ 'criteria' ].append( {
557 "type":"ETH_TYPE",
558 "ethType":2048 } )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -0700559 elif ethType:
560 intentJson[ 'selector' ][ 'criteria' ].append( {
561 "type":"ETH_TYPE",
562 "ethType":ethType } )
563
kelvin-onlabb50074f2015-07-27 16:18:32 -0700564 if ethSrc:
565 intentJson[ 'selector' ][ 'criteria' ].append(
566 { "type":"ETH_SRC",
567 "mac":ethSrc } )
568 if ethDst:
569 intentJson[ 'selector' ][ 'criteria' ].append(
570 { "type":"ETH_DST",
571 "mac":ethDst } )
572 if ipSrc:
573 intentJson[ 'selector' ][ 'criteria' ].append(
574 { "type":"IPV4_SRC",
575 "ip":ipSrc } )
576 if ipDst:
577 intentJson[ 'selector' ][ 'criteria' ].append(
578 { "type":"IPV4_DST",
579 "ip":ipDst } )
580 if tcpSrc:
581 intentJson[ 'selector' ][ 'criteria' ].append(
582 { "type":"TCP_SRC",
583 "tcpPort": tcpSrc } )
584 if tcpDst:
585 intentJson[ 'selector' ][ 'criteria' ].append(
586 { "type":"TCP_DST",
587 "tcpPort": tcpDst } )
588 if ipProto:
589 intentJson[ 'selector' ][ 'criteria' ].append(
590 { "type":"IP_PROTO",
591 "protocol": ipProto } )
592
593 # TODO: Bandwidth and Lambda will be implemented if needed
594
595 main.log.debug( intentJson )
596
597 output = None
598 if ip == "DEFAULT":
599 main.log.warn( "No ip given, reverting to ip from topo file" )
600 ip = self.ip_address
601 if port == "DEFAULT":
602 main.log.warn( "No port given, reverting to port " +
603 "from topo file" )
604 port = self.port
605 response = self.send( ip,
606 port,
607 method="POST",
608 url="/intents",
609 data=json.dumps( intentJson ) )
610 if response:
611 if 201:
612 main.log.info( self.name + ": Successfully POST point" +
613 " intent between ingress: " + ingressDevice +
614 " and egress: " + egressDevice + " devices" )
615 return main.TRUE
616 else:
617 main.log.error( "Error with REST request, response was: " +
618 str( response ) )
619 return main.FALSE
620
Jon Halle401b092015-09-23 13:34:24 -0700621 except ( AttributeError, TypeError ):
622 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlabb50074f2015-07-27 16:18:32 -0700623 return None
Jon Halle401b092015-09-23 13:34:24 -0700624 except Exception:
625 main.log.exception( self.name + ": Uncaught exception!" )
626 main.cleanup()
627 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700628
629 def removeIntent( self, intentId, appId='org.onosproject.cli',
630 ip="DEFAULT", port="DEFAULT" ):
631 """
632 Remove intent for specified application id and intent id;
633 Returns None for exception
634 """
635 try:
636 output = None
637 if ip == "DEFAULT":
638 main.log.warn( "No ip given, reverting to ip from topo file" )
639 ip = self.ip_address
640 if port == "DEFAULT":
641 main.log.warn( "No port given, reverting to port " +
642 "from topo file" )
643 port = self.port
644 # NOTE: REST url requires the intent id to be in decimal form
645 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
646 response = self.send( ip,
647 port,
648 method="DELETE",
649 url="/intents" + query )
650 if response:
651 if 200 <= response[ 0 ] <= 299:
652 return main.TRUE
653 else:
654 main.log.error( "Error with REST request, response was: " +
655 str( response ) )
656 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700657 except ( AttributeError, TypeError ):
658 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700659 return None
Jon Halle401b092015-09-23 13:34:24 -0700660 except Exception:
661 main.log.exception( self.name + ": Uncaught exception!" )
662 main.cleanup()
663 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700664
665 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
666 """
667 Returns a list of intents id; Returns None for exception
668 """
669 try:
670 intentIdList = []
671 intentsJson = json.loads( self.intents() )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700672 for intent in intentsJson:
673 intentIdList.append( intent.get( 'id' ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700674 return intentIdList
Jon Halle401b092015-09-23 13:34:24 -0700675 except ( AttributeError, TypeError ):
676 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700677 return None
Jon Halle401b092015-09-23 13:34:24 -0700678 except Exception:
679 main.log.exception( self.name + ": Uncaught exception!" )
680 main.cleanup()
681 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700682
683 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
684 ip="DEFAULT", port="DEFAULT", delay=5 ):
685 """
686 Description:
687 Remove all the intents
688 Returns:
689 Returns main.TRUE if all intents are removed, otherwise returns
690 main.FALSE; Returns None for exception
691 """
692 try:
693 results = []
694 if intentIdList == 'ALL':
695 intentIdList = self.getIntentsId( ip=ip, port=port )
696
697 main.log.info( self.name + ": Removing intents " +
698 str( intentIdList ) )
699
700 if isinstance( intentIdList, types.ListType ):
701 for intent in intentIdList:
702 results.append( self.removeIntent( intentId=intent,
703 appId=appId,
704 ip=ip,
705 port=port ) )
706 # Check for remaining intents
707 # NOTE: Noticing some delay on Deleting the intents so i put
708 # this time out
709 import time
710 time.sleep( delay )
711 intentRemain = len( json.loads( self.intents() ) )
712 if all( result==main.TRUE for result in results ) and \
713 intentRemain == 0:
714 main.log.info( self.name + ": All intents are removed " )
715 return main.TRUE
716 else:
717 main.log.error( self.name + ": Did not removed all intents,"
718 + " there are " + str( intentRemain )
719 + " intents remaining" )
720 return main.FALSE
721 else:
722 main.log.debug( self.name + ": There is no intents ID list" )
Jon Halle401b092015-09-23 13:34:24 -0700723 except ( AttributeError, TypeError ):
724 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700725 return None
Jon Halle401b092015-09-23 13:34:24 -0700726 except Exception:
727 main.log.exception( self.name + ": Uncaught exception!" )
728 main.cleanup()
729 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700730
731 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
732 """
733 Description:
734 Get a list of dictionary of all discovered hosts
735 Returns:
736 Returns a list of dictionary of information of the hosts currently
737 discovered by ONOS; Returns main.FALSE if error on requests;
738 Returns None for exception
739 """
740 try:
741 output = None
742 if ip == "DEFAULT":
743 main.log.warn( "No ip given, reverting to ip from topo file" )
744 ip = self.ip_address
745 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700746 main.log.warn( "No port given, reverting to port " +
747 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700748 port = self.port
749 response = self.send( ip, port, url="/hosts" )
750 if response:
751 if 200 <= response[ 0 ] <= 299:
752 output = response[ 1 ]
753 a = json.loads( output ).get( 'hosts' )
Jon Halle401b092015-09-23 13:34:24 -0700754 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700755 b = json.dumps( a )
756 return b
757 else:
758 main.log.error( "Error with REST request, response was: " +
759 str( response ) )
760 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700761 except ( AttributeError, AssertionError, TypeError ):
762 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700763 return None
Jon Halle401b092015-09-23 13:34:24 -0700764 except Exception:
765 main.log.exception( self.name + ": Uncaught exception!" )
766 main.cleanup()
767 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700768
769 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
770 """
771 Description:
772 Gets the information from the given host
773 Required:
774 str mac - MAC address of the host
775 Optional:
776 str vlan - VLAN tag of the host, defaults to -1
777 Returns:
778 Return the host id from the hosts/mac/vlan output in REST api
779 whose 'id' contains mac/vlan; Returns None for exception;
780 Returns main.FALSE if error on requests
781
782 NOTE:
783 Not sure what this function should do, any suggestion?
784 """
785 try:
786 output = None
787 if ip == "DEFAULT":
788 main.log.warn( "No ip given, reverting to ip from topo file" )
789 ip = self.ip_address
790 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700791 main.log.warn( "No port given, reverting to port " +
792 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700793 port = self.port
794 query = "/" + mac + "/" + vlan
795 response = self.send( ip, port, url="/hosts" + query )
796 if response:
797 # NOTE: What if the person wants other values? would it be better
798 # to have a function that gets a key and return a value instead?
799 # This function requires mac and vlan and returns an ID which
800 # makes this current function useless
801 if 200 <= response[ 0 ] <= 299:
802 output = response[ 1 ]
803 hostId = json.loads( output ).get( 'id' )
804 return hostId
805 else:
806 main.log.error( "Error with REST request, response was: " +
807 str( response ) )
808 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700809 except ( AttributeError, TypeError ):
810 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700811 return None
Jon Halle401b092015-09-23 13:34:24 -0700812 except Exception:
813 main.log.exception( self.name + ": Uncaught exception!" )
814 main.cleanup()
815 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700816
817 def topology( self, ip="DEFAULT", port="DEFAULT" ):
818 """
819 Description:
820 Gets the overview of network topology
821 Returns:
822 Returns a dictionary containing information about network topology;
823 Returns None for exception
824 """
825 try:
826 output = None
827 if ip == "DEFAULT":
828 main.log.warn( "No ip given, reverting to ip from topo file" )
829 ip = self.ip_address
830 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -0700831 main.log.warn( "No port given, reverting to port " +
832 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700833 port = self.port
834 response = self.send( ip, port, url="/topology" )
835 if response:
836 if 200 <= response[ 0 ] <= 299:
837 output = response[ 1 ]
838 a = json.loads( output )
839 b = json.dumps( a )
840 return b
841 else:
842 main.log.error( "Error with REST request, response was: " +
843 str( response ) )
844 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -0700845 except ( AttributeError, TypeError ):
846 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700847 return None
Jon Halle401b092015-09-23 13:34:24 -0700848 except Exception:
849 main.log.exception( self.name + ": Uncaught exception!" )
850 main.cleanup()
851 main.exit()
852
853 def devices( self, ip="DEFAULT", port="DEFAULT" ):
854 """
855 Description:
856 Get the devices discovered by ONOS is json string format
857 Returns:
858 a json string of the devices currently discovered by ONOS OR
859 main.FALSE if there is an error in the request OR
860 Returns None for exception
861 """
862 try:
863 output = None
864 if ip == "DEFAULT":
865 main.log.warn( "No ip given, reverting to ip from topo file" )
866 ip = self.ip_address
867 if port == "DEFAULT":
868 main.log.warn( "No port given, reverting to port " +
869 "from topo file" )
870 port = self.port
871 response = self.send( ip, port, url="/devices" )
872 if response:
873 if 200 <= response[ 0 ] <= 299:
874 output = response[ 1 ]
875 a = json.loads( output ).get( 'devices' )
876 assert a is not None, "Error parsing json object"
877 b = json.dumps( a )
878 return b
879 else:
880 main.log.error( "Error with REST request, response was: " +
881 str( response ) )
882 return main.FALSE
883 except ( AttributeError, AssertionError, TypeError ):
884 main.log.exception( self.name + ": Object not as expected" )
885 return None
886 except Exception:
887 main.log.exception( self.name + ": Uncaught exception!" )
888 main.cleanup()
889 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700890
891 def getIntentState( self, intentsId, intentsJson=None,
892 ip="DEFAULT", port="DEFAULT" ):
893 """
894 Description:
895 Get intent state.
896 Accepts a single intent ID (string type) or a list of intent IDs.
897 Returns the state(string type) of the id if a single intent ID is
898 accepted.
899 Required:
900 intentId: intent ID (string type)
901 intentsJson: parsed json object from the onos:intents api
902 Returns:
903 Returns a dictionary with intent IDs as the key and its
904 corresponding states as the values; Returns None for invalid IDs or
905 Type error and any exceptions
906 NOTE:
907 An intent's state consist of INSTALLED,WITHDRAWN etc.
908 """
909 try:
910 state = "State is Undefined"
911 if not intentsJson:
912 intentsJsonTemp = json.loads( self.intents() )
913 else:
914 intentsJsonTemp = json.loads( intentsJson )
915 if isinstance( intentsId, types.StringType ):
916 for intent in intentsJsonTemp:
917 if intentsId == intent[ 'id' ]:
918 state = intent[ 'state' ]
919 return state
920 main.log.info( "Cannot find intent ID" + str( intentsId ) +
921 " on the list" )
922 return state
923 elif isinstance( intentsId, types.ListType ):
924 dictList = []
925 for i in xrange( len( intentsId ) ):
926 stateDict = {}
927 for intents in intentsJsonTemp:
928 if intentsId[ i ] == intents[ 'id' ]:
929 stateDict[ 'state' ] = intents[ 'state' ]
930 stateDict[ 'id' ] = intentsId[ i ]
931 dictList.append( stateDict )
932 break
933 if len( intentsId ) != len( dictList ):
934 main.log.info( "Cannot find some of the intent ID state" )
935 return dictList
936 else:
937 main.log.info( "Invalid intents ID entry" )
938 return None
939
Jon Halle401b092015-09-23 13:34:24 -0700940 except ( AttributeError, TypeError ):
941 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700942 return None
Jon Halle401b092015-09-23 13:34:24 -0700943 except Exception:
944 main.log.exception( self.name + ": Uncaught exception!" )
945 main.cleanup()
946 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700947
948 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
949 ip="DEFAULT", port="DEFAULT"):
950 """
951 Description:
952 Check intents state based on expected state which defaults to
953 INSTALLED state
954 Required:
955 intentsId - List of intents ID to be checked
956 Optional:
957 expectedState - Check the expected state(s) of each intents
958 state in the list.
959 *NOTE: You can pass in a list of expected state,
960 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
961 Return:
962 Returns main.TRUE only if all intent are the same as expected states
963 , otherwise, returns main.FALSE; Returns None for general exception
964 """
965 try:
966 # Generating a dictionary: intent id as a key and state as value
967 returnValue = main.TRUE
968 if intentsId == "ALL":
969 intentsId = self.getIntentsId( ip=ip, port=port )
970 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
971
972 #print "len of intentsDict ", str( len( intentsDict ) )
973 if len( intentsId ) != len( intentsDict ):
974 main.log.error( self.name + ": There is something wrong " +
975 "getting intents state" )
976 return main.FALSE
977
978 if isinstance( expectedState, types.StringType ):
979 for intents in intentsDict:
980 if intents.get( 'state' ) != expectedState:
981 main.log.debug( self.name + " : Intent ID - " +
982 intents.get( 'id' ) +
983 " actual state = " +
984 intents.get( 'state' )
985 + " does not equal expected state = "
986 + expectedState )
987 returnValue = main.FALSE
988
989 elif isinstance( expectedState, types.ListType ):
990 for intents in intentsDict:
991 if not any( state == intents.get( 'state' ) for state in
992 expectedState ):
993 main.log.debug( self.name + " : Intent ID - " +
994 intents.get( 'id' ) +
995 " actual state = " +
996 intents.get( 'state' ) +
997 " does not equal expected states = "
998 + str( expectedState ) )
999 returnValue = main.FALSE
1000
1001 if returnValue == main.TRUE:
1002 main.log.info( self.name + ": All " +
1003 str( len( intentsDict ) ) +
1004 " intents are in " + str( expectedState ) +
1005 " state" )
1006 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001007 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001008 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001009 return None
Jon Halle401b092015-09-23 13:34:24 -07001010 except Exception:
1011 main.log.exception( self.name + ": Uncaught exception!" )
1012 main.cleanup()
1013 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001014
1015 def flows( self, ip="DEFAULT", port="DEFAULT" ):
1016 """
1017 Description:
1018 Get flows currently added to the system
1019 NOTE:
1020 The flows -j cli command has completely different format than
Jon Halle401b092015-09-23 13:34:24 -07001021 the REST output
1022
1023 Returns None for exception
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001024 """
1025 try:
1026 output = None
1027 if ip == "DEFAULT":
1028 main.log.warn( "No ip given, reverting to ip from topo file" )
1029 ip = self.ip_address
1030 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001031 main.log.warn( "No port given, reverting to port " +
1032 "from topo file" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001033 port = self.port
1034 response = self.send( ip, port, url="/flows" )
1035 if response:
1036 if 200 <= response[ 0 ] <= 299:
1037 output = response[ 1 ]
1038 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001039 assert a is not None, "Error parsing json object"
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001040 b = json.dumps( a )
1041 return b
1042 else:
1043 main.log.error( "Error with REST request, response was: " +
1044 str( response ) )
1045 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001046 except ( AttributeError, AssertionError, TypeError ):
1047 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001048 return None
Jon Halle401b092015-09-23 13:34:24 -07001049 except Exception:
1050 main.log.exception( self.name + ": Uncaught exception!" )
1051 main.cleanup()
1052 main.exit()
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001053
Jon Halle401b092015-09-23 13:34:24 -07001054 def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001055 """
1056 Description:
1057 Gets all the flows of the device or get a specific flow in the
1058 device by giving its flow ID
1059 Required:
Jon Halle401b092015-09-23 13:34:24 -07001060 str deviceId - device/switch Id
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001061 Optional:
1062 int/hex flowId - ID of the flow
1063 """
1064 try:
1065 output = None
1066 if ip == "DEFAULT":
1067 main.log.warn( "No ip given, reverting to ip from topo file" )
1068 ip = self.ip_address
1069 if port == "DEFAULT":
Jon Hallf7234882015-08-28 13:16:31 -07001070 main.log.warn( "No port given, reverting to port " +
1071 "from topo file" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001072 port = self.port
Jon Halle401b092015-09-23 13:34:24 -07001073 url = "/flows/" + deviceId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001074 if flowId:
1075 url += "/" + str( int( flowId ) )
1076 print url
1077 response = self.send( ip, port, url=url )
1078 if response:
1079 if 200 <= response[ 0 ] <= 299:
1080 output = response[ 1 ]
1081 a = json.loads( output ).get( 'flows' )
Jon Halle401b092015-09-23 13:34:24 -07001082 assert a is not None, "Error parsing json object"
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001083 b = json.dumps( a )
1084 return b
1085 else:
1086 main.log.error( "Error with REST request, response was: " +
1087 str( response ) )
1088 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001089 except ( AttributeError, AssertionError, TypeError ):
1090 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001091 return None
Jon Halle401b092015-09-23 13:34:24 -07001092 except Exception:
1093 main.log.exception( self.name + ": Uncaught exception!" )
1094 main.cleanup()
1095 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001096
1097 def addFlow( self,
1098 deviceId,
1099 appId=0,
1100 ingressPort="",
1101 egressPort="",
1102 ethType="",
1103 ethSrc="",
1104 ethDst="",
1105 bandwidth="",
1106 lambdaAlloc=False,
1107 ipProto="",
1108 ipSrc="",
1109 ipDst="",
1110 tcpSrc="",
1111 tcpDst="",
1112 ip="DEFAULT",
1113 port="DEFAULT" ):
1114 """
1115 Description:
1116 Creates a single flow in the specified device
1117 Required:
1118 * deviceId: id of the device
1119 Optional:
1120 * ingressPort: port ingress device
1121 * egressPort: port of egress device
1122 * ethType: specify ethType
1123 * ethSrc: specify ethSrc ( i.e. src mac addr )
1124 * ethDst: specify ethDst ( i.e. dst mac addr )
1125 * ipProto: specify ip protocol
1126 * ipSrc: specify ip source address with mask eg. ip#/24
1127 * ipDst: specify ip destination address eg. ip#/24
1128 * tcpSrc: specify tcp source port
1129 * tcpDst: specify tcp destination port
1130 Returns:
1131 Returns main.TRUE for successful requests; Returns main.FALSE
1132 if error on requests;
1133 Returns None for exceptions
1134 NOTE:
1135 The ip and port option are for the requests input's ip and port
1136 of the ONOS node
1137 """
1138 try:
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001139 flowJson = { "priority":100,
1140 "isPermanent":"true",
1141 "timeout":0,
1142 "deviceId":deviceId,
1143 "treatment":{"instructions":[]},
1144 "selector": {"criteria":[]}}
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001145 if appId:
1146 flowJson[ "appId" ] = appId
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001147 if egressPort:
1148 flowJson[ 'treatment' ][ 'instructions' ].append(
1149 { "type":"OUTPUT",
1150 "port":egressPort } )
1151 if ingressPort:
1152 flowJson[ 'selector' ][ 'criteria' ].append(
1153 { "type":"IN_PORT",
1154 "port":ingressPort } )
1155 if ethType == "IPV4":
1156 flowJson[ 'selector' ][ 'criteria' ].append( {
1157 "type":"ETH_TYPE",
1158 "ethType":2048 } )
1159 elif ethType:
1160 flowJson[ 'selector' ][ 'criteria' ].append( {
1161 "type":"ETH_TYPE",
1162 "ethType":ethType } )
1163 if ethSrc:
1164 flowJson[ 'selector' ][ 'criteria' ].append(
1165 { "type":"ETH_SRC",
1166 "mac":ethSrc } )
1167 if ethDst:
1168 flowJson[ 'selector' ][ 'criteria' ].append(
1169 { "type":"ETH_DST",
1170 "mac":ethDst } )
1171 if ipSrc:
1172 flowJson[ 'selector' ][ 'criteria' ].append(
1173 { "type":"IPV4_SRC",
1174 "ip":ipSrc } )
1175 if ipDst:
1176 flowJson[ 'selector' ][ 'criteria' ].append(
1177 { "type":"IPV4_DST",
1178 "ip":ipDst } )
1179 if tcpSrc:
1180 flowJson[ 'selector' ][ 'criteria' ].append(
1181 { "type":"TCP_SRC",
1182 "tcpPort": tcpSrc } )
1183 if tcpDst:
1184 flowJson[ 'selector' ][ 'criteria' ].append(
1185 { "type":"TCP_DST",
1186 "tcpPort": tcpDst } )
1187 if ipProto:
1188 flowJson[ 'selector' ][ 'criteria' ].append(
1189 { "type":"IP_PROTO",
1190 "protocol": ipProto } )
Jon Halle401b092015-09-23 13:34:24 -07001191 if bandwidth or lambdaAlloc:
1192 # TODO: Bandwidth and Lambda will be implemented if needed
1193 raise NotImplementedError
1194 main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001195 output = None
1196 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":
1200 main.log.warn( "No port given, reverting to port " +
1201 "from topo file" )
1202 port = self.port
1203 url = "/flows/" + deviceId
1204 response = self.send( ip,
1205 port,
1206 method="POST",
1207 url=url,
1208 data=json.dumps( flowJson ) )
1209 if response:
1210 if 201:
1211 main.log.info( self.name + ": Successfully POST flow" +
1212 "in device: " + str( deviceId ) )
1213 return main.TRUE
1214 else:
1215 main.log.error( "Error with REST request, response was: " +
1216 str( response ) )
1217 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001218 except NotImplementedError as e:
1219 raise e # Inform the caller
1220 except ( AttributeError, TypeError ):
1221 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001222 return None
Jon Halle401b092015-09-23 13:34:24 -07001223 except Exception:
1224 main.log.exception( self.name + ": Uncaught exception!" )
1225 main.cleanup()
1226 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001227
1228 def removeFlow( self, deviceId, flowId,
1229 ip="DEFAULT", port="DEFAULT" ):
1230 """
1231 Description:
1232 Remove specific device flow
1233 Required:
1234 str deviceId - id of the device
1235 str flowId - id of the flow
1236 Return:
1237 Returns main.TRUE if successfully deletes flows, otherwise
1238 Returns main.FALSE, Returns None on error
1239 """
1240 try:
1241 output = None
1242 if ip == "DEFAULT":
1243 main.log.warn( "No ip given, reverting to ip from topo file" )
1244 ip = self.ip_address
1245 if port == "DEFAULT":
1246 main.log.warn( "No port given, reverting to port " +
1247 "from topo file" )
1248 port = self.port
1249 # NOTE: REST url requires the intent id to be in decimal form
1250 query = "/" + str( deviceId ) + "/" + str( int( flowId ) )
1251 response = self.send( ip,
1252 port,
1253 method="DELETE",
1254 url="/flows" + query )
1255 if response:
1256 if 200 <= response[ 0 ] <= 299:
1257 return main.TRUE
1258 else:
1259 main.log.error( "Error with REST request, response was: " +
1260 str( response ) )
1261 return main.FALSE
Jon Halle401b092015-09-23 13:34:24 -07001262 except ( AttributeError, TypeError ):
1263 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001264 return None
Jon Halle401b092015-09-23 13:34:24 -07001265 except Exception:
1266 main.log.exception( self.name + ": Uncaught exception!" )
1267 main.cleanup()
1268 main.exit()
kelvin-onlab9b42b0a2015-08-05 14:43:58 -07001269
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001270 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1271 """
1272 Description:
1273 Check if all the current flows are in ADDED state
1274 Return:
1275 returnValue - Returns main.TRUE only if all flows are in
1276 return main.FALSE otherwise;
1277 Returns None for exception
1278 """
1279 try:
1280 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
1281 returnValue = main.TRUE
1282 for flow in tempFlows:
1283 if flow.get( 'state' ) != 'ADDED':
1284 main.log.info( self.name + ": flow Id: " +
1285 str( flow.get( 'groupId' ) ) +
1286 " | state:" +
1287 str( flow.get( 'state' ) ) )
1288 returnValue = main.FALSE
1289 return returnValue
Jon Halle401b092015-09-23 13:34:24 -07001290 except ( AttributeError, TypeError ):
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001291 main.log.exception( self.name + ": Object not as expected" )
kelvin-onlab03eb88d2015-07-22 10:29:02 -07001292 return None
1293 except Exception:
1294 main.log.exception( self.name + ": Uncaught exception!" )
1295 main.cleanup()
1296 main.exit()