Clean up ONOS REST driver

- add a pretty print function
- Exception handling
- Add devices function

Change-Id: I024cd66f9d610ea56446c69d5916a42a2154597b
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index 66cd8b4..3ff43c7 100644
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -20,6 +20,7 @@
 import os
 import requests
 import types
+import sys
 
 from drivers.common.api.controllerdriver import Controller
 
@@ -32,6 +33,7 @@
         super( Controller, self ).__init__()
         self.ip_address = "localhost"
         self.port = "8080"
+        self.wrapped = sys.modules[ __name__ ]
 
     def connect( self, **connectargs ):
         try:
@@ -55,6 +57,24 @@
         self.handle = super( OnosRestDriver, self ).connect()
         return self.handle
 
+    def pprint( self, jsonObject ):
+        """
+        Pretty Prints a json object
+
+        arguments:
+            jsonObject - a parsed json object
+        returns:
+            A formatted string for printing or None on error
+        """
+        try:
+            if isinstance( jsonObject, str ):
+                jsonObject = json.loads( jsonObject )
+            return json.dumps( jsonObject, sort_keys=True,
+                               indent=4, separators=(',', ': '))
+        except ( TypeError, ValueError ):
+            main.log.exception( "Error parsing jsonObject" )
+            return None
+
     def send( self, ip, port, url, base="/onos/v1", method="GET",
               query=None, data=None, debug=False ):
         """
@@ -93,10 +113,10 @@
         except requests.exceptions:
             main.log.exception( "Error sending request." )
             return None
-        except Exception as e:
-            main.log.exception( e )
-            return None
-        # FIXME: add other exceptions
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def intents( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -121,15 +141,20 @@
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
                     a = json.loads( output ).get( 'intents' )
+                    assert a is not None, "Error parsing json object"
                     b = json.dumps( a )
                     return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def intent( self, intentId, appId="org.onosproject.cli",
                 ip="DEFAULT", port="DEFAULT" ):
@@ -169,9 +194,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -187,18 +216,19 @@
             intentsDict = json.loads( self.intents( ip=ip, port=port ) )
             for intent in intentsDict:
                 intentsIdList.append( intent.get( 'id' ) )
-
             if not intentsIdList:
                 main.log.debug( "Cannot find any intents" )
                 return main.FALSE
             else:
                 main.log.info( "Found intents: " + str( intentsIdList ) )
                 return main.TRUE
-
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
-
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def apps( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -222,15 +252,20 @@
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
                     a = json.loads( output ).get( 'applications' )
+                    assert a is not None, "Error parsing json object"
                     b = json.dumps( a )
                     return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
         """
@@ -280,9 +315,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
                        check=True ):
@@ -332,9 +371,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
                 port="DEFAULT" ):
@@ -367,9 +410,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
                        ip="DEFAULT", port="DEFAULT" ):
@@ -419,9 +466,13 @@
                                     str( response ) )
                     return main.FALSE
 
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def addPointIntent( self,
                         ingressDevice,
@@ -567,10 +618,13 @@
                                     str( response ) )
                     return main.FALSE
 
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
-
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def removeIntent( self, intentId, appId='org.onosproject.cli',
                        ip="DEFAULT", port="DEFAULT" ):
@@ -600,9 +654,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -611,14 +669,16 @@
         try:
             intentIdList = []
             intentsJson = json.loads( self.intents() )
-            print intentsJson
             for intent in intentsJson:
                 intentIdList.append( intent.get( 'id' ) )
-            print intentIdList
             return intentIdList
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
                           ip="DEFAULT", port="DEFAULT", delay=5 ):
@@ -660,10 +720,13 @@
                     return main.FALSE
             else:
                 main.log.debug( self.name + ": There is no intents ID list" )
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
-
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def hosts( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -688,15 +751,20 @@
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
                     a = json.loads( output ).get( 'hosts' )
+                    assert a is not None, "Error parsing json object"
                     b = json.dumps( a )
                     return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
         """
@@ -738,9 +806,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def topology( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -770,9 +842,51 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def devices( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Get the devices discovered by ONOS is json string format
+        Returns:
+            a json string of the devices currently discovered by ONOS OR
+            main.FALSE if there is an error in the request OR
+            Returns None for exception
+        """
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            response = self.send( ip, port, url="/devices" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output ).get( 'devices' )
+                    assert a is not None, "Error parsing json object"
+                    b = json.dumps( a )
+                    return b
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def getIntentState( self, intentsId, intentsJson=None,
                         ip="DEFAULT", port="DEFAULT" ):
@@ -823,12 +937,13 @@
                 main.log.info( "Invalid intents ID entry" )
                 return None
 
-        except TypeError:
-            main.log.exception( self.name + ": Object Type not as expected" )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
-        except Exception as e:
-            main.log.exception( e )
-            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
                           ip="DEFAULT", port="DEFAULT"):
@@ -889,12 +1004,13 @@
                                " intents are in " + str( expectedState ) +
                                " state" )
             return returnValue
-        except TypeError:
+        except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
-            return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def flows( self, ip="DEFAULT", port="DEFAULT" ):
         """
@@ -902,7 +1018,9 @@
             Get flows currently added to the system
         NOTE:
             The flows -j cli command has completely different format than
-            the REST output; Returns None for exception
+            the REST output
+
+        Returns None for exception
         """
         try:
             output = None
@@ -918,23 +1036,28 @@
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
                     a = json.loads( output ).get( 'flows' )
+                    assert a is not None, "Error parsing json object"
                     b = json.dumps( a )
                     return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
-    def getFlows( self, device, flowId=0, ip="DEFAULT", port="DEFAULT" ):
+    def getFlows( self, deviceId, flowId=None, ip="DEFAULT", port="DEFAULT" ):
         """
         Description:
             Gets all the flows of the device or get a specific flow in the
             device by giving its flow ID
         Required:
-            str device - device/switch Id
+            str deviceId - device/switch Id
         Optional:
             int/hex flowId - ID of the flow
         """
@@ -947,7 +1070,7 @@
                 main.log.warn( "No port given, reverting to port " +
                                "from topo file" )
                 port = self.port
-            url = "/flows/" + device
+            url = "/flows/" + deviceId
             if flowId:
                 url += "/" + str( int( flowId ) )
             print url
@@ -956,16 +1079,20 @@
                 if 200 <= response[ 0 ] <= 299:
                     output = response[ 1 ]
                     a = json.loads( output ).get( 'flows' )
+                    assert a is not None, "Error parsing json object"
                     b = json.dumps( a )
                     return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, AssertionError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
-
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def addFlow( self,
                  deviceId,
@@ -1009,17 +1136,14 @@
             of the ONOS node
         """
         try:
-
             flowJson = { "priority":100,
                            "isPermanent":"true",
                            "timeout":0,
                            "deviceId":deviceId,
                            "treatment":{"instructions":[]},
                            "selector": {"criteria":[]}}
-
             if appId:
                 flowJson[ "appId" ] = appId
-
             if egressPort:
                 flowJson[ 'treatment' ][ 'instructions' ].append(
                                                        { "type":"OUTPUT",
@@ -1064,11 +1188,10 @@
                 flowJson[ 'selector' ][ 'criteria' ].append(
                                                        { "type":"IP_PROTO",
                                                          "protocol": ipProto } )
-
-            # TODO: Bandwidth and Lambda will be implemented if needed
-
-            main.log.debug( flowJson )
-
+            if bandwidth or lambdaAlloc:
+                # TODO: Bandwidth and Lambda will be implemented if needed
+                raise NotImplementedError
+            main.log.debug( "Adding flow: " + self.pprint( flowJson ) )
             output = None
             if ip == "DEFAULT":
                 main.log.warn( "No ip given, reverting to ip from topo file" )
@@ -1092,10 +1215,15 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-
-        except Exception as e:
-            main.log.exception( e )
+        except NotImplementedError as e:
+            raise e  # Inform the caller
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def removeFlow( self, deviceId, flowId,
                        ip="DEFAULT", port="DEFAULT" ):
@@ -1131,9 +1259,13 @@
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
                     return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
             return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
 
     def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
         """
@@ -1155,11 +1287,8 @@
                                    str( flow.get( 'state' ) ) )
                     returnValue = main.FALSE
             return returnValue
-        except TypeError:
+        except ( AttributeError, TypeError ):
             main.log.exception( self.name + ": Object not as expected" )
-            return main.FALSE
-        except Exception as e:
-            main.log.exception( e )
             return None
         except Exception:
             main.log.exception( self.name + ": Uncaught exception!" )