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/__init__.py b/src/test/cli/__init__.py
new file mode 100644
index 0000000..900be31
--- /dev/null
+++ b/src/test/cli/__init__.py
@@ -0,0 +1,5 @@
+import os,sys
+##add the python path to lookup the utils
+working_dir = os.path.dirname(os.path.realpath(sys.argv[-1]))
+utils_dir = os.path.join(working_dir, '../utils')
+__path__.append(utils_dir)
diff --git a/src/test/cli/ast.py b/src/test/cli/ast.py
new file mode 100644
index 0000000..fd5dfdb
--- /dev/null
+++ b/src/test/cli/ast.py
@@ -0,0 +1,311 @@
+# -*- coding: utf-8 -*-
+"""
+    ast
+    ~~~
+
+    The `ast` module helps Python applications to process trees of the Python
+    abstract syntax grammar.  The abstract syntax itself might change with
+    each Python release; this module helps to find out programmatically what
+    the current grammar looks like and allows modifications of it.
+
+    An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
+    a flag to the `compile()` builtin function or by using the `parse()`
+    function from this module.  The result will be a tree of objects whose
+    classes all inherit from `ast.AST`.
+
+    A modified abstract syntax tree can be compiled into a Python code object
+    using the built-in `compile()` function.
+
+    Additionally various helper functions are provided that make working with
+    the trees simpler.  The main intention of the helper functions and this
+    module in general is to provide an easy to use interface for libraries
+    that work tightly with the python syntax (template engines for example).
+
+
+    :copyright: Copyright 2008 by Armin Ronacher.
+    :license: Python License.
+"""
+from _ast import *
+from _ast import __version__
+
+
+def parse(source, filename='<unknown>', mode='exec'):
+    """
+    Parse the source into an AST node.
+    Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
+    """
+    return compile(source, filename, mode, PyCF_ONLY_AST)
+
+
+def literal_eval(node_or_string):
+    """
+    Safely evaluate an expression node or a string containing a Python
+    expression.  The string or node provided may only consist of the following
+    Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
+    and None.
+    """
+    _safe_names = {'None': None, 'True': True, 'False': False}
+    if isinstance(node_or_string, basestring):
+        node_or_string = parse(node_or_string, mode='eval')
+    if isinstance(node_or_string, Expression):
+        node_or_string = node_or_string.body
+    def _convert(node):
+        if isinstance(node, Str):
+            return node.s
+        elif isinstance(node, Num):
+            return node.n
+        elif isinstance(node, Tuple):
+            return tuple(map(_convert, node.elts))
+        elif isinstance(node, List):
+            return list(map(_convert, node.elts))
+        elif isinstance(node, Dict):
+            return dict((_convert(k), _convert(v)) for k, v
+                        in zip(node.keys, node.values))
+        elif isinstance(node, Name):
+            if node.id in _safe_names:
+                return _safe_names[node.id]
+        elif isinstance(node, BinOp) and \
+             isinstance(node.op, (Add, Sub)) and \
+             isinstance(node.right, Num) and \
+             isinstance(node.right.n, complex) and \
+             isinstance(node.left, Num) and \
+             isinstance(node.left.n, (int, long, float)):
+            left = node.left.n
+            right = node.right.n
+            if isinstance(node.op, Add):
+                return left + right
+            else:
+                return left - right
+        raise ValueError('malformed string')
+    return _convert(node_or_string)
+
+
+def dump(node, annotate_fields=True, include_attributes=False):
+    """
+    Return a formatted dump of the tree in *node*.  This is mainly useful for
+    debugging purposes.  The returned string will show the names and the values
+    for fields.  This makes the code impossible to evaluate, so if evaluation is
+    wanted *annotate_fields* must be set to False.  Attributes such as line
+    numbers and column offsets are not dumped by default.  If this is wanted,
+    *include_attributes* can be set to True.
+    """
+    def _format(node):
+        if isinstance(node, AST):
+            fields = [(a, _format(b)) for a, b in iter_fields(node)]
+            rv = '%s(%s' % (node.__class__.__name__, ', '.join(
+                ('%s=%s' % field for field in fields)
+                if annotate_fields else
+                (b for a, b in fields)
+            ))
+            if include_attributes and node._attributes:
+                rv += fields and ', ' or ' '
+                rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
+                                for a in node._attributes)
+            return rv + ')'
+        elif isinstance(node, list):
+            return '[%s]' % ', '.join(_format(x) for x in node)
+        return repr(node)
+    if not isinstance(node, AST):
+        raise TypeError('expected AST, got %r' % node.__class__.__name__)
+    return _format(node)
+
+
+def copy_location(new_node, old_node):
+    """
+    Copy source location (`lineno` and `col_offset` attributes) from
+    *old_node* to *new_node* if possible, and return *new_node*.
+    """
+    for attr in 'lineno', 'col_offset':
+        if attr in old_node._attributes and attr in new_node._attributes \
+           and hasattr(old_node, attr):
+            setattr(new_node, attr, getattr(old_node, attr))
+    return new_node
+
+
+def fix_missing_locations(node):
+    """
+    When you compile a node tree with compile(), the compiler expects lineno and
+    col_offset attributes for every node that supports them.  This is rather
+    tedious to fill in for generated nodes, so this helper adds these attributes
+    recursively where not already set, by setting them to the values of the
+    parent node.  It works recursively starting at *node*.
+    """
+    def _fix(node, lineno, col_offset):
+        if 'lineno' in node._attributes:
+            if not hasattr(node, 'lineno'):
+                node.lineno = lineno
+            else:
+                lineno = node.lineno
+        if 'col_offset' in node._attributes:
+            if not hasattr(node, 'col_offset'):
+                node.col_offset = col_offset
+            else:
+                col_offset = node.col_offset
+        for child in iter_child_nodes(node):
+            _fix(child, lineno, col_offset)
+    _fix(node, 1, 0)
+    return node
+
+
+def increment_lineno(node, n=1):
+    """
+    Increment the line number of each node in the tree starting at *node* by *n*.
+    This is useful to "move code" to a different location in a file.
+    """
+    for child in walk(node):
+        if 'lineno' in child._attributes:
+            child.lineno = getattr(child, 'lineno', 0) + n
+    return node
+
+
+def iter_fields(node):
+    """
+    Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
+    that is present on *node*.
+    """
+    for field in node._fields:
+        try:
+            yield field, getattr(node, field)
+        except AttributeError:
+            pass
+
+
+def iter_child_nodes(node):
+    """
+    Yield all direct child nodes of *node*, that is, all fields that are nodes
+    and all items of fields that are lists of nodes.
+    """
+    for name, field in iter_fields(node):
+        if isinstance(field, AST):
+            yield field
+        elif isinstance(field, list):
+            for item in field:
+                if isinstance(item, AST):
+                    yield item
+
+
+def get_docstring(node, clean=True):
+    """
+    Return the docstring for the given node or None if no docstring can
+    be found.  If the node provided does not have docstrings a TypeError
+    will be raised.
+    """
+    if not isinstance(node, (FunctionDef, ClassDef, Module)):
+        raise TypeError("%r can't have docstrings" % node.__class__.__name__)
+    if node.body and isinstance(node.body[0], Expr) and \
+       isinstance(node.body[0].value, Str):
+        if clean:
+            import inspect
+            return inspect.cleandoc(node.body[0].value.s)
+        return node.body[0].value.s
+
+
+def walk(node):
+    """
+    Recursively yield all descendant nodes in the tree starting at *node*
+    (including *node* itself), in no specified order.  This is useful if you
+    only want to modify nodes in place and don't care about the context.
+    """
+    from collections import deque
+    todo = deque([node])
+    while todo:
+        node = todo.popleft()
+        todo.extend(iter_child_nodes(node))
+        yield node
+
+
+class NodeVisitor(object):
+    """
+    A node visitor base class that walks the abstract syntax tree and calls a
+    visitor function for every node found.  This function may return a value
+    which is forwarded by the `visit` method.
+
+    This class is meant to be subclassed, with the subclass adding visitor
+    methods.
+
+    Per default the visitor functions for the nodes are ``'visit_'`` +
+    class name of the node.  So a `TryFinally` node visit function would
+    be `visit_TryFinally`.  This behavior can be changed by overriding
+    the `visit` method.  If no visitor function exists for a node
+    (return value `None`) the `generic_visit` visitor is used instead.
+
+    Don't use the `NodeVisitor` if you want to apply changes to nodes during
+    traversing.  For this a special visitor exists (`NodeTransformer`) that
+    allows modifications.
+    """
+
+    def visit(self, node):
+        """Visit a node."""
+        method = 'visit_' + node.__class__.__name__
+        visitor = getattr(self, method, self.generic_visit)
+        return visitor(node)
+
+    def generic_visit(self, node):
+        """Called if no explicit visitor function exists for a node."""
+        for field, value in iter_fields(node):
+            if isinstance(value, list):
+                for item in value:
+                    if isinstance(item, AST):
+                        self.visit(item)
+            elif isinstance(value, AST):
+                self.visit(value)
+
+
+class NodeTransformer(NodeVisitor):
+    """
+    A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
+    allows modification of nodes.
+
+    The `NodeTransformer` will walk the AST and use the return value of the
+    visitor methods to replace or remove the old node.  If the return value of
+    the visitor method is ``None``, the node will be removed from its location,
+    otherwise it is replaced with the return value.  The return value may be the
+    original node in which case no replacement takes place.
+
+    Here is an example transformer that rewrites all occurrences of name lookups
+    (``foo``) to ``data['foo']``::
+
+       class RewriteName(NodeTransformer):
+
+           def visit_Name(self, node):
+               return copy_location(Subscript(
+                   value=Name(id='data', ctx=Load()),
+                   slice=Index(value=Str(s=node.id)),
+                   ctx=node.ctx
+               ), node)
+
+    Keep in mind that if the node you're operating on has child nodes you must
+    either transform the child nodes yourself or call the :meth:`generic_visit`
+    method for the node first.
+
+    For nodes that were part of a collection of statements (that applies to all
+    statement nodes), the visitor may also return a list of nodes rather than
+    just a single node.
+
+    Usually you use the transformer like this::
+
+       node = YourTransformer().visit(node)
+    """
+
+    def generic_visit(self, node):
+        for field, old_value in iter_fields(node):
+            old_value = getattr(node, field, None)
+            if isinstance(old_value, list):
+                new_values = []
+                for value in old_value:
+                    if isinstance(value, AST):
+                        value = self.visit(value)
+                        if value is None:
+                            continue
+                        elif not isinstance(value, AST):
+                            new_values.extend(value)
+                            continue
+                    new_values.append(value)
+                old_value[:] = new_values
+            elif isinstance(old_value, AST):
+                new_node = self.visit(old_value)
+                if new_node is None:
+                    delattr(node, field)
+                else:
+                    setattr(node, field, new_node)
+        return node
diff --git a/src/test/cli/clicommon.py b/src/test/cli/clicommon.py
new file mode 100644
index 0000000..9110fdf
--- /dev/null
+++ b/src/test/cli/clicommon.py
@@ -0,0 +1,21 @@
+import os,sys
+from utilities import Utilities, utilities
+from scapy.all import *
+
+#log.setLevel('INFO')
+class MAIN(object):
+    def __init__(self):
+        global utilities
+        self.log = log
+        self.logdir = os.getenv('HOME')
+        self.logHeader = ''
+        self.utilities = utilities
+        self.TRUE = True
+        self.FALSE = False
+        self.EXPERIMENTAL_MODE = self.FALSE
+
+    def cleanup(self): pass
+
+    def exit(self): pass
+
+main = MAIN()
diff --git a/src/test/cli/clidriver.py b/src/test/cli/clidriver.py
new file mode 100644
index 0000000..ce10a2c
--- /dev/null
+++ b/src/test/cli/clidriver.py
@@ -0,0 +1,353 @@
+#!/usr/bin/env python
+"""
+Created on 24-Oct-2012
+
+author:s: Anil Kumar ( anilkumar.s@paxterrasolutions.com ),
+          Raghav Kashyap( raghavkashyap@paxterrasolutions.com )
+
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    ( at your option ) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+
+
+
+"""
+import pexpect
+import re
+from component import Component
+from clicommon import *
+import os
+
+class CLI( Component ):
+
+    """
+        This will define common functions for CLI included.
+    """
+    def __init__( self ):
+        super( Component, self ).__init__()
+
+    def connect( self, **connectargs ):
+        """
+           Connection will establish to the remote host using ssh.
+           It will take user_name ,ip_address and password as arguments<br>
+           and will return the handle.
+        """
+        for key in connectargs:
+            vars( self )[ key ] = connectargs[ key ]
+
+        connect_result = super( CLI, self ).connect()
+        ssh_newkey = 'Are you sure you want to continue connecting'
+        refused = "ssh: connect to host " + \
+            self.ip_address + " port 22: Connection refused"
+        if self.port:
+            ssh_hosts_file = os.path.join(os.getenv('HOME'), '.ssh', 'known_hosts')
+            cmd_host_remove = 'ssh-keygen -f "%s" -R [%s]:8101' %(ssh_hosts_file, self.ip_address)
+            #os.system(cmd_host_remove)
+            #main.log.info('SSH host remove cmd: %s' %cmd_host_remove)
+            main.log.info('Spawning pexpect for ip %s' %self.ip_address)
+            self.handle = pexpect.spawn(
+                'ssh -p ' +
+                self.port +
+                ' ' +
+                '-o StrictHostKeyChecking=no ' + 
+                self.user_name +
+                '@' +
+                self.ip_address,
+                env={ "TERM": "xterm-mono" },
+                maxread=50000 )
+        else:
+            self.handle = pexpect.spawn(
+                'ssh -X ' +
+                self.user_name +
+                '@' +
+                self.ip_address,
+                env={ "TERM": "xterm-mono" },
+                maxread=1000000,
+                timeout=60 )
+
+        self.handle.logfile = self.logfile_handler
+        i = 5
+        while i == 5:
+            i = self.handle.expect( [
+                                    ssh_newkey,
+                                    'password:|Password:',
+                                    pexpect.EOF,
+                                    pexpect.TIMEOUT,
+                                    refused,
+                                    'teston>',
+                                    '>|#|\$' ],
+                            120 )
+            if i == 0:  # Accept key, then expect either a password prompt or access
+                main.log.info( "ssh key confirmation received, send yes" )
+                self.handle.sendline( 'yes' )
+                i = 5  # Run the loop again
+                continue
+            if i == 1:  # Password required
+                if self.pwd:
+                    main.log.info(
+                    "ssh connection asked for password, gave password" )
+                else:
+                    main.log.info( "Server asked for password, but none was "
+                                    "given in the .topo file. Trying "
+                                    "no password.")
+                    self.pwd = ""
+                self.handle.sendline( self.pwd )
+                j = self.handle.expect( [
+                                        '>|#|\$',
+                                        'password:|Password:',
+                                        pexpect.EOF,
+                                        pexpect.TIMEOUT ],
+                                        120 )
+                if j != 0:
+                    main.log.error( "Incorrect Password" )
+                    return main.FALSE
+            elif i == 2:
+                main.log.error( "Connection timeout" )
+                return main.FALSE
+            elif i == 3:  # timeout
+                main.log.error(
+                    "No route to the Host " +
+                    self.user_name +
+                    "@" +
+                    self.ip_address )
+                return main.FALSE
+            elif i == 4:
+                main.log.error(
+                    "ssh: connect to host " +
+                    self.ip_address +
+                    " port 22: Connection refused" )
+                return main.FALSE
+            elif i == 6:
+                main.log.info( "Password not required logged in" )
+
+        self.handle.sendline( "" )
+        self.handle.expect( '>|#|\$' )
+        return self.handle
+
+    def disconnect( self ):
+        result = super( CLI, self ).disconnect( self )
+        result = main.TRUE
+        # self.execute( cmd="exit",timeout=120,prompt="(.*)" )
+
+    def execute( self, **execparams ):
+        """
+        It facilitates the command line execution of a given command. It has arguments as :
+        cmd => represents command to be executed,
+        prompt => represents expect command prompt or output,
+        timeout => timeout for command execution,
+        more => to provide a key press if it is on.
+
+        It will return output of command exection.
+        """
+        result = super( CLI, self ).execute( self )
+        defaultPrompt = '.*[$>\#]'
+        args = utilities.parse_args( [ "CMD",
+                                       "TIMEOUT",
+                                       "PROMPT",
+                                       "MORE" ],
+                                     **execparams )
+
+        expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
+        self.LASTRSP = ""
+        timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
+        cmd = ''
+        if args[ "CMD" ]:
+            cmd = args[ "CMD" ]
+        else:
+            return 0
+        if args[ "MORE" ] is None:
+            args[ "MORE" ] = " "
+        self.handle.sendline( cmd )
+        self.lastCommand = cmd
+        index = self.handle.expect( [ expectPrompt,
+                                      "--More--",
+                                      'Command not found.',
+                                      pexpect.TIMEOUT,
+                                      "^:$" ],
+                                    timeout=timeoutVar )
+        if index == 0:
+            self.LASTRSP = self.LASTRSP + \
+                self.handle.before + self.handle.after
+            main.log.info( "Executed :" + str(cmd ) +
+                           " \t\t Expected Prompt '" + str( expectPrompt) +
+                           "' Found" )
+        elif index == 1:
+            self.LASTRSP = self.LASTRSP + self.handle.before
+            self.handle.send( args[ "MORE" ] )
+            main.log.info(
+                "Found More screen to go , Sending a key to proceed" )
+            indexMore = self.handle.expect(
+                [ "--More--", expectPrompt ], timeout=timeoutVar )
+            while indexMore == 0:
+                main.log.info(
+                    "Found anoother More screen to go , Sending a key to proceed" )
+                self.handle.send( args[ "MORE" ] )
+                indexMore = self.handle.expect(
+                    [ "--More--", expectPrompt ], timeout=timeoutVar )
+                self.LASTRSP = self.LASTRSP + self.handle.before
+        elif index == 2:
+            main.log.error( "Command not found" )
+            self.LASTRSP = self.LASTRSP + self.handle.before
+        elif index == 3:
+            main.log.error( "Expected Prompt not found, Time Out!!" )
+            main.log.error( expectPrompt )
+            self.LASTRSP = self.LASTRSP + self.handle.before
+            return self.LASTRSP
+        elif index == 4:
+            self.LASTRSP = self.LASTRSP + self.handle.before
+            # self.handle.send( args[ "MORE" ] )
+            self.handle.sendcontrol( "D" )
+            main.log.info(
+                "Found More screen to go, Sending a key to proceed" )
+            indexMore = self.handle.expect(
+                [ "^:$", expectPrompt ], timeout=timeoutVar )
+            while indexMore == 0:
+                main.log.info(
+                    "Found another More screen to go, Sending a key to proceed" )
+                self.handle.sendcontrol( "D" )
+                indexMore = self.handle.expect(
+                    [ "^:$", expectPrompt ], timeout=timeoutVar )
+                self.LASTRSP = self.LASTRSP + self.handle.before
+        main.last_response = self.remove_contol_chars( self.LASTRSP )
+        return self.LASTRSP
+
+    def remove_contol_chars( self, response ):
+        # RE_XML_ILLEGAL = '([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])|([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])'%( unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ),unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ),unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ) )
+        # response = re.sub( RE_XML_ILLEGAL, "\n", response )
+        response = re.sub( r"[\x01-\x1F\x7F]", "", response )
+        # response = re.sub( r"\[\d+\;1H", "\n", response )
+        response = re.sub( r"\[\d+\;\d+H", "", response )
+        return response
+
+    def runAsSudoUser( self, handle, pwd, default ):
+
+        i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
+        if i == 0:
+            handle.sendline( pwd )
+            handle.sendline( "\n" )
+
+        if i == 1:
+            handle.expect( default )
+
+        if i == 2:
+            main.log.error( "Unable to run as Sudo user" )
+
+        return handle
+
+    def onfail( self ):
+        if 'onfail' in main.componentDictionary[ self.name ]:
+            commandList = main.componentDictionary[
+                self.name ][ 'onfail' ].split( "," )
+            for command in commandList:
+                response = self.execute(
+                    cmd=command,
+                    prompt="(.*)",
+                    timeout=120 )
+
+    def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
+                    direction="from" ):
+        """
+        Definition:
+            Execute scp command in linux to copy to/from a remote host
+        Required:
+            str userName - User name of the remote host
+            str ipAddress - IP address of the remote host
+            str filePath - File path including the file it self
+            str dstPath - Destination path
+        Optional:
+            str pwd - Password of the host
+            str direction - Direction of the scp, default to "from" which means
+                            copy "from" the remote machine to local machine,
+                            while "to" means copy "to" the remote machine from
+                            local machine
+        """
+        returnVal = main.TRUE
+        ssh_newkey = 'Are you sure you want to continue connecting'
+        refused = "ssh: connect to host " + \
+                  ipAddress + " port 22: Connection refused"
+
+        if direction == "from":
+            cmd = 'scp ' + str( userName ) + '@' + str( ipAddress ) + ':' + \
+                  str( filePath ) + ' ' + str( dstPath )
+        elif direction == "to":
+            cmd = 'scp ' + str( filePath ) + ' ' + str( userName ) + \
+                  '@' + str( ipAddress ) + ':' + str( dstPath )
+        else:
+            main.log.debug( "Wrong direction using secure copy command!" )
+            return main.FALSE
+
+        main.log.info( "Sending: " + cmd )
+        self.handle.sendline( cmd )
+        i = 0
+        while i < 2:
+            i = self.handle.expect( [
+                                ssh_newkey,
+                                'password:',
+                                "100%",
+                                refused,
+                                "No such file or directory",
+                                pexpect.EOF,
+                                pexpect.TIMEOUT ],
+                                120 )
+            if i == 0:  # ask for ssh key confirmation
+                main.log.info( "ssh key confirmation received, sending yes" )
+                self.handle.sendline( 'yes' )
+            elif i == 1:  # Asked for ssh password
+                main.log.info( "ssh connection asked for password, gave password" )
+                self.handle.sendline( pwd )
+            elif i == 2:  # File finished transfering
+                main.log.info( "Secure copy successful" )
+                returnVal = main.TRUE
+            elif i == 3:  # Connection refused
+                main.log.error(
+                    "ssh: connect to host " +
+                    ipAddress +
+                    " port 22: Connection refused" )
+                returnVal = main.FALSE
+            elif i == 4:  # File Not found
+                main.log.error( "No such file found" )
+                returnVal = main.FALSE
+            elif i == 5:  # EOF
+                main.log.error( "Pexpect.EOF found!!!" )
+                main.cleanup()
+                main.exit()
+            elif i == 6:  # timeout
+                main.log.error(
+                    "No route to the Host " +
+                    userName +
+                    "@" +
+                    ipAddress )
+                returnVal = main.FALSE
+        self.handle.expect( "\$" )
+        return returnVal
+
+    def scp( self, remoteHost, filePath, dstPath, direction="from" ):
+        """
+        Definition:
+            Execute scp command in linux to copy to/from a remote host
+        Required:
+            * remoteHost - Test ON component to be parsed
+            str filePath - File path including the file it self
+            str dstPath - Destination path
+        Optional:
+            str direction - Direction of the scp, default to "from" which means
+                            copy "from" the remote machine to local machine,
+                            while "to" means copy "to" the remote machine from
+                            local machine
+        """
+        return self.secureCopy( remoteHost.user_name,
+                                remoteHost.ip_address,
+                                filePath,
+                                dstPath,
+                                pwd=remoteHost.pwd,
+                                direction=direction )
diff --git a/src/test/cli/component.py b/src/test/cli/component.py
new file mode 100644
index 0000000..6cf4cd8
--- /dev/null
+++ b/src/test/cli/component.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+"""
+Created on 24-Oct-2012
+
+author:s: Anil Kumar ( anilkumar.s@paxterrasolutions.com ),
+          Raghav Kashyap( raghavkashyap@paxterrasolutions.com )
+
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    ( at your option ) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+
+
+
+"""
+import logging
+from clicommon import *
+
+class Component( object ):
+
+    """
+    This is the tempalte class for components
+    """
+    def __init__( self ):
+        self.default = ''
+        self.wrapped = sys.modules[ __name__ ]
+        self.count = 0
+
+    def __getattr__( self, name ):
+        """
+         This will invoke, if the attribute wasn't found the usual ways.
+         Here it will look for assert_attribute and will execute when
+         AttributeError occurs.
+         It will return the result of the assert_attribute.
+        """
+        try:
+            return getattr( self.wrapped, name )
+        except AttributeError as error:
+            # NOTE: The first time we load a driver module we get this error
+            if "'module' object has no attribute '__path__'" in error:
+                pass
+            else:
+                main.log.error( str(error.__class__) + " " + str(error) )
+            try:
+                def experimentHandling( *args, **kwargs ):
+                    if main.EXPERIMENTAL_MODE == main.TRUE:
+                        result = self.experimentRun( *args, **kwargs )
+                        main.log.info( "EXPERIMENTAL MODE. API " +
+                                       str( name ) +
+                                       " not yet implemented. " +
+                                       "Returning dummy values" )
+                        return result
+                    else:
+                        return main.FALSE
+                return experimentHandling
+            except TypeError as e:
+                main.log.error( "Arguments for experimental mode does not" +
+                                " have key 'retruns'" + e )
+
+    def connect( self ):
+
+        vars( main )[ self.name + 'log' ] = logging.getLogger( self.name )
+
+        session_file = main.logdir + "/" + self.name + ".session"
+        self.log_handler = logging.FileHandler( session_file )
+        self.log_handler.setLevel( logging.DEBUG )
+
+        vars( main )[ self.name + 'log' ].setLevel( logging.DEBUG )
+        _formatter = logging.Formatter(
+            "%(asctime)s  %(name)-10s: %(levelname)-8s: %(message)s" )
+        self.log_handler.setFormatter( _formatter )
+        vars( main )[ self.name + 'log' ].addHandler( self.log_handler )
+        # Adding header for the component log
+        vars( main )[ self.name + 'log' ].info( main.logHeader )
+        # Opening the session log to append command's execution output
+        self.logfile_handler = open( session_file, "a" )
+
+        return "Dummy"
+
+    def execute( self, cmd ):
+        return main.TRUE
+        # import commands
+        # return commands.getoutput( cmd )
+
+    def disconnect( self ):
+        return main.TRUE
+
+    def config( self ):
+        self = self
+        # Need to update the configuration code
+
+    def cleanup( self ):
+        return main.TRUE
+
+    def log( self, message ):
+        """
+        Here finding the for the component to which the
+        log message based on the called child object.
+        """
+        vars( main )[ self.name + 'log' ].info( "\n" + message + "\n" )
+
+    def close_log_handles( self ):
+        vars( main )[ self.name + 'log' ].removeHandler( self.log_handler )
+        if self.logfile_handler:
+            self.logfile_handler.close()
+
+    def get_version( self ):
+        return "Version unknown"
+
+    def experimentRun( self, *args, **kwargs ):
+        # FIXME handle *args
+        args = utilities.parse_args( [ "RETURNS" ], **kwargs )
+        return args[ "RETURNS" ]
+
+
+if __name__ != "__main__":
+    import sys
+    sys.modules[ __name__ ] = Component()
+
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()
+  
diff --git a/src/test/cli/utilities.py b/src/test/cli/utilities.py
new file mode 100644
index 0000000..15320da
--- /dev/null
+++ b/src/test/cli/utilities.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python
+'''
+Created on 23-Oct-2012
+
+@authors: Anil Kumar (anilkumar.s@paxterrasolutions.com),
+          Raghav Kashyap(raghavkashyap@paxterrasolutions.com)
+
+
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+
+
+Utilities will take care about the basic functions like :
+   * Extended assertion,
+   * parse_args for key-value pair handling
+   * Parsing the params or topology file.
+
+'''
+import re
+from configobj import ConfigObj
+import ast
+import smtplib
+
+import email
+import os
+import email.mime.application
+import time
+import random
+from clicommon import *
+
+class Utilities:
+    '''
+       Utilities will take care about the basic functions like :
+       * Extended assertion,
+       * parse_args for key-value pair handling
+       * Parsing the params or topology file.
+    '''
+
+    def __init__(self):
+        self.wrapped = sys.modules[__name__]
+
+    def __getattr__(self, name):
+        '''
+        This will invoke, if the attribute wasn't found the usual ways.
+        Here it will look for assert_attribute and will execute when AttributeError occurs.
+        It will return the result of the assert_attribute.
+        '''
+        try:
+            return getattr(self.wrapped, name)
+        except AttributeError:
+            def assertHandling(**kwargs):
+                nameVar = re.match("^assert",name,flags=0)
+                matchVar = re.match("assert(_not_|_)(equals|matches|greater|lesser)",name,flags=0)
+                notVar = 0
+                operators = ""
+
+                try :
+                    if matchVar.group(1) == "_not_" and matchVar.group(2) :
+                        notVar = 1
+                        operators = matchVar.group(2)
+                    elif matchVar.group(1) == "_" and matchVar.group(2):
+                        operators = matchVar.group(2)
+                except AttributeError:
+                    if matchVar==None and nameVar:
+                        operators ='equals'
+                result = self._assert(NOT=notVar,operator=operators,**kwargs)
+                if result == main.TRUE:
+                    main.log.info("Assertion Passed")
+                    main.STEPRESULT = main.TRUE
+                elif result == main.FALSE:
+                    main.log.warn("Assertion Failed")
+                    main.STEPRESULT = main.FALSE
+                else:
+                    main.log.error("There is an Error in Assertion")
+                    main.STEPRESULT = main.ERROR
+                return result
+            return assertHandling
+
+    def _assert (self,**assertParam):
+        '''
+        It will take the arguments :
+        expect:'Expected output'
+        actual:'Actual output'
+        onpass:'Action or string to be triggered or displayed respectively when the assert passed'
+        onfail:'Action or string to be triggered or displayed respectively when the assert failed'
+        not:'optional argument to specify the negation of the each assertion type'
+        operator:'assertion type will be defined by using operator. Like equal , greater, lesser, matches.'
+
+        It will return the assertion result.
+
+        '''
+
+        arguments = self.parse_args(["EXPECT","ACTUAL","ONPASS","ONFAIL","NOT","OPERATOR"],**assertParam)
+
+        result = 0
+        valuetype = ''
+        operation = "not "+ str(arguments["OPERATOR"]) if arguments['NOT'] and arguments['NOT'] == 1 else arguments["OPERATOR"]
+        operators = {'equals':{'STR':'==','NUM':'=='}, 'matches' : '=~', 'greater':'>' ,'lesser':'<'}
+
+        expectMatch = re.match('^\s*[+-]?0(e0)?\s*$', str(arguments["EXPECT"]), re.I+re.M)
+        if not ((not expectMatch) and (arguments["EXPECT"]==0)):
+            valuetype = 'NUM'
+        else :
+            if arguments["OPERATOR"] == 'greater' or arguments["OPERATOR"] == 'lesser':
+                main.log.error("Numeric comparison on strings is not possibele")
+                return main.ERROR
+
+        valuetype = 'STR'
+        arguments["ACTUAL"] = str(arguments["ACTUAL"])
+        if arguments["OPERATOR"] != 'matches':
+            arguments["EXPECT"] = str(arguments["EXPECT"])
+
+        try :
+            opcode = operators[str(arguments["OPERATOR"])][valuetype] if arguments["OPERATOR"] == 'equals' else operators[str(arguments["OPERATOR"])]
+
+        except KeyError as e:
+            print "Key Error in assertion"
+            print e
+            return main.FALSE
+
+        if opcode == '=~':
+            try:
+                assert re.search(str(arguments["EXPECT"]),str(arguments["ACTUAL"]))
+                result = main.TRUE
+            except AssertionError:
+                try :
+                    assert re.match(str(arguments["EXPECT"]),str(arguments["ACTUAL"]))
+                    result = main.TRUE
+                except AssertionError:
+                    main.log.error("Assertion Failed")
+                    result = main.FALSE
+        else :
+            try:
+                if str(opcode)=="==":
+                    main.log.info("Verifying the Expected is equal to the actual or not using assert_equal")
+                    if (arguments["EXPECT"] == arguments["ACTUAL"]):
+                        result = main.TRUE
+                    else :
+                        result = main.FALSE
+                elif str(opcode) == ">":
+                    main.log.info("Verifying the Expected is Greater than the actual or not using assert_greater")
+                    if (ast.literal_eval(arguments["EXPECT"]) > ast.literal_eval(arguments["ACTUAL"])) :
+                        result = main.TRUE
+                    else :
+                        result = main.FALSE
+                elif str(opcode) == "<":
+                    main.log.info("Verifying the Expected is Lesser than the actual or not using assert_lesser")
+                    if (ast.literal_eval(arguments["EXPECT"]) < ast.literal_eval(arguments["ACTUAL"])):
+                        result = main.TRUE
+                    else :
+                        result = main.FALSE
+            except AssertionError:
+                main.log.error("Assertion Failed")
+                result = main.FALSE
+        result = result if result else 0
+        result = not result if arguments["NOT"] and arguments["NOT"] == 1 else result
+        resultString = ""
+        if result :
+            resultString = str(resultString) + "PASS"
+            main.log.info(arguments["ONPASS"])
+        else :
+            resultString = str(resultString) + "FAIL"
+            if not isinstance(arguments["ONFAIL"],str):
+                eval(str(arguments["ONFAIL"]))
+            else :
+                main.log.error(arguments["ONFAIL"])
+                main.log.report(arguments["ONFAIL"])
+                main.onFailMsg = arguments[ 'ONFAIL' ]
+
+        msg = arguments["ON" + str(resultString)]
+
+        if not isinstance(msg,str):
+            try:
+                eval(str(msg))
+            except SyntaxError as e:
+                print "function definition is not right"
+                print e
+
+        main.last_result = result
+        if main.stepResults[2]:
+            main.stepResults[2][-1] = result
+            try:
+                main.stepResults[3][-1] = arguments[ 'ONFAIL' ]
+            except AttributeError:
+                pass
+        else:
+            main.log.warn( "Assertion called before a test step" )
+        return result
+
+    def parse_args(self,args, **kwargs):
+        '''
+        It will accept the (key,value) pair and will return the (key,value) pairs with keys in uppercase.
+        '''
+        newArgs = {}
+        for key,value in kwargs.iteritems():
+            if isinstance(args,list) and str.upper(key) in args:
+                for each in args:
+                    if each==str.upper(key):
+                        newArgs [str(each)] = value
+                    elif each != str.upper(key) and (newArgs.has_key(str(each)) == False ):
+                        newArgs[str(each)] = None
+
+        return newArgs
+
+    def send_mail(self):
+        # Create a text/plain message
+        msg = email.mime.Multipart.MIMEMultipart()
+        try :
+            if main.test_target:
+                sub = "Result summary of \"" + main.TEST + "\" run on component \"" +\
+                      main.test_target + "\" Version \"" +\
+                      vars( main )[main.test_target].get_version() + "\": " +\
+                      str( main.TOTAL_TC_SUCCESS ) + "% Passed"
+            else :
+                sub = "Result summary of \"" + main.TEST + "\": " +\
+                      str( main.TOTAL_TC_SUCCESS ) + "% Passed"
+        except ( KeyError, AttributeError ):
+            sub = "Result summary of \"" + main.TEST + "\": " +\
+                  str( main.TOTAL_TC_SUCCESS ) + "% Passed"
+
+        msg['Subject'] = sub
+        msg['From'] = main.sender
+        msg['To'] = main.mail
+
+        # The main body is just another attachment
+        body = email.mime.Text.MIMEText( main.logHeader + "\n" +
+                                         main.testResult)
+        msg.attach( body )
+
+        # Attachments
+        for filename in os.listdir( main.logdir ):
+            filepath = main.logdir + "/" + filename
+            fp = open( filepath, 'rb' )
+            att = email.mime.application.MIMEApplication( fp.read(),
+                                                          _subtype="" )
+            fp.close()
+            att.add_header( 'Content-Disposition',
+                            'attachment',
+                            filename=filename )
+            msg.attach( att )
+        try:
+            smtp = smtplib.SMTP( main.smtp )
+            smtp.starttls()
+            smtp.login( main.sender, main.senderPwd )
+            smtp.sendmail( msg['From'], [msg['To']], msg.as_string() )
+            smtp.quit()
+        except Exception:
+            main.log.exception( "Error sending email" )
+        return main.TRUE
+
+    def send_warning_email( self, subject=None ):
+        try:
+            if not subject:
+                subject = main.TEST + " PAUSED!"
+            # Create a text/plain message
+            msg = email.mime.Multipart.MIMEMultipart()
+
+            msg['Subject'] = subject
+            msg['From'] = main.sender
+            msg['To'] = main.mail
+
+            smtp = smtplib.SMTP( main.smtp )
+            smtp.starttls()
+            smtp.login( main.sender, main.senderPwd )
+            smtp.sendmail( msg['From'], [msg['To']], msg.as_string() )
+            smtp.quit()
+        except Exception:
+            main.log.exception( "" )
+            return main.FALSE
+        return main.TRUE
+
+    def parse(self,fileName):
+        '''
+        This will parse the params or topo or cfg file and return content in the file as Dictionary
+        '''
+        self.fileName = fileName
+        matchFileName = re.match(r'(.*)\.(cfg|params|topo)',self.fileName,re.M|re.I)
+        if matchFileName:
+            try :
+                parsedInfo = ConfigObj(self.fileName)
+                return parsedInfo
+            except StandardError:
+                print "There is no such file to parse "+fileName
+        else:
+            return 0
+
+    def retry( self, f, retValue, args=(), kwargs={},
+               sleep=1, attempts=2, randomTime=False ):
+        """
+        Given a function and bad return values, retry will retry a function
+        until successful or give up after a certain number of attempts.
+
+        Arguments:
+        f        - a callable object
+        retValue - Return value(s) of f to retry on. This can be a list or an
+                   object.
+        args     - A tuple containing the arguments of f.
+        kwargs   - A dictionary containing the keyword arguments of f.
+        sleep    - Time in seconds to sleep between retries. If random is True,
+                   this is the max time to wait. Defaults to 1 second.
+        attempts - Max number of attempts before returning. If set to 1,
+                   f will only be called once. Defaults to 2 trys.
+        random   - Boolean indicating if the wait time is random between 0
+                   and sleep or exactly sleep seconds. Defaults to False.
+        """
+        # TODO: be able to pass in a conditional statement(s). For example:
+        #      retCondition = "< 7"
+        #      Then we do something like 'if eval( "ret " + retCondition ):break'
+        try:
+            assert attempts > 0, "attempts must be more than 1"
+            assert sleep >= 0, "sleep must be >= 0"
+            if not isinstance( retValue, list ):
+                retValue = [ retValue ]
+            for i in range( 0, attempts ):
+                ret = f( *args, **kwargs )
+                if ret not in retValue:
+                # NOTE that False in [ 0 ] == True
+                    break
+                if randomTime:
+                    sleeptime = random.randint( 0, sleep )
+                else:
+                    sleeptime = sleep
+                time.sleep( sleeptime )
+            return ret
+        except AssertionError:
+            main.log.exception( "Invalid arguements for retry: " )
+            main.cleanup()
+            main.exit()
+        except Exception:
+            main.log.exception( "Uncaught exception in retry: " )
+            main.cleanup()
+            main.exit()
+
+utilities = Utilities()
+
+if __name__ != "__main__":
+    import sys
+
+    sys.modules[__name__] = Utilities()
diff --git a/src/test/setup/cord-test.py b/src/test/setup/cord-test.py
index ea7319e..2e73bc3 100755
--- a/src/test/setup/cord-test.py
+++ b/src/test/setup/cord-test.py
@@ -337,10 +337,11 @@
  cd openvswitch-2.4.0 && \
  ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-ssl && make && make install)
 RUN service openvswitch-switch restart || /bin/true
-RUN apt-get -y install python-twisted python-sqlite sqlite3
+RUN apt-get -y install python-twisted python-sqlite sqlite3 python-pexpect
 RUN pip install scapy-ssl_tls
 RUN pip install -U scapy
 RUN pip install monotonic
+RUN pip install configObj
 RUN mv /usr/sbin/tcpdump /sbin/
 RUN ln -sf /sbin/tcpdump /usr/sbin/tcpdump
 CMD ["/bin/bash"]