blob: d7e0572c5eb6f290fef438a3716ad8de744e8108 [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 Hallfc915882015-07-14 13:33:17 -070023
Jon Hallfc915882015-07-14 13:33:17 -070024from drivers.common.api.controllerdriver import Controller
25
26
27class OnosRestDriver( Controller ):
28
29 def __init__( self ):
30 super( Controller, self ).__init__()
31 self.ip_address = "localhost"
32 self.port = "8080"
33 self.user_name = "user"
34 self.password = "CHANGEME"
35
36 def connect( self, **connectargs ):
37 try:
38 for key in connectargs:
39 vars( self )[ key ] = connectargs[ key ]
40 self.name = self.options[ 'name' ]
41 except Exception as e:
42 main.log.exception( e )
43 try:
44 if os.getenv( str( self.ip_address ) ) != None:
45 self.ip_address = os.getenv( str( self.ip_address ) )
46 else:
kelvin-onlab03eb88d2015-07-22 10:29:02 -070047 main.log.info( self.name + ": ip set to " + self.ip_address )
Jon Hallfc915882015-07-14 13:33:17 -070048 except KeyError:
49 main.log.info( "Invalid host name," +
50 "defaulting to 'localhost' instead" )
51 self.ip_address = 'localhost'
52 except Exception as inst:
53 main.log.error( "Uncaught exception: " + str( inst ) )
54
55 self.handle = super( OnosRestDriver, self ).connect()
56 return self.handle
57
58 def send( self, ip, port, url, base="/onos/v1", method="GET",
59 query=None, data=None ):
60 """
61 Arguments:
62 str ip: ONOS IP Address
63 str port: ONOS REST Port
64 str url: ONOS REST url path.
65 NOTE that this is is only the relative path. IE "/devices"
66 str base: The base url for the given REST api. Applications could
67 potentially have their own base url
68 str method: HTTP method type
kelvin-onlab03eb88d2015-07-22 10:29:02 -070069 dict query: Dictionary to be sent in the query string for
Jon Hallfc915882015-07-14 13:33:17 -070070 the request
71 dict data: Dictionary to be sent in the body of the request
72 """
73 # TODO: Authentication - simple http (user,pass) tuple
74 # TODO: should we maybe just pass kwargs straight to response?
75 # TODO: Do we need to allow for other protocols besides http?
76 # ANSWER: Not yet, but potentially https with certificates
77 try:
78 path = "http://" + str( ip ) + ":" + str( port ) + base + url
79 main.log.info( "Sending request " + path + " using " +
80 method.upper() + " method." )
81 response = requests.request( method.upper(),
82 path,
83 params=query,
84 data=data )
85 return ( response.status_code, response.text.encode( 'utf8' ) )
86 except requests.exceptions:
87 main.log.exception( "Error sending request." )
88 return None
89 except Exception as e:
90 main.log.exception( e )
91 return None
kelvin-onlab03eb88d2015-07-22 10:29:02 -070092 # FIXME: add other exceptions
Jon Hallfc915882015-07-14 13:33:17 -070093
94 def intents( self, ip="DEFAULT", port="DEFAULT" ):
95 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -070096 Description:
97 Gets a list of dictionary of all intents in the system
98 Returns:
99 A list of dictionary of intents in string type to match the cli
100 version for now; Returns main.FALSE if error on request;
101 Returns None for exception
Jon Hallfc915882015-07-14 13:33:17 -0700102 """
103 try:
104 output = None
105 if ip == "DEFAULT":
106 main.log.warn( "No ip given, reverting to ip from topo file" )
107 ip = self.ip_address
108 if port == "DEFAULT":
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700109 main.log.warn( "No port given, reverting to port " +
110 "from topo file" )
Jon Hallfc915882015-07-14 13:33:17 -0700111 port = self.port
112 response = self.send( ip, port, url="/intents" )
113 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700114 if 200 <= response[ 0 ] <= 299:
115 output = response[ 1 ]
116 a = json.loads( output ).get( 'intents' )
117 b = json.dumps( a )
118 return b
Jon Hallfc915882015-07-14 13:33:17 -0700119 else:
120 main.log.error( "Error with REST request, response was: " +
121 str( response ) )
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700122 return main.FALSE
Jon Hallfc915882015-07-14 13:33:17 -0700123 except Exception as e:
124 main.log.exception( e )
125 return None
126
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700127 def intent( self, intentId, appId="org.onosproject.cli",
128 ip="DEFAULT", port="DEFAULT" ):
Jon Hallfc915882015-07-14 13:33:17 -0700129 """
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700130 Description:
131 Get the specific intent information of the given application ID and
132 intent ID
133 Required:
134 str intentId - Intent id in hexadecimal form
135 Optional:
136 str appId - application id of intent
137 Returns:
138 Returns an information dictionary of the given intent;
139 Returns main.FALSE if error on requests; Returns None for exception
140 NOTE:
141 The GET /intents REST api command accepts application id but the
142 api will get updated to accept application name instead
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
153 # NOTE: REST url requires the intent id to be in decimal form
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700154 query = "/" + str( appId ) + "/" + str( intentId )
Jon Hallfc915882015-07-14 13:33:17 -0700155 response = self.send( ip, port, url="/intents" + query )
156 if response:
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700157 if 200 <= response[ 0 ] <= 299:
158 output = response[ 1 ]
159 a = json.loads( output )
160 return a
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 Hallfc915882015-07-14 13:33:17 -0700165 except Exception as e:
166 main.log.exception( e )
167 return None
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700168
169 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
170 """
171 Description:
172 Gets all intents ID using intents function
173 Returns:
174 List of intents ID;Returns None for exception; Returns None for
175 exception; Returns None for exception
176 """
177 try:
178 intentsDict = {}
179 intentsIdList = []
180 intentsDict = json.loads( self.intents( ip=ip, port=port ) )
181 for intent in intentsDict:
182 intentsIdList.append( intent.get( 'id' ) )
183
184 if not intentsIdList:
185 main.log.debug( "Cannot find any intents" )
186 return main.FALSE
187 else:
188 main.log.info( "Found intents: " + str( intentsIdList ) )
189 return main.TRUE
190
191 except Exception as e:
192 main.log.exception( e )
193 return None
194
195
196 def apps( self, ip="DEFAULT", port="DEFAULT" ):
197 """
198 Description:
199 Returns all the current application installed in the system
200 Returns:
201 List of dictionary of installed application; Returns main.FALSE for
202 error on request; Returns None for exception
203 """
204 try:
205 output = None
206 if ip == "DEFAULT":
207 main.log.warn( "No ip given, reverting to ip from topo file" )
208 ip = self.ip_address
209 if port == "DEFAULT":
210 main.log.warn( "No port given, reverting to port \
211 from topo file" )
212 port = self.port
213 response = self.send( ip, port, url="/applications" )
214 if response:
215 if 200 <= response[ 0 ] <= 299:
216 output = response[ 1 ]
217 a = json.loads( output ).get( 'applications' )
218 b = json.dumps( a )
219 return b
220 else:
221 main.log.error( "Error with REST request, response was: " +
222 str( response ) )
223 return main.FALSE
224 except Exception as e:
225 main.log.exception( e )
226 return None
227
228 def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
229 """
230 Decription:
231 Activate an app that is already installed in ONOS
232 Optional:
233 bool check - If check is True, method will check the status
234 of the app after the command is issued
235 Returns:
236 Returns main.TRUE if the command was successfully or main.FALSE
237 if the REST responded with an error or given incorrect input;
238 Returns None for exception
239
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":
247 main.log.warn( "No port given, reverting to port" +
248 "from topo file" )
249 port = self.port
250 query = "/" + str( appName ) + "/active"
251 response = self.send( ip, port, method="POST",
252 url="/applications" + query )
253 if response:
254 output = response[ 1 ]
255 app = json.loads( output )
256 if 200 <= response[ 0 ] <= 299:
257 if check:
258 if app.get( 'state' ) == 'ACTIVE':
259 main.log.info( self.name + ": " + appName +
260 " application" +
261 " is in ACTIVE state" )
262 return main.TRUE
263 else:
264 main.log.error( self.name + ": " + appName +
265 " application" + " is in " +
266 app.get( 'state' ) + " state" )
267 return main.FALSE
268 else:
269 main.log.warn( "Skipping " + appName +
270 "application check" )
271 return main.TRUE
272 else:
273 main.log.error( "Error with REST request, response was: " +
274 str( response ) )
275 return main.FALSE
276 except Exception as e:
277 main.log.exception( e )
278 return None
279
280 def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
281 check=True ):
282 """
283 Required:
284 Deactivate an app that is already activated in ONOS
285 Optional:
286 bool check - If check is True, method will check the status of the
287 app after the command is issued
288 Returns:
289 Returns main.TRUE if the command was successfully sent
290 main.FALSE if the REST responded with an error or given
291 incorrect input; Returns None for exception
292 """
293 try:
294 output = None
295 if ip == "DEFAULT":
296 main.log.warn( "No ip given, reverting to ip from topo file" )
297 ip = self.ip_address
298 if port == "DEFAULT":
299 main.log.warn( "No port given, reverting to port" +
300 "from topo file" )
301 port = self.port
302 query = "/" + str( appName ) + "/active"
303 response = self.send( ip, port, method="DELETE",
304 url="/applications" + query )
305 if response:
306 output = response[ 1 ]
307 app = json.loads( output )
308 if 200 <= response[ 0 ] <= 299:
309 if check:
310 if app.get( 'state' ) == 'INSTALLED':
311 main.log.info( self.name + ": " + appName +
312 " application" +
313 " is in INSTALLED state" )
314 return main.TRUE
315 else:
316 main.log.error( self.name + ": " + appName +
317 " application" + " is in " +
318 app.get( 'state' ) + " state" )
319 return main.FALSE
320 else:
321 main.log.warn( "Skipping " + appName +
322 "application check" )
323 return main.TRUE
324 else:
325 main.log.error( "Error with REST request, response was: " +
326 str( response ) )
327 return main.FALSE
328 except Exception as e:
329 main.log.exception( e )
330 return None
331
332 def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
333 port="DEFAULT" ):
334 """
335 Decription:
336 Gets the informaion of the given application
337 Required:
338 str name - Name of onos application
339 Returns:
340 Returns a dictionary of information ONOS application in string type;
341 Returns main.FALSE if error on requests; Returns None for exception
342 """
343 try:
344 output = None
345 if ip == "DEFAULT":
346 main.log.warn( "No ip given, reverting to ip from topo file" )
347 ip = self.ip_address
348 if port == "DEFAULT":
349 main.log.warn( "No port given, reverting to port" +
350 "from topo file" )
351 port = self.port
352 query = "/" + project + str( appName )
353 response = self.send( ip, port, url="/applications" + query )
354 if response:
355 if 200 <= response[ 0 ] <= 299:
356 output = response[ 1 ]
357 a = json.loads( output )
358 return a
359 else:
360 main.log.error( "Error with REST request, response was: " +
361 str( response ) )
362 return main.FALSE
363 except Exception as e:
364 main.log.exception( e )
365 return None
366
367 def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
368 ip="DEFAULT", port="DEFAULT" ):
369 """
370 Description:
371 Adds a host-to-host intent ( bidirectional ) by
372 specifying the two hosts.
373 Required:
374 * hostIdOne: ONOS host id for host1
375 * hostIdTwo: ONOS host id for host2
376 Optional:
377 str appId - Application name of intent identifier
378 Returns:
kelvin-onlabb50074f2015-07-27 16:18:32 -0700379 Returns main.TRUE for successful requests; Returns main.FALSE if
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700380 error on requests; Returns None for exceptions
381 """
382 try:
383 intentJson = {"two": str( hostIdTwo ),
384 "selector": {"criteria": []}, "priority": 7,
385 "treatment": {"deferred": [], "instructions": []},
386 "appId": appId, "one": str( hostIdOne ),
387 "type": "HostToHostIntent",
388 "constraints": [{"type": "LinkTypeConstraint",
389 "types": ["OPTICAL"],
390 "inclusive": 'false' }]}
391 output = None
392 if ip == "DEFAULT":
393 main.log.warn( "No ip given, reverting to ip from topo file" )
394 ip = self.ip_address
395 if port == "DEFAULT":
396 main.log.warn( "No port given, reverting to port " +
397 "from topo file" )
398 port = self.port
399 response = self.send( ip,
400 port,
401 method="POST",
402 url="/intents",
403 data=json.dumps( intentJson ) )
404 if response:
405 if 201:
406 main.log.info( self.name + ": Successfully POST host" +
407 " intent between host: " + hostIdOne +
408 " and host: " + hostIdTwo )
409 return main.TRUE
410 else:
411 main.log.error( "Error with REST request, response was: " +
412 str( response ) )
413 return main.FALSE
414
415 except Exception as e:
416 main.log.exception( e )
417 return None
418
kelvin-onlabb50074f2015-07-27 16:18:32 -0700419 def addPointIntent( self,
420 ingressDevice,
421 egressDevice,
422 ip="DEFAULT",
423 port="DEFAULT",
424 appId='org.onosproject.cli',
425 ingressPort="",
426 egressPort="",
427 ethType="",
428 ethSrc="",
429 ethDst="",
430 bandwidth="",
431 lambdaAlloc=False,
432 ipProto="",
433 ipSrc="",
434 ipDst="",
435 tcpSrc="",
436 tcpDst="" ):
437 """
438 Description:
439 Adds a point-to-point intent ( uni-directional ) by
440 specifying device id's and optional fields
441 Required:
442 * ingressDevice: device id of ingress device
443 * egressDevice: device id of egress device
444 Optional:
445 * ethType: specify ethType
446 * ethSrc: specify ethSrc ( i.e. src mac addr )
447 * ethDst: specify ethDst ( i.e. dst mac addr )
448 * bandwidth: specify bandwidth capacity of link (TODO)
449 * lambdaAlloc: if True, intent will allocate lambda
450 for the specified intent (TODO)
451 * ipProto: specify ip protocol
452 * ipSrc: specify ip source address with mask eg. ip#/24
453 * ipDst: specify ip destination address eg. ip#/24
454 * tcpSrc: specify tcp source port
455 * tcpDst: specify tcp destination port
456 Returns:
457 Returns main.TRUE for successful requests; Returns main.FALSE if
458 no ingress|egress port found and if error on requests;
459 Returns None for exceptions
460 NOTE:
461 The ip and port option are for the requests input's ip and port
462 of the ONOS node
463 """
464 try:
465 if "/" in ingressDevice:
466 if not ingressPort:
467 ingressPort = ingressDevice.split( "/" )[ 1 ]
468 ingressDevice = ingressDevice.split( "/" )[ 0 ]
469 else:
470 if not ingressPort:
471 main.log.debug( self.name + ": Ingress port not specified" )
472 return main.FALSE
473
474 if "/" in egressDevice:
475 if not egressPort:
476 egressPort = egressDevice.split( "/" )[ 1 ]
477 egressDevice = egressDevice.split( "/" )[ 0 ]
478 else:
479 if not egressPort:
480 main.log.debug( self.name + ": Egress port not specified" )
481 return main.FALSE
482
483 intentJson ={ "ingressPoint": { "device": ingressDevice,
484 "port": ingressPort },
485 "selector": { "criteria": [] },
486 "priority": 55,
487 "treatment": { "deferred": [],
488 "instructions": [] },
489 "egressPoint": { "device": egressDevice,
490 "port": egressPort },
491 "appId": appId,
492 "type": "PointToPointIntent",
493 "constraints": [ { "type": "LinkTypeConstraint",
494 "types": [ "OPTICAL" ],
495 "inclusive": "false" } ] }
496
497 if ethType == "IPV4":
498 intentJson[ 'selector' ][ 'criteria' ].append( {
499 "type":"ETH_TYPE",
500 "ethType":2048 } )
501 if ethSrc:
502 intentJson[ 'selector' ][ 'criteria' ].append(
503 { "type":"ETH_SRC",
504 "mac":ethSrc } )
505 if ethDst:
506 intentJson[ 'selector' ][ 'criteria' ].append(
507 { "type":"ETH_DST",
508 "mac":ethDst } )
509 if ipSrc:
510 intentJson[ 'selector' ][ 'criteria' ].append(
511 { "type":"IPV4_SRC",
512 "ip":ipSrc } )
513 if ipDst:
514 intentJson[ 'selector' ][ 'criteria' ].append(
515 { "type":"IPV4_DST",
516 "ip":ipDst } )
517 if tcpSrc:
518 intentJson[ 'selector' ][ 'criteria' ].append(
519 { "type":"TCP_SRC",
520 "tcpPort": tcpSrc } )
521 if tcpDst:
522 intentJson[ 'selector' ][ 'criteria' ].append(
523 { "type":"TCP_DST",
524 "tcpPort": tcpDst } )
525 if ipProto:
526 intentJson[ 'selector' ][ 'criteria' ].append(
527 { "type":"IP_PROTO",
528 "protocol": ipProto } )
529
530 # TODO: Bandwidth and Lambda will be implemented if needed
531
532 main.log.debug( intentJson )
533
534 output = None
535 if ip == "DEFAULT":
536 main.log.warn( "No ip given, reverting to ip from topo file" )
537 ip = self.ip_address
538 if port == "DEFAULT":
539 main.log.warn( "No port given, reverting to port " +
540 "from topo file" )
541 port = self.port
542 response = self.send( ip,
543 port,
544 method="POST",
545 url="/intents",
546 data=json.dumps( intentJson ) )
547 if response:
548 if 201:
549 main.log.info( self.name + ": Successfully POST point" +
550 " intent between ingress: " + ingressDevice +
551 " and egress: " + egressDevice + " devices" )
552 return main.TRUE
553 else:
554 main.log.error( "Error with REST request, response was: " +
555 str( response ) )
556 return main.FALSE
557
558 except Exception as e:
559 main.log.exception( e )
560 return None
561
kelvin-onlab03eb88d2015-07-22 10:29:02 -0700562
563 def removeIntent( self, intentId, appId='org.onosproject.cli',
564 ip="DEFAULT", port="DEFAULT" ):
565 """
566 Remove intent for specified application id and intent id;
567 Returns None for exception
568 """
569 try:
570 output = None
571 if ip == "DEFAULT":
572 main.log.warn( "No ip given, reverting to ip from topo file" )
573 ip = self.ip_address
574 if port == "DEFAULT":
575 main.log.warn( "No port given, reverting to port " +
576 "from topo file" )
577 port = self.port
578 # NOTE: REST url requires the intent id to be in decimal form
579 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
580 response = self.send( ip,
581 port,
582 method="DELETE",
583 url="/intents" + query )
584 if response:
585 if 200 <= response[ 0 ] <= 299:
586 return main.TRUE
587 else:
588 main.log.error( "Error with REST request, response was: " +
589 str( response ) )
590 return main.FALSE
591 except Exception as e:
592 main.log.exception( e )
593 return None
594
595 def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
596 """
597 Returns a list of intents id; Returns None for exception
598 """
599 try:
600 intentIdList = []
601 intentsJson = json.loads( self.intents() )
602 print intentsJson
603 for intent in intentsJson:
604 intentIdList.append( intent.get( 'id' ) )
605 print intentIdList
606 return intentIdList
607 except Exception as e:
608 main.log.exception( e )
609 return None
610
611 def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
612 ip="DEFAULT", port="DEFAULT", delay=5 ):
613 """
614 Description:
615 Remove all the intents
616 Returns:
617 Returns main.TRUE if all intents are removed, otherwise returns
618 main.FALSE; Returns None for exception
619 """
620 try:
621 results = []
622 if intentIdList == 'ALL':
623 intentIdList = self.getIntentsId( ip=ip, port=port )
624
625 main.log.info( self.name + ": Removing intents " +
626 str( intentIdList ) )
627
628 if isinstance( intentIdList, types.ListType ):
629 for intent in intentIdList:
630 results.append( self.removeIntent( intentId=intent,
631 appId=appId,
632 ip=ip,
633 port=port ) )
634 # Check for remaining intents
635 # NOTE: Noticing some delay on Deleting the intents so i put
636 # this time out
637 import time
638 time.sleep( delay )
639 intentRemain = len( json.loads( self.intents() ) )
640 if all( result==main.TRUE for result in results ) and \
641 intentRemain == 0:
642 main.log.info( self.name + ": All intents are removed " )
643 return main.TRUE
644 else:
645 main.log.error( self.name + ": Did not removed all intents,"
646 + " there are " + str( intentRemain )
647 + " intents remaining" )
648 return main.FALSE
649 else:
650 main.log.debug( self.name + ": There is no intents ID list" )
651 except Exception as e:
652 main.log.exception( e )
653 return None
654
655
656 def hosts( self, ip="DEFAULT", port="DEFAULT" ):
657 """
658 Description:
659 Get a list of dictionary of all discovered hosts
660 Returns:
661 Returns a list of dictionary of information of the hosts currently
662 discovered by ONOS; Returns main.FALSE if error on requests;
663 Returns None for exception
664 """
665 try:
666 output = None
667 if ip == "DEFAULT":
668 main.log.warn( "No ip given, reverting to ip from topo file" )
669 ip = self.ip_address
670 if port == "DEFAULT":
671 main.log.warn( "No port given, reverting to port \
672 from topo file" )
673 port = self.port
674 response = self.send( ip, port, url="/hosts" )
675 if response:
676 if 200 <= response[ 0 ] <= 299:
677 output = response[ 1 ]
678 a = json.loads( output ).get( 'hosts' )
679 b = json.dumps( a )
680 return b
681 else:
682 main.log.error( "Error with REST request, response was: " +
683 str( response ) )
684 return main.FALSE
685 except Exception as e:
686 main.log.exception( e )
687 return None
688
689 def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
690 """
691 Description:
692 Gets the information from the given host
693 Required:
694 str mac - MAC address of the host
695 Optional:
696 str vlan - VLAN tag of the host, defaults to -1
697 Returns:
698 Return the host id from the hosts/mac/vlan output in REST api
699 whose 'id' contains mac/vlan; Returns None for exception;
700 Returns main.FALSE if error on requests
701
702 NOTE:
703 Not sure what this function should do, any suggestion?
704 """
705 try:
706 output = None
707 if ip == "DEFAULT":
708 main.log.warn( "No ip given, reverting to ip from topo file" )
709 ip = self.ip_address
710 if port == "DEFAULT":
711 main.log.warn( "No port given, reverting to port \
712 from topo file" )
713 port = self.port
714 query = "/" + mac + "/" + vlan
715 response = self.send( ip, port, url="/hosts" + query )
716 if response:
717 # NOTE: What if the person wants other values? would it be better
718 # to have a function that gets a key and return a value instead?
719 # This function requires mac and vlan and returns an ID which
720 # makes this current function useless
721 if 200 <= response[ 0 ] <= 299:
722 output = response[ 1 ]
723 hostId = json.loads( output ).get( 'id' )
724 return hostId
725 else:
726 main.log.error( "Error with REST request, response was: " +
727 str( response ) )
728 return main.FALSE
729 except Exception as e:
730 main.log.exception( e )
731 return None
732
733 def topology( self, ip="DEFAULT", port="DEFAULT" ):
734 """
735 Description:
736 Gets the overview of network topology
737 Returns:
738 Returns a dictionary containing information about network topology;
739 Returns None for exception
740 """
741 try:
742 output = None
743 if ip == "DEFAULT":
744 main.log.warn( "No ip given, reverting to ip from topo file" )
745 ip = self.ip_address
746 if port == "DEFAULT":
747 main.log.warn( "No port given, reverting to port \
748 from topo file" )
749 port = self.port
750 response = self.send( ip, port, url="/topology" )
751 if response:
752 if 200 <= response[ 0 ] <= 299:
753 output = response[ 1 ]
754 a = json.loads( output )
755 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
761 except Exception as e:
762 main.log.exception( e )
763 return None
764
765 def getIntentState( self, intentsId, intentsJson=None,
766 ip="DEFAULT", port="DEFAULT" ):
767 """
768 Description:
769 Get intent state.
770 Accepts a single intent ID (string type) or a list of intent IDs.
771 Returns the state(string type) of the id if a single intent ID is
772 accepted.
773 Required:
774 intentId: intent ID (string type)
775 intentsJson: parsed json object from the onos:intents api
776 Returns:
777 Returns a dictionary with intent IDs as the key and its
778 corresponding states as the values; Returns None for invalid IDs or
779 Type error and any exceptions
780 NOTE:
781 An intent's state consist of INSTALLED,WITHDRAWN etc.
782 """
783 try:
784 state = "State is Undefined"
785 if not intentsJson:
786 intentsJsonTemp = json.loads( self.intents() )
787 else:
788 intentsJsonTemp = json.loads( intentsJson )
789 if isinstance( intentsId, types.StringType ):
790 for intent in intentsJsonTemp:
791 if intentsId == intent[ 'id' ]:
792 state = intent[ 'state' ]
793 return state
794 main.log.info( "Cannot find intent ID" + str( intentsId ) +
795 " on the list" )
796 return state
797 elif isinstance( intentsId, types.ListType ):
798 dictList = []
799 for i in xrange( len( intentsId ) ):
800 stateDict = {}
801 for intents in intentsJsonTemp:
802 if intentsId[ i ] == intents[ 'id' ]:
803 stateDict[ 'state' ] = intents[ 'state' ]
804 stateDict[ 'id' ] = intentsId[ i ]
805 dictList.append( stateDict )
806 break
807 if len( intentsId ) != len( dictList ):
808 main.log.info( "Cannot find some of the intent ID state" )
809 return dictList
810 else:
811 main.log.info( "Invalid intents ID entry" )
812 return None
813
814 except TypeError:
815 main.log.exception( self.name + ": Object Type not as expected" )
816 return None
817 except Exception as e:
818 main.log.exception( e )
819 return None
820
821 def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
822 ip="DEFAULT", port="DEFAULT"):
823 """
824 Description:
825 Check intents state based on expected state which defaults to
826 INSTALLED state
827 Required:
828 intentsId - List of intents ID to be checked
829 Optional:
830 expectedState - Check the expected state(s) of each intents
831 state in the list.
832 *NOTE: You can pass in a list of expected state,
833 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
834 Return:
835 Returns main.TRUE only if all intent are the same as expected states
836 , otherwise, returns main.FALSE; Returns None for general exception
837 """
838 try:
839 # Generating a dictionary: intent id as a key and state as value
840 returnValue = main.TRUE
841 if intentsId == "ALL":
842 intentsId = self.getIntentsId( ip=ip, port=port )
843 intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
844
845 #print "len of intentsDict ", str( len( intentsDict ) )
846 if len( intentsId ) != len( intentsDict ):
847 main.log.error( self.name + ": There is something wrong " +
848 "getting intents state" )
849 return main.FALSE
850
851 if isinstance( expectedState, types.StringType ):
852 for intents in intentsDict:
853 if intents.get( 'state' ) != expectedState:
854 main.log.debug( self.name + " : Intent ID - " +
855 intents.get( 'id' ) +
856 " actual state = " +
857 intents.get( 'state' )
858 + " does not equal expected state = "
859 + expectedState )
860 returnValue = main.FALSE
861
862 elif isinstance( expectedState, types.ListType ):
863 for intents in intentsDict:
864 if not any( state == intents.get( 'state' ) for state in
865 expectedState ):
866 main.log.debug( self.name + " : Intent ID - " +
867 intents.get( 'id' ) +
868 " actual state = " +
869 intents.get( 'state' ) +
870 " does not equal expected states = "
871 + str( expectedState ) )
872 returnValue = main.FALSE
873
874 if returnValue == main.TRUE:
875 main.log.info( self.name + ": All " +
876 str( len( intentsDict ) ) +
877 " intents are in " + str( expectedState ) +
878 " state" )
879 return returnValue
880 except TypeError:
881 main.log.exception( self.name + ": Object not as expected" )
882 return main.FALSE
883 except Exception as e:
884 main.log.exception( e )
885 return None
886
887 def flows( self, ip="DEFAULT", port="DEFAULT" ):
888 """
889 Description:
890 Get flows currently added to the system
891 NOTE:
892 The flows -j cli command has completely different format than
893 the REST output; Returns None for exception
894 """
895 try:
896 output = None
897 if ip == "DEFAULT":
898 main.log.warn( "No ip given, reverting to ip from topo file" )
899 ip = self.ip_address
900 if port == "DEFAULT":
901 main.log.warn( "No port given, reverting to port \
902 from topo file" )
903 port = self.port
904 response = self.send( ip, port, url="/flows" )
905 if response:
906 if 200 <= response[ 0 ] <= 299:
907 output = response[ 1 ]
908 a = json.loads( output ).get( 'flows' )
909 b = json.dumps( a )
910 return b
911 else:
912 main.log.error( "Error with REST request, response was: " +
913 str( response ) )
914 return main.FALSE
915 except Exception as e:
916 main.log.exception( e )
917 return None
918
919 def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
920 """
921 Description:
922 Check if all the current flows are in ADDED state
923 Return:
924 returnValue - Returns main.TRUE only if all flows are in
925 return main.FALSE otherwise;
926 Returns None for exception
927 """
928 try:
929 tempFlows = json.loads( self.flows( ip=ip, port=port ) )
930 returnValue = main.TRUE
931 for flow in tempFlows:
932 if flow.get( 'state' ) != 'ADDED':
933 main.log.info( self.name + ": flow Id: " +
934 str( flow.get( 'groupId' ) ) +
935 " | state:" +
936 str( flow.get( 'state' ) ) )
937 returnValue = main.FALSE
938 return returnValue
939 except TypeError:
940 main.log.exception( self.name + ": Object not as expected" )
941 return main.FALSE
942 except Exception as e:
943 main.log.exception( e )
944 return None
945 except Exception:
946 main.log.exception( self.name + ": Uncaught exception!" )
947 main.cleanup()
948 main.exit()