Introduce ONOS cli interface adapted from TestON project.
This is made to work with cord tester through a wrapper: clicommon.py
Also change the cord-tester docker image builder to include dependencies for the ONOS cli interface.
diff --git a/src/test/cli/onosclidriver.py b/src/test/cli/onosclidriver.py
new file mode 100644
index 0000000..15753a6
--- /dev/null
+++ b/src/test/cli/onosclidriver.py
@@ -0,0 +1,4595 @@
+#!/usr/bin/env python
+
+"""
+This driver enters the onos> prompt to issue commands.
+
+Please follow the coding style demonstrated by existing
+functions and document properly.
+
+If you are a contributor to the driver, please
+list your email here for future contact:
+
+jhall@onlab.us
+andrew@onlab.us
+shreya@onlab.us
+
+OCT 13 2014
+
+"""
+import pexpect
+import re
+import json
+import types
+import time
+import os
+from clidriver import CLI
+from clicommon import *
+
+class OnosCliDriver( CLI ):
+
+ def __init__( self, controller = None, connect = True):
+ """
+ Initialize client
+ """
+ self.name = None
+ self.home = None
+ self.handle = None
+ self.controller = os.getenv('ONOS_CONTROLLER_IP') or 'localhost'
+ super( CLI, self ).__init__()
+ if connect == True:
+ self.connect_cli()
+
+ def connect_cli(self):
+ options = { 'name' : 'onoscli', 'onosIp': '{0}'.format(self.controller) }
+ self.connect(name = options['name'], user_name = 'onos', pwd = 'rocks',
+ ip_address = self.controller, port = '8101', options = options)
+
+ def connect( self, **connectargs ):
+ """
+ Creates ssh handle for ONOS cli.
+ """
+ try:
+ for key in connectargs:
+ vars( self )[ key ] = connectargs[ key ]
+ self.home = "~/onos"
+ for key in self.options:
+ if key == "home":
+ self.home = self.options[ 'home' ]
+ break
+ if self.home is None or self.home == "":
+ self.home = "~/onos"
+
+ for key in self.options:
+ if key == 'onosIp':
+ self.onosIp = self.options[ 'onosIp' ]
+ break
+
+ self.name = self.options[ 'name' ]
+
+ try:
+ if os.getenv( str( self.ip_address ) ) is not None:
+ self.ip_address = os.getenv( str( self.ip_address ) )
+ else:
+ main.log.info( self.name +
+ ": Trying to connect to " +
+ self.ip_address )
+
+ except KeyError:
+ main.log.info( "Invalid host name," +
+ " connecting to local host instead" )
+ self.ip_address = 'localhost'
+ except Exception as inst:
+ main.log.error( "Uncaught exception: " + str( inst ) )
+
+ self.handle = super( OnosCliDriver, self ).connect(
+ user_name=self.user_name,
+ ip_address=self.ip_address,
+ port=self.port,
+ pwd=self.pwd,
+ home=self.home )
+
+ #self.handle.sendline( "cd " + self.home )
+ #self.handle.expect( "\$" )
+ if self.handle:
+ return self.handle
+ else:
+ main.log.info( "NO ONOS HANDLE" )
+ return main.FALSE
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def disconnect( self ):
+ """
+ Called when Test is complete to disconnect the ONOS handle.
+ """
+ response = main.TRUE
+ try:
+ if self.handle:
+ i = self.logout()
+ if i == main.TRUE:
+ self.handle.sendline( "" )
+ self.handle.expect( "\$" )
+ self.handle.sendline( "exit" )
+ self.handle.expect( "closed" )
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ response = main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ except ValueError:
+ main.log.exception( "Exception in disconnect of " + self.name )
+ response = main.TRUE
+ except Exception:
+ main.log.exception( self.name + ": Connection failed to the host" )
+ response = main.FALSE
+ return response
+
+ def logout( self ):
+ """
+ Sends 'logout' command to ONOS cli
+ Returns main.TRUE if exited CLI and
+ main.FALSE on timeout (not guranteed you are disconnected)
+ None on TypeError
+ Exits test on unknown error or pexpect exits unexpectedly
+ """
+ try:
+ if self.handle:
+ self.handle.sendline( "" )
+ i = self.handle.expect( [ "onos>", "\$", pexpect.TIMEOUT ],
+ timeout=10 )
+ if i == 0: # In ONOS CLI
+ self.handle.sendline( "logout" )
+ j = self.handle.expect( [ "\$",
+ "Command not found:",
+ pexpect.TIMEOUT ] )
+ if j == 0: # Successfully logged out
+ return main.TRUE
+ elif j == 1 or j == 2:
+ # ONOS didn't fully load, and logout command isn't working
+ # or the command timed out
+ self.handle.send( "\x04" ) # send ctrl-d
+ self.handle.expect( "\$" )
+ return main.TRUE
+ else: # some other output
+ main.log.warn( "Unknown repsonse to logout command: '{}'",
+ repr( self.handle.before ) )
+ return main.FALSE
+ elif i == 1: # not in CLI
+ return main.TRUE
+ elif i == 3: # Timeout
+ return main.FALSE
+ else:
+ return main.TRUE
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": eof exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except ValueError:
+ main.log.error( self.name +
+ "ValueError exception in logout method" )
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setCell( self, cellname ):
+ """
+ Calls 'cell <name>' to set the environment variables on ONOSbench
+
+ Before issuing any cli commands, set the environment variable first.
+ """
+ try:
+ if not cellname:
+ main.log.error( "Must define cellname" )
+ main.cleanup()
+ main.exit()
+ else:
+ self.handle.sendline( "cell " + str( cellname ) )
+ # Expect the cellname in the ONOSCELL variable.
+ # Note that this variable name is subject to change
+ # and that this driver will have to change accordingly
+ self.handle.expect(str(cellname))
+ handleBefore = self.handle.before
+ handleAfter = self.handle.after
+ # Get the rest of the handle
+ self.handle.sendline("")
+ self.handle.expect("\$")
+ handleMore = self.handle.before
+
+ main.log.info( "Cell call returned: " + handleBefore +
+ handleAfter + handleMore )
+
+ return main.TRUE
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": eof exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def startOnosCli( self, ONOSIp, karafTimeout="",
+ commandlineTimeout=10, onosStartTimeout=60 ):
+ """
+ karafTimeout is an optional argument. karafTimeout value passed
+ by user would be used to set the current karaf shell idle timeout.
+ Note that when ever this property is modified the shell will exit and
+ the subsequent login would reflect new idle timeout.
+ Below is an example to start a session with 60 seconds idle timeout
+ ( input value is in milliseconds ):
+
+ tValue = "60000"
+ main.ONOScli1.startOnosCli( ONOSIp, karafTimeout=tValue )
+
+ Note: karafTimeout is left as str so that this could be read
+ and passed to startOnosCli from PARAMS file as str.
+ """
+ self.onosIp = ONOSIp
+ try:
+ self.handle.sendline( "" )
+ x = self.handle.expect( [
+ "\$", "onos>" ], commandlineTimeout)
+
+ if x == 1:
+ main.log.info( "ONOS cli is already running" )
+ return main.TRUE
+
+ # Wait for onos start ( -w ) and enter onos cli
+ self.handle.sendline( "onos -w " + str( ONOSIp ) )
+ i = self.handle.expect( [
+ "onos>",
+ pexpect.TIMEOUT ], onosStartTimeout )
+
+ if i == 0:
+ main.log.info( str( ONOSIp ) + " CLI Started successfully" )
+ if karafTimeout:
+ self.handle.sendline(
+ "config:property-set -p org.apache.karaf.shell\
+ sshIdleTimeout " +
+ karafTimeout )
+ self.handle.expect( "\$" )
+ self.handle.sendline( "onos -w " + str( ONOSIp ) )
+ self.handle.expect( "onos>" )
+ return main.TRUE
+ else:
+ # If failed, send ctrl+c to process and try again
+ main.log.info( "Starting CLI failed. Retrying..." )
+ self.handle.send( "\x03" )
+ self.handle.sendline( "onos -w " + str( ONOSIp ) )
+ i = self.handle.expect( [ "onos>", pexpect.TIMEOUT ],
+ timeout=30 )
+ if i == 0:
+ main.log.info( str( ONOSIp ) + " CLI Started " +
+ "successfully after retry attempt" )
+ if karafTimeout:
+ self.handle.sendline(
+ "config:property-set -p org.apache.karaf.shell\
+ sshIdleTimeout " +
+ karafTimeout )
+ self.handle.expect( "\$" )
+ self.handle.sendline( "onos -w " + str( ONOSIp ) )
+ self.handle.expect( "onos>" )
+ return main.TRUE
+ else:
+ main.log.error( "Connection to CLI " +
+ str( ONOSIp ) + " timeout" )
+ return main.FALSE
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def log( self, cmdStr, level="" ):
+ """
+ log the commands in the onos CLI.
+ returns main.TRUE on success
+ returns main.FALSE if Error occurred
+ Available level: DEBUG, TRACE, INFO, WARN, ERROR
+ Level defaults to INFO
+ """
+ try:
+ lvlStr = ""
+ if level:
+ lvlStr = "--level=" + level
+
+ self.handle.sendline( "" )
+ i = self.handle.expect( [ "onos>", "\$", pexpect.TIMEOUT ] )
+ if i == 1:
+ main.log.error( self.name + ": onos cli session closed. ")
+ if self.onosIp:
+ main.log.warn( "Trying to reconnect " + self.onosIp )
+ reconnectResult = self.startOnosCli( self.onosIp )
+ if reconnectResult:
+ main.log.info( self.name + ": onos cli session reconnected." )
+ else:
+ main.log.error( self.name + ": reconnection failed." )
+ main.cleanup()
+ main.exit()
+ else:
+ main.cleanup()
+ main.exit()
+ if i == 2:
+ self.handle.sendline( "" )
+ self.handle.expect( "onos>" )
+ self.handle.sendline( "log:log " + lvlStr + " " + cmdStr )
+ self.handle.expect( "log:log" )
+ self.handle.expect( "onos>" )
+
+ response = self.handle.before
+ if re.search( "Error", response ):
+ return main.FALSE
+ return main.TRUE
+ except pexpect.TIMEOUT:
+ main.log.exception( self.name + ": TIMEOUT exception found" )
+ main.cleanup()
+ main.exit()
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def sendline( self, cmdStr, showResponse=False, debug=False, timeout=10 ):
+ """
+ Send a completely user specified string to
+ the onos> prompt. Use this function if you have
+ a very specific command to send.
+
+ Warning: There are no sanity checking to commands
+ sent using this method.
+
+ """
+ try:
+ logStr = "\"Sending CLI command: '" + cmdStr + "'\""
+ self.log( logStr )
+ self.handle.sendline( cmdStr )
+ i = self.handle.expect( ["onos>", "\$"], timeout )
+ response = self.handle.before
+ # TODO: do something with i
+ main.log.info( "Command '" + str( cmdStr ) + "' sent to "
+ + self.name + "." )
+ if debug:
+ main.log.debug( self.name + ": Raw output" )
+ main.log.debug( self.name + ": " + repr( response ) )
+
+ # Remove ANSI color control strings from output
+ ansiEscape = re.compile( r'\x1b[^m]*m' )
+ response = ansiEscape.sub( '', response )
+ if debug:
+ main.log.debug( self.name + ": ansiEscape output" )
+ main.log.debug( self.name + ": " + repr( response ) )
+
+ # Remove extra return chars that get added
+ response = re.sub( r"\s\r", "", response )
+ if debug:
+ main.log.debug( self.name + ": Removed extra returns " +
+ "from output" )
+ main.log.debug( self.name + ": " + repr( response ) )
+
+ # Strip excess whitespace
+ response = response.strip()
+ if debug:
+ main.log.debug( self.name + ": parsed and stripped output" )
+ main.log.debug( self.name + ": " + repr( response ) )
+
+ # parse for just the output, remove the cmd from response
+ output = response.split( cmdStr.strip(), 1 )
+ if debug:
+ main.log.debug( self.name + ": split output" )
+ for r in output:
+ main.log.debug( self.name + ": " + repr( r ) )
+ output = output[1].strip()
+ if showResponse:
+ main.log.info( "Response from ONOS: {}".format( output ) )
+ return output
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ":ONOS timeout" )
+ if debug:
+ main.log.debug( self.handle.before )
+ return None
+ except IndexError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ # IMPORTANT NOTE:
+ # For all cli commands, naming convention should match
+ # the cli command changing 'a:b' with 'aB'.
+ # Ex ) onos:topology > onosTopology
+ # onos:links > onosLinks
+ # feature:list > featureList
+
+ def addNode( self, nodeId, ONOSIp, tcpPort="" ):
+ """
+ Adds a new cluster node by ID and address information.
+ Required:
+ * nodeId
+ * ONOSIp
+ Optional:
+ * tcpPort
+ """
+ try:
+ cmdStr = "add-node " + str( nodeId ) + " " +\
+ str( ONOSIp ) + " " + str( tcpPort )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding node" )
+ main.log.error( handle )
+ return main.FALSE
+ else:
+ main.log.info( "Node " + str( ONOSIp ) + " added" )
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def removeNode( self, nodeId ):
+ """
+ Removes a cluster by ID
+ Issues command: 'remove-node [<node-id>]'
+ Required:
+ * nodeId
+ """
+ try:
+
+ cmdStr = "remove-node " + str( nodeId )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in removing node" )
+ main.log.error( handle )
+ return main.FALSE
+ else:
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def nodes( self, jsonFormat=True):
+ """
+ List the nodes currently visible
+ Issues command: 'nodes'
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "nodes"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def topology( self ):
+ """
+ Definition:
+ Returns the output of topology command.
+ Return:
+ topology = current ONOS topology
+ """
+ try:
+ cmdStr = "topology -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ main.log.info( cmdStr + " returned: " + str( handle ) )
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def deviceRemove( self, deviceId ):
+ """
+ Removes particular device from storage
+
+ TODO: refactor this function
+ """
+ try:
+ cmdStr = "device-remove " + str( deviceId )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in removing device" )
+ main.log.error( handle )
+ return main.FALSE
+ else:
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def devices( self, jsonFormat=True ):
+ """
+ Lists all infrastructure devices or switches
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "devices"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def balanceMasters( self ):
+ """
+ This balances the devices across all controllers
+ by issuing command: 'onos> onos:balance-masters'
+ If required this could be extended to return devices balanced output.
+ """
+ try:
+ cmdStr = "onos:balance-masters"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in balancing masters" )
+ main.log.error( handle )
+ return main.FALSE
+ else:
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def checkMasters( self, jsonFormat=True ):
+ """
+ Returns the output of the masters command.
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "onos:masters"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def checkBalanceMasters( self, jsonFormat=True ):
+ """
+ Uses the master command to check that the devices' leadership
+ is evenly divided
+
+ Dependencies: checkMasters() and summary()
+
+ Returns main.True if the devices are balanced
+ Returns main.False if the devices are unbalanced
+ Exits on Exception
+ Returns None on TypeError
+ """
+ try:
+ summaryOutput = self.summary()
+ totalDevices = json.loads( summaryOutput )[ "devices" ]
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, summaryOutput ) )
+ return None
+ try:
+ totalOwnedDevices = 0
+ mastersOutput = self.checkMasters()
+ masters = json.loads( mastersOutput )
+ first = masters[ 0 ][ "size" ]
+ for master in masters:
+ totalOwnedDevices += master[ "size" ]
+ if master[ "size" ] > first + 1 or master[ "size" ] < first - 1:
+ main.log.error( "Mastership not balanced" )
+ main.log.info( "\n" + self.checkMasters( False ) )
+ return main.FALSE
+ main.log.info( "Mastership balanced between " \
+ + str( len(masters) ) + " masters" )
+ return main.TRUE
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, mastersOutput ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def links( self, jsonFormat=True ):
+ """
+ Lists all core links
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "links"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def ports( self, jsonFormat=True ):
+ """
+ Lists all ports
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "ports"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def roles( self, jsonFormat=True ):
+ """
+ Lists all devices and the controllers with roles assigned to them
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "roles"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getRole( self, deviceId ):
+ """
+ Given the a string containing the json representation of the "roles"
+ cli command and a partial or whole device id, returns a json object
+ containing the roles output for the first device whose id contains
+ "device_id"
+
+ Returns:
+ A dict of the role assignments for the given device or
+ None if no match
+ """
+ try:
+ if deviceId is None:
+ return None
+ else:
+ rawRoles = self.roles()
+ rolesJson = json.loads( rawRoles )
+ # search json for the device with id then return the device
+ for device in rolesJson:
+ # print device
+ if str( deviceId ) in device[ 'id' ]:
+ return device
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawRoles ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def rolesNotNull( self ):
+ """
+ Iterates through each device and checks if there is a master assigned
+ Returns: main.TRUE if each device has a master
+ main.FALSE any device has no master
+ """
+ try:
+ rawRoles = self.roles()
+ rolesJson = json.loads( rawRoles )
+ # search json for the device with id then return the device
+ for device in rolesJson:
+ # print device
+ if device[ 'master' ] == "none":
+ main.log.warn( "Device has no master: " + str( device ) )
+ return main.FALSE
+ return main.TRUE
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawRoles ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def paths( self, srcId, dstId ):
+ """
+ Returns string of paths, and the cost.
+ Issues command: onos:paths <src> <dst>
+ """
+ try:
+ cmdStr = "onos:paths " + str( srcId ) + " " + str( dstId )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in getting paths" )
+ return ( handle, "Error" )
+ else:
+ path = handle.split( ";" )[ 0 ]
+ cost = handle.split( ";" )[ 1 ]
+ return ( path, cost )
+ except AssertionError:
+ main.log.exception( "" )
+ return ( handle, "Error" )
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return ( handle, "Error" )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def hosts( self, jsonFormat=True ):
+ """
+ Lists all discovered hosts
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "hosts"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in handle
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in handle
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( handle ) )
+ return None
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getHost( self, mac ):
+ """
+ Return the first host from the hosts api whose 'id' contains 'mac'
+
+ Note: mac must be a colon separated mac address, but could be a
+ partial mac address
+
+ Return None if there is no match
+ """
+ try:
+ if mac is None:
+ return None
+ else:
+ mac = mac
+ rawHosts = self.hosts()
+ hostsJson = json.loads( rawHosts )
+ # search json for the host with mac then return the device
+ for host in hostsJson:
+ # print "%s in %s?" % ( mac, host[ 'id' ] )
+ if not host:
+ pass
+ elif mac in host[ 'id' ]:
+ return host
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawHosts ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getHostsId( self, hostList ):
+ """
+ Obtain list of hosts
+ Issues command: 'onos> hosts'
+
+ Required:
+ * hostList: List of hosts obtained by Mininet
+ IMPORTANT:
+ This function assumes that you started your
+ topology with the option '--mac'.
+ Furthermore, it assumes that value of VLAN is '-1'
+ Description:
+ Converts mininet hosts ( h1, h2, h3... ) into
+ ONOS format ( 00:00:00:00:00:01/-1 , ... )
+ """
+ try:
+ onosHostList = []
+
+ for host in hostList:
+ host = host.replace( "h", "" )
+ hostHex = hex( int( host ) ).zfill( 12 )
+ hostHex = str( hostHex ).replace( 'x', '0' )
+ i = iter( str( hostHex ) )
+ hostHex = ":".join( a + b for a, b in zip( i, i ) )
+ hostHex = hostHex + "/-1"
+ onosHostList.append( hostHex )
+
+ return onosHostList
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addHostIntent( self, hostIdOne, hostIdTwo ):
+ """
+ Required:
+ * hostIdOne: ONOS host id for host1
+ * hostIdTwo: ONOS host id for host2
+ Description:
+ Adds a host-to-host intent ( bidirectional ) by
+ specifying the two hosts.
+ Returns:
+ A string of the intent id or None on Error
+ """
+ try:
+ cmdStr = "add-host-intent " + str( hostIdOne ) +\
+ " " + str( hostIdTwo )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding Host intent" )
+ main.log.debug( "Response from ONOS was: " + repr( handle ) )
+ return None
+ else:
+ main.log.info( "Host intent installed between " +
+ str( hostIdOne ) + " and " + str( hostIdTwo ) )
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ main.log.debug( "Response from ONOS was: " +
+ repr( handle ) )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addOpticalIntent( self, ingressDevice, egressDevice ):
+ """
+ Required:
+ * ingressDevice: device id of ingress device
+ * egressDevice: device id of egress device
+ Optional:
+ TODO: Still needs to be implemented via dev side
+ Description:
+ Adds an optical intent by specifying an ingress and egress device
+ Returns:
+ A string of the intent id or None on error
+ """
+ try:
+ cmdStr = "add-optical-intent " + str( ingressDevice ) +\
+ " " + str( egressDevice )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ # If error, return error message
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding Optical intent" )
+ return None
+ else:
+ main.log.info( "Optical intent installed between " +
+ str( ingressDevice ) + " and " +
+ str( egressDevice ) )
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addPointIntent(
+ self,
+ ingressDevice,
+ egressDevice,
+ portIngress="",
+ portEgress="",
+ ethType="",
+ ethSrc="",
+ ethDst="",
+ bandwidth="",
+ lambdaAlloc=False,
+ ipProto="",
+ ipSrc="",
+ ipDst="",
+ tcpSrc="",
+ tcpDst="" ):
+ """
+ Required:
+ * ingressDevice: device id of ingress device
+ * egressDevice: device id of egress device
+ Optional:
+ * ethType: specify ethType
+ * ethSrc: specify ethSrc ( i.e. src mac addr )
+ * ethDst: specify ethDst ( i.e. dst mac addr )
+ * bandwidth: specify bandwidth capacity of link
+ * lambdaAlloc: if True, intent will allocate lambda
+ for the specified intent
+ * ipProto: specify ip protocol
+ * ipSrc: specify ip source address
+ * ipDst: specify ip destination address
+ * tcpSrc: specify tcp source port
+ * tcpDst: specify tcp destination port
+ Description:
+ Adds a point-to-point intent ( uni-directional ) by
+ specifying device id's and optional fields
+ Returns:
+ A string of the intent id or None on error
+
+ NOTE: This function may change depending on the
+ options developers provide for point-to-point
+ intent via cli
+ """
+ try:
+ # If there are no optional arguments
+ if not ethType and not ethSrc and not ethDst\
+ and not bandwidth and not lambdaAlloc \
+ and not ipProto and not ipSrc and not ipDst \
+ and not tcpSrc and not tcpDst:
+ cmd = "add-point-intent"
+
+ else:
+ cmd = "add-point-intent"
+
+ if ethType:
+ cmd += " --ethType " + str( ethType )
+ if ethSrc:
+ cmd += " --ethSrc " + str( ethSrc )
+ if ethDst:
+ cmd += " --ethDst " + str( ethDst )
+ if bandwidth:
+ cmd += " --bandwidth " + str( bandwidth )
+ if lambdaAlloc:
+ cmd += " --lambda "
+ if ipProto:
+ cmd += " --ipProto " + str( ipProto )
+ if ipSrc:
+ cmd += " --ipSrc " + str( ipSrc )
+ if ipDst:
+ cmd += " --ipDst " + str( ipDst )
+ if tcpSrc:
+ cmd += " --tcpSrc " + str( tcpSrc )
+ if tcpDst:
+ cmd += " --tcpDst " + str( tcpDst )
+
+ # Check whether the user appended the port
+ # or provided it as an input
+ if "/" in ingressDevice:
+ cmd += " " + str( ingressDevice )
+ else:
+ if not portIngress:
+ main.log.error( "You must specify the ingress port" )
+ # TODO: perhaps more meaningful return
+ # Would it make sense to throw an exception and exit
+ # the test?
+ return None
+
+ cmd += " " + \
+ str( ingressDevice ) + "/" +\
+ str( portIngress ) + " "
+
+ if "/" in egressDevice:
+ cmd += " " + str( egressDevice )
+ else:
+ if not portEgress:
+ main.log.error( "You must specify the egress port" )
+ return None
+
+ cmd += " " +\
+ str( egressDevice ) + "/" +\
+ str( portEgress )
+
+ handle = self.sendline( cmd )
+ assert "Command not found:" not in handle, handle
+ # If error, return error message
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding point-to-point intent" )
+ return None
+ else:
+ # TODO: print out all the options in this message?
+ main.log.info( "Point-to-point intent installed between " +
+ str( ingressDevice ) + " and " +
+ str( egressDevice ) )
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addMultipointToSinglepointIntent(
+ self,
+ ingressDeviceList,
+ egressDevice,
+ portIngressList=None,
+ portEgress="",
+ ethType="",
+ ethSrc="",
+ ethDst="",
+ bandwidth="",
+ lambdaAlloc=False,
+ ipProto="",
+ ipSrc="",
+ ipDst="",
+ tcpSrc="",
+ tcpDst="",
+ setEthSrc="",
+ setEthDst="" ):
+ """
+ Note:
+ This function assumes the format of all ingress devices
+ is same. That is, all ingress devices include port numbers
+ with a "/" or all ingress devices could specify device
+ ids and port numbers seperately.
+ Required:
+ * ingressDeviceList: List of device ids of ingress device
+ ( Atleast 2 ingress devices required in the list )
+ * egressDevice: device id of egress device
+ Optional:
+ * ethType: specify ethType
+ * ethSrc: specify ethSrc ( i.e. src mac addr )
+ * ethDst: specify ethDst ( i.e. dst mac addr )
+ * bandwidth: specify bandwidth capacity of link
+ * lambdaAlloc: if True, intent will allocate lambda
+ for the specified intent
+ * ipProto: specify ip protocol
+ * ipSrc: specify ip source address
+ * ipDst: specify ip destination address
+ * tcpSrc: specify tcp source port
+ * tcpDst: specify tcp destination port
+ * setEthSrc: action to Rewrite Source MAC Address
+ * setEthDst: action to Rewrite Destination MAC Address
+ Description:
+ Adds a multipoint-to-singlepoint intent ( uni-directional ) by
+ specifying device id's and optional fields
+ Returns:
+ A string of the intent id or None on error
+
+ NOTE: This function may change depending on the
+ options developers provide for multipoint-to-singlepoint
+ intent via cli
+ """
+ try:
+ # If there are no optional arguments
+ if not ethType and not ethSrc and not ethDst\
+ and not bandwidth and not lambdaAlloc\
+ and not ipProto and not ipSrc and not ipDst\
+ and not tcpSrc and not tcpDst and not setEthSrc\
+ and not setEthDst:
+ cmd = "add-multi-to-single-intent"
+
+ else:
+ cmd = "add-multi-to-single-intent"
+
+ if ethType:
+ cmd += " --ethType " + str( ethType )
+ if ethSrc:
+ cmd += " --ethSrc " + str( ethSrc )
+ if ethDst:
+ cmd += " --ethDst " + str( ethDst )
+ if bandwidth:
+ cmd += " --bandwidth " + str( bandwidth )
+ if lambdaAlloc:
+ cmd += " --lambda "
+ if ipProto:
+ cmd += " --ipProto " + str( ipProto )
+ if ipSrc:
+ cmd += " --ipSrc " + str( ipSrc )
+ if ipDst:
+ cmd += " --ipDst " + str( ipDst )
+ if tcpSrc:
+ cmd += " --tcpSrc " + str( tcpSrc )
+ if tcpDst:
+ cmd += " --tcpDst " + str( tcpDst )
+ if setEthSrc:
+ cmd += " --setEthSrc " + str( setEthSrc )
+ if setEthDst:
+ cmd += " --setEthDst " + str( setEthDst )
+
+ # Check whether the user appended the port
+ # or provided it as an input
+
+ if portIngressList is None:
+ for ingressDevice in ingressDeviceList:
+ if "/" in ingressDevice:
+ cmd += " " + str( ingressDevice )
+ else:
+ main.log.error( "You must specify " +
+ "the ingress port" )
+ # TODO: perhaps more meaningful return
+ return main.FALSE
+ else:
+ if len( ingressDeviceList ) == len( portIngressList ):
+ for ingressDevice, portIngress in zip( ingressDeviceList,
+ portIngressList ):
+ cmd += " " + \
+ str( ingressDevice ) + "/" +\
+ str( portIngress ) + " "
+ else:
+ main.log.error( "Device list and port list does not " +
+ "have the same length" )
+ return main.FALSE
+ if "/" in egressDevice:
+ cmd += " " + str( egressDevice )
+ else:
+ if not portEgress:
+ main.log.error( "You must specify " +
+ "the egress port" )
+ return main.FALSE
+
+ cmd += " " +\
+ str( egressDevice ) + "/" +\
+ str( portEgress )
+ handle = self.sendline( cmd )
+ assert "Command not found:" not in handle, handle
+ # If error, return error message
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding multipoint-to-singlepoint " +
+ "intent" )
+ return None
+ else:
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addSinglepointToMultipointIntent(
+ self,
+ ingressDevice,
+ egressDeviceList,
+ portIngress="",
+ portEgressList=None,
+ ethType="",
+ ethSrc="",
+ ethDst="",
+ bandwidth="",
+ lambdaAlloc=False,
+ ipProto="",
+ ipSrc="",
+ ipDst="",
+ tcpSrc="",
+ tcpDst="",
+ setEthSrc="",
+ setEthDst="" ):
+ """
+ Note:
+ This function assumes the format of all egress devices
+ is same. That is, all egress devices include port numbers
+ with a "/" or all egress devices could specify device
+ ids and port numbers seperately.
+ Required:
+ * EgressDeviceList: List of device ids of egress device
+ ( Atleast 2 eress devices required in the list )
+ * ingressDevice: device id of ingress device
+ Optional:
+ * ethType: specify ethType
+ * ethSrc: specify ethSrc ( i.e. src mac addr )
+ * ethDst: specify ethDst ( i.e. dst mac addr )
+ * bandwidth: specify bandwidth capacity of link
+ * lambdaAlloc: if True, intent will allocate lambda
+ for the specified intent
+ * ipProto: specify ip protocol
+ * ipSrc: specify ip source address
+ * ipDst: specify ip destination address
+ * tcpSrc: specify tcp source port
+ * tcpDst: specify tcp destination port
+ * setEthSrc: action to Rewrite Source MAC Address
+ * setEthDst: action to Rewrite Destination MAC Address
+ Description:
+ Adds a singlepoint-to-multipoint intent ( uni-directional ) by
+ specifying device id's and optional fields
+ Returns:
+ A string of the intent id or None on error
+
+ NOTE: This function may change depending on the
+ options developers provide for singlepoint-to-multipoint
+ intent via cli
+ """
+ try:
+ # If there are no optional arguments
+ if not ethType and not ethSrc and not ethDst\
+ and not bandwidth and not lambdaAlloc\
+ and not ipProto and not ipSrc and not ipDst\
+ and not tcpSrc and not tcpDst and not setEthSrc\
+ and not setEthDst:
+ cmd = "add-single-to-multi-intent"
+
+ else:
+ cmd = "add-single-to-multi-intent"
+
+ if ethType:
+ cmd += " --ethType " + str( ethType )
+ if ethSrc:
+ cmd += " --ethSrc " + str( ethSrc )
+ if ethDst:
+ cmd += " --ethDst " + str( ethDst )
+ if bandwidth:
+ cmd += " --bandwidth " + str( bandwidth )
+ if lambdaAlloc:
+ cmd += " --lambda "
+ if ipProto:
+ cmd += " --ipProto " + str( ipProto )
+ if ipSrc:
+ cmd += " --ipSrc " + str( ipSrc )
+ if ipDst:
+ cmd += " --ipDst " + str( ipDst )
+ if tcpSrc:
+ cmd += " --tcpSrc " + str( tcpSrc )
+ if tcpDst:
+ cmd += " --tcpDst " + str( tcpDst )
+ if setEthSrc:
+ cmd += " --setEthSrc " + str( setEthSrc )
+ if setEthDst:
+ cmd += " --setEthDst " + str( setEthDst )
+
+ # Check whether the user appended the port
+ # or provided it as an input
+
+ if "/" in ingressDevice:
+ cmd += " " + str( ingressDevice )
+ else:
+ if not portIngress:
+ main.log.error( "You must specify " +
+ "the Ingress port" )
+ return main.FALSE
+
+ cmd += " " +\
+ str( ingressDevice ) + "/" +\
+ str( portIngress )
+
+ if portEgressList is None:
+ for egressDevice in egressDeviceList:
+ if "/" in egressDevice:
+ cmd += " " + str( egressDevice )
+ else:
+ main.log.error( "You must specify " +
+ "the egress port" )
+ # TODO: perhaps more meaningful return
+ return main.FALSE
+ else:
+ if len( egressDeviceList ) == len( portEgressList ):
+ for egressDevice, portEgress in zip( egressDeviceList,
+ portEgressList ):
+ cmd += " " + \
+ str( egressDevice ) + "/" +\
+ str( portEgress )
+ else:
+ main.log.error( "Device list and port list does not " +
+ "have the same length" )
+ return main.FALSE
+ handle = self.sendline( cmd )
+ assert "Command not found:" not in handle, handle
+ # If error, return error message
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding singlepoint-to-multipoint " +
+ "intent" )
+ return None
+ else:
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def addMplsIntent(
+ self,
+ ingressDevice,
+ egressDevice,
+ ingressPort="",
+ egressPort="",
+ ethType="",
+ ethSrc="",
+ ethDst="",
+ bandwidth="",
+ lambdaAlloc=False,
+ ipProto="",
+ ipSrc="",
+ ipDst="",
+ tcpSrc="",
+ tcpDst="",
+ ingressLabel="",
+ egressLabel="",
+ priority=""):
+ """
+ Required:
+ * ingressDevice: device id of ingress device
+ * egressDevice: device id of egress device
+ Optional:
+ * ethType: specify ethType
+ * ethSrc: specify ethSrc ( i.e. src mac addr )
+ * ethDst: specify ethDst ( i.e. dst mac addr )
+ * bandwidth: specify bandwidth capacity of link
+ * lambdaAlloc: if True, intent will allocate lambda
+ for the specified intent
+ * ipProto: specify ip protocol
+ * ipSrc: specify ip source address
+ * ipDst: specify ip destination address
+ * tcpSrc: specify tcp source port
+ * tcpDst: specify tcp destination port
+ * ingressLabel: Ingress MPLS label
+ * egressLabel: Egress MPLS label
+ Description:
+ Adds MPLS intent by
+ specifying device id's and optional fields
+ Returns:
+ A string of the intent id or None on error
+
+ NOTE: This function may change depending on the
+ options developers provide for MPLS
+ intent via cli
+ """
+ try:
+ # If there are no optional arguments
+ if not ethType and not ethSrc and not ethDst\
+ and not bandwidth and not lambdaAlloc \
+ and not ipProto and not ipSrc and not ipDst \
+ and not tcpSrc and not tcpDst and not ingressLabel \
+ and not egressLabel:
+ cmd = "add-mpls-intent"
+
+ else:
+ cmd = "add-mpls-intent"
+
+ if ethType:
+ cmd += " --ethType " + str( ethType )
+ if ethSrc:
+ cmd += " --ethSrc " + str( ethSrc )
+ if ethDst:
+ cmd += " --ethDst " + str( ethDst )
+ if bandwidth:
+ cmd += " --bandwidth " + str( bandwidth )
+ if lambdaAlloc:
+ cmd += " --lambda "
+ if ipProto:
+ cmd += " --ipProto " + str( ipProto )
+ if ipSrc:
+ cmd += " --ipSrc " + str( ipSrc )
+ if ipDst:
+ cmd += " --ipDst " + str( ipDst )
+ if tcpSrc:
+ cmd += " --tcpSrc " + str( tcpSrc )
+ if tcpDst:
+ cmd += " --tcpDst " + str( tcpDst )
+ if ingressLabel:
+ cmd += " --ingressLabel " + str( ingressLabel )
+ if egressLabel:
+ cmd += " --egressLabel " + str( egressLabel )
+ if priority:
+ cmd += " --priority " + str( priority )
+
+ # Check whether the user appended the port
+ # or provided it as an input
+ if "/" in ingressDevice:
+ cmd += " " + str( ingressDevice )
+ else:
+ if not ingressPort:
+ main.log.error( "You must specify the ingress port" )
+ return None
+
+ cmd += " " + \
+ str( ingressDevice ) + "/" +\
+ str( ingressPort ) + " "
+
+ if "/" in egressDevice:
+ cmd += " " + str( egressDevice )
+ else:
+ if not egressPort:
+ main.log.error( "You must specify the egress port" )
+ return None
+
+ cmd += " " +\
+ str( egressDevice ) + "/" +\
+ str( egressPort )
+
+ handle = self.sendline( cmd )
+ assert "Command not found:" not in handle, handle
+ # If error, return error message
+ if re.search( "Error", handle ):
+ main.log.error( "Error in adding mpls intent" )
+ return None
+ else:
+ # TODO: print out all the options in this message?
+ main.log.info( "MPLS intent installed between " +
+ str( ingressDevice ) + " and " +
+ str( egressDevice ) )
+ match = re.search('id=0x([\da-f]+),', handle)
+ if match:
+ return match.group()[3:-1]
+ else:
+ main.log.error( "Error, intent ID not found" )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def removeIntent( self, intentId, app='org.onosproject.cli',
+ purge=False, sync=False ):
+ """
+ Remove intent for specified application id and intent id
+ Optional args:-
+ -s or --sync: Waits for the removal before returning
+ -p or --purge: Purge the intent from the store after removal
+
+ Returns:
+ main.False on error and
+ cli output otherwise
+ """
+ try:
+ cmdStr = "remove-intent"
+ if purge:
+ cmdStr += " -p"
+ if sync:
+ cmdStr += " -s"
+
+ cmdStr += " " + app + " " + str( intentId )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in removing intent" )
+ return main.FALSE
+ else:
+ # TODO: Should this be main.TRUE
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def removeAllIntents( self, purge=False, sync=False, app='org.onosproject.cli' ):
+ """
+ Description:
+ Remove all the intents
+ Optional args:-
+ -s or --sync: Waits for the removal before returning
+ -p or --purge: Purge the intent from the store after removal
+ Returns:
+ Returns main.TRUE if all intents are removed, otherwise returns
+ main.FALSE; Returns None for exception
+ """
+ try:
+ cmdStr = "remove-intent"
+ if purge:
+ cmdStr += " -p"
+ if sync:
+ cmdStr += " -s"
+
+ cmdStr += " " + app
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in removing intent" )
+ return main.FALSE
+ else:
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def purgeWithdrawnIntents( self ):
+ """
+ Purges all WITHDRAWN Intents
+ """
+ try:
+ cmdStr = "purge-intents"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ main.log.error( "Error in purging intents" )
+ return main.FALSE
+ else:
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def routes( self, jsonFormat=False ):
+ """
+ NOTE: This method should be used after installing application:
+ onos-app-sdnip
+ Optional:
+ * jsonFormat: enable output formatting in json
+ Description:
+ Obtain all routes in the system
+ """
+ try:
+ cmdStr = "routes"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def ipv4RouteNumber( self ):
+ """
+ NOTE: This method should be used after installing application:
+ onos-app-sdnip
+ Description:
+ Obtain the total IPv4 routes number in the system
+ """
+ try:
+ cmdStr = "routes -s -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ jsonResult = json.loads( handle )
+ return jsonResult['totalRoutes4']
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, handle ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def intents( self, jsonFormat = True, summary = False, **intentargs):
+ """
+ Description:
+ Obtain intents from the ONOS cli.
+ Optional:
+ * jsonFormat: Enable output formatting in json, default to True
+ * summary: Whether only output the intent summary, defaults to False
+ * type: Only output a certain type of intent. This options is valid
+ only when jsonFormat is True and summary is True.
+ """
+ try:
+ cmdStr = "intents"
+ if summary:
+ cmdStr += " -s"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ args = utilities.parse_args( [ "TYPE" ], **intentargs )
+ if "TYPE" in args.keys():
+ intentType = args[ "TYPE" ]
+ else:
+ intentType = ""
+ # IF we want the summary of a specific intent type
+ if jsonFormat and summary and ( intentType != "" ):
+ jsonResult = json.loads( handle )
+ if intentType in jsonResult.keys():
+ return jsonResult[ intentType ]
+ else:
+ main.log.error( "unknown TYPE, returning all types of intents" )
+ return handle
+ else:
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, handle ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getIntentState(self, intentsId, intentsJson=None):
+ """
+ Check intent state.
+ Accepts a single intent ID (string type) or a list of intent IDs.
+ Returns the state(string type) of the id if a single intent ID is
+ accepted.
+ Returns a dictionary with intent IDs as the key and its
+ corresponding states as the values
+ Parameters:
+ intentId: intent ID (string type)
+ intentsJson: parsed json object from the onos:intents api
+ Returns:
+ state = An intent's state- INSTALL,WITHDRAWN etc.
+ stateDict = Dictionary of intent's state. intent ID as the keys and
+ state as the values.
+ """
+ try:
+ state = "State is Undefined"
+ if not intentsJson:
+ rawJson = self.intents()
+ else:
+ rawJson = intentsJson
+ parsedIntentsJson = json.loads( rawJson )
+ if isinstance( intentsId, types.StringType ):
+ for intent in parsedIntentsJson:
+ if intentsId == intent[ 'id' ]:
+ state = intent[ 'state' ]
+ return state
+ main.log.info( "Cannot find intent ID" + str( intentsId ) +
+ " on the list" )
+ return state
+ elif isinstance( intentsId, types.ListType ):
+ dictList = []
+ for i in xrange( len( intentsId ) ):
+ stateDict = {}
+ for intents in parsedIntentsJson:
+ if intentsId[ i ] == intents[ 'id' ]:
+ stateDict[ 'state' ] = intents[ 'state' ]
+ stateDict[ 'id' ] = intentsId[ i ]
+ dictList.append( stateDict )
+ break
+ if len( intentsId ) != len( dictList ):
+ main.log.info( "Cannot find some of the intent ID state" )
+ return dictList
+ else:
+ main.log.info( "Invalid intents ID entry" )
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawJson ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def checkIntentState( self, intentsId, expectedState='INSTALLED' ):
+ """
+ Description:
+ Check intents state
+ Required:
+ intentsId - List of intents ID to be checked
+ Optional:
+ expectedState - Check the expected state(s) of each intents
+ state in the list.
+ *NOTE: You can pass in a list of expected state,
+ Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
+ Return:
+ Returns main.TRUE only if all intent are the same as expected states
+ , otherwise, returns main.FALSE.
+ """
+ try:
+ # Generating a dictionary: intent id as a key and state as value
+ returnValue = main.TRUE
+ intentsDict = self.getIntentState( intentsId )
+ if len( intentsId ) != len( intentsDict ):
+ main.log.info( self.name + ": There is something wrong " +
+ "getting intents state" )
+ return main.FALSE
+
+ if isinstance( expectedState, types.StringType ):
+ for intents in intentsDict:
+ if intents.get( 'state' ) != expectedState:
+ main.log.debug( self.name + " : Intent ID - " +
+ intents.get( 'id' ) +
+ " actual state = " +
+ intents.get( 'state' )
+ + " does not equal expected state = "
+ + expectedState )
+ returnValue = main.FALSE
+
+ elif isinstance( expectedState, types.ListType ):
+ for intents in intentsDict:
+ if not any( state == intents.get( 'state' ) for state in
+ expectedState ):
+ main.log.debug( self.name + " : Intent ID - " +
+ intents.get( 'id' ) +
+ " actual state = " +
+ intents.get( 'state' ) +
+ " does not equal expected states = "
+ + str( expectedState ) )
+ returnValue = main.FALSE
+
+ if returnValue == main.TRUE:
+ main.log.info( self.name + ": All " +
+ str( len( intentsDict ) ) +
+ " intents are in " + str( expectedState ) +
+ " state" )
+ return returnValue
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def checkIntentSummary( self, timeout=60 ):
+ """
+ Description:
+ Check the number of installed intents.
+ Optional:
+ timeout - the timeout for pexcept
+ Return:
+ Returns main.TRUE only if the number of all installed intents are the same as total intents number
+ , otherwise, returns main.FALSE.
+ """
+
+ try:
+ cmd = "intents -s -j"
+
+ # Check response if something wrong
+ response = self.sendline( cmd, timeout=timeout )
+ if response == None:
+ return main.False
+ response = json.loads( response )
+
+ # get total and installed number, see if they are match
+ allState = response.get( 'all' )
+ if allState.get('total') == allState.get('installed'):
+ main.log.info( 'Total Intents: {} Installed Intents: {}'.format( allState.get('total'), allState.get('installed') ) )
+ return main.TRUE
+ main.log.info( 'Verified Intents failed Excepte intetnes: {} installed intents: {}'.format( allState.get('total'), allState.get('installed') ) )
+ return main.FALSE
+
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, response ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def flows( self, state="", jsonFormat=True, timeout=60 ):
+ """
+ Optional:
+ * jsonFormat: enable output formatting in json
+ Description:
+ Obtain flows currently installed
+ """
+ try:
+ cmdStr = "flows"
+ if jsonFormat:
+ cmdStr += " -j "
+ cmdStr += state
+ handle = self.sendline( cmdStr, timeout=timeout )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error:", handle ):
+ main.log.error( self.name + ": flows() response: " +
+ str( handle ) )
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": ONOS timeout" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+
+ def checkFlowsState( self, isPENDING=True, timeout=60 ):
+ """
+ Description:
+ Check the if all the current flows are in ADDED state
+ We check PENDING_ADD, PENDING_REMOVE, REMOVED, and FAILED flows,
+ if the count of those states is 0, which means all current flows
+ are in ADDED state, and return main.TRUE otherwise return main.FALSE
+ Optional:
+ * isPENDING: whether the PENDING_ADD is also a correct status
+ Return:
+ returnValue - Returns main.TRUE only if all flows are in
+ ADDED state or PENDING_ADD if the isPENDING
+ parameter is set true, return main.FALSE otherwise.
+ """
+ try:
+ states = ["PENDING_ADD", "PENDING_REMOVE", "REMOVED", "FAILED"]
+ checkedStates = []
+ statesCount = [0, 0, 0, 0]
+ for s in states:
+ rawFlows = self.flows( state=s, timeout = timeout )
+ checkedStates.append( json.loads( rawFlows ) )
+ for i in range( len( states ) ):
+ for c in checkedStates[i]:
+ try:
+ statesCount[i] += int( c.get( "flowCount" ) )
+ except TypeError:
+ main.log.exception( "Json object not as expected" )
+ main.log.info( states[i] + " flows: " + str( statesCount[i] ) )
+
+ # We want to count PENDING_ADD if isPENDING is true
+ if isPENDING:
+ if statesCount[1] + statesCount[2] + statesCount[3] > 0:
+ return main.FALSE
+ else:
+ if statesCount[0] + statesCount[1] + statesCount[2] + statesCount[3] > 0:
+ return main.FALSE
+ return main.TRUE
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawFlows ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def pushTestIntents( self, ingress, egress, batchSize, offset="",
+ options="", timeout=10, background = False ):
+ """
+ Description:
+ Push a number of intents in a batch format to
+ a specific point-to-point intent definition
+ Required:
+ * ingress: specify source dpid
+ * egress: specify destination dpid
+ * batchSize: specify number of intents to push
+ Optional:
+ * offset: the keyOffset is where the next batch of intents
+ will be installed
+ Returns: If failed to push test intents, it will returen None,
+ if successful, return true.
+ Timeout expection will return None,
+ TypeError will return false
+ other expections will exit()
+ """
+ try:
+ if background:
+ back = "&"
+ else:
+ back = ""
+ cmd = "push-test-intents {} {} {} {} {} {}".format( options,
+ ingress,
+ egress,
+ batchSize,
+ offset,
+ back )
+ response = self.sendline( cmd, timeout=timeout )
+ assert "Command not found:" not in response, response
+ main.log.info( response )
+ if response == None:
+ return None
+
+ # TODO: We should handle if there is failure in installation
+ return main.TRUE
+
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except pexpect.TIMEOUT:
+ main.log.error( self.name + ": ONOS timeout" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except 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 getTotalFlowsNum( self, timeout=60 ):
+ """
+ Description:
+ Get the number of ADDED flows.
+ Return:
+ The number of ADDED flows
+ """
+
+ try:
+ # get total added flows number
+ cmd = "flows -s|grep ADDED|wc -l"
+ totalFlows = self.sendline( cmd, timeout=timeout )
+
+ if totalFlows == None:
+ # if timeout, we will get total number of all flows, and subtract other states
+ states = ["PENDING_ADD", "PENDING_REMOVE", "REMOVED", "FAILED"]
+ checkedStates = []
+ totalFlows = 0
+ statesCount = [0, 0, 0, 0]
+
+ # get total flows from summary
+ response = json.loads( self.sendline( "summary -j", timeout=timeout ) )
+ totalFlows = int( response.get("flows") )
+
+ for s in states:
+ rawFlows = self.flows( state=s, timeout = timeout )
+ if rawFlows == None:
+ # if timeout, return the total flows number from summary command
+ return totalFlows
+ checkedStates.append( json.loads( rawFlows ) )
+
+ # Calculate ADDED flows number, equal total subtracts others
+ for i in range( len( states ) ):
+ for c in checkedStates[i]:
+ try:
+ statesCount[i] += int( c.get( "flowCount" ) )
+ except TypeError:
+ main.log.exception( "Json object not as expected" )
+ totalFlows = totalFlows - int( statesCount[i] )
+ main.log.info( states[i] + " flows: " + str( statesCount[i] ) )
+
+ return totalFlows
+
+ return totalFlows
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getTotalIntentsNum( self ):
+ """
+ Description:
+ Get the total number of intents, include every states.
+ Return:
+ The number of intents
+ """
+ try:
+ cmd = "summary -j"
+ response = self.sendline( cmd )
+ if response == None:
+ return -1
+ response = json.loads( response )
+ return int( response.get("intents") )
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def intentsEventsMetrics( self, jsonFormat=True ):
+ """
+ Description:Returns topology metrics
+ Optional:
+ * jsonFormat: enable json formatting of output
+ """
+ try:
+ cmdStr = "intents-events-metrics"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def topologyEventsMetrics( self, jsonFormat=True ):
+ """
+ Description:Returns topology metrics
+ Optional:
+ * jsonFormat: enable json formatting of output
+ """
+ try:
+ cmdStr = "topology-events-metrics"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if handle:
+ return handle
+ elif jsonFormat:
+ # Return empty json
+ return '{}'
+ else:
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ # Wrapper functions ****************
+ # Wrapper functions use existing driver
+ # functions and extends their use case.
+ # For example, we may use the output of
+ # a normal driver function, and parse it
+ # using a wrapper function
+
+ def getAllIntentsId( self ):
+ """
+ Description:
+ Obtain all intent id's in a list
+ """
+ try:
+ # Obtain output of intents function
+ intentsStr = self.intents(jsonFormat=False)
+ intentIdList = []
+
+ # Parse the intents output for ID's
+ intentsList = [ s.strip() for s in intentsStr.splitlines() ]
+ for intents in intentsList:
+ match = re.search('id=0x([\da-f]+),', intents)
+ if match:
+ tmpId = match.group()[3:-1]
+ intentIdList.append( tmpId )
+ return intentIdList
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def FlowAddedCount( self, deviceId ):
+ """
+ Determine the number of flow rules for the given device id that are
+ in the added state
+ """
+ try:
+ cmdStr = "flows any " + str( deviceId ) + " | " +\
+ "grep 'state=ADDED' | wc -l"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getAllDevicesId( self ):
+ """
+ Use 'devices' function to obtain list of all devices
+ and parse the result to obtain a list of all device
+ id's. Returns this list. Returns empty list if no
+ devices exist
+ List is ordered sequentially
+
+ This function may be useful if you are not sure of the
+ device id, and wish to execute other commands using
+ the ids. By obtaining the list of device ids on the fly,
+ you can iterate through the list to get mastership, etc.
+ """
+ try:
+ # Call devices and store result string
+ devicesStr = self.devices( jsonFormat=False )
+ idList = []
+
+ if not devicesStr:
+ main.log.info( "There are no devices to get id from" )
+ return idList
+
+ # Split the string into list by comma
+ deviceList = devicesStr.split( "," )
+ # Get temporary list of all arguments with string 'id='
+ tempList = [ dev for dev in deviceList if "id=" in dev ]
+ # Split list further into arguments before and after string
+ # 'id='. Get the latter portion ( the actual device id ) and
+ # append to idList
+ for arg in tempList:
+ idList.append( arg.split( "id=" )[ 1 ] )
+ return idList
+
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getAllNodesId( self ):
+ """
+ Uses 'nodes' function to obtain list of all nodes
+ and parse the result of nodes to obtain just the
+ node id's.
+ Returns:
+ list of node id's
+ """
+ try:
+ nodesStr = self.nodes( jsonFormat=True )
+ idList = []
+ # Sample nodesStr output
+ # id=local, address=127.0.0.1:9876, state=READY *
+ if not nodesStr:
+ main.log.info( "There are no nodes to get id from" )
+ return idList
+ nodesJson = json.loads( nodesStr )
+ idList = [ node.get('id') for node in nodesJson ]
+ return idList
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, nodesStr ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getDevice( self, dpid=None ):
+ """
+ Return the first device from the devices api whose 'id' contains 'dpid'
+ Return None if there is no match
+ """
+ try:
+ if dpid is None:
+ return None
+ else:
+ dpid = dpid.replace( ':', '' )
+ rawDevices = self.devices()
+ devicesJson = json.loads( rawDevices )
+ # search json for the device with dpid then return the device
+ for device in devicesJson:
+ # print "%s in %s?" % ( dpid, device[ 'id' ] )
+ if dpid in device[ 'id' ]:
+ return device
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawDevices ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def checkStatus( self, ip, numoswitch, numolink, logLevel="info" ):
+ """
+ Checks the number of switches & links that ONOS sees against the
+ supplied values. By default this will report to main.log, but the
+ log level can be specified.
+
+ Params: ip = ip used for the onos cli
+ numoswitch = expected number of switches
+ numolink = expected number of links
+ logLevel = level to log to. Currently accepts
+ 'info', 'warn' and 'report'
+
+
+ logLevel can
+
+ Returns: main.TRUE if the number of switches and links are correct,
+ main.FALSE if the number of switches and links is incorrect,
+ and main.ERROR otherwise
+ """
+ try:
+ topology = self.getTopology( ip )
+ if topology == {}:
+ return main.ERROR
+ output = ""
+ # Is the number of switches is what we expected
+ devices = topology.get( 'devices', False )
+ links = topology.get( 'links', False )
+ if devices is False or links is False:
+ return main.ERROR
+ switchCheck = ( int( devices ) == int( numoswitch ) )
+ # Is the number of links is what we expected
+ linkCheck = ( int( links ) == int( numolink ) )
+ if ( switchCheck and linkCheck ):
+ # We expected the correct numbers
+ output += "The number of links and switches match " +\
+ "what was expected"
+ result = main.TRUE
+ else:
+ output += "The number of links and switches does not match " +\
+ "what was expected"
+ result = main.FALSE
+ output = output + "\n ONOS sees %i devices (%i expected) \
+ and %i links (%i expected)" % (
+ int( devices ), int( numoswitch ), int( links ),
+ int( numolink ) )
+ if logLevel == "report":
+ main.log.report( output )
+ elif logLevel == "warn":
+ main.log.warn( output )
+ else:
+ main.log.info( self.name + ": " + output )
+ return result
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def deviceRole( self, deviceId, onosNode, role="master" ):
+ """
+ Calls the device-role cli command.
+ deviceId must be the id of a device as seen in the onos devices command
+ onosNode is the ip of one of the onos nodes in the cluster
+ role must be either master, standby, or none
+
+ Returns:
+ main.TRUE or main.FALSE based on argument verification and
+ main.ERROR if command returns and error
+ """
+ try:
+ if role.lower() == "master" or role.lower() == "standby" or\
+ role.lower() == "none":
+ cmdStr = "device-role " +\
+ str( deviceId ) + " " +\
+ str( onosNode ) + " " +\
+ str( role )
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ if re.search( "Error", handle ):
+ # end color output to escape any colours
+ # from the cli
+ main.log.error( self.name + ": " +
+ handle + '\033[0m' )
+ return main.ERROR
+ return main.TRUE
+ else:
+ main.log.error( "Invalid 'role' given to device_role(). " +
+ "Value was '" + str(role) + "'." )
+ return main.FALSE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def clusters( self, jsonFormat=True ):
+ """
+ Lists all clusters
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "clusters"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def electionTestLeader( self ):
+ """
+ CLI command to get the current leader for the Election test application
+ NOTE: Requires installation of the onos-app-election feature
+ Returns: Node IP of the leader if one exists
+ None if none exists
+ Main.FALSE on error
+ """
+ try:
+ cmdStr = "election-test-leader"
+ response = self.sendline( cmdStr )
+ assert "Command not found:" not in response, response
+ # Leader
+ leaderPattern = "The\scurrent\sleader\sfor\sthe\sElection\s" +\
+ "app\sis\s(?P<node>.+)\."
+ nodeSearch = re.search( leaderPattern, response )
+ if nodeSearch:
+ node = nodeSearch.group( 'node' )
+ main.log.info( "Election-test-leader on " + str( self.name ) +
+ " found " + node + " as the leader" )
+ return node
+ # no leader
+ nullPattern = "There\sis\scurrently\sno\sleader\selected\sfor\s" +\
+ "the\sElection\sapp"
+ nullSearch = re.search( nullPattern, response )
+ if nullSearch:
+ main.log.info( "Election-test-leader found no leader on " +
+ self.name )
+ return None
+ # error
+ errorPattern = "Command\snot\sfound"
+ if re.search( errorPattern, response ):
+ main.log.error( "Election app is not loaded on " + self.name )
+ # TODO: Should this be main.ERROR?
+ return main.FALSE
+ else:
+ main.log.error( "Error in electionTestLeader on " + self.name +
+ ": " + "unexpected response" )
+ main.log.error( repr( response ) )
+ return main.FALSE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def electionTestRun( self ):
+ """
+ CLI command to run for leadership of the Election test application.
+ NOTE: Requires installation of the onos-app-election feature
+ Returns: Main.TRUE on success
+ Main.FALSE on error
+ """
+ try:
+ cmdStr = "election-test-run"
+ response = self.sendline( cmdStr )
+ assert "Command not found:" not in response, response
+ # success
+ successPattern = "Entering\sleadership\selections\sfor\sthe\s" +\
+ "Election\sapp."
+ search = re.search( successPattern, response )
+ if search:
+ main.log.info( self.name + " entering leadership elections " +
+ "for the Election app." )
+ return main.TRUE
+ # error
+ errorPattern = "Command\snot\sfound"
+ if re.search( errorPattern, response ):
+ main.log.error( "Election app is not loaded on " + self.name )
+ return main.FALSE
+ else:
+ main.log.error( "Error in electionTestRun on " + self.name +
+ ": " + "unexpected response" )
+ main.log.error( repr( response ) )
+ return main.FALSE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def electionTestWithdraw( self ):
+ """
+ * CLI command to withdraw the local node from leadership election for
+ * the Election test application.
+ #NOTE: Requires installation of the onos-app-election feature
+ Returns: Main.TRUE on success
+ Main.FALSE on error
+ """
+ try:
+ cmdStr = "election-test-withdraw"
+ response = self.sendline( cmdStr )
+ assert "Command not found:" not in response, response
+ # success
+ successPattern = "Withdrawing\sfrom\sleadership\selections\sfor" +\
+ "\sthe\sElection\sapp."
+ if re.search( successPattern, response ):
+ main.log.info( self.name + " withdrawing from leadership " +
+ "elections for the Election app." )
+ return main.TRUE
+ # error
+ errorPattern = "Command\snot\sfound"
+ if re.search( errorPattern, response ):
+ main.log.error( "Election app is not loaded on " + self.name )
+ return main.FALSE
+ else:
+ main.log.error( "Error in electionTestWithdraw on " +
+ self.name + ": " + "unexpected response" )
+ main.log.error( repr( response ) )
+ return main.FALSE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getDevicePortsEnabledCount( self, dpid ):
+ """
+ Get the count of all enabled ports on a particular device/switch
+ """
+ try:
+ dpid = str( dpid )
+ cmdStr = "onos:ports -e " + dpid + " | wc -l"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ if re.search( "No such device", output ):
+ main.log.error( "Error in getting ports" )
+ return ( output, "Error" )
+ else:
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return ( output, "Error" )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getDeviceLinksActiveCount( self, dpid ):
+ """
+ Get the count of all enabled ports on a particular device/switch
+ """
+ try:
+ dpid = str( dpid )
+ cmdStr = "onos:links " + dpid + " | grep ACTIVE | wc -l"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ if re.search( "No such device", output ):
+ main.log.error( "Error in getting ports " )
+ return ( output, "Error " )
+ else:
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return ( output, "Error " )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getAllIntentIds( self ):
+ """
+ Return a list of all Intent IDs
+ """
+ try:
+ cmdStr = "onos:intents | grep id="
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ if re.search( "Error", output ):
+ main.log.error( "Error in getting ports" )
+ return ( output, "Error" )
+ else:
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return ( output, "Error" )
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def intentSummary( self ):
+ """
+ Returns a dictionary containing the current intent states and the count
+ """
+ try:
+ intents = self.intents( )
+ states = []
+ for intent in json.loads( intents ):
+ states.append( intent.get( 'state', None ) )
+ out = [ ( i, states.count( i ) ) for i in set( states ) ]
+ main.log.info( dict( out ) )
+ return dict( out )
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, intents ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def leaders( self, jsonFormat=True ):
+ """
+ Returns the output of the leaders command.
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "onos:leaders"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def leaderCandidates( self, jsonFormat=True ):
+ """
+ Returns the output of the leaders -c command.
+ Optional argument:
+ * jsonFormat - boolean indicating if you want output in json
+ """
+ try:
+ cmdStr = "onos:leaders -c"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def specificLeaderCandidate( self, topic ):
+ """
+ Returns a list in format [leader,candidate1,candidate2,...] for a given
+ topic parameter and an empty list if the topic doesn't exist
+ If no leader is elected leader in the returned list will be "none"
+ Returns None if there is a type error processing the json object
+ """
+ try:
+ cmdStr = "onos:leaders -j"
+ rawOutput = self.sendline( cmdStr )
+ assert "Command not found:" not in rawOutput, rawOutput
+ output = json.loads( rawOutput )
+ results = []
+ for dict in output:
+ if dict["topic"] == topic:
+ leader = dict["leader"]
+ candidates = re.split( ", ", dict["candidates"][1:-1] )
+ results.append( leader )
+ results.extend( candidates )
+ return results
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawOutput ) )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def pendingMap( self, jsonFormat=True ):
+ """
+ Returns the output of the intent Pending map.
+ """
+ try:
+ cmdStr = "onos:intents -p"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def partitions( self, jsonFormat=True ):
+ """
+ Returns the output of the raft partitions command for ONOS.
+ """
+ # Sample JSON
+ # {
+ # "leader": "tcp://10.128.30.11:7238",
+ # "members": [
+ # "tcp://10.128.30.11:7238",
+ # "tcp://10.128.30.17:7238",
+ # "tcp://10.128.30.13:7238",
+ # ],
+ # "name": "p1",
+ # "term": 3
+ # },
+ try:
+ cmdStr = "onos:partitions"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def apps( self, jsonFormat=True ):
+ """
+ Returns the output of the apps command for ONOS. This command lists
+ information about installed ONOS applications
+ """
+ # Sample JSON object
+ # [{"name":"org.onosproject.openflow","id":0,"version":"1.2.0",
+ # "description":"ONOS OpenFlow protocol southbound providers",
+ # "origin":"ON.Lab","permissions":"[]","featuresRepo":"",
+ # "features":"[onos-openflow]","state":"ACTIVE"}]
+ try:
+ cmdStr = "onos:apps"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ return output
+ # FIXME: look at specific exceptions/Errors
+ except AssertionError:
+ main.log.exception( "Error in processing onos:app command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def appStatus( self, appName ):
+ """
+ Uses the onos:apps cli command to return the status of an application.
+ Returns:
+ "ACTIVE" - If app is installed and activated
+ "INSTALLED" - If app is installed and deactivated
+ "UNINSTALLED" - If app is not installed
+ None - on error
+ """
+ try:
+ if not isinstance( appName, types.StringType ):
+ main.log.error( self.name + ".appStatus(): appName must be" +
+ " a string" )
+ return None
+ output = self.apps( jsonFormat=True )
+ appsJson = json.loads( output )
+ state = None
+ for app in appsJson:
+ if appName == app.get('name'):
+ state = app.get('state')
+ break
+ if state == "ACTIVE" or state == "INSTALLED":
+ return state
+ elif state is None:
+ return "UNINSTALLED"
+ elif state:
+ main.log.error( "Unexpected state from 'onos:apps': " +
+ str( state ) )
+ return state
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, output ) )
+ main.stop()
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def app( self, appName, option ):
+ """
+ Interacts with the app command for ONOS. This command manages
+ application inventory.
+ """
+ try:
+ # Validate argument types
+ valid = True
+ if not isinstance( appName, types.StringType ):
+ main.log.error( self.name + ".app(): appName must be a " +
+ "string" )
+ valid = False
+ if not isinstance( option, types.StringType ):
+ main.log.error( self.name + ".app(): option must be a string" )
+ valid = False
+ if not valid:
+ return main.FALSE
+ # Validate Option
+ option = option.lower()
+ # NOTE: Install may become a valid option
+ if option == "activate":
+ pass
+ elif option == "deactivate":
+ pass
+ elif option == "uninstall":
+ pass
+ else:
+ # Invalid option
+ main.log.error( "The ONOS app command argument only takes " +
+ "the values: (activate|deactivate|uninstall)" +
+ "; was given '" + option + "'")
+ return main.FALSE
+ cmdStr = "onos:app " + option + " " + appName
+ output = self.sendline( cmdStr )
+ if "Error executing command" in output:
+ main.log.error( "Error in processing onos:app command: " +
+ str( output ) )
+ return main.FALSE
+ elif "No such application" in output:
+ main.log.error( "The application '" + appName +
+ "' is not installed in ONOS" )
+ return main.FALSE
+ elif "Command not found:" in output:
+ main.log.error( "Error in processing onos:app command: " +
+ str( output ) )
+ return main.FALSE
+ elif "Unsupported command:" in output:
+ main.log.error( "Incorrect command given to 'app': " +
+ str( output ) )
+ # NOTE: we may need to add more checks here
+ # else: Command was successful
+ # main.log.debug( "app response: " + repr( output ) )
+ return main.TRUE
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def activateApp( self, appName, check=True ):
+ """
+ Activate an app that is already installed in ONOS
+ appName is the hierarchical app name, not the feature name
+ If check is True, method will check the status of the app after the
+ command is issued
+ Returns main.TRUE if the command was successfully sent
+ main.FALSE if the cli responded with an error or given
+ incorrect input
+ """
+ try:
+ if not isinstance( appName, types.StringType ):
+ main.log.error( self.name + ".activateApp(): appName must be" +
+ " a string" )
+ return main.FALSE
+ status = self.appStatus( appName )
+ if status == "INSTALLED":
+ response = self.app( appName, "activate" )
+ if check and response == main.TRUE:
+ for i in range(10): # try 10 times then give up
+ status = self.appStatus( appName )
+ if status == "ACTIVE":
+ return main.TRUE
+ else:
+ main.log.debug( "The state of application " +
+ appName + " is " + status )
+ time.sleep( 1 )
+ return main.FALSE
+ else: # not 'check' or command didn't succeed
+ return response
+ elif status == "ACTIVE":
+ return main.TRUE
+ elif status == "UNINSTALLED":
+ main.log.error( self.name + ": Tried to activate the " +
+ "application '" + appName + "' which is not " +
+ "installed." )
+ else:
+ main.log.error( "Unexpected return value from appStatus: " +
+ str( status ) )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def deactivateApp( self, appName, check=True ):
+ """
+ Deactivate an app that is already activated in ONOS
+ appName is the hierarchical app name, not the feature name
+ If check is True, method will check the status of the app after the
+ command is issued
+ Returns main.TRUE if the command was successfully sent
+ main.FALSE if the cli responded with an error or given
+ incorrect input
+ """
+ try:
+ if not isinstance( appName, types.StringType ):
+ main.log.error( self.name + ".deactivateApp(): appName must " +
+ "be a string" )
+ return main.FALSE
+ status = self.appStatus( appName )
+ if status == "INSTALLED":
+ return main.TRUE
+ elif status == "ACTIVE":
+ response = self.app( appName, "deactivate" )
+ if check and response == main.TRUE:
+ for i in range(10): # try 10 times then give up
+ status = self.appStatus( appName )
+ if status == "INSTALLED":
+ return main.TRUE
+ else:
+ time.sleep( 1 )
+ return main.FALSE
+ else: # not check or command didn't succeed
+ return response
+ elif status == "UNINSTALLED":
+ main.log.warn( self.name + ": Tried to deactivate the " +
+ "application '" + appName + "' which is not " +
+ "installed." )
+ return main.TRUE
+ else:
+ main.log.error( "Unexpected return value from appStatus: " +
+ str( status ) )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def uninstallApp( self, appName, check=True ):
+ """
+ Uninstall an app that is already installed in ONOS
+ appName is the hierarchical app name, not the feature name
+ If check is True, method will check the status of the app after the
+ command is issued
+ Returns main.TRUE if the command was successfully sent
+ main.FALSE if the cli responded with an error or given
+ incorrect input
+ """
+ # TODO: check with Thomas about the state machine for apps
+ try:
+ if not isinstance( appName, types.StringType ):
+ main.log.error( self.name + ".uninstallApp(): appName must " +
+ "be a string" )
+ return main.FALSE
+ status = self.appStatus( appName )
+ if status == "INSTALLED":
+ response = self.app( appName, "uninstall" )
+ if check and response == main.TRUE:
+ for i in range(10): # try 10 times then give up
+ status = self.appStatus( appName )
+ if status == "UNINSTALLED":
+ return main.TRUE
+ else:
+ time.sleep( 1 )
+ return main.FALSE
+ else: # not check or command didn't succeed
+ return response
+ elif status == "ACTIVE":
+ main.log.warn( self.name + ": Tried to uninstall the " +
+ "application '" + appName + "' which is " +
+ "currently active." )
+ response = self.app( appName, "uninstall" )
+ if check and response == main.TRUE:
+ for i in range(10): # try 10 times then give up
+ status = self.appStatus( appName )
+ if status == "UNINSTALLED":
+ return main.TRUE
+ else:
+ time.sleep( 1 )
+ return main.FALSE
+ else: # not check or command didn't succeed
+ return response
+ elif status == "UNINSTALLED":
+ return main.TRUE
+ else:
+ main.log.error( "Unexpected return value from appStatus: " +
+ str( status ) )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def appIDs( self, jsonFormat=True ):
+ """
+ Show the mappings between app id and app names given by the 'app-ids'
+ cli command
+ """
+ try:
+ cmdStr = "app-ids"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "Error in processing onos:app-ids command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def appToIDCheck( self ):
+ """
+ This method will check that each application's ID listed in 'apps' is
+ the same as the ID listed in 'app-ids'. The check will also check that
+ there are no duplicate IDs issued. Note that an app ID should be
+ a globaly unique numerical identifier for app/app-like features. Once
+ an ID is registered, the ID is never freed up so that if an app is
+ reinstalled it will have the same ID.
+
+ Returns: main.TRUE if the check passes and
+ main.FALSE if the check fails or
+ main.ERROR if there is some error in processing the test
+ """
+ try:
+ bail = False
+ rawJson = self.appIDs( jsonFormat=True )
+ if rawJson:
+ ids = json.loads( rawJson )
+ else:
+ main.log.error( "app-ids returned nothing:" + repr( rawJson ) )
+ bail = True
+ rawJson = self.apps( jsonFormat=True )
+ if rawJson:
+ apps = json.loads( rawJson )
+ else:
+ main.log.error( "apps returned nothing:" + repr( rawJson ) )
+ bail = True
+ if bail:
+ return main.FALSE
+ result = main.TRUE
+ for app in apps:
+ appID = app.get( 'id' )
+ if appID is None:
+ main.log.error( "Error parsing app: " + str( app ) )
+ result = main.FALSE
+ appName = app.get( 'name' )
+ if appName is None:
+ main.log.error( "Error parsing app: " + str( app ) )
+ result = main.FALSE
+ # get the entry in ids that has the same appID
+ current = filter( lambda item: item[ 'id' ] == appID, ids )
+ # main.log.debug( "Comparing " + str( app ) + " to " +
+ # str( current ) )
+ if not current: # if ids doesn't have this id
+ result = main.FALSE
+ main.log.error( "'app-ids' does not have the ID for " +
+ str( appName ) + " that apps does." )
+ elif len( current ) > 1:
+ # there is more than one app with this ID
+ result = main.FALSE
+ # We will log this later in the method
+ elif not current[0][ 'name' ] == appName:
+ currentName = current[0][ 'name' ]
+ result = main.FALSE
+ main.log.error( "'app-ids' has " + str( currentName ) +
+ " registered under id:" + str( appID ) +
+ " but 'apps' has " + str( appName ) )
+ else:
+ pass # id and name match!
+ # now make sure that app-ids has no duplicates
+ idsList = []
+ namesList = []
+ for item in ids:
+ idsList.append( item[ 'id' ] )
+ namesList.append( item[ 'name' ] )
+ if len( idsList ) != len( set( idsList ) ) or\
+ len( namesList ) != len( set( namesList ) ):
+ main.log.error( "'app-ids' has some duplicate entries: \n"
+ + json.dumps( ids,
+ sort_keys=True,
+ indent=4,
+ separators=( ',', ': ' ) ) )
+ result = main.FALSE
+ return result
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, rawJson ) )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getCfg( self, component=None, propName=None, short=False,
+ jsonFormat=True ):
+ """
+ Get configuration settings from onos cli
+ Optional arguments:
+ component - Optionally only list configurations for a specific
+ component. If None, all components with configurations
+ are displayed. Case Sensitive string.
+ propName - If component is specified, propName option will show
+ only this specific configuration from that component.
+ Case Sensitive string.
+ jsonFormat - Returns output as json. Note that this will override
+ the short option
+ short - Short, less verbose, version of configurations.
+ This is overridden by the json option
+ returns:
+ Output from cli as a string or None on error
+ """
+ try:
+ baseStr = "cfg"
+ cmdStr = " get"
+ componentStr = ""
+ if component:
+ componentStr += " " + component
+ if propName:
+ componentStr += " " + propName
+ if jsonFormat:
+ baseStr += " -j"
+ elif short:
+ baseStr += " -s"
+ output = self.sendline( baseStr + cmdStr + componentStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ return output
+ except AssertionError:
+ main.log.exception( "Error in processing 'cfg get' command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setCfg( self, component, propName, value=None, check=True ):
+ """
+ Set/Unset configuration settings from ONOS cli
+ Required arguments:
+ component - The case sensitive name of the component whose
+ property is to be set
+ propName - The case sensitive name of the property to be set/unset
+ Optional arguments:
+ value - The value to set the property to. If None, will unset the
+ property and revert it to it's default value(if applicable)
+ check - Boolean, Check whether the option was successfully set this
+ only applies when a value is given.
+ returns:
+ main.TRUE on success or main.FALSE on failure. If check is False,
+ will return main.TRUE unless there is an error
+ """
+ try:
+ baseStr = "cfg"
+ cmdStr = " set " + str( component ) + " " + str( propName )
+ if value is not None:
+ cmdStr += " " + str( value )
+ output = self.sendline( baseStr + cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ if value and check:
+ results = self.getCfg( component=str( component ),
+ propName=str( propName ),
+ jsonFormat=True )
+ # Check if current value is what we just set
+ try:
+ jsonOutput = json.loads( results )
+ current = jsonOutput[ 'value' ]
+ except ( TypeError, ValueError ):
+ main.log.exception( "Error parsing cfg output" )
+ main.log.error( "output:" + repr( results ) )
+ return main.FALSE
+ if current == str( value ):
+ return main.TRUE
+ return main.FALSE
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "Error in processing 'cfg set' command." )
+ return main.FALSE
+ except ( TypeError, ValueError ):
+ main.log.exception( "{}: Object not as expected: {!r}".format( self.name, results ) )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setTestAdd( self, setName, values ):
+ """
+ CLI command to add elements to a distributed set.
+ Arguments:
+ setName - The name of the set to add to.
+ values - The value(s) to add to the set, space seperated.
+ Example usages:
+ setTestAdd( "set1", "a b c" )
+ setTestAdd( "set2", "1" )
+ returns:
+ main.TRUE on success OR
+ main.FALSE if elements were already in the set OR
+ main.ERROR on error
+ """
+ try:
+ cmdStr = "set-test-add " + str( setName ) + " " + str( values )
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Error executing command" not in output
+ positiveMatch = "\[(.*)\] was added to the set " + str( setName )
+ negativeMatch = "\[(.*)\] was already in set " + str( setName )
+ main.log.info( self.name + ": " + output )
+ if re.search( positiveMatch, output):
+ return main.TRUE
+ elif re.search( negativeMatch, output):
+ return main.FALSE
+ else:
+ main.log.error( self.name + ": setTestAdd did not" +
+ " match expected output" )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return main.ERROR
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' command. " )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setTestRemove( self, setName, values, clear=False, retain=False ):
+ """
+ CLI command to remove elements from a distributed set.
+ Required arguments:
+ setName - The name of the set to remove from.
+ values - The value(s) to remove from the set, space seperated.
+ Optional arguments:
+ clear - Clear all elements from the set
+ retain - Retain only the given values. (intersection of the
+ original set and the given set)
+ returns:
+ main.TRUE on success OR
+ main.FALSE if the set was not changed OR
+ main.ERROR on error
+ """
+ try:
+ cmdStr = "set-test-remove "
+ if clear:
+ cmdStr += "-c " + str( setName )
+ elif retain:
+ cmdStr += "-r " + str( setName ) + " " + str( values )
+ else:
+ cmdStr += str( setName ) + " " + str( values )
+ output = self.sendline( cmdStr )
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+ if clear:
+ pattern = "Set " + str( setName ) + " cleared"
+ if re.search( pattern, output ):
+ return main.TRUE
+ elif retain:
+ positivePattern = str( setName ) + " was pruned to contain " +\
+ "only elements of set \[(.*)\]"
+ negativePattern = str( setName ) + " was not changed by " +\
+ "retaining only elements of the set " +\
+ "\[(.*)\]"
+ if re.search( positivePattern, output ):
+ return main.TRUE
+ elif re.search( negativePattern, output ):
+ return main.FALSE
+ else:
+ positivePattern = "\[(.*)\] was removed from the set " +\
+ str( setName )
+ if ( len( values.split() ) == 1 ):
+ negativePattern = "\[(.*)\] was not in set " +\
+ str( setName )
+ else:
+ negativePattern = "No element of \[(.*)\] was in set " +\
+ str( setName )
+ if re.search( positivePattern, output ):
+ return main.TRUE
+ elif re.search( negativePattern, output ):
+ return main.FALSE
+ main.log.error( self.name + ": setTestRemove did not" +
+ " match expected output" )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return main.ERROR
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' commandr. " )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setTestGet( self, setName, values="" ):
+ """
+ CLI command to get the elements in a distributed set.
+ Required arguments:
+ setName - The name of the set to remove from.
+ Optional arguments:
+ values - The value(s) to check if in the set, space seperated.
+ returns:
+ main.ERROR on error OR
+ A list of elements in the set if no optional arguments are
+ supplied OR
+ A tuple containing the list then:
+ main.FALSE if the given values are not in the set OR
+ main.TRUE if the given values are in the set OR
+ """
+ try:
+ values = str( values ).strip()
+ setName = str( setName ).strip()
+ length = len( values.split() )
+ containsCheck = None
+ # Patterns to match
+ setPattern = "\[(.*)\]"
+ pattern = "Items in set " + setName + ":\n" + setPattern
+ containsTrue = "Set " + setName + " contains the value " + values
+ containsFalse = "Set " + setName + " did not contain the value " +\
+ values
+ containsAllTrue = "Set " + setName + " contains the the subset " +\
+ setPattern
+ containsAllFalse = "Set " + setName + " did not contain the the" +\
+ " subset " + setPattern
+
+ cmdStr = "set-test-get "
+ cmdStr += setName + " " + values
+ output = self.sendline( cmdStr )
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+
+ if length == 0:
+ match = re.search( pattern, output )
+ else: # if given values
+ if length == 1: # Contains output
+ patternTrue = pattern + "\n" + containsTrue
+ patternFalse = pattern + "\n" + containsFalse
+ else: # ContainsAll output
+ patternTrue = pattern + "\n" + containsAllTrue
+ patternFalse = pattern + "\n" + containsAllFalse
+ matchTrue = re.search( patternTrue, output )
+ matchFalse = re.search( patternFalse, output )
+ if matchTrue:
+ containsCheck = main.TRUE
+ match = matchTrue
+ elif matchFalse:
+ containsCheck = main.FALSE
+ match = matchFalse
+ else:
+ main.log.error( self.name + " setTestGet did not match " +\
+ "expected output" )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ match = None
+ if match:
+ setMatch = match.group( 1 )
+ if setMatch == '':
+ setList = []
+ else:
+ setList = setMatch.split( ", " )
+ if length > 0:
+ return ( setList, containsCheck )
+ else:
+ return setList
+ else: # no match
+ main.log.error( self.name + ": setTestGet did not" +
+ " match expected output" )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return main.ERROR
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' command." )
+ return main.ERROR
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.ERROR
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setTestSize( self, setName ):
+ """
+ CLI command to get the elements in a distributed set.
+ Required arguments:
+ setName - The name of the set to remove from.
+ returns:
+ The integer value of the size returned or
+ None on error
+ """
+ try:
+ # TODO: Should this check against the number of elements returned
+ # and then return true/false based on that?
+ setName = str( setName ).strip()
+ # Patterns to match
+ setPattern = "\[(.*)\]"
+ pattern = "There are (\d+) items in set " + setName + ":\n" +\
+ setPattern
+ cmdStr = "set-test-get -s "
+ cmdStr += setName
+ output = self.sendline( cmdStr )
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+ match = re.search( pattern, output )
+ if match:
+ setSize = int( match.group( 1 ) )
+ setMatch = match.group( 2 )
+ if len( setMatch.split() ) == setSize:
+ main.log.info( "The size returned by " + self.name +
+ " matches the number of elements in " +
+ "the returned set" )
+ else:
+ main.log.error( "The size returned by " + self.name +
+ " does not match the number of " +
+ "elements in the returned set." )
+ return setSize
+ else: # no match
+ main.log.error( self.name + ": setTestGet did not" +
+ " match expected output" )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return None
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def counters( self, jsonFormat=True ):
+ """
+ Command to list the various counters in the system.
+ returns:
+ if jsonFormat, a string of the json object returned by the cli
+ command
+ if not jsonFormat, the normal string output of the cli command
+ None on error
+ """
+ try:
+ counters = {}
+ cmdStr = "counters"
+ if jsonFormat:
+ cmdStr += " -j"
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+ return output
+ except AssertionError:
+ main.log.exception( "Error in processing 'counters' command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def counterTestAddAndGet( self, counter, delta=1, inMemory=False ):
+ """
+ CLI command to add a delta to then get a distributed counter.
+ Required arguments:
+ counter - The name of the counter to increment.
+ Optional arguments:
+ delta - The long to add to the counter
+ inMemory - use in memory map for the counter
+ returns:
+ integer value of the counter or
+ None on Error
+ """
+ try:
+ counter = str( counter )
+ delta = int( delta )
+ cmdStr = "counter-test-increment "
+ if inMemory:
+ cmdStr += "-i "
+ cmdStr += counter
+ if delta != 1:
+ cmdStr += " " + str( delta )
+ output = self.sendline( cmdStr )
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+ pattern = counter + " was updated to (-?\d+)"
+ match = re.search( pattern, output )
+ if match:
+ return int( match.group( 1 ) )
+ else:
+ main.log.error( self.name + ": counterTestAddAndGet did not" +
+ " match expected output." )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return None
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def counterTestGetAndAdd( self, counter, delta=1, inMemory=False ):
+ """
+ CLI command to get a distributed counter then add a delta to it.
+ Required arguments:
+ counter - The name of the counter to increment.
+ Optional arguments:
+ delta - The long to add to the counter
+ inMemory - use in memory map for the counter
+ returns:
+ integer value of the counter or
+ None on Error
+ """
+ try:
+ counter = str( counter )
+ delta = int( delta )
+ cmdStr = "counter-test-increment -g "
+ if inMemory:
+ cmdStr += "-i "
+ cmdStr += counter
+ if delta != 1:
+ cmdStr += " " + str( delta )
+ output = self.sendline( cmdStr )
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ retryTime = 30 # Conservative time, given by Madan
+ main.log.info( "Waiting " + str( retryTime ) +
+ "seconds before retrying." )
+ time.sleep( retryTime ) # Due to change in mastership
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ assert "Error executing command" not in output, output
+ main.log.info( self.name + ": " + output )
+ pattern = counter + " was updated to (-?\d+)"
+ match = re.search( pattern, output )
+ if match:
+ return int( match.group( 1 ) )
+ else:
+ main.log.error( self.name + ": counterTestGetAndAdd did not" +
+ " match expected output." )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return None
+ except AssertionError:
+ main.log.exception( "Error in processing '" + cmdStr + "' command." )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def summary( self, jsonFormat=True ):
+ """
+ Description: Execute summary command in onos
+ Returns: json object ( summary -j ), returns main.FALSE if there is
+ no output
+
+ """
+ try:
+ cmdStr = "summary"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ assert "Error:" not in handle, handle
+ if not handle:
+ main.log.error( self.name + ": There is no output in " +
+ "summary command" )
+ return main.FALSE
+ return handle
+ except AssertionError:
+ main.log.exception( "{} Error in summary output:".format( self.name ) )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def transactionalMapGet( self, keyName, inMemory=False ):
+ """
+ CLI command to get the value of a key in a consistent map using
+ transactions. This a test function and can only get keys from the
+ test map hard coded into the cli command
+ Required arguments:
+ keyName - The name of the key to get
+ Optional arguments:
+ inMemory - use in memory map for the counter
+ returns:
+ The string value of the key or
+ None on Error
+ """
+ try:
+ keyName = str( keyName )
+ cmdStr = "transactional-map-test-get "
+ if inMemory:
+ cmdStr += "-i "
+ cmdStr += keyName
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ return None
+ pattern = "Key-value pair \(" + keyName + ", (?P<value>.+)\) found."
+ if "Key " + keyName + " not found." in output:
+ return None
+ else:
+ match = re.search( pattern, output )
+ if match:
+ return match.groupdict()[ 'value' ]
+ else:
+ main.log.error( self.name + ": transactionlMapGet did not" +
+ " match expected output." )
+ main.log.debug( self.name + " expected: " + pattern )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return None
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def transactionalMapPut( self, numKeys, value, inMemory=False ):
+ """
+ CLI command to put a value into 'numKeys' number of keys in a
+ consistent map using transactions. This a test function and can only
+ put into keys named 'Key#' of the test map hard coded into the cli command
+ Required arguments:
+ numKeys - Number of keys to add the value to
+ value - The string value to put into the keys
+ Optional arguments:
+ inMemory - use in memory map for the counter
+ returns:
+ A dictionary whose keys are the name of the keys put into the map
+ and the values of the keys are dictionaries whose key-values are
+ 'value': value put into map and optionaly
+ 'oldValue': Previous value in the key or
+ None on Error
+
+ Example output
+ { 'Key1': {'oldValue': 'oldTestValue', 'value': 'Testing'},
+ 'Key2': {'value': 'Testing'} }
+ """
+ try:
+ numKeys = str( numKeys )
+ value = str( value )
+ cmdStr = "transactional-map-test-put "
+ if inMemory:
+ cmdStr += "-i "
+ cmdStr += numKeys + " " + value
+ output = self.sendline( cmdStr )
+ assert "Command not found:" not in output, output
+ try:
+ # TODO: Maybe make this less hardcoded
+ # ConsistentMap Exceptions
+ assert "org.onosproject.store.service" not in output
+ # Node not leader
+ assert "java.lang.IllegalStateException" not in output
+ except AssertionError:
+ main.log.error( "Error in processing '" + cmdStr + "' " +
+ "command: " + str( output ) )
+ return None
+ newPattern = 'Created Key (?P<key>(\w)+) with value (?P<value>(.)+)\.'
+ updatedPattern = "Put (?P<value>(.)+) into key (?P<key>(\w)+)\. The old value was (?P<oldValue>(.)+)\."
+ results = {}
+ for line in output.splitlines():
+ new = re.search( newPattern, line )
+ updated = re.search( updatedPattern, line )
+ if new:
+ results[ new.groupdict()[ 'key' ] ] = { 'value': new.groupdict()[ 'value' ] }
+ elif updated:
+ results[ updated.groupdict()[ 'key' ] ] = { 'value': updated.groupdict()[ 'value' ],
+ 'oldValue': updated.groupdict()[ 'oldValue' ] }
+ else:
+ main.log.error( self.name + ": transactionlMapGet did not" +
+ " match expected output." )
+ main.log.debug( "{} expected: {!r} or {!r}".format( self.name,
+ newPattern,
+ updatedPattern ) )
+ main.log.debug( self.name + " actual: " + repr( output ) )
+ return results
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def maps( self, jsonFormat=True ):
+ """
+ Description: Returns result of onos:maps
+ Optional:
+ * jsonFormat: enable json formatting of output
+ """
+ try:
+ cmdStr = "maps"
+ if jsonFormat:
+ cmdStr += " -j"
+ handle = self.sendline( cmdStr )
+ assert "Command not found:" not in handle, handle
+ return handle
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def getSwController( self, uri, jsonFormat=True ):
+ """
+ Descrition: Gets the controller information from the device
+ """
+ try:
+ cmd = "device-controllers "
+ if jsonFormat:
+ cmd += "-j "
+ response = self.sendline( cmd + uri )
+ assert "Command not found:" not in response, response
+ return response
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return None
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def setSwController( self, uri, ip, proto="tcp", port="6653", jsonFormat=True ):
+ """
+ Descrition: sets the controller(s) for the specified device
+
+ Parameters:
+ Required: uri - String: The uri of the device(switch).
+ ip - String or List: The ip address of the controller.
+ This parameter can be formed in a couple of different ways.
+ VALID:
+ 10.0.0.1 - just the ip address
+ tcp:10.0.0.1 - the protocol and the ip address
+ tcp:10.0.0.1:6653 - the protocol and port can be specified,
+ so that you can add controllers with different
+ protocols and ports
+ INVALID:
+ 10.0.0.1:6653 - this is not supported by ONOS
+
+ Optional: proto - The type of connection e.g. tcp, ssl. If a list of ips are given
+ port - The port number.
+ jsonFormat - If set ONOS will output in json NOTE: This is currently not supported
+
+ Returns: main.TRUE if ONOS returns without any errors, otherwise returns main.FALSE
+ """
+ try:
+ cmd = "device-setcontrollers"
+
+ if jsonFormat:
+ cmd += " -j"
+ cmd += " " + uri
+ if isinstance( ip, str ):
+ ip = [ip]
+ for item in ip:
+ if ":" in item:
+ sitem = item.split( ":" )
+ if len(sitem) == 3:
+ cmd += " " + item
+ elif "." in sitem[1]:
+ cmd += " {}:{}".format(item, port)
+ else:
+ main.log.error( "Malformed entry: " + item )
+ raise TypeError
+ else:
+ cmd += " {}:{}:{}".format( proto, item, port )
+ response = self.sendline( cmd )
+ assert "Command not found:" not in response, response
+ if "Error" in response:
+ main.log.error( response )
+ return main.FALSE
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def removeDevice( self, device ):
+ '''
+ Description:
+ Remove a device from ONOS by passing the uri of the device(s).
+ Parameters:
+ device - (str or list) the id or uri of the device ex. "of:0000000000000001"
+ Returns:
+ Returns main.FALSE if an exception is thrown or an error is present
+ in the response. Otherwise, returns main.TRUE.
+ NOTE:
+ If a host cannot be removed, then this function will return main.FALSE
+ '''
+ try:
+ if type( device ) is str:
+ device = list( device )
+
+ for d in device:
+ time.sleep( 1 )
+ response = self.sendline( "device-remove {}".format( d ) )
+ assert "Command not found:" not in response, response
+ if "Error" in response:
+ main.log.warn( "Error for device: {}\nResponse: {}".format( d, response ) )
+ return main.FALSE
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return main.FALSE
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def removeHost( self, host ):
+ '''
+ Description:
+ Remove a host from ONOS by passing the id of the host(s)
+ Parameters:
+ hostId - (str or list) the id or mac of the host ex. "00:00:00:00:00:01"
+ Returns:
+ Returns main.FALSE if an exception is thrown or an error is present
+ in the response. Otherwise, returns main.TRUE.
+ NOTE:
+ If a host cannot be removed, then this function will return main.FALSE
+ '''
+ try:
+ if type( host ) is str:
+ host = list( host )
+
+ for h in host:
+ time.sleep( 1 )
+ response = self.sendline( "host-remove {}".format( h ) )
+ assert "Command not found:" not in response, response
+ if "Error" in response:
+ main.log.warn( "Error for host: {}\nResponse: {}".format( h, response ) )
+ return main.FALSE
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+ def link( self, begin, end, state ):
+ '''
+ Description:
+ Bring link down or up in the null-provider.
+ params:
+ begin - (string) One end of a device or switch.
+ end - (string) the other end of the device or switch
+ returns:
+ main.TRUE if no exceptions were thrown and no Errors are
+ present in the resoponse. Otherwise, returns main.FALSE
+ '''
+ try:
+ cmd = "null-link null:{} null:{} {}".format( begin, end, state )
+ response = self.sendline( cmd, showResponse=True )
+ assert "Command not found:" not in response, response
+ if "Error" in response or "Failure" in response:
+ main.log.error( response )
+ return main.FALSE
+ return main.TRUE
+ except AssertionError:
+ main.log.exception( "" )
+ return None
+ except TypeError:
+ main.log.exception( self.name + ": Object not as expected" )
+ return main.FALSE
+ except pexpect.EOF:
+ main.log.error( self.name + ": EOF exception found" )
+ main.log.error( self.name + ": " + self.handle.before )
+ main.cleanup()
+ main.exit()
+ except Exception:
+ main.log.exception( self.name + ": Uncaught exception!" )
+ main.cleanup()
+ main.exit()
+
+if __name__ == '__main__':
+ onos_cli = OnosCliDriver()
+ name = 'onos_cli'
+ user = 'onos'
+ passwd = 'rocks'
+ ip = '172.17.0.2'
+ options = { 'name': '{0}'.format(name), 'onosIp' : '{0}'.format(ip) }
+ onos_cli.connect(name = 'onoscli', user_name = user, pwd = passwd, ip_address = ip,
+ port = '8101', options = options)
+ device_str = onos_cli.devices(jsonFormat = False)
+ print('Devices: %s' %device_str)
+ device_json = onos_cli.devices()
+ print('Device json: %s' %device_json)
+ routes_str = onos_cli.routes(jsonFormat = False)
+ print('Routes %s' %routes_str)
+ flows_json = onos_cli.flows(state = "ADDED")
+ print('Flows %s' %flows_json)
+ onos_cli.disconnect()
+