Package TestON :: Package drivers :: Package common :: Package api :: Package controller :: Module onosrestdriver
[hide private]
[frames] | no frames]

Source Code for Module TestON.drivers.common.api.controller.onosrestdriver

   1  #!/usr/bin/env python 
   2  """ 
   3  Created 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  """ 
  19  import json 
  20  import os 
  21  import requests 
  22  import types 
  23   
  24  from drivers.common.api.controllerdriver import Controller 
  25   
  26   
27 -class 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: 47 main.log.info( self.name + ": ip set to " + self.ip_address ) 48 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 69 dict query: Dictionary to be sent in the query string for 70 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
92 # FIXME: add other exceptions 93
94 - def intents( self, ip="DEFAULT", port="DEFAULT" ):
95 """ 96 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 102 """ 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": 109 main.log.warn( "No port given, reverting to port " + 110 "from topo file" ) 111 port = self.port 112 response = self.send( ip, port, url="/intents" ) 113 if response: 114 if 200 <= response[ 0 ] <= 299: 115 output = response[ 1 ] 116 a = json.loads( output ).get( 'intents' ) 117 b = json.dumps( a ) 118 return b 119 else: 120 main.log.error( "Error with REST request, response was: " + 121 str( response ) ) 122 return main.FALSE 123 except Exception as e: 124 main.log.exception( e ) 125 return None
126
127 - def intent( self, intentId, appId="org.onosproject.cli", 128 ip="DEFAULT", port="DEFAULT" ):
129 """ 130 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 143 """ 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": 150 main.log.warn( "No port given, reverting to port" + 151 "from topo file" ) 152 port = self.port 153 # NOTE: REST url requires the intent id to be in decimal form 154 query = "/" + str( appId ) + "/" + str( intentId ) 155 response = self.send( ip, port, url="/intents" + query ) 156 if response: 157 if 200 <= response[ 0 ] <= 299: 158 output = response[ 1 ] 159 a = json.loads( output ) 160 return a 161 else: 162 main.log.error( "Error with REST request, response was: " + 163 str( response ) ) 164 return main.FALSE 165 except Exception as e: 166 main.log.exception( e ) 167 return None
168
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: 379 Returns main.TRUE for successful requests; Returns main.FALSE if 380 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
419 - def addPointIntent( self, 420 ingressDevice, 421 egressDevice, 422 appId='org.onosproject.cli', 423 ingressPort="", 424 egressPort="", 425 ethType="", 426 ethSrc="", 427 ethDst="", 428 bandwidth="", 429 lambdaAlloc=False, 430 ipProto="", 431 ipSrc="", 432 ipDst="", 433 tcpSrc="", 434 tcpDst="", 435 ip="DEFAULT", 436 port="DEFAULT" ):
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 elif ethType: 502 intentJson[ 'selector' ][ 'criteria' ].append( { 503 "type":"ETH_TYPE", 504 "ethType":ethType } ) 505 506 if ethSrc: 507 intentJson[ 'selector' ][ 'criteria' ].append( 508 { "type":"ETH_SRC", 509 "mac":ethSrc } ) 510 if ethDst: 511 intentJson[ 'selector' ][ 'criteria' ].append( 512 { "type":"ETH_DST", 513 "mac":ethDst } ) 514 if ipSrc: 515 intentJson[ 'selector' ][ 'criteria' ].append( 516 { "type":"IPV4_SRC", 517 "ip":ipSrc } ) 518 if ipDst: 519 intentJson[ 'selector' ][ 'criteria' ].append( 520 { "type":"IPV4_DST", 521 "ip":ipDst } ) 522 if tcpSrc: 523 intentJson[ 'selector' ][ 'criteria' ].append( 524 { "type":"TCP_SRC", 525 "tcpPort": tcpSrc } ) 526 if tcpDst: 527 intentJson[ 'selector' ][ 'criteria' ].append( 528 { "type":"TCP_DST", 529 "tcpPort": tcpDst } ) 530 if ipProto: 531 intentJson[ 'selector' ][ 'criteria' ].append( 532 { "type":"IP_PROTO", 533 "protocol": ipProto } ) 534 535 # TODO: Bandwidth and Lambda will be implemented if needed 536 537 main.log.debug( intentJson ) 538 539 output = None 540 if ip == "DEFAULT": 541 main.log.warn( "No ip given, reverting to ip from topo file" ) 542 ip = self.ip_address 543 if port == "DEFAULT": 544 main.log.warn( "No port given, reverting to port " + 545 "from topo file" ) 546 port = self.port 547 response = self.send( ip, 548 port, 549 method="POST", 550 url="/intents", 551 data=json.dumps( intentJson ) ) 552 if response: 553 if 201: 554 main.log.info( self.name + ": Successfully POST point" + 555 " intent between ingress: " + ingressDevice + 556 " and egress: " + egressDevice + " devices" ) 557 return main.TRUE 558 else: 559 main.log.error( "Error with REST request, response was: " + 560 str( response ) ) 561 return main.FALSE 562 563 except Exception as e: 564 main.log.exception( e ) 565 return None
566 567
568 - def removeIntent( self, intentId, appId='org.onosproject.cli', 569 ip="DEFAULT", port="DEFAULT" ):
570 """ 571 Remove intent for specified application id and intent id; 572 Returns None for exception 573 """ 574 try: 575 output = None 576 if ip == "DEFAULT": 577 main.log.warn( "No ip given, reverting to ip from topo file" ) 578 ip = self.ip_address 579 if port == "DEFAULT": 580 main.log.warn( "No port given, reverting to port " + 581 "from topo file" ) 582 port = self.port 583 # NOTE: REST url requires the intent id to be in decimal form 584 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) ) 585 response = self.send( ip, 586 port, 587 method="DELETE", 588 url="/intents" + query ) 589 if response: 590 if 200 <= response[ 0 ] <= 299: 591 return main.TRUE 592 else: 593 main.log.error( "Error with REST request, response was: " + 594 str( response ) ) 595 return main.FALSE 596 except Exception as e: 597 main.log.exception( e ) 598 return None
599
600 - def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
601 """ 602 Returns a list of intents id; Returns None for exception 603 """ 604 try: 605 intentIdList = [] 606 intentsJson = json.loads( self.intents() ) 607 print intentsJson 608 for intent in intentsJson: 609 intentIdList.append( intent.get( 'id' ) ) 610 print intentIdList 611 return intentIdList 612 except Exception as e: 613 main.log.exception( e ) 614 return None
615
616 - def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli', 617 ip="DEFAULT", port="DEFAULT", delay=5 ):
618 """ 619 Description: 620 Remove all the intents 621 Returns: 622 Returns main.TRUE if all intents are removed, otherwise returns 623 main.FALSE; Returns None for exception 624 """ 625 try: 626 results = [] 627 if intentIdList == 'ALL': 628 intentIdList = self.getIntentsId( ip=ip, port=port ) 629 630 main.log.info( self.name + ": Removing intents " + 631 str( intentIdList ) ) 632 633 if isinstance( intentIdList, types.ListType ): 634 for intent in intentIdList: 635 results.append( self.removeIntent( intentId=intent, 636 appId=appId, 637 ip=ip, 638 port=port ) ) 639 # Check for remaining intents 640 # NOTE: Noticing some delay on Deleting the intents so i put 641 # this time out 642 import time 643 time.sleep( delay ) 644 intentRemain = len( json.loads( self.intents() ) ) 645 if all( result==main.TRUE for result in results ) and \ 646 intentRemain == 0: 647 main.log.info( self.name + ": All intents are removed " ) 648 return main.TRUE 649 else: 650 main.log.error( self.name + ": Did not removed all intents," 651 + " there are " + str( intentRemain ) 652 + " intents remaining" ) 653 return main.FALSE 654 else: 655 main.log.debug( self.name + ": There is no intents ID list" ) 656 except Exception as e: 657 main.log.exception( e ) 658 return None
659 660
661 - def hosts( self, ip="DEFAULT", port="DEFAULT" ):
662 """ 663 Description: 664 Get a list of dictionary of all discovered hosts 665 Returns: 666 Returns a list of dictionary of information of the hosts currently 667 discovered by ONOS; Returns main.FALSE if error on requests; 668 Returns None for exception 669 """ 670 try: 671 output = None 672 if ip == "DEFAULT": 673 main.log.warn( "No ip given, reverting to ip from topo file" ) 674 ip = self.ip_address 675 if port == "DEFAULT": 676 main.log.warn( "No port given, reverting to port \ 677 from topo file" ) 678 port = self.port 679 response = self.send( ip, port, url="/hosts" ) 680 if response: 681 if 200 <= response[ 0 ] <= 299: 682 output = response[ 1 ] 683 a = json.loads( output ).get( 'hosts' ) 684 b = json.dumps( a ) 685 return b 686 else: 687 main.log.error( "Error with REST request, response was: " + 688 str( response ) ) 689 return main.FALSE 690 except Exception as e: 691 main.log.exception( e ) 692 return None
693
694 - def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
695 """ 696 Description: 697 Gets the information from the given host 698 Required: 699 str mac - MAC address of the host 700 Optional: 701 str vlan - VLAN tag of the host, defaults to -1 702 Returns: 703 Return the host id from the hosts/mac/vlan output in REST api 704 whose 'id' contains mac/vlan; Returns None for exception; 705 Returns main.FALSE if error on requests 706 707 NOTE: 708 Not sure what this function should do, any suggestion? 709 """ 710 try: 711 output = None 712 if ip == "DEFAULT": 713 main.log.warn( "No ip given, reverting to ip from topo file" ) 714 ip = self.ip_address 715 if port == "DEFAULT": 716 main.log.warn( "No port given, reverting to port \ 717 from topo file" ) 718 port = self.port 719 query = "/" + mac + "/" + vlan 720 response = self.send( ip, port, url="/hosts" + query ) 721 if response: 722 # NOTE: What if the person wants other values? would it be better 723 # to have a function that gets a key and return a value instead? 724 # This function requires mac and vlan and returns an ID which 725 # makes this current function useless 726 if 200 <= response[ 0 ] <= 299: 727 output = response[ 1 ] 728 hostId = json.loads( output ).get( 'id' ) 729 return hostId 730 else: 731 main.log.error( "Error with REST request, response was: " + 732 str( response ) ) 733 return main.FALSE 734 except Exception as e: 735 main.log.exception( e ) 736 return None
737
738 - def topology( self, ip="DEFAULT", port="DEFAULT" ):
739 """ 740 Description: 741 Gets the overview of network topology 742 Returns: 743 Returns a dictionary containing information about network topology; 744 Returns None for exception 745 """ 746 try: 747 output = None 748 if ip == "DEFAULT": 749 main.log.warn( "No ip given, reverting to ip from topo file" ) 750 ip = self.ip_address 751 if port == "DEFAULT": 752 main.log.warn( "No port given, reverting to port \ 753 from topo file" ) 754 port = self.port 755 response = self.send( ip, port, url="/topology" ) 756 if response: 757 if 200 <= response[ 0 ] <= 299: 758 output = response[ 1 ] 759 a = json.loads( output ) 760 b = json.dumps( a ) 761 return b 762 else: 763 main.log.error( "Error with REST request, response was: " + 764 str( response ) ) 765 return main.FALSE 766 except Exception as e: 767 main.log.exception( e ) 768 return None
769
770 - def getIntentState( self, intentsId, intentsJson=None, 771 ip="DEFAULT", port="DEFAULT" ):
772 """ 773 Description: 774 Get intent state. 775 Accepts a single intent ID (string type) or a list of intent IDs. 776 Returns the state(string type) of the id if a single intent ID is 777 accepted. 778 Required: 779 intentId: intent ID (string type) 780 intentsJson: parsed json object from the onos:intents api 781 Returns: 782 Returns a dictionary with intent IDs as the key and its 783 corresponding states as the values; Returns None for invalid IDs or 784 Type error and any exceptions 785 NOTE: 786 An intent's state consist of INSTALLED,WITHDRAWN etc. 787 """ 788 try: 789 state = "State is Undefined" 790 if not intentsJson: 791 intentsJsonTemp = json.loads( self.intents() ) 792 else: 793 intentsJsonTemp = json.loads( intentsJson ) 794 if isinstance( intentsId, types.StringType ): 795 for intent in intentsJsonTemp: 796 if intentsId == intent[ 'id' ]: 797 state = intent[ 'state' ] 798 return state 799 main.log.info( "Cannot find intent ID" + str( intentsId ) + 800 " on the list" ) 801 return state 802 elif isinstance( intentsId, types.ListType ): 803 dictList = [] 804 for i in xrange( len( intentsId ) ): 805 stateDict = {} 806 for intents in intentsJsonTemp: 807 if intentsId[ i ] == intents[ 'id' ]: 808 stateDict[ 'state' ] = intents[ 'state' ] 809 stateDict[ 'id' ] = intentsId[ i ] 810 dictList.append( stateDict ) 811 break 812 if len( intentsId ) != len( dictList ): 813 main.log.info( "Cannot find some of the intent ID state" ) 814 return dictList 815 else: 816 main.log.info( "Invalid intents ID entry" ) 817 return None 818 819 except TypeError: 820 main.log.exception( self.name + ": Object Type not as expected" ) 821 return None 822 except Exception as e: 823 main.log.exception( e ) 824 return None
825
826 - def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED', 827 ip="DEFAULT", port="DEFAULT"):
828 """ 829 Description: 830 Check intents state based on expected state which defaults to 831 INSTALLED state 832 Required: 833 intentsId - List of intents ID to be checked 834 Optional: 835 expectedState - Check the expected state(s) of each intents 836 state in the list. 837 *NOTE: You can pass in a list of expected state, 838 Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ] 839 Return: 840 Returns main.TRUE only if all intent are the same as expected states 841 , otherwise, returns main.FALSE; Returns None for general exception 842 """ 843 try: 844 # Generating a dictionary: intent id as a key and state as value 845 returnValue = main.TRUE 846 if intentsId == "ALL": 847 intentsId = self.getIntentsId( ip=ip, port=port ) 848 intentsDict = self.getIntentState( intentsId, ip=ip, port=port ) 849 850 #print "len of intentsDict ", str( len( intentsDict ) ) 851 if len( intentsId ) != len( intentsDict ): 852 main.log.error( self.name + ": There is something wrong " + 853 "getting intents state" ) 854 return main.FALSE 855 856 if isinstance( expectedState, types.StringType ): 857 for intents in intentsDict: 858 if intents.get( 'state' ) != expectedState: 859 main.log.debug( self.name + " : Intent ID - " + 860 intents.get( 'id' ) + 861 " actual state = " + 862 intents.get( 'state' ) 863 + " does not equal expected state = " 864 + expectedState ) 865 returnValue = main.FALSE 866 867 elif isinstance( expectedState, types.ListType ): 868 for intents in intentsDict: 869 if not any( state == intents.get( 'state' ) for state in 870 expectedState ): 871 main.log.debug( self.name + " : Intent ID - " + 872 intents.get( 'id' ) + 873 " actual state = " + 874 intents.get( 'state' ) + 875 " does not equal expected states = " 876 + str( expectedState ) ) 877 returnValue = main.FALSE 878 879 if returnValue == main.TRUE: 880 main.log.info( self.name + ": All " + 881 str( len( intentsDict ) ) + 882 " intents are in " + str( expectedState ) + 883 " state" ) 884 return returnValue 885 except TypeError: 886 main.log.exception( self.name + ": Object not as expected" ) 887 return main.FALSE 888 except Exception as e: 889 main.log.exception( e ) 890 return None
891
892 - def flows( self, ip="DEFAULT", port="DEFAULT" ):
893 """ 894 Description: 895 Get flows currently added to the system 896 NOTE: 897 The flows -j cli command has completely different format than 898 the REST output; Returns None for exception 899 """ 900 try: 901 output = None 902 if ip == "DEFAULT": 903 main.log.warn( "No ip given, reverting to ip from topo file" ) 904 ip = self.ip_address 905 if port == "DEFAULT": 906 main.log.warn( "No port given, reverting to port \ 907 from topo file" ) 908 port = self.port 909 response = self.send( ip, port, url="/flows" ) 910 if response: 911 if 200 <= response[ 0 ] <= 299: 912 output = response[ 1 ] 913 a = json.loads( output ).get( 'flows' ) 914 b = json.dumps( a ) 915 return b 916 else: 917 main.log.error( "Error with REST request, response was: " + 918 str( response ) ) 919 return main.FALSE 920 except Exception as e: 921 main.log.exception( e ) 922 return None
923
924 - def getFlows( self, device, flowId=0, ip="DEFAULT", port="DEFAULT" ):
925 """ 926 Description: 927 Gets all the flows of the device or get a specific flow in the 928 device by giving its flow ID 929 Required: 930 str device - device/switch Id 931 Optional: 932 int/hex flowId - ID of the flow 933 """ 934 try: 935 output = None 936 if ip == "DEFAULT": 937 main.log.warn( "No ip given, reverting to ip from topo file" ) 938 ip = self.ip_address 939 if port == "DEFAULT": 940 main.log.warn( "No port given, reverting to port \ 941 from topo file" ) 942 port = self.port 943 url = "/flows/" + device 944 if flowId: 945 url += "/" + str( int( flowId ) ) 946 print url 947 response = self.send( ip, port, url=url ) 948 if response: 949 if 200 <= response[ 0 ] <= 299: 950 output = response[ 1 ] 951 a = json.loads( output ).get( 'flows' ) 952 b = json.dumps( a ) 953 return b 954 else: 955 main.log.error( "Error with REST request, response was: " + 956 str( response ) ) 957 return main.FALSE 958 except Exception as e: 959 main.log.exception( e ) 960 return None
961 962
963 - def addFlow( self, 964 deviceId, 965 appId=0, 966 ingressPort="", 967 egressPort="", 968 ethType="", 969 ethSrc="", 970 ethDst="", 971 bandwidth="", 972 lambdaAlloc=False, 973 ipProto="", 974 ipSrc="", 975 ipDst="", 976 tcpSrc="", 977 tcpDst="", 978 ip="DEFAULT", 979 port="DEFAULT" ):
980 """ 981 Description: 982 Creates a single flow in the specified device 983 Required: 984 * deviceId: id of the device 985 Optional: 986 * ingressPort: port ingress device 987 * egressPort: port of egress device 988 * ethType: specify ethType 989 * ethSrc: specify ethSrc ( i.e. src mac addr ) 990 * ethDst: specify ethDst ( i.e. dst mac addr ) 991 * ipProto: specify ip protocol 992 * ipSrc: specify ip source address with mask eg. ip#/24 993 * ipDst: specify ip destination address eg. ip#/24 994 * tcpSrc: specify tcp source port 995 * tcpDst: specify tcp destination port 996 Returns: 997 Returns main.TRUE for successful requests; Returns main.FALSE 998 if error on requests; 999 Returns None for exceptions 1000 NOTE: 1001 The ip and port option are for the requests input's ip and port 1002 of the ONOS node 1003 """ 1004 try: 1005 1006 flowJson = { "priority":100, 1007 "isPermanent":"true", 1008 "timeout":0, 1009 "deviceId":deviceId, 1010 "treatment":{"instructions":[]}, 1011 "selector": {"criteria":[]}} 1012 1013 if appId: 1014 flowJson[ "appId" ] = appId 1015 1016 if egressPort: 1017 flowJson[ 'treatment' ][ 'instructions' ].append( 1018 { "type":"OUTPUT", 1019 "port":egressPort } ) 1020 if ingressPort: 1021 flowJson[ 'selector' ][ 'criteria' ].append( 1022 { "type":"IN_PORT", 1023 "port":ingressPort } ) 1024 if ethType == "IPV4": 1025 flowJson[ 'selector' ][ 'criteria' ].append( { 1026 "type":"ETH_TYPE", 1027 "ethType":2048 } ) 1028 elif ethType: 1029 flowJson[ 'selector' ][ 'criteria' ].append( { 1030 "type":"ETH_TYPE", 1031 "ethType":ethType } ) 1032 if ethSrc: 1033 flowJson[ 'selector' ][ 'criteria' ].append( 1034 { "type":"ETH_SRC", 1035 "mac":ethSrc } ) 1036 if ethDst: 1037 flowJson[ 'selector' ][ 'criteria' ].append( 1038 { "type":"ETH_DST", 1039 "mac":ethDst } ) 1040 if ipSrc: 1041 flowJson[ 'selector' ][ 'criteria' ].append( 1042 { "type":"IPV4_SRC", 1043 "ip":ipSrc } ) 1044 if ipDst: 1045 flowJson[ 'selector' ][ 'criteria' ].append( 1046 { "type":"IPV4_DST", 1047 "ip":ipDst } ) 1048 if tcpSrc: 1049 flowJson[ 'selector' ][ 'criteria' ].append( 1050 { "type":"TCP_SRC", 1051 "tcpPort": tcpSrc } ) 1052 if tcpDst: 1053 flowJson[ 'selector' ][ 'criteria' ].append( 1054 { "type":"TCP_DST", 1055 "tcpPort": tcpDst } ) 1056 if ipProto: 1057 flowJson[ 'selector' ][ 'criteria' ].append( 1058 { "type":"IP_PROTO", 1059 "protocol": ipProto } ) 1060 1061 # TODO: Bandwidth and Lambda will be implemented if needed 1062 1063 main.log.debug( flowJson ) 1064 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": 1070 main.log.warn( "No port given, reverting to port " + 1071 "from topo file" ) 1072 port = self.port 1073 url = "/flows/" + deviceId 1074 response = self.send( ip, 1075 port, 1076 method="POST", 1077 url=url, 1078 data=json.dumps( flowJson ) ) 1079 if response: 1080 if 201: 1081 main.log.info( self.name + ": Successfully POST flow" + 1082 "in device: " + str( deviceId ) ) 1083 return main.TRUE 1084 else: 1085 main.log.error( "Error with REST request, response was: " + 1086 str( response ) ) 1087 return main.FALSE 1088 1089 except Exception as e: 1090 main.log.exception( e ) 1091 return None
1092
1093 - def removeFlow( self, deviceId, flowId, 1094 ip="DEFAULT", port="DEFAULT" ):
1095 """ 1096 Description: 1097 Remove specific device flow 1098 Required: 1099 str deviceId - id of the device 1100 str flowId - id of the flow 1101 Return: 1102 Returns main.TRUE if successfully deletes flows, otherwise 1103 Returns main.FALSE, Returns None on error 1104 """ 1105 try: 1106 output = None 1107 if ip == "DEFAULT": 1108 main.log.warn( "No ip given, reverting to ip from topo file" ) 1109 ip = self.ip_address 1110 if port == "DEFAULT": 1111 main.log.warn( "No port given, reverting to port " + 1112 "from topo file" ) 1113 port = self.port 1114 # NOTE: REST url requires the intent id to be in decimal form 1115 query = "/" + str( deviceId ) + "/" + str( int( flowId ) ) 1116 response = self.send( ip, 1117 port, 1118 method="DELETE", 1119 url="/flows" + query ) 1120 if response: 1121 if 200 <= response[ 0 ] <= 299: 1122 return main.TRUE 1123 else: 1124 main.log.error( "Error with REST request, response was: " + 1125 str( response ) ) 1126 return main.FALSE 1127 except Exception as e: 1128 main.log.exception( e ) 1129 return None
1130
1131 - def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
1132 """ 1133 Description: 1134 Check if all the current flows are in ADDED state 1135 Return: 1136 returnValue - Returns main.TRUE only if all flows are in 1137 return main.FALSE otherwise; 1138 Returns None for exception 1139 """ 1140 try: 1141 tempFlows = json.loads( self.flows( ip=ip, port=port ) ) 1142 returnValue = main.TRUE 1143 for flow in tempFlows: 1144 if flow.get( 'state' ) != 'ADDED': 1145 main.log.info( self.name + ": flow Id: " + 1146 str( flow.get( 'groupId' ) ) + 1147 " | state:" + 1148 str( flow.get( 'state' ) ) ) 1149 returnValue = main.FALSE 1150 return returnValue 1151 except TypeError: 1152 main.log.exception( self.name + ": Object not as expected" ) 1153 return main.FALSE 1154 except Exception as e: 1155 main.log.exception( e ) 1156 return None 1157 except Exception: 1158 main.log.exception( self.name + ": Uncaught exception!" ) 1159 main.cleanup() 1160 main.exit()
1161