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"]