blob: 2f0b28a100c835ee4676b43d24ea75ae907b6953 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 22-Oct-2012
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
Jon Hall65844a32015-03-09 19:09:37 -07005
Jeremy Songsterae01bba2016-07-11 15:39:17 -07006Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
adminbae64d82013-08-01 10:50:15 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 (at your option) any later version.
14
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
Jon Hall65844a32015-03-09 19:09:37 -070021 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070022
23
24
25teston is the main module.
26
27'''
28
29import sys
30import getpass
31import os
32import re
33import __builtin__
34import new
35import xmldict
Jon Hall30b82fa2015-03-04 17:15:43 -080036import importlib
Jon Hall5b586732015-06-11 11:39:39 -070037import threading
Jon Hall714eeba2015-09-29 17:53:10 -070038module = new.module( "test" )
adminbae64d82013-08-01 10:50:15 -070039import openspeak
Hari Krishnabe4b97b2015-07-15 12:19:43 -070040import subprocess
Jon Hall714eeba2015-09-29 17:53:10 -070041global path, drivers_path, core_path, tests_path, logs_path
Jon Hall44506242015-07-29 17:40:26 -070042location = os.path.abspath( os.path.dirname( __file__ ) )
43path = re.sub( "(core|bin)$", "", location )
Jon Hall714eeba2015-09-29 17:53:10 -070044drivers_path = path + "drivers"
45core_path = path + "core"
46tests_path = path + "tests"
47logs_path = path + "logs/"
adminbae64d82013-08-01 10:50:15 -070048config_path = path + "config/"
Jon Hall44506242015-07-29 17:40:26 -070049sys.path.append( path )
50sys.path.append( drivers_path )
51sys.path.append( core_path )
52sys.path.append( tests_path )
adminbae64d82013-08-01 10:50:15 -070053
54from core.utilities import Utilities
kelvin-onlabfb521662015-02-27 09:52:40 -080055from core.Thread import Thread
adminbae64d82013-08-01 10:50:15 -070056
adminbae64d82013-08-01 10:50:15 -070057
58class TestON:
59 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070060 TestON will initiate the specified test.
Jon Hall714eeba2015-09-29 17:53:10 -070061 The main tasks are:
kelvin-onlabf70fd542015-05-07 18:41:40 -070062 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070063 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070064 '''
Jon Hall714eeba2015-09-29 17:53:10 -070065 def __init__( self, options ):
adminbae64d82013-08-01 10:50:15 -070066 '''
Jon Hall714eeba2015-09-29 17:53:10 -070067 Initialise the component handles specified in the topology file of
68 the specified test.
adminbae64d82013-08-01 10:50:15 -070069 '''
70 # Initialization of the variables.
71 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070072 __builtin__.path = path
73 __builtin__.utilities = Utilities()
74 self.TRUE = 1
75 self.FALSE = 0
76 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070077 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070078 self.FAIL = False
79 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070080 self.CASERESULT = self.ERROR
81 self.STEPRESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -070082 self.init_result = self.TRUE
83 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070084 self.stepName = ""
85 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080086 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070087 self.test_target = None
88 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080089 self.testDir = tests_path
90 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070091 self.parsingClass = "xmlparser"
92 self.parserPath = core_path + "/xmlparser"
93 self.loggerPath = core_path + "/logger"
94 self.loggerClass = "Logger"
95 self.logs_path = logs_path
96 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -080097 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -070098 self.cleanupFlag = False
99 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -0700100 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -0700101
Jon Hall25079782015-10-13 13:54:39 -0700102 self.config = self.configparser()
Jon Hall714eeba2015-09-29 17:53:10 -0700103 verifyOptions( options )
adminbae64d82013-08-01 10:50:15 -0700104 load_logger()
105 self.componentDictionary = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700106 self.componentDictionary = self.topology['COMPONENT']
107 self.driversList = []
108 if isinstance( self.componentDictionary, str):
109 self.componentDictionary = dict( self.componentDictionary )
Jon Hall65844a32015-03-09 19:09:37 -0700110
Jon Hall714eeba2015-09-29 17:53:10 -0700111 for component in self.componentDictionary:
112 self.driversList.append( self.componentDictionary[component]['type'] )
Jon Hall65844a32015-03-09 19:09:37 -0700113
Jon Hall714eeba2015-09-29 17:53:10 -0700114 self.driversList = list( set( self.driversList ) ) # Removing duplicates.
adminbae64d82013-08-01 10:50:15 -0700115 # Checking the test_target option set for the component or not
Jon Hall714eeba2015-09-29 17:53:10 -0700116 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700117 for component in self.componentDictionary.keys():
118 if 'test_target' in self.componentDictionary[component].keys():
119 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700120
Jon Halld61331b2015-02-17 16:35:47 -0800121 # Checking for the openspeak file and test script
Jon Hall714eeba2015-09-29 17:53:10 -0700122 self.logger.initlog( self )
adminbae64d82013-08-01 10:50:15 -0700123
124 # Creating Drivers Handles
Jon Hall714eeba2015-09-29 17:53:10 -0700125 initString = "\n" + "*" * 30 + "\n CASE INIT \n" + "*" * 30 + "\n"
126 self.log.exact( initString )
adminbae64d82013-08-01 10:50:15 -0700127 self.driverObject = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700128 self.random_order = 111 # Random order id to connect the components
adminbae64d82013-08-01 10:50:15 -0700129 components_connect_order = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700130 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700131 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700132 if 'connect_order' not in self.componentDictionary[component].keys():
133 self.componentDictionary[component]['connect_order'] = str( self.get_random() )
134 components_connect_order[component] = eval( self.componentDictionary[component]['connect_order'] )
135 # Ordering components based on the connect order.
136 ordered_component_list = sorted( components_connect_order,
137 key=lambda key: components_connect_order[key] )
adminbae64d82013-08-01 10:50:15 -0700138 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700139 for component in ordered_component_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700140 self.componentInit( component )
adminbae64d82013-08-01 10:50:15 -0700141
Jon Hall714eeba2015-09-29 17:53:10 -0700142 def configparser( self ):
adminbae64d82013-08-01 10:50:15 -0700143 '''
144 It will parse the config file (teston.cfg) and return as dictionary
145 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700146 matchFileName = re.match( r'(.*)\.cfg', self.configFile, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700147 if matchFileName:
Jon Hall714eeba2015-09-29 17:53:10 -0700148 xml = open( self.configFile ).read()
149 try:
150 self.configDict = xmldict.xml_to_dict( xml )
adminbae64d82013-08-01 10:50:15 -0700151 return self.configDict
Jon Hall1306a562015-09-04 11:21:24 -0700152 except IOError:
adminbae64d82013-08-01 10:50:15 -0700153 print "There is no such file to parse " + self.configFile
Jon Hall1306a562015-09-04 11:21:24 -0700154 else:
155 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700156
Jon Hall714eeba2015-09-29 17:53:10 -0700157 def componentInit( self, component ):
adminbae64d82013-08-01 10:50:15 -0700158 '''
159 This method will initialize specified component
160 '''
161 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700162 self.initiated = False
Jon Hall714eeba2015-09-29 17:53:10 -0700163 self.log.info( "Creating component Handle: " + component )
Jon Halld61331b2015-02-17 16:35:47 -0800164 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700165 if 'COMPONENTS' in self.componentDictionary[component].keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700166 driver_options = dict( self.componentDictionary[component]['COMPONENTS'] )
Jon Hall714eeba2015-09-29 17:53:10 -0700167 driver_options['name'] = component
adminbae64d82013-08-01 10:50:15 -0700168 driverName = self.componentDictionary[component]['type']
Jon Hall714eeba2015-09-29 17:53:10 -0700169 driver_options['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700170
Jon Hall714eeba2015-09-29 17:53:10 -0700171 classPath = self.getDriverPath( driverName.lower() )
172 driverModule = importlib.import_module( classPath )
173 driverClass = getattr( driverModule, driverName )
adminbae64d82013-08-01 10:50:15 -0700174 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700175
Jon Hall714eeba2015-09-29 17:53:10 -0700176 if "OCN" in self.componentDictionary[component]['host'] and\
177 main.onoscell:
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700178 self.componentDictionary[component]['host'] = main.mnIP
179
Jon Hall714eeba2015-09-29 17:53:10 -0700180 user_name = self.componentDictionary[component].get( 'user',
181 getpass.getuser() )
182 ip_address = self.componentDictionary[component].get( 'host',
183 'localhost' )
184 pwd = self.componentDictionary[component].get( 'password',
185 'changeme' )
186 port = self.componentDictionary[component].get( 'port' )
187 connect_result = driverObject.connect( user_name=user_name,
188 ip_address=ip_address,
189 pwd=pwd,
190 port=port,
191 options=driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700192
adminbae64d82013-08-01 10:50:15 -0700193 if not connect_result:
Jon Hall714eeba2015-09-29 17:53:10 -0700194 self.log.error( "Exiting from the test execution because connecting to the " +
195 component + " component failed." )
Jon Halld61331b2015-02-17 16:35:47 -0800196 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700197
Jon Hall714eeba2015-09-29 17:53:10 -0700198 vars( self )[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700199 self.initiated = True
kelvin-onlabf70fd542015-05-07 18:41:40 -0700200
Jon Hall714eeba2015-09-29 17:53:10 -0700201 def run( self ):
adminbae64d82013-08-01 10:50:15 -0700202 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700203 The Execution of the test script's cases listed in the Test params
204 file will be done here then update each test case result.
205 This method will return main.TRUE if it executed all the test cases
206 successfully, else will retun main.FALSE
adminbae64d82013-08-01 10:50:15 -0700207 '''
adminbae64d82013-08-01 10:50:15 -0700208 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700209 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700210 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800211 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700212 self.TOTAL_TC_NORESULT = 0
213 self.TOTAL_TC_FAIL = 0
214 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700215 self.TEST_ITERATION = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700216
217 # NOTE: number of main.step statements in the
218 # outer most level of the test case. used to
219 # execute code in smaller steps
220 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700221 self.CASERESULT = self.NORESULT
222
Jon Halld61331b2015-02-17 16:35:47 -0800223 import testparser
Jon Hall53c5e662016-04-13 16:06:56 -0700224 test = testparser.TestParser( main.testFile )
adminbae64d82013-08-01 10:50:15 -0700225 self.testscript = test.testscript
226 self.code = test.getStepCode()
Jon Hall714eeba2015-09-29 17:53:10 -0700227 repeat = int( self.params.get( 'repeat', 1 ) )
228 self.TOTAL_TC_PLANNED = len( self.testcases_list ) * repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700229
adminbae64d82013-08-01 10:50:15 -0700230 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700231 while repeat:
Jon Halla1185982014-09-15 14:55:10 -0700232 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700233 result = self.runCase( self.CurrentTestCaseNumber )
234 repeat -= 1
adminbae64d82013-08-01 10:50:15 -0700235 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700236
Jon Halle234cc42015-08-31 15:26:47 -0700237 def runCase( self, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700238 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700239 self.CurrentTestCase = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700240
241 # List of step results in a case. ANDed together to get the result
242 self.stepResultsList = []
kelvin-onlabf70fd542015-05-07 18:41:40 -0700243 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700244 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700245 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700246
247 # NOTE: number of main.step statements in the
248 # outer most level of the test case. used to
249 # execute code in smaller steps
250 self.stepCount = 0
251
252 # NOTE: This is the current number of main.step()'s executed
253 # in a case. Used for logging.
254 self.stepNumber = 0
adminbae64d82013-08-01 10:50:15 -0700255 self.EXPERIMENTAL_MODE = self.FALSE
256 self.addCaseHeader()
Jon Halle234cc42015-08-31 15:26:47 -0700257 self.testCaseNumber = str( testCaseNumber )
258 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700259 stopped = False
Jon Hall5a72b712015-09-28 12:20:59 -0700260 try:
261 self.code[self.testCaseNumber]
Jon Halld61331b2015-02-17 16:35:47 -0800262 except KeyError:
Jon Halle234cc42015-08-31 15:26:47 -0700263 self.log.error( "There is no Test-Case " + self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800264 return self.FALSE
adminbae64d82013-08-01 10:50:15 -0700265 self.stepCount = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700266 while self.stepCount < len( self.code[self.testCaseNumber].keys() ):
267 result = self.runStep( self.code, self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800268 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700269 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800270 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700271 continue
Jon Hall5a72b712015-09-28 12:20:59 -0700272 # stepResults format: ( stepNo[], stepName[], stepResult[], onFail[] )
273 stepResults = self.stepResultsList
Jon Halle234cc42015-08-31 15:26:47 -0700274 if not stopped:
275 if self.CASERESULT == self.TRUE or self.CASERESULT == self.FALSE:
Jon Hall714eeba2015-09-29 17:53:10 -0700276 # Result was already explitily set somewhere else like
277 # in skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700278 pass
Jon Hall5a72b712015-09-28 12:20:59 -0700279 elif all( self.TRUE == i for i in stepResults ):
kelvin-onlabf70fd542015-05-07 18:41:40 -0700280 # ALL PASSED
281 self.CASERESULT = self.TRUE
Jon Hall5a72b712015-09-28 12:20:59 -0700282 elif self.FALSE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700283 # AT LEAST ONE FAILED
284 self.CASERESULT = self.FALSE
Jon Hall5a72b712015-09-28 12:20:59 -0700285 elif self.TRUE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700286 # AT LEAST ONE PASSED
287 self.CASERESULT = self.TRUE
288 else:
289 self.CASERESULT = self.NORESULT
Jon Hall714eeba2015-09-29 17:53:10 -0700290 self.testCaseResult[str( self.CurrentTestCaseNumber )] = self.CASERESULT
291 self.logger.updateCaseResults( self )
Jon Hall783bbf92015-07-23 14:33:19 -0700292 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
293 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700294 self.log.wiki( "<ul>" )
acsmarse2d1ed12015-10-05 13:51:17 -0700295 subcaseMessage = False
kelvin-onlabf70fd542015-05-07 18:41:40 -0700296 for line in self.stepCache.splitlines():
acsmarse2d1ed12015-10-05 13:51:17 -0700297 if re.search( "[0-9]\.[0-9]", line ): # Step
298 if subcaseMessage: # End of Failure Message Printout
299 self.log.wiki( "</ul>\n" )
300 subcaseMessage = False
301 if re.search( " - PASS$", line ):
302 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
303 elif re.search( " - FAIL$", line ):
304 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
305 elif re.search( " - No Result$", line ):
306 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
307 else: # Substep
308 if not subcaseMessage: # Open Failure Message Printout
309 self.log.wiki( "<ul><li>" + line + "</li>\n" )
310 subcaseMessage = True
311 else: # Add to Failure Message Printout
312 self.log.wiki( "<li>" + line + "</li>\n" )
acsmars27e62dd2015-10-06 11:35:47 -0700313 if subcaseMessage: # End of Failure Message Printout for last item
314 self.log.wiki( "</ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700315 self.log.wiki( "</ul>" )
316 self.log.summary( self.stepCache )
317 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700318 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700319
Jon Hall714eeba2015-09-29 17:53:10 -0700320 def runStep( self, code, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700321 if not cli.pause:
Jon Hall5a72b712015-09-28 12:20:59 -0700322 try:
323 step = self.stepCount
324 # stepResults format: ( stepNo, stepName, stepResult, onFail )
325 # NOTE: This is needed to catch results of main.step()'s
326 # called inside functions or loops
327 self.stepResults = ( [], [], [], [] )
adminbae64d82013-08-01 10:50:15 -0700328 exec code[testCaseNumber][step] in module.__dict__
329 self.stepCount = self.stepCount + 1
Jon Hall96b816f2015-11-03 12:00:56 -0800330 self.parseStepResults( testCaseNumber )
Jon Halle234cc42015-08-31 15:26:47 -0700331 except StopIteration: # Raised in self.skipCase()
332 self.log.warn( "Skipping the rest of CASE" +
333 str( testCaseNumber ) )
Jon Hall96b816f2015-11-03 12:00:56 -0800334 self.parseStepResults( testCaseNumber )
Jon Hall714eeba2015-09-29 17:53:10 -0700335 self.stepResultsList.append( self.STEPRESULT )
Jon Halle234cc42015-08-31 15:26:47 -0700336 self.stepCache += "\t\t" + self.onFailMsg + "\n"
337 self.stepCount = self.stepCount + 1
338 return self.FALSE
Jon Hallc1606352015-10-06 14:51:36 -0700339 except StandardError as e:
340 try:
341 stepNo = self.stepResults[0][ self.stepNumber - 1 ]
342 except IndexError:
343 stepNo = "<IndexError>"
344 main.log.warn( "Error trying to get step number. " +
345 "It is likely between step " +
Jon Hall6e709752016-02-01 13:38:46 -0800346 str( self.stepNumber ) + " and step " +
Jon Hallc1606352015-10-06 14:51:36 -0700347 str( self.stepNumber + 1 ) )
348 try:
349 stepName = self.stepResults[1][ self.stepNumber - 1 ]
350 except IndexError:
351 stepName = "<IndexError>"
352 self.log.error( "\nException in the following section of" +
353 " code: " + str( testCaseNumber ) + "." +
354 str( stepNo ) + ": " + stepName )
Jeremyd9e4eb12016-04-13 12:09:06 -0700355 self.log.error( str( e.__class__ ) + str( e.message ) )
adminbae64d82013-08-01 10:50:15 -0700356 self.stepCount = self.stepCount + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700357 self.logger.updateCaseResults( self )
358 # WIKI results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700359 self.log.wiki( "<ul>" )
360 for line in self.stepCache.splitlines():
361 if re.search( " - PASS$", line ):
362 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
363 elif re.search( " - FAIL$", line ):
364 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
365 elif re.search( " - No Result$", line ):
366 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700367 else: # Should only be on fail message
368 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700369 self.log.wiki( "</ul>" )
Jon Hall714eeba2015-09-29 17:53:10 -0700370 # summary results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700371 self.log.summary( self.stepCache )
372 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700373 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700374 self.exit()
Jon Halle234cc42015-08-31 15:26:47 -0700375 return self.TRUE
adminbae64d82013-08-01 10:50:15 -0700376 if cli.stop:
377 cli.stop = False
adminbae64d82013-08-01 10:50:15 -0700378 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700379 self.testCaseResult[str( self.CurrentTestCaseNumber )] = "Stopped"
380 self.logger.updateCaseResults( self )
adminbae64d82013-08-01 10:50:15 -0700381 result = self.cleanup()
Jon Halle234cc42015-08-31 15:26:47 -0700382 return self.FALSE
383
Jon Hall96b816f2015-11-03 12:00:56 -0800384 def parseStepResults( self, testCaseNumber ):
385 """
386 Parse throught the step results for the wiki
387 """
388 try:
389 # Iterate through each of the steps and print them
390 for index in range( len( self.stepResults[0] ) ):
391 # stepResults = ( stepNo, stepName, stepResult, onFail )
392 stepNo = self.stepResults[0][ index ]
393 stepName = self.stepResults[1][ index ]
394 stepResult = self.stepResults[2][ index ]
395 onFail = self.stepResults[3][ index ]
396 self.stepCache += "\t" + str( testCaseNumber ) + "."
397 self.stepCache += str( stepNo ) + " "
398 self.stepCache += stepName + " - "
399 if stepResult == self.TRUE:
400 self.stepCache += "PASS\n"
401 elif stepResult == self.FALSE:
402 self.stepCache += "FAIL\n"
403 self.stepCache += "\t\t" + onFail + "\n"
404 else:
405 self.stepCache += "No Result\n"
406 self.stepResultsList.append( stepResult )
407 except Exception:
408 self.log.exception( "Error parsing step results" )
409
Jon Halle234cc42015-08-31 15:26:47 -0700410 def skipCase( self, result="DEFAULT", msg=None ):
411 """
412 Will skip the rest of the code in a test case. The case results will be
413 determined as normal based on completed assertions unless the result
414 argument is given.
415
416 Optional Arguments:
417 result: Case insensite string. Can be 'PASS' or 'FAIL' and will set
418 the case result accordingly.
419 msg: Message to be printed when the case is skipped in the reports.
420 """
421 result = result.upper().strip()
422 if result == "PASS":
423 self.CASERESULT = self.TRUE
424 elif result == "FAIL":
425 self.CASERESULT = self.FALSE
426 self.onFailMsg = "Skipping the rest of this case. "
427 if msg:
428 self.onFailMsg += str( msg )
429 raise StopIteration
kelvin-onlabf70fd542015-05-07 18:41:40 -0700430
Jon Hall714eeba2015-09-29 17:53:10 -0700431 def addCaseHeader( self ):
432 caseHeader = "\n" + "*" * 30 + "\n Result summary for Testcase" +\
433 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 30 + "\n"
434 self.log.exact( caseHeader )
435 caseHeader = "\n" + "*" * 40 + "\nStart of Test Case" +\
436 str( self.CurrentTestCaseNumber ) + " : "
adminbae64d82013-08-01 10:50:15 -0700437 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700438 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700439
Jon Hall714eeba2015-09-29 17:53:10 -0700440 def addCaseFooter( self ):
Jon Hall5a72b712015-09-28 12:20:59 -0700441 stepNo = self.stepResults[0][-2]
Jon Hall714eeba2015-09-29 17:53:10 -0700442 if stepNo > 0:
443 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
444 str( stepNo ) + ": " + str( self.stepName )
445 stepHeader = "\n" + "*" * 40 + "\nEnd of Step " + previousStep +\
446 "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700447
Jon Hall714eeba2015-09-29 17:53:10 -0700448 caseFooter = "\n" + "*" * 40 + "\nEnd of Test case " +\
449 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700450
adminbae64d82013-08-01 10:50:15 -0700451 for driver in self.driversList:
Jon Hall714eeba2015-09-29 17:53:10 -0700452 vars( self )[driver].write( stepHeader + "\n" + caseFooter )
adminbae64d82013-08-01 10:50:15 -0700453
Jon Hall714eeba2015-09-29 17:53:10 -0700454 def cleanup( self ):
adminbae64d82013-08-01 10:50:15 -0700455 '''
Jon Hall5b586732015-06-11 11:39:39 -0700456 Print a summary of the current test's results then attempt to release
457 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700458
Jon Hall5b586732015-06-11 11:39:39 -0700459 This function shouldbe threadsafe such that cleanup will only be
460 executed once per test.
461
462 This will return TRUE if all the component handles and log handles
463 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700464 '''
465 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700466 lock = self.cleanupLock
467 if lock.acquire( False ):
468 try:
469 if self.cleanupFlag is False: # First thread to run this
470 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700471 if self.initiated:
Jon Hall714eeba2015-09-29 17:53:10 -0700472 self.logger.testSummary( self )
Jon Hall892818c2015-10-20 17:58:34 -0700473 components = self.componentDictionary
474 for component in sorted( components,
475 key=lambda item: components[item]['connect_order'],
476 reverse=True ):
Jon Hall714eeba2015-09-29 17:53:10 -0700477 try:
478 tempObject = vars( self )[component]
479 print "Disconnecting from " + str( tempObject.name ) +\
480 ": " + str( tempObject.__class__)
Jon Hall5b586732015-06-11 11:39:39 -0700481 tempObject.disconnect()
Jon Hall1306a562015-09-04 11:21:24 -0700482 except KeyboardInterrupt:
483 pass
484 except KeyError:
485 # Component not created yet
486 self.log.warn( "Could not find the component " +
487 str( component ) )
488 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700489 self.log.exception( "Exception while disconnecting from " +
490 str( component ) )
491 result = self.FALSE
492 # Closing all the driver's session files
493 for driver in self.componentDictionary.keys():
494 try:
Jon Hall714eeba2015-09-29 17:53:10 -0700495 vars( self )[driver].close_log_handles()
Jon Hall1306a562015-09-04 11:21:24 -0700496 except KeyboardInterrupt:
497 pass
498 except KeyError:
499 # Component not created yet
500 self.log.warn( "Could not find the component " +
501 str( driver ) + " while trying to" +
502 " close log file" )
503 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700504 self.log.exception( "Exception while closing log files for " +
505 str( driver ) )
506 result = self.FALSE
507 else:
508 pass # Someone else already ran through this function
509 finally:
510 lock.release()
511 else: # Someone already has a lock
512 # NOTE: This could cause problems if we don't release the lock
513 # correctly
514 lock.acquire() # Wait for the other thread to finish
515 # NOTE: If we don't wait, exit could be called while the thread
516 # with the lock is still cleaning up
517 lock.release()
adminbae64d82013-08-01 10:50:15 -0700518 return result
Jon Halld61331b2015-02-17 16:35:47 -0800519
Jon Hall714eeba2015-09-29 17:53:10 -0700520 def pause( self ):
adminbae64d82013-08-01 10:50:15 -0700521 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700522 This function will pause the test's execution, and will continue after
523 user provide 'resume' command.
adminbae64d82013-08-01 10:50:15 -0700524 '''
525 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700526
Jon Hall714eeba2015-09-29 17:53:10 -0700527 def onfail( self, *components ):
adminbae64d82013-08-01 10:50:15 -0700528 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700529 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700530 '''
adminbae64d82013-08-01 10:50:15 -0700531 if not components:
Jon Hall714eeba2015-09-29 17:53:10 -0700532 try:
adminbae64d82013-08-01 10:50:15 -0700533 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700534 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700535 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700536 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700537 print str( e )
adminbae64d82013-08-01 10:50:15 -0700538 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700539 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700540 try:
adminbae64d82013-08-01 10:50:15 -0700541 for component in components:
Jon Hall714eeba2015-09-29 17:53:10 -0700542 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700543 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700544 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700545 print str( e )
adminbae64d82013-08-01 10:50:15 -0700546 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700547
Jon Hall714eeba2015-09-29 17:53:10 -0700548 def getDriverPath( self, driverName ):
adminbae64d82013-08-01 10:50:15 -0700549 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700550 Based on the component 'type' specified in the params , this method
551 will find the absolute path, by recursively searching the name of
552 the component.
553
554 NOTE: This function requires the linux 'find' command.
adminbae64d82013-08-01 10:50:15 -0700555 '''
556 import commands
557
Jon Hall714eeba2015-09-29 17:53:10 -0700558 cmd = "find " + drivers_path + " -name " + driverName + ".py"
559 result = commands.getoutput( cmd )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700560
Jon Hall714eeba2015-09-29 17:53:10 -0700561 result_array = str( result ).split( '\n' )
adminbae64d82013-08-01 10:50:15 -0700562 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700563
adminbae64d82013-08-01 10:50:15 -0700564 for drivers_list in result_array:
Jon Hall714eeba2015-09-29 17:53:10 -0700565 result_count = result_count + 1
566 if result_count > 1:
567 print "Found " + driverName + " " + str( result_count ) + " times:"
568 print str( result_array )
adminbae64d82013-08-01 10:50:15 -0700569 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700570
Jon Hall714eeba2015-09-29 17:53:10 -0700571 result = re.sub( "(.*)drivers", "", result )
572 result = re.sub( "\/\/", "/", result )
573 result = re.sub( "\.py", "", result )
574 result = re.sub( "\.pyc", "", result )
575 result = re.sub( "\/", ".", result )
576 result = "drivers" + result
adminbae64d82013-08-01 10:50:15 -0700577 return result
adminbae64d82013-08-01 10:50:15 -0700578
Jon Hall714eeba2015-09-29 17:53:10 -0700579 def step( self, stepDesc ):
adminbae64d82013-08-01 10:50:15 -0700580 '''
581 The step information of the test-case will append to the logs.
582 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700583 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
584 str( self.stepNumber ) + ": " + str( self.stepName )
adminbae64d82013-08-01 10:50:15 -0700585 self.stepName = stepDesc
Jon Hall5a72b712015-09-28 12:20:59 -0700586 self.stepNumber += 1
587 self.stepResults[0].append( self.stepNumber )
588 self.stepResults[1].append( stepDesc )
589 self.stepResults[2].append( self.NORESULT )
590 self.stepResults[3].append( "No on fail message given" )
adminbae64d82013-08-01 10:50:15 -0700591
Jon Hall714eeba2015-09-29 17:53:10 -0700592 stepName = " " + str( self.CurrentTestCaseNumber ) + "." +\
593 str( self.stepNumber ) + ": " + str( stepDesc )
adminbae64d82013-08-01 10:50:15 -0700594 self.log.step(stepName)
595 stepHeader = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700596 line = "\n" + "-" * 45 + "\n"
597 if self.stepNumber > 1:
598 stepHeader = line + "End of Step " + previousStep + line
599 stepHeader += line + "Start of Step" + stepName + line
adminbae64d82013-08-01 10:50:15 -0700600 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700601 vars( self )[driver + 'log'].info( stepHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700602
Jon Hall714eeba2015-09-29 17:53:10 -0700603 def case( self, testCaseName ):
adminbae64d82013-08-01 10:50:15 -0700604 '''
605 Test's each test-case information will append to the logs.
606 '''
Jon Halld61331b2015-02-17 16:35:47 -0800607 self.CurrentTestCase = testCaseName
Jon Hall714eeba2015-09-29 17:53:10 -0700608 testCaseName = " " + str( testCaseName )
609 self.log.case( testCaseName )
610 caseHeader = testCaseName + "\n" + "*" * 40 + "\n"
adminbae64d82013-08-01 10:50:15 -0700611 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700612 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700613
Jon Hall714eeba2015-09-29 17:53:10 -0700614 def testDesc( self, description ):
adminbae64d82013-08-01 10:50:15 -0700615 '''
616 Test description will append to the logs.
617 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700618 description = "Test Description : " + str( description )
619 self.log.info( description )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700620
Jon Hall714eeba2015-09-29 17:53:10 -0700621 def _getTest( self ):
adminbae64d82013-08-01 10:50:15 -0700622 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700623 This method will parse the test script to find required test
624 information.
adminbae64d82013-08-01 10:50:15 -0700625 '''
Jon Hall53c5e662016-04-13 16:06:56 -0700626 testFileHandler = open( main.testFile, 'r' )
adminbae64d82013-08-01 10:50:15 -0700627 testFileList = testFileHandler.readlines()
628 testFileHandler.close()
adminbae64d82013-08-01 10:50:15 -0700629 counter = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700630 for index in range( len( testFileList ) ):
631 lineMatch = re.match( '\s+def CASE(\d+)(.*):',
632 testFileList[index],
633 0 )
adminbae64d82013-08-01 10:50:15 -0700634 if lineMatch:
Jon Hall714eeba2015-09-29 17:53:10 -0700635 counter = counter + 1
636 self.TC_PLANNED = len( self.testcases_list )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700637
Jon Hall714eeba2015-09-29 17:53:10 -0700638 def response_parser( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700639 ''' It will load the default response parser '''
640 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700641 response_dict = self.response_to_dict( response, return_format )
642 return_format_string = self.dict_to_return_format( response,
643 return_format,
644 response_dict )
adminbae64d82013-08-01 10:50:15 -0700645 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700646
Jon Hall714eeba2015-09-29 17:53:10 -0700647 def response_to_dict( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700648 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700649 json_match = re.search( '^\s*{', response )
650 xml_match = re.search( '^\s*\<', response )
651 ini_match = re.search( '^\s*\[', response )
652 if json_match:
653 self.log.info( "Response is in 'JSON' format, converting to '" +
654 return_format + "' format" )
Jon Halld61331b2015-02-17 16:35:47 -0800655 # Formatting the json string
Jon Hall714eeba2015-09-29 17:53:10 -0700656 response = re.sub( r"{\s*'?(\w)", r'{"\1', response )
657 response = re.sub( r",\s*'?(\w)", r',"\1', response )
658 response = re.sub( r"(\w)'?\s*:", r'\1":', response )
659 response = re.sub( r":\s*'(\w)'\s*([,}])", r':"\1"\2', response )
660 try:
adminbae64d82013-08-01 10:50:15 -0700661 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700662 response_dict = json.loads( response )
Jon Hall1306a562015-09-04 11:21:24 -0700663 except StandardError:
Jon Hall2a5002c2015-08-21 16:49:11 -0700664 self.log.exception( "Json Parser is unable to parse the string" )
adminbae64d82013-08-01 10:50:15 -0700665 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700666 elif ini_match:
667 self.log.info( "Response is in 'INI' format, converting to '" +
668 return_format + "' format" )
adminbae64d82013-08-01 10:50:15 -0700669 from configobj import ConfigObj
Jon Hall714eeba2015-09-29 17:53:10 -0700670 response_file = open( "respnse_file.temp", 'w' )
671 response_file.write( response )
Jon Halld61331b2015-02-17 16:35:47 -0800672 response_file.close()
Jon Hall714eeba2015-09-29 17:53:10 -0700673 response_dict = ConfigObj( "respnse_file.temp" )
adminbae64d82013-08-01 10:50:15 -0700674 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700675 elif xml_match:
676 self.log.info( "Response is in 'XML' format, converting to '" +
677 return_format + "' format" )
678 try:
679 response_dict = xmldict.xml_to_dict( "<response> " +
680 str( response ) +
681 " </response>" )
Jon Hall1306a562015-09-04 11:21:24 -0700682 except StandardError:
683 self.log.exception()
adminbae64d82013-08-01 10:50:15 -0700684 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700685
Jon Hall714eeba2015-09-29 17:53:10 -0700686 def dict_to_return_format( self, response, return_format, response_dict ):
687 if return_format == 'table':
adminbae64d82013-08-01 10:50:15 -0700688 ''' Will return in table format'''
689 to_do = "Call the table output formatter"
690 global response_table
691 response_table = '\n'
Jon Hall714eeba2015-09-29 17:53:10 -0700692 response_table = response_table + '\t'.join( response_dict ) + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700693
Jon Hall714eeba2015-09-29 17:53:10 -0700694 def get_table( value_to_convert ):
695 ''' This will parse the dictionary recusrsively and print as
696 table format'''
adminbae64d82013-08-01 10:50:15 -0700697 table_data = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700698 if isinstance( value_to_convert, dict ):
699 table_data = table_data + '\t'.join( value_to_convert ) +\
700 "\n"
701 for temp_val in value_to_convert.values():
702 table_data = table_data + get_table( temp_val )
703 else:
704 table_data = table_data + str( value_to_convert ) + "\t"
Jon Halld61331b2015-02-17 16:35:47 -0800705 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700706
Jon Hall714eeba2015-09-29 17:53:10 -0700707 for value in response_dict.values():
708 response_table = response_table + get_table( value )
adminbae64d82013-08-01 10:50:15 -0700709 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700710
Jon Hall714eeba2015-09-29 17:53:10 -0700711 elif return_format == 'config':
adminbae64d82013-08-01 10:50:15 -0700712 ''' Will return in config format'''
713 to_do = 'Call dict to config coverter'
Jon Hall714eeba2015-09-29 17:53:10 -0700714 response_string = str( response_dict )
adminbae64d82013-08-01 10:50:15 -0700715 print response_string
Jon Hall714eeba2015-09-29 17:53:10 -0700716 response_config = re.sub( ",", "\n\t", response_string )
717 response_config = re.sub( "u\'", "\'", response_config )
718 response_config = re.sub( "{", "", response_config )
719 response_config = re.sub( "}", "\n", response_config )
720 response_config = re.sub( ":", " =", response_config )
721 return "[response]\n\t " + response_config
adminbae64d82013-08-01 10:50:15 -0700722 elif return_format == 'xml':
723 ''' Will return in xml format'''
Jon Hall714eeba2015-09-29 17:53:10 -0700724 response_xml = xmldict.dict_to_xml( response_dict )
725 response_xml = re.sub( ">\s*<", ">\n<", response_xml )
726 return "\n" + response_xml
adminbae64d82013-08-01 10:50:15 -0700727 elif return_format == 'json':
728 ''' Will return in json format'''
729 to_do = 'Call dict to xml coverter'
730 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700731 response_json = json.dumps( response_dict )
adminbae64d82013-08-01 10:50:15 -0700732 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700733
Jon Hall714eeba2015-09-29 17:53:10 -0700734 def get_random( self ):
adminbae64d82013-08-01 10:50:15 -0700735 self.random_order = self.random_order + 1
736 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700737
Jon Hall714eeba2015-09-29 17:53:10 -0700738 def exit( self ):
adminbae64d82013-08-01 10:50:15 -0700739 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700740 for thread in threading.enumerate():
741 if thread.isAlive():
742 try:
743 thread._Thread__stop()
744 except:
Jon Hall1306a562015-09-04 11:21:24 -0700745 # NOTE: We should catch any exceptions while trying to
746 # close the thread so that we can try to close the other
747 # threads as well
Jon Hall714eeba2015-09-29 17:53:10 -0700748 print str( thread.getName() ) +\
749 ' could not be terminated'
adminbae64d82013-08-01 10:50:15 -0700750 sys.exit()
751
Jon Hallcd3d2a32015-10-01 11:07:28 -0700752 def stop( self, email=False ):
753 """
754 Stop the test until Ctrl-D is entered.
755 Ctrl-C will kill the test
Jon Hall25079782015-10-13 13:54:39 -0700756
757 Optional arguments:
758 email can either be a bool, or you can specify the email address
759 to send the email to
Jon Hallcd3d2a32015-10-01 11:07:28 -0700760 """
761 try:
762 if email:
Jon Hall25079782015-10-13 13:54:39 -0700763 if '@' in email:
764 main.mail = email
765 utilities.send_warning_email()
Jon Hallcd3d2a32015-10-01 11:07:28 -0700766 self.log.error( "Test execution suspended. Press Ctrl-D to "
767 "resume or Ctrl-C to exit the test" )
768 # NOTE: Ctrl-D needs to be entered on a new line
769 while True:
770 # TODO: we could give the user an interactive prompt where
771 # they could call functions
772 raw_input()
773 except EOFError:
774 return
775 # Pass all other exceptions up to caller
776
777
Jon Hall714eeba2015-09-29 17:53:10 -0700778def verifyOptions( options ):
adminbae64d82013-08-01 10:50:15 -0700779 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700780 This will verify the command line options and set to default values,
781 if any option not given in command line.
adminbae64d82013-08-01 10:50:15 -0700782 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700783 verifyTest( options )
784 verifyExample( options )
785 verifyTestScript( options )
YPZhang1c89e762016-06-29 10:43:58 -0700786 verifyParams( options )
Jon Hall714eeba2015-09-29 17:53:10 -0700787 verifyLogdir( options )
788 verifyMail( options )
789 verifyTestCases( options )
790 verifyOnosCell( options )
adminbae64d82013-08-01 10:50:15 -0700791
Jon Hall714eeba2015-09-29 17:53:10 -0700792def verifyTest( options ):
Jon Hall44506242015-07-29 17:40:26 -0700793 try:
794 if options.testname:
795 main.TEST = options.testname
Jon Hall714eeba2015-09-29 17:53:10 -0700796 main.classPath = "tests." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700797 main.tests_path = tests_path
798 elif options.example:
799 main.TEST = options.example
Jon Hall714eeba2015-09-29 17:53:10 -0700800 main.tests_path = path + "/examples/"
801 main.classPath = "examples." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700802 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700803 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700804 main.exit()
adminbae64d82013-08-01 10:50:15 -0700805
Jon Hall714eeba2015-09-29 17:53:10 -0700806def verifyExample( options ):
adminbae64d82013-08-01 10:50:15 -0700807 if options.example:
Jon Hall714eeba2015-09-29 17:53:10 -0700808 main.testDir = path + '/examples/'
809 main.tests_path = path + "/examples/"
810 main.classPath = "examples." + main.TEST + "." + main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700811
Jon Hall714eeba2015-09-29 17:53:10 -0700812def verifyLogdir( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800813 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700814 if options.logdir:
815 main.logdir = options.logdir
Jon Hall714eeba2015-09-29 17:53:10 -0700816 else:
Jon Halld61331b2015-02-17 16:35:47 -0800817 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700818
Jon Hall714eeba2015-09-29 17:53:10 -0700819def verifyMail( options ):
Jon Hall25079782015-10-13 13:54:39 -0700820 # Mail-To: field
821 if options.mail: # Test run specific
adminbae64d82013-08-01 10:50:15 -0700822 main.mail = options.mail
Jon Hall25079782015-10-13 13:54:39 -0700823 elif main.params.get('mail'): # Test suite specific
824 main.mail = main.params.get( 'mail' )
825 else: # TestON specific
826 main.mail = main.config['config'].get( 'mail_to' )
827 # Mail-From: field
828 main.sender = main.config['config'].get( 'mail_from' )
829 # Mail smtp server
830 main.smtp = main.config['config'].get( 'mail_server' )
831 # Mail-From account password
832 main.senderPwd = main.config['config'].get( 'mail_pass' )
833
adminbae64d82013-08-01 10:50:15 -0700834
Jon Hall714eeba2015-09-29 17:53:10 -0700835def verifyTestCases( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800836 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700837 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800838 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800839 # sys.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700840 testcases_list = re.sub( "(\[|\])", "", options.testcases )
841 main.testcases_list = eval( testcases_list + "," )
842 else:
adminbae64d82013-08-01 10:50:15 -0700843 if 'testcases' in main.params.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700844 temp = eval( main.params['testcases'] + "," )
845 list1 = []
846 if isinstance( temp[0], list ):
Jon Hallfebb1c72015-03-05 13:30:09 -0800847 for test in temp:
848 for testcase in test:
Jon Hall714eeba2015-09-29 17:53:10 -0700849 if isinstance( testcase, int ):
850 testcase = [testcase]
851 list1.extend( testcase )
852 else:
853 temp = list( temp )
Jon Hallfebb1c72015-03-05 13:30:09 -0800854 for testcase in temp:
Jon Hall714eeba2015-09-29 17:53:10 -0700855 if isinstance( testcase, int ):
856 testcase = [testcase]
857 list1.extend( testcase )
858 main.testcases_list = list1
859 else:
860 print "Testcases not specifed in params, please provide in " +\
861 "params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800862 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700863
Jon Hall714eeba2015-09-29 17:53:10 -0700864def verifyOnosCell( options ):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700865 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700866 if options.onoscell:
867 main.onoscell = options.onoscell
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700868 main.onosIPs = []
869 main.mnIP = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700870 cellCMD = ". ~/.profile; cell " + main.onoscell
871 output = subprocess.check_output( ["bash", '-c', cellCMD] )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700872 splitOutput = output.splitlines()
Jon Hall714eeba2015-09-29 17:53:10 -0700873 for i in range( len( splitOutput ) ):
874 if re.match( "OCN", splitOutput[i] ):
875 mnNode = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700876 main.mnIP = mnNode[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700877 # cell already sorts OC variables in bash, so no need to
878 # sort in TestON
879 if re.match( "OC[1-9]", splitOutput[i] ):
880 onosNodes = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700881 main.onosIPs.append( onosNodes[1] )
Jon Hall714eeba2015-09-29 17:53:10 -0700882 else:
Hari Krishna03f530e2015-07-10 17:28:27 -0700883 main.onoscell = main.FALSE
884
Jon Hall714eeba2015-09-29 17:53:10 -0700885def verifyTestScript( options ):
adminbae64d82013-08-01 10:50:15 -0700886 '''
887 Verifyies test script.
888 '''
Jon Halld61331b2015-02-17 16:35:47 -0800889 main.openspeak = openspeak.OpenSpeak()
Jon Hall53c5e662016-04-13 16:06:56 -0700890 directory = main.testDir + "/" + main.TEST
891 if os.path.exists( directory ):
892 pass
893 else:
894 directory = ""
895 for root, dirs, files in os.walk( main.testDir, topdown=True):
896 if not directory:
897 for name in dirs:
898 if name == main.TEST:
899 directory = ( os.path.join( root, name ) )
900 index = directory.find( "/tests/" ) + 1
901 main.classPath = directory[index:].replace( '/', '.' ) + "." + main.TEST
902 break
903 openspeakfile = directory + "/" + main.TEST + ".ospk"
904 main.testFile = directory + "/" + main.TEST + ".py"
Jon Hall714eeba2015-09-29 17:53:10 -0700905 if os.path.exists( openspeakfile ):
Jon Hall44506242015-07-29 17:40:26 -0700906 # Openspeak file found, compiling to python
Jon Hall714eeba2015-09-29 17:53:10 -0700907 main.openspeak.compiler( openspeakfile=openspeakfile, writetofile=1 )
Jon Hall53c5e662016-04-13 16:06:56 -0700908 elif os.path.exists( main.testFile ):
Jon Hall44506242015-07-29 17:40:26 -0700909 # No openspeak found, using python file instead
910 pass
adminbae64d82013-08-01 10:50:15 -0700911 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700912 print "\nThere is no \"" + main.TEST + "\" test script.\nPlease provide a " +\
Jon Hall44506242015-07-29 17:40:26 -0700913 "Python or OpenSpeak test script in the tests folder: " +\
Jon Hall714eeba2015-09-29 17:53:10 -0700914 main.testDir + "/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700915 __builtin__.testthread = None
916 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700917 try:
918 testModule = __import__( main.classPath,
919 globals(),
920 locals(),
921 [main.TEST],
922 -1 )
Jon Hall1306a562015-09-04 11:21:24 -0700923 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -0700924 print "There was an import error, it might mean that there is " +\
925 "no test named " + main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800926 main.exit()
adminbae64d82013-08-01 10:50:15 -0700927
Jon Hall714eeba2015-09-29 17:53:10 -0700928 testClass = getattr( testModule, main.TEST )
adminbae64d82013-08-01 10:50:15 -0700929 main.testObject = testClass()
930 load_parser()
Jon Hall714eeba2015-09-29 17:53:10 -0700931 main.params = main.parser.parseParams( main.classPath )
932 main.topology = main.parser.parseTopology( main.classPath )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700933
YPZhang1c89e762016-06-29 10:43:58 -0700934def verifyParams( options ):
Jon Hall714eeba2015-09-29 17:53:10 -0700935 try:
adminbae64d82013-08-01 10:50:15 -0700936 main.params = main.params['PARAMS']
Jon Hall1306a562015-09-04 11:21:24 -0700937 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700938 print "Error with the params file: Either the file not specified " +\
939 "or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800940 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700941 try:
adminbae64d82013-08-01 10:50:15 -0700942 main.topology = main.topology['TOPOLOGY']
Jon Hall1306a562015-09-04 11:21:24 -0700943 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700944 print "Error with the Topology file: Either the file not specified " +\
945 "or the format is not correct"
adminbae64d82013-08-01 10:50:15 -0700946 main.exit()
YPZhang1c89e762016-06-29 10:43:58 -0700947 # Overwrite existing params variables if they are specified from command line
948 if len(options.params) > 0:
949 # Some params variables are specified from command line
950 for param in options.params:
951 if not re.search( ".=.", param ):
952 print( "Error when parsing params: params should follow key=value format" )
953 continue
954 # Split the param string to netest keys and value
955 [ keys, value ] = param.split( "=" )
956 # Split the nested keys according to its hierarchy
957 keyList = keys.split( "/" )
958 # Get the outermost dictionary
959 paramDict = main.params
960 # Get the innermost dictionary
961 try:
962 while len( keyList ) > 1:
963 key = keyList.pop(0)
964 assert isinstance( paramDict[ key ], dict )
965 paramDict = paramDict[ key ]
966 except KeyError:
967 print( "Error when parsing params: key \"" + key + "\" not found in main.params" )
968 main.exit()
969 except AssertionError:
970 print( "Error when parsing params: \"" + key + "\" is already the innermost level in main.params" )
971 main.exit()
972 # Change the value
973 if not paramDict.has_key( keyList[0] ):
974 print( "Error when parsing params: key \"" + keyList[0] + "\" not found in main.params" )
975 main.exit()
976 elif isinstance( paramDict[ keyList[0] ], dict ):
977 print( "Error when parsing params: more levels under key \"" + keyList[0] + "\" in main.params" )
978 main.exit()
979 else:
980 paramDict[ keyList[0] ] = value
kelvin-onlabf70fd542015-05-07 18:41:40 -0700981
Jon Hall714eeba2015-09-29 17:53:10 -0700982def load_parser():
adminbae64d82013-08-01 10:50:15 -0700983 '''
984 It facilitates the loading customised parser for topology and params file.
985 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -0700986 It also loads default xmlparser if no parser have specified in teston.cfg
987 file.
adminbae64d82013-08-01 10:50:15 -0700988
989 '''
990 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -0700991 if 'file' in confighash['config']['parser'] and\
992 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -0700993 path = confighash['config']['parser']['file']
Jon Hall714eeba2015-09-29 17:53:10 -0700994 if path is not None or\
995 confighash['config']['parser']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -0700996 try:
997 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700998 moduleList = module.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -0700999 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001000 parsingClass = confighash['config']['parser']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001001 parsingModule = __import__( newModule,
1002 globals(),
1003 locals(),
1004 [parsingClass],
1005 -1 )
1006 parsingClass = getattr( parsingModule, parsingClass )
Jon Hall44506242015-07-29 17:40:26 -07001007 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001008 if hasattr( main.parser, "parseParams" ) and\
1009 hasattr( main.parser, "parseTopology" ) and\
1010 hasattr( main.parser, "parse" ):
Jon Hall44506242015-07-29 17:40:26 -07001011 pass
1012 else:
1013 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -07001014 main.exit()
Jon Hall44506242015-07-29 17:40:26 -07001015 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001016 print "Could not find the file " + path +\
1017 " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -08001018 load_defaultParser()
Jon Hall714eeba2015-09-29 17:53:10 -07001019 elif confighash['config']['parser']['file'] is None or\
1020 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001021 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -07001022 else:
1023 load_defaultParser()
1024
1025def load_defaultParser():
1026 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001027 It will load the default parser which is xml parser to parse the params and
1028 topology file.
adminbae64d82013-08-01 10:50:15 -07001029 '''
1030 moduleList = main.parserPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001031 newModule = ".".join( moduleList[-2:] )
1032 try:
Jon Halld61331b2015-02-17 16:35:47 -08001033 parsingClass = main.parsingClass
Jon Hall714eeba2015-09-29 17:53:10 -07001034 parsingModule = __import__( newModule,
1035 globals(),
1036 locals(),
1037 [parsingClass],
1038 -1 )
1039 parsingClass = getattr( parsingModule, parsingClass )
adminbae64d82013-08-01 10:50:15 -07001040 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001041 if hasattr( main.parser, "parseParams" ) and\
1042 hasattr( main.parser, "parseTopology" ) and\
1043 hasattr( main.parser, "parse" ):
adminbae64d82013-08-01 10:50:15 -07001044 pass
1045 else:
1046 main.exit()
adminbae64d82013-08-01 10:50:15 -07001047 except ImportError:
1048 print sys.exc_info()[1]
1049
Jon Hall714eeba2015-09-29 17:53:10 -07001050def load_logger():
adminbae64d82013-08-01 10:50:15 -07001051 '''
1052 It facilitates the loading customised parser for topology and params file.
1053 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001054 It also loads default xmlparser if no parser have specified in teston.cfg
1055 file.
adminbae64d82013-08-01 10:50:15 -07001056 '''
1057 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001058 if 'file' in confighash['config']['logger'] and\
1059 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -07001060 path = confighash['config']['logger']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001061 if path is not None or\
1062 confighash['config']['logger']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001063 try:
1064 module = re.sub( r".py\s*$", "", path )
Jon Hall714eeba2015-09-29 17:53:10 -07001065 moduleList = module.split( "/" )
1066 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001067 loggerClass = confighash['config']['logger']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001068 loggerModule = __import__( newModule,
1069 globals(),
1070 locals(),
1071 [loggerClass],
1072 -1 )
1073 loggerClass = getattr( loggerModule, loggerClass )
Jon Hall44506242015-07-29 17:40:26 -07001074 main.logger = loggerClass()
Jon Hall44506242015-07-29 17:40:26 -07001075 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001076 print "Could not find the file " + path +\
1077 " using default logger."
adminbae64d82013-08-01 10:50:15 -07001078 load_defaultlogger()
Jon Hall714eeba2015-09-29 17:53:10 -07001079 elif confighash['config']['parser']['file'] is None or\
1080 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001081 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -07001082 else:
1083 load_defaultlogger()
1084
1085def load_defaultlogger():
1086 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001087 It will load the default parser which is xml parser to parse the params and
1088 topology file.
adminbae64d82013-08-01 10:50:15 -07001089 '''
1090 moduleList = main.loggerPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001091 newModule = ".".join( moduleList[-2:] )
1092 try:
Jon Halld61331b2015-02-17 16:35:47 -08001093 loggerClass = main.loggerClass
Jon Hall714eeba2015-09-29 17:53:10 -07001094 loggerModule = __import__( newModule,
1095 globals(),
1096 locals(),
1097 [loggerClass],
1098 -1 )
1099 loggerClass = getattr( loggerModule, loggerClass )
adminbae64d82013-08-01 10:50:15 -07001100 main.logger = loggerClass()
1101
1102 except ImportError:
1103 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -08001104 main.exit()
adminbae64d82013-08-01 10:50:15 -07001105
Jon Hall714eeba2015-09-29 17:53:10 -07001106def _echo( self ):
adminbae64d82013-08-01 10:50:15 -07001107 print "THIS IS ECHO"