| """ |
| Copyright 2016 Open Networking Foundation ( ONF ) |
| |
| Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>, |
| the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>, |
| or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg> |
| |
| 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 re |
| |
| |
| class ONOSSetup: |
| main = None |
| |
| def __init__( self ): |
| self.default = '' |
| |
| def envSetupDescription( self, includeCaseDesc=True ): |
| """ |
| Introduction part of the test. It will initialize some basic vairables. |
| """ |
| if includeCaseDesc: |
| main.case( "Constructing test variables and building ONOS package" ) |
| main.caseExplanation = "For loading from params file, and pull" + \ |
| " and build the latest ONOS package" |
| main.step( "Constructing test variables" ) |
| try: |
| from tests.dependencies.Cluster import Cluster |
| except ImportError: |
| main.log.error( "Cluster not found. exiting the test" ) |
| main.cleanAndExit() |
| try: |
| main.Cluster |
| except ( NameError, AttributeError ): |
| main.Cluster = Cluster( main.ONOScell.nodes ) |
| main.ONOSbench = main.Cluster.controllers[ 0 ].Bench |
| main.testOnDirectory = re.sub( "(/tests)$", "", main.testsRoot ) |
| |
| def gitPulling( self, includeCaseDesc=True ): |
| """ |
| it will do git checkout or pull if they are enabled from the params file. |
| """ |
| |
| if includeCaseDesc: |
| main.case( "Pull onos branch and build onos on Teststation." ) |
| |
| gitPull = main.params[ 'GIT' ][ 'pull' ] |
| gitBranch = main.params[ 'GIT' ][ 'branch' ] |
| if gitPull == 'True': |
| main.step( "Git Checkout ONOS branch: " + gitBranch ) |
| stepResult = main.ONOSbench.gitCheckout( branch=gitBranch ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=stepResult, |
| onpass="Successfully checkout onos branch.", |
| onfail="Failed to checkout onos branch. Exiting test..." ) |
| if not stepResult: |
| main.cleanAndExit() |
| |
| main.step( "Git Pull on ONOS branch:" + gitBranch ) |
| stepResult = main.ONOSbench.gitPull() |
| utilities.assert_equals( expect=main.TRUE, |
| actual=stepResult, |
| onpass="Successfully pull onos. ", |
| onfail="Failed to pull onos. Exiting test ..." ) |
| if not stepResult: |
| main.cleanAndExit() |
| |
| else: |
| main.log.info( "Skipped git checkout and pull as they are disabled in params file" ) |
| |
| def envSetup( self, includeGitPull=True, includeCaseDesc=True ): |
| """ |
| Description: |
| some environment setup for the test. |
| Required: |
| * includeGitPull - if wish to git pulling function to be executed. |
| Returns: |
| Returns main.TRUE |
| """ |
| if includeGitPull: |
| self.gitPulling( includeCaseDesc ) |
| |
| try: |
| from tests.dependencies.Cluster import Cluster |
| except ImportError: |
| main.log.error( "Cluster not found. exiting the test" ) |
| main.cleanAndExit() |
| try: |
| main.Cluster |
| except ( NameError, AttributeError ): |
| main.Cluster = Cluster( main.ONOScell.nodes ) |
| |
| main.cellData = {} # For creating cell file |
| |
| return main.TRUE |
| |
| def envSetupException( self, e ): |
| """ |
| Description: |
| handles the exception that might occur from the environment setup. |
| Required: |
| * includeGitPull - exceeption code e. |
| """ |
| main.log.exception( e ) |
| main.cleanAndExit() |
| |
| def evnSetupConclusion( self, stepResult ): |
| """ |
| Description: |
| compare the result of the envSetup of the test. |
| Required: |
| * stepResult - Result of the envSetup. |
| """ |
| utilities.assert_equals( expect=main.TRUE, |
| actual=stepResult, |
| onpass="Successfully construct " + |
| "test variables ", |
| onfail="Failed to construct test variables" ) |
| |
| url = self.generateGraphURL() |
| main.log.wiki( url ) |
| |
| main.commit = main.ONOSbench.getVersion( report=True ) |
| |
| def generateGraphURL( self, width=525, height=350 ): |
| """ |
| Description: |
| Obtain the URL for the graph that corresponds to the test being run. |
| """ |
| |
| nodeCluster = main.params[ 'GRAPH' ][ 'nodeCluster' ] |
| testname = main.TEST |
| branch = main.ONOSbench.getBranchName() |
| maxBuildsToShow = main.params[ 'GRAPH' ][ 'builds' ] |
| |
| return '<ac:structured-macro ac:name="html">\n' + \ |
| '<ac:plain-text-body><![CDATA[\n' + \ |
| '<img src="https://jenkins.onosproject.org/view/QA/job/postjob-' + \ |
| nodeCluster + \ |
| '/lastSuccessfulBuild/artifact/' + \ |
| testname + \ |
| '_' + \ |
| branch + \ |
| '_' + \ |
| maxBuildsToShow + \ |
| '-builds_graph.jpg", alt="' + \ |
| testname + \ |
| '", style="width:' + \ |
| str( width ) + \ |
| 'px;height:' + \ |
| str( height ) + \ |
| 'px;border:0"' + \ |
| '>' + \ |
| ']]></ac:plain-text-body>\n' + \ |
| '</ac:structured-macro>\n' |
| |
| def setNumCtrls( self, hasMultiNodeRounds ): |
| """ |
| Description: |
| Set new number of controllers if it uses different number of nodes. |
| different number of nodes should be pre-defined in main.scale. |
| Required: |
| * hasMultiNodeRouds - if the test is testing different number of nodes. |
| """ |
| if hasMultiNodeRounds: |
| try: |
| main.cycle |
| except Exception: |
| main.cycle = 0 |
| main.cycle += 1 |
| # main.scale[ 0 ] determines the current number of ONOS controllers |
| main.Cluster.setRunningNode( int( main.scale.pop( 0 ) ) ) |
| |
| def killingAllAtomix( self, cluster, killRemoveMax, stopAtomix ): |
| """ |
| Description: |
| killing atomix. It will either kill the current runningnodes or |
| max number of the nodes. |
| Required: |
| * cluster - the cluster driver that will be used. |
| * killRemoveMax - The boolean that will decide either to kill |
| only running nodes ( False ) or max number of nodes ( True ). |
| * stopAtomix - If wish to stop atomix before killing it. True for |
| enable stop, False for disable stop. |
| Returns: |
| Returns main.TRUE if successfully killing it. |
| """ |
| main.log.info( "Safety check, killing all Atomix processes" ) |
| return cluster.killAtomix( killRemoveMax, stopAtomix ) |
| |
| def killingAllOnos( self, cluster, killRemoveMax, stopOnos ): |
| """ |
| Description: |
| killing the onos. It will either kill the current runningnodes or |
| max number of the nodes. |
| Required: |
| * cluster - the cluster driver that will be used. |
| * killRemoveMax - The boolean that will decide either to kill |
| only running nodes ( False ) or max number of nodes ( True ). |
| * stopOnos - If wish to stop onos before killing it. True for |
| enable stop, False for disable stop. |
| Returns: |
| Returns main.TRUE if successfully killing it. |
| """ |
| main.log.info( "Safety check, killing all ONOS processes" ) |
| return cluster.killOnos( killRemoveMax, stopOnos ) |
| |
| def createApplyCell( self, cluster, newCell, cellName, cellApps, |
| mininetIp, useSSH, onosIps, installMax=False, |
| atomixClusterSize=None ): |
| """ |
| Description: |
| create new cell ( optional ) and apply it. It will also verify the |
| cell. |
| Required Arguments: |
| * cluster - the cluster driver that will be used. |
| * newCell - True for making a new cell and False for not making it. |
| * cellName - The name of the cell. |
| * cellApps - The onos apps string. |
| * mininetIp - Mininet IP address. |
| * useSSH - True for using ssh when creating a cell |
| * onosIps - ip( s ) of the ONOS node( s ). |
| Optional Arguments: |
| * installMax |
| * atomixClusterSize - The size of the atomix cluster. Defaults to same |
| as ONOS Cluster size |
| Returns: |
| Returns main.TRUE if it successfully executed. |
| """ |
| if atomixClusterSize is None: |
| atomixClusterSize = len( cluster.runningNodes ) |
| atomixClusterSize = int( atomixClusterSize ) |
| cluster.setAtomixNodes( atomixClusterSize ) |
| atomixIps = [ node.ipAddress for node in cluster.atomixNodes ] |
| main.log.info( "Atomix Cluster Size = {} ".format( atomixClusterSize ) ) |
| if newCell: |
| cluster.createCell( cellName, cellApps, mininetIp, useSSH, onosIps, |
| atomixIps, installMax ) |
| main.step( "Apply cell to environment" ) |
| stepResult = cluster.applyCell( cellName ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=stepResult, |
| onpass="Successfully applied cell to " + |
| "environment", |
| onfail="Failed to apply cell to environment" ) |
| return stepResult |
| |
| def uninstallAtomix( self, cluster, uninstallMax ): |
| """ |
| Description: |
| uninstalling atomix and verify the result. |
| Required: |
| * cluster - a cluster driver that will be used. |
| * uninstallMax - True for uninstalling max number of nodes |
| False for uninstalling the current running nodes. |
| Returns: |
| Returns main.TRUE if it successfully uninstalled. |
| """ |
| main.step( "Uninstalling Atomix" ) |
| atomixUninstallResult = cluster.uninstallAtomix( uninstallMax ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=atomixUninstallResult, |
| onpass="Successfully uninstalled Atomix", |
| onfail="Failed to uninstall Atomix" ) |
| return atomixUninstallResult |
| |
| def uninstallOnos( self, cluster, uninstallMax ): |
| """ |
| Description: |
| uninstalling onos and verify the result. |
| Required: |
| * cluster - a cluster driver that will be used. |
| * uninstallMax - True for uninstalling max number of nodes |
| False for uninstalling the current running nodes. |
| Returns: |
| Returns main.TRUE if it successfully uninstalled. |
| """ |
| main.step( "Uninstalling ONOS package" ) |
| onosUninstallResult = cluster.uninstallOnos( uninstallMax ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=onosUninstallResult, |
| onpass="Successfully uninstalled ONOS package", |
| onfail="Failed to uninstall ONOS package" ) |
| return onosUninstallResult |
| |
| def buildOnos( self, cluster ): |
| """ |
| Description: |
| build the onos using bazel build onos and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| Returns: |
| Returns main.TRUE if it successfully built. |
| """ |
| main.step( "Creating ONOS package" ) |
| packageResult = main.ONOSbench.bazelBuild() |
| utilities.assert_equals( expect=main.TRUE, |
| actual=packageResult, |
| onpass="Successfully created ONOS package", |
| onfail="Failed to create ONOS package" ) |
| return packageResult |
| |
| def installAtomix( self, cluster, parallel=True ): |
| """ |
| Description: |
| Installing atomix and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| False for installing current running nodes only. |
| Returns: |
| Returns main.TRUE if it successfully installed |
| """ |
| main.step( "Installing Atomix" ) |
| atomixInstallResult = main.TRUE |
| |
| atomixInstallResult = cluster.installAtomix( parallel ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=atomixInstallResult, |
| onpass="Successfully installed Atomix", |
| onfail="Failed to install Atomix" ) |
| if not atomixInstallResult: |
| main.cleanAndExit() |
| return atomixInstallResult |
| |
| def installOnos( self, cluster, installMax, parallel=True ): |
| """ |
| Description: |
| Installing onos and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| * installMax - True for installing max number of nodes |
| False for installing current running nodes only. |
| Returns: |
| Returns main.TRUE if it successfully installed |
| """ |
| main.step( "Installing ONOS package" ) |
| onosInstallResult = main.TRUE |
| |
| cluster.installOnos( installMax, parallel ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=onosInstallResult, |
| onpass="Successfully installed ONOS package", |
| onfail="Failed to install ONOS package" ) |
| if not onosInstallResult: |
| main.cleanAndExit() |
| return onosInstallResult |
| |
| def setupSsh( self, cluster ): |
| """ |
| Description: |
| set up ssh to the onos and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| Returns: |
| Returns main.TRUE if it successfully setup the ssh to |
| the onos. |
| """ |
| main.step( "Set up ONOS secure SSH" ) |
| secureSshResult = cluster.ssh() |
| utilities.assert_equals( expect=main.TRUE, |
| actual=secureSshResult, |
| onpass="Secured ONOS ssh", |
| onfail="Failed to secure ssh" ) |
| return secureSshResult |
| |
| def checkOnosService( self, cluster ): |
| """ |
| Description: |
| Checking if the onos service is up and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| Returns: |
| Returns main.TRUE if it successfully checked |
| """ |
| main.step( "Checking ONOS service" ) |
| stepResult = cluster.checkService() |
| utilities.assert_equals( expect=main.TRUE, |
| actual=stepResult, |
| onpass="ONOS service is ready on all nodes", |
| onfail="ONOS service did not start properly on all nodes" ) |
| return stepResult |
| |
| def startOnosClis( self, cluster ): |
| """ |
| Description: |
| starting Onos using onosCli driver and verify the result |
| Required: |
| * cluster - the cluster driver that will be used. |
| Returns: |
| Returns main.TRUE if it successfully started. It will exit |
| the test if not started properly. |
| """ |
| main.step( "Starting ONOS CLI sessions" ) |
| startCliResult = cluster.startCLIs() |
| if not startCliResult: |
| main.log.info( "ONOS CLI did not start up properly" ) |
| main.cleanAndExit() |
| else: |
| main.log.info( "Successful CLI startup" ) |
| utilities.assert_equals( expect=main.TRUE, |
| actual=startCliResult, |
| onpass="Successfully start ONOS cli", |
| onfail="Failed to start ONOS cli" ) |
| return startCliResult |
| |
| def checkOnosNodes( self, cluster ): |
| """ |
| Description: |
| Checking if the onos nodes are in READY state |
| Required: |
| * cluster - the cluster driver that will be used. |
| Returns: |
| Returns main.TRUE if it successfully checked |
| """ |
| main.step( "Checking ONOS nodes" ) |
| stepResult = utilities.retry( main.Cluster.nodesCheck, |
| False, |
| attempts=90 ) |
| |
| utilities.assert_equals( expect=True, |
| actual=stepResult, |
| onpass="All ONOS nodes are in READY state", |
| onfail="Not all ONOS nodes are in READY state" ) |
| |
| if not stepResult: |
| for ctrl in main.Cluster.active(): |
| main.log.debug( "{} components not ACTIVE: \n{}".format( |
| ctrl.name, |
| ctrl.CLI.sendline( "onos:scr-list | grep -v ACTIVE" ) ) ) #FIXME: This output has changed a lot |
| main.log.error( "Failed to start ONOS, stopping test" ) |
| main.cleanAndExit() |
| return main.TRUE |
| |
| def checkOnosApps( self, cluster, apps ): |
| """ |
| Description: |
| Checking if the onos applications are activated |
| Required: |
| * cluster - the cluster driver that will be used. |
| * apps: list of applications that are expected to be activated |
| Returns: |
| Returns main.TRUE if it successfully checked |
| """ |
| main.step( "Checking ONOS applications" ) |
| stepResult = utilities.retry( main.Cluster.appsCheck, |
| False, |
| args = [ apps ], |
| sleep=5, |
| attempts=90 ) |
| |
| utilities.assert_equals( expect=True, |
| actual=stepResult, |
| onpass="All ONOS apps are activated", |
| onfail="Not all ONOS apps are activated" ) |
| |
| return main.TRUE if stepResult else main.FALSE |
| |
| def processList( self, functions, args ): |
| if functions is not None: |
| if isinstance( functions, list ): |
| i = 0 |
| for f in functions: |
| f( *( args[ i ] ) ) if args is not None and args[ i ] is not None else f() |
| i += 1 |
| else: |
| functions( *args ) if args is not None else functions() |
| |
| def ONOSSetUp( self, cluster, hasMultiNodeRounds=False, startOnos=True, newCell=True, |
| cellName="temp", cellApps="drivers", appPrefix="org.onosproject.", |
| mininetIp="", extraApply=None, applyArgs=None, |
| extraClean=None, cleanArgs=None, skipPack=False, installMax=False, |
| atomixClusterSize=None, useSSH=True, killRemoveMax=True, stopAtomix=False, |
| stopOnos=False, installParallel=True, cellApply=True, |
| includeCaseDesc=True, restartCluster=True ): |
| """ |
| Description: |
| Initial ONOS setting up of the tests. It will also verify the result of each steps. |
| The procedures will be: |
| killing onos |
| creating ( optional ) /applying cell |
| removing raft logs ( optional ) |
| uninstalling onos |
| extra procedure to be applied( optional ) |
| building onos |
| installing onos |
| extra cleanup to be applied ( optional ) |
| setting up ssh to the onos |
| checking the onos service |
| starting onos |
| Required Arguments: |
| * cluster - the cluster driver that will be used. |
| Optional Arguments: |
| * hasMultiNodeRounds - True if the test is testing different set of nodes |
| * startOnos - True if wish to start onos. |
| * newCell - True for making a new cell and False for not making it. |
| * cellName - Name of the cell that will be used. |
| * cellApps - The cell apps string. Will be overwritten by main.apps if it exists |
| * appPrefix - Prefix of app names. Will use "org.onosproject." by default |
| * mininetIp - Mininet IP address. |
| * extraApply - Function( s ) that will be called before building ONOS. Default to None. |
| * applyArgs - argument of the functon( s ) of the extraApply. Should be in list. |
| * extraClean - Function( s ) that will be called after building ONOS. Defaults to None. |
| * cleanArgs - argument of the functon( s ) of the extraClean. Should be in list. |
| * skipPack - True if wish to skip some packing. |
| * installMax - True if wish to install onos max number of nodes |
| False if wish to install onos of running nodes only |
| * atomixClusterSize - The number of Atomix nodes in the cluster. |
| Defaults to None which will be the number of OC# nodes in the cell |
| * useSSH - True for using ssh when creating a cell |
| * killRemoveMax - True for removing/killing max number of nodes. False for |
| removing/killing running nodes only. |
| * stopAtomix - True if wish to stop atomix before killing it. |
| * stopOnos - True if wish to stop onos before killing it. |
| * restartCluster - True if wish to kill and restart atomix and onos clusters |
| Returns: |
| Returns main.TRUE if it everything successfully proceeded. |
| """ |
| self.setNumCtrls( hasMultiNodeRounds ) |
| if includeCaseDesc: |
| main.case( "Starting up " + str( cluster.numCtrls ) + |
| " node(s) ONOS cluster" ) |
| main.caseExplanation = "Set up ONOS with " + str( cluster.numCtrls ) + \ |
| " node(s) ONOS cluster" |
| |
| main.log.info( "ONOS cluster size = " + str( cluster.numCtrls ) ) |
| cellResult = main.TRUE |
| if cellApply: |
| try: |
| apps = main.apps |
| except ( NameError, AttributeError ): |
| apps = cellApps |
| main.log.debug( "Apps: " + str( apps ) ) |
| tempOnosIp = [] |
| for ctrl in cluster.runningNodes: |
| tempOnosIp.append( ctrl.ipAddress ) |
| if mininetIp == "": |
| mininetIp = "localhost" |
| for key, value in main.componentDictionary.items(): |
| if value['type'] in ['MininetCliDriver', 'RemoteMininetDriver'] and hasattr( main, key ): |
| mininetIp = getattr( main, key ).ip_address |
| break |
| cellResult = self.createApplyCell( cluster, newCell, |
| cellName, apps, |
| mininetIp, useSSH, |
| tempOnosIp, installMax, |
| atomixClusterSize ) |
| |
| if restartCluster: |
| atomixKillResult = self.killingAllAtomix( cluster, killRemoveMax, stopAtomix ) |
| onosKillResult = self.killingAllOnos( cluster, killRemoveMax, stopOnos ) |
| killResult = atomixKillResult and onosKillResult |
| else: |
| killResult = main.TRUE |
| |
| if restartCluster: |
| atomixUninstallResult = self.uninstallAtomix( cluster, killRemoveMax ) |
| onosUninstallResult = self.uninstallOnos( cluster, killRemoveMax ) |
| uninstallResult = atomixUninstallResult and onosUninstallResult |
| self.processList( extraApply, applyArgs ) |
| |
| packageResult = main.TRUE |
| if not skipPack: |
| packageResult = self.buildOnos(cluster) |
| |
| atomixInstallResult = self.installAtomix( cluster, installParallel ) |
| onosInstallResult = self.installOnos( cluster, installMax, installParallel ) |
| installResult = atomixInstallResult and onosInstallResult |
| |
| self.processList( extraClean, cleanArgs ) |
| secureSshResult = self.setupSsh( cluster ) |
| else: |
| packageResult = main.TRUE |
| uninstallResult = main.TRUE |
| installResult = main.TRUE |
| secureSshResult = main.TRUE |
| |
| onosServiceResult = self.checkOnosService( cluster ) |
| |
| onosCliResult = main.TRUE |
| if startOnos: |
| onosCliResult = self.startOnosClis( cluster ) |
| |
| onosNodesResult = self.checkOnosNodes( cluster ) |
| |
| onosAppsResult = main.TRUE |
| if cellApply: |
| if apps: |
| apps = apps.split( ',' ) |
| apps = [ appPrefix + app for app in apps ] |
| onosAppsResult = self.checkOnosApps( cluster, apps ) |
| else: |
| main.log.warn( "No apps were specified to be checked after startup" ) |
| |
| return killResult and cellResult and packageResult and uninstallResult and \ |
| installResult and secureSshResult and onosServiceResult and onosCliResult and \ |
| onosNodesResult and onosAppsResult |