blob: 15c12f36f35026050b18ef34705ffc5e666d84e1 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 22-Oct-2012
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -07004Modified 2017 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
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -070038import pdb
Jon Hall714eeba2015-09-29 17:53:10 -070039module = new.module( "test" )
adminbae64d82013-08-01 10:50:15 -070040import openspeak
Hari Krishnabe4b97b2015-07-15 12:19:43 -070041import subprocess
Jon Hall714eeba2015-09-29 17:53:10 -070042global path, drivers_path, core_path, tests_path, logs_path
Jon Hall44506242015-07-29 17:40:26 -070043location = os.path.abspath( os.path.dirname( __file__ ) )
44path = re.sub( "(core|bin)$", "", location )
Jon Hall714eeba2015-09-29 17:53:10 -070045drivers_path = path + "drivers"
46core_path = path + "core"
47tests_path = path + "tests"
48logs_path = path + "logs/"
adminbae64d82013-08-01 10:50:15 -070049config_path = path + "config/"
Jon Hall44506242015-07-29 17:40:26 -070050sys.path.append( path )
51sys.path.append( drivers_path )
52sys.path.append( core_path )
53sys.path.append( tests_path )
adminbae64d82013-08-01 10:50:15 -070054
55from core.utilities import Utilities
kelvin-onlabfb521662015-02-27 09:52:40 -080056from core.Thread import Thread
adminbae64d82013-08-01 10:50:15 -070057
adminbae64d82013-08-01 10:50:15 -070058
59class TestON:
60 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070061 TestON will initiate the specified test.
Jon Hall714eeba2015-09-29 17:53:10 -070062 The main tasks are:
kelvin-onlabf70fd542015-05-07 18:41:40 -070063 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070064 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070065 '''
Jon Hall714eeba2015-09-29 17:53:10 -070066 def __init__( self, options ):
adminbae64d82013-08-01 10:50:15 -070067 '''
Jon Hall714eeba2015-09-29 17:53:10 -070068 Initialise the component handles specified in the topology file of
69 the specified test.
adminbae64d82013-08-01 10:50:15 -070070 '''
71 # Initialization of the variables.
72 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070073 __builtin__.path = path
74 __builtin__.utilities = Utilities()
75 self.TRUE = 1
76 self.FALSE = 0
77 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070078 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070079 self.FAIL = False
80 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070081 self.CASERESULT = self.ERROR
82 self.STEPRESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -070083 self.init_result = self.TRUE
84 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070085 self.stepName = ""
86 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080087 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070088 self.test_target = None
89 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080090 self.testDir = tests_path
91 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070092 self.parsingClass = "xmlparser"
93 self.parserPath = core_path + "/xmlparser"
94 self.loggerPath = core_path + "/logger"
95 self.loggerClass = "Logger"
96 self.logs_path = logs_path
97 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -080098 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -070099 self.cleanupFlag = False
100 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -0700101 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -0700102
Jon Hall25079782015-10-13 13:54:39 -0700103 self.config = self.configparser()
Jon Hall714eeba2015-09-29 17:53:10 -0700104 verifyOptions( options )
adminbae64d82013-08-01 10:50:15 -0700105 load_logger()
106 self.componentDictionary = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700107 self.componentDictionary = self.topology['COMPONENT']
108 self.driversList = []
109 if isinstance( self.componentDictionary, str):
110 self.componentDictionary = dict( self.componentDictionary )
Jon Hall65844a32015-03-09 19:09:37 -0700111
Jon Hall714eeba2015-09-29 17:53:10 -0700112 for component in self.componentDictionary:
113 self.driversList.append( self.componentDictionary[component]['type'] )
Jon Hall65844a32015-03-09 19:09:37 -0700114
Jon Hall714eeba2015-09-29 17:53:10 -0700115 self.driversList = list( set( self.driversList ) ) # Removing duplicates.
adminbae64d82013-08-01 10:50:15 -0700116 # Checking the test_target option set for the component or not
Jon Hall714eeba2015-09-29 17:53:10 -0700117 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700118 for component in self.componentDictionary.keys():
119 if 'test_target' in self.componentDictionary[component].keys():
120 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700121
Jon Halld61331b2015-02-17 16:35:47 -0800122 # Checking for the openspeak file and test script
Jon Hall714eeba2015-09-29 17:53:10 -0700123 self.logger.initlog( self )
adminbae64d82013-08-01 10:50:15 -0700124
125 # Creating Drivers Handles
Jon Hall714eeba2015-09-29 17:53:10 -0700126 initString = "\n" + "*" * 30 + "\n CASE INIT \n" + "*" * 30 + "\n"
127 self.log.exact( initString )
adminbae64d82013-08-01 10:50:15 -0700128 self.driverObject = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700129 self.random_order = 111 # Random order id to connect the components
adminbae64d82013-08-01 10:50:15 -0700130 components_connect_order = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700131 if isinstance( self.componentDictionary, dict ):
adminbae64d82013-08-01 10:50:15 -0700132 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700133 if 'connect_order' not in self.componentDictionary[component].keys():
134 self.componentDictionary[component]['connect_order'] = str( self.get_random() )
135 components_connect_order[component] = eval( self.componentDictionary[component]['connect_order'] )
136 # Ordering components based on the connect order.
137 ordered_component_list = sorted( components_connect_order,
138 key=lambda key: components_connect_order[key] )
adminbae64d82013-08-01 10:50:15 -0700139 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700140 for component in ordered_component_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700141 self.componentInit( component )
adminbae64d82013-08-01 10:50:15 -0700142
Jon Hall714eeba2015-09-29 17:53:10 -0700143 def configparser( self ):
adminbae64d82013-08-01 10:50:15 -0700144 '''
145 It will parse the config file (teston.cfg) and return as dictionary
146 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700147 matchFileName = re.match( r'(.*)\.cfg', self.configFile, re.M | re.I )
adminbae64d82013-08-01 10:50:15 -0700148 if matchFileName:
Jon Hall714eeba2015-09-29 17:53:10 -0700149 xml = open( self.configFile ).read()
150 try:
151 self.configDict = xmldict.xml_to_dict( xml )
adminbae64d82013-08-01 10:50:15 -0700152 return self.configDict
Jon Hall1306a562015-09-04 11:21:24 -0700153 except IOError:
adminbae64d82013-08-01 10:50:15 -0700154 print "There is no such file to parse " + self.configFile
Jon Hall1306a562015-09-04 11:21:24 -0700155 else:
156 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700157
Jon Hall714eeba2015-09-29 17:53:10 -0700158 def componentInit( self, component ):
adminbae64d82013-08-01 10:50:15 -0700159 '''
160 This method will initialize specified component
161 '''
162 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700163 self.initiated = False
Jon Hall714eeba2015-09-29 17:53:10 -0700164 self.log.info( "Creating component Handle: " + component )
Jon Halld61331b2015-02-17 16:35:47 -0800165 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700166 if 'COMPONENTS' in self.componentDictionary[component].keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700167 driver_options = dict( self.componentDictionary[component]['COMPONENTS'] )
Jon Hall714eeba2015-09-29 17:53:10 -0700168 driver_options['name'] = component
adminbae64d82013-08-01 10:50:15 -0700169 driverName = self.componentDictionary[component]['type']
Jon Hall714eeba2015-09-29 17:53:10 -0700170 driver_options['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700171
Jon Hall714eeba2015-09-29 17:53:10 -0700172 classPath = self.getDriverPath( driverName.lower() )
173 driverModule = importlib.import_module( classPath )
174 driverClass = getattr( driverModule, driverName )
adminbae64d82013-08-01 10:50:15 -0700175 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700176
Jon Hall714eeba2015-09-29 17:53:10 -0700177 if "OCN" in self.componentDictionary[component]['host'] and\
178 main.onoscell:
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700179 self.componentDictionary[component]['host'] = main.mnIP
180
Jon Hall714eeba2015-09-29 17:53:10 -0700181 user_name = self.componentDictionary[component].get( 'user',
182 getpass.getuser() )
183 ip_address = self.componentDictionary[component].get( 'host',
184 'localhost' )
185 pwd = self.componentDictionary[component].get( 'password',
186 'changeme' )
187 port = self.componentDictionary[component].get( 'port' )
188 connect_result = driverObject.connect( user_name=user_name,
189 ip_address=ip_address,
190 pwd=pwd,
191 port=port,
192 options=driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700193
adminbae64d82013-08-01 10:50:15 -0700194 if not connect_result:
Jon Hall714eeba2015-09-29 17:53:10 -0700195 self.log.error( "Exiting from the test execution because connecting to the " +
196 component + " component failed." )
Jon Halld61331b2015-02-17 16:35:47 -0800197 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700198
Jon Hall714eeba2015-09-29 17:53:10 -0700199 vars( self )[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700200 self.initiated = True
kelvin-onlabf70fd542015-05-07 18:41:40 -0700201
Jon Hall714eeba2015-09-29 17:53:10 -0700202 def run( self ):
adminbae64d82013-08-01 10:50:15 -0700203 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700204 The Execution of the test script's cases listed in the Test params
205 file will be done here then update each test case result.
206 This method will return main.TRUE if it executed all the test cases
207 successfully, else will retun main.FALSE
adminbae64d82013-08-01 10:50:15 -0700208 '''
adminbae64d82013-08-01 10:50:15 -0700209 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700210 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700211 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800212 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700213 self.TOTAL_TC_NORESULT = 0
214 self.TOTAL_TC_FAIL = 0
215 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700216 self.TEST_ITERATION = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700217
218 # NOTE: number of main.step statements in the
219 # outer most level of the test case. used to
220 # execute code in smaller steps
221 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700222 self.CASERESULT = self.NORESULT
223
Jon Halld61331b2015-02-17 16:35:47 -0800224 import testparser
Jon Hall53c5e662016-04-13 16:06:56 -0700225 test = testparser.TestParser( main.testFile )
adminbae64d82013-08-01 10:50:15 -0700226 self.testscript = test.testscript
227 self.code = test.getStepCode()
Jon Hall714eeba2015-09-29 17:53:10 -0700228 repeat = int( self.params.get( 'repeat', 1 ) )
229 self.TOTAL_TC_PLANNED = len( self.testcases_list ) * repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700230
adminbae64d82013-08-01 10:50:15 -0700231 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700232 while repeat:
Jon Halla1185982014-09-15 14:55:10 -0700233 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Hall714eeba2015-09-29 17:53:10 -0700234 result = self.runCase( self.CurrentTestCaseNumber )
235 repeat -= 1
adminbae64d82013-08-01 10:50:15 -0700236 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700237
Jon Halle234cc42015-08-31 15:26:47 -0700238 def runCase( self, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700239 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700240 self.CurrentTestCase = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700241
242 # List of step results in a case. ANDed together to get the result
243 self.stepResultsList = []
kelvin-onlabf70fd542015-05-07 18:41:40 -0700244 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700245 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700246 result = self.TRUE
Jon Hall714eeba2015-09-29 17:53:10 -0700247
248 # NOTE: number of main.step statements in the
249 # outer most level of the test case. used to
250 # execute code in smaller steps
251 self.stepCount = 0
252
253 # NOTE: This is the current number of main.step()'s executed
254 # in a case. Used for logging.
255 self.stepNumber = 0
adminbae64d82013-08-01 10:50:15 -0700256 self.EXPERIMENTAL_MODE = self.FALSE
257 self.addCaseHeader()
Jon Halle234cc42015-08-31 15:26:47 -0700258 self.testCaseNumber = str( testCaseNumber )
259 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700260 stopped = False
Jon Hall5a72b712015-09-28 12:20:59 -0700261 try:
262 self.code[self.testCaseNumber]
Jon Halld61331b2015-02-17 16:35:47 -0800263 except KeyError:
Jon Halle234cc42015-08-31 15:26:47 -0700264 self.log.error( "There is no Test-Case " + self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800265 return self.FALSE
adminbae64d82013-08-01 10:50:15 -0700266 self.stepCount = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700267 while self.stepCount < len( self.code[self.testCaseNumber].keys() ):
268 result = self.runStep( self.code, self.testCaseNumber )
Jon Hallfebb1c72015-03-05 13:30:09 -0800269 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700270 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800271 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700272 continue
Jon Hall5a72b712015-09-28 12:20:59 -0700273 # stepResults format: ( stepNo[], stepName[], stepResult[], onFail[] )
274 stepResults = self.stepResultsList
Jon Halle234cc42015-08-31 15:26:47 -0700275 if not stopped:
276 if self.CASERESULT == self.TRUE or self.CASERESULT == self.FALSE:
Jon Hall714eeba2015-09-29 17:53:10 -0700277 # Result was already explitily set somewhere else like
278 # in skipCase()
Jon Halle234cc42015-08-31 15:26:47 -0700279 pass
Jon Hall5a72b712015-09-28 12:20:59 -0700280 elif all( self.TRUE == i for i in stepResults ):
kelvin-onlabf70fd542015-05-07 18:41:40 -0700281 # ALL PASSED
282 self.CASERESULT = self.TRUE
Jon Hall5a72b712015-09-28 12:20:59 -0700283 elif self.FALSE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700284 # AT LEAST ONE FAILED
285 self.CASERESULT = self.FALSE
Jon Hall5a72b712015-09-28 12:20:59 -0700286 elif self.TRUE in stepResults:
kelvin-onlabf70fd542015-05-07 18:41:40 -0700287 # AT LEAST ONE PASSED
288 self.CASERESULT = self.TRUE
289 else:
290 self.CASERESULT = self.NORESULT
Jon Hall714eeba2015-09-29 17:53:10 -0700291 self.testCaseResult[str( self.CurrentTestCaseNumber )] = self.CASERESULT
292 self.logger.updateCaseResults( self )
Jon Hall783bbf92015-07-23 14:33:19 -0700293 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
294 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700295 self.log.wiki( "<ul>" )
acsmarse2d1ed12015-10-05 13:51:17 -0700296 subcaseMessage = False
kelvin-onlabf70fd542015-05-07 18:41:40 -0700297 for line in self.stepCache.splitlines():
acsmarse2d1ed12015-10-05 13:51:17 -0700298 if re.search( "[0-9]\.[0-9]", line ): # Step
299 if subcaseMessage: # End of Failure Message Printout
300 self.log.wiki( "</ul>\n" )
301 subcaseMessage = False
302 if re.search( " - PASS$", line ):
303 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
304 elif re.search( " - FAIL$", line ):
305 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
306 elif re.search( " - No Result$", line ):
307 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
308 else: # Substep
309 if not subcaseMessage: # Open Failure Message Printout
310 self.log.wiki( "<ul><li>" + line + "</li>\n" )
311 subcaseMessage = True
312 else: # Add to Failure Message Printout
313 self.log.wiki( "<li>" + line + "</li>\n" )
acsmars27e62dd2015-10-06 11:35:47 -0700314 if subcaseMessage: # End of Failure Message Printout for last item
315 self.log.wiki( "</ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700316 self.log.wiki( "</ul>" )
317 self.log.summary( self.stepCache )
318 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700319 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700320
Jon Hall714eeba2015-09-29 17:53:10 -0700321 def runStep( self, code, testCaseNumber ):
adminbae64d82013-08-01 10:50:15 -0700322 if not cli.pause:
Jon Hall5a72b712015-09-28 12:20:59 -0700323 try:
324 step = self.stepCount
325 # stepResults format: ( stepNo, stepName, stepResult, onFail )
326 # NOTE: This is needed to catch results of main.step()'s
327 # called inside functions or loops
328 self.stepResults = ( [], [], [], [] )
adminbae64d82013-08-01 10:50:15 -0700329 exec code[testCaseNumber][step] in module.__dict__
330 self.stepCount = self.stepCount + 1
Jon Hall96b816f2015-11-03 12:00:56 -0800331 self.parseStepResults( testCaseNumber )
Jon Halle234cc42015-08-31 15:26:47 -0700332 except StopIteration: # Raised in self.skipCase()
333 self.log.warn( "Skipping the rest of CASE" +
334 str( testCaseNumber ) )
Jon Hall96b816f2015-11-03 12:00:56 -0800335 self.parseStepResults( testCaseNumber )
Jon Hall714eeba2015-09-29 17:53:10 -0700336 self.stepResultsList.append( self.STEPRESULT )
Jon Halle234cc42015-08-31 15:26:47 -0700337 self.stepCache += "\t\t" + self.onFailMsg + "\n"
338 self.stepCount = self.stepCount + 1
339 return self.FALSE
Jon Hallc1606352015-10-06 14:51:36 -0700340 except StandardError as e:
341 try:
342 stepNo = self.stepResults[0][ self.stepNumber - 1 ]
343 except IndexError:
344 stepNo = "<IndexError>"
345 main.log.warn( "Error trying to get step number. " +
346 "It is likely between step " +
Jon Hall6e709752016-02-01 13:38:46 -0800347 str( self.stepNumber ) + " and step " +
Jon Hallc1606352015-10-06 14:51:36 -0700348 str( self.stepNumber + 1 ) )
349 try:
350 stepName = self.stepResults[1][ self.stepNumber - 1 ]
351 except IndexError:
352 stepName = "<IndexError>"
353 self.log.error( "\nException in the following section of" +
354 " code: " + str( testCaseNumber ) + "." +
355 str( stepNo ) + ": " + stepName )
Jeremyd9e4eb12016-04-13 12:09:06 -0700356 self.log.error( str( e.__class__ ) + str( e.message ) )
adminbae64d82013-08-01 10:50:15 -0700357 self.stepCount = self.stepCount + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700358 self.logger.updateCaseResults( self )
359 # WIKI results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700360 self.log.wiki( "<ul>" )
361 for line in self.stepCache.splitlines():
362 if re.search( " - PASS$", line ):
363 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
364 elif re.search( " - FAIL$", line ):
365 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
366 elif re.search( " - No Result$", line ):
367 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700368 else: # Should only be on fail message
369 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700370 self.log.wiki( "</ul>" )
Jon Hall714eeba2015-09-29 17:53:10 -0700371 # summary results
kelvin-onlabf70fd542015-05-07 18:41:40 -0700372 self.log.summary( self.stepCache )
373 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700374 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700375 self.exit()
Jon Halle234cc42015-08-31 15:26:47 -0700376 return self.TRUE
adminbae64d82013-08-01 10:50:15 -0700377 if cli.stop:
378 cli.stop = False
adminbae64d82013-08-01 10:50:15 -0700379 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
Jon Hall714eeba2015-09-29 17:53:10 -0700380 self.testCaseResult[str( self.CurrentTestCaseNumber )] = "Stopped"
381 self.logger.updateCaseResults( self )
adminbae64d82013-08-01 10:50:15 -0700382 result = self.cleanup()
Jon Halle234cc42015-08-31 15:26:47 -0700383 return self.FALSE
384
Jon Hall96b816f2015-11-03 12:00:56 -0800385 def parseStepResults( self, testCaseNumber ):
386 """
387 Parse throught the step results for the wiki
388 """
389 try:
390 # Iterate through each of the steps and print them
391 for index in range( len( self.stepResults[0] ) ):
392 # stepResults = ( stepNo, stepName, stepResult, onFail )
393 stepNo = self.stepResults[0][ index ]
394 stepName = self.stepResults[1][ index ]
395 stepResult = self.stepResults[2][ index ]
396 onFail = self.stepResults[3][ index ]
397 self.stepCache += "\t" + str( testCaseNumber ) + "."
398 self.stepCache += str( stepNo ) + " "
399 self.stepCache += stepName + " - "
400 if stepResult == self.TRUE:
401 self.stepCache += "PASS\n"
402 elif stepResult == self.FALSE:
403 self.stepCache += "FAIL\n"
404 self.stepCache += "\t\t" + onFail + "\n"
405 else:
406 self.stepCache += "No Result\n"
407 self.stepResultsList.append( stepResult )
408 except Exception:
409 self.log.exception( "Error parsing step results" )
410
Jon Halle234cc42015-08-31 15:26:47 -0700411 def skipCase( self, result="DEFAULT", msg=None ):
412 """
413 Will skip the rest of the code in a test case. The case results will be
414 determined as normal based on completed assertions unless the result
415 argument is given.
416
417 Optional Arguments:
Jon Hall7c4f4302016-07-15 14:39:02 -0700418 result: Case insensitive string. Can be 'PASS' or 'FAIL' and will set
Jon Halle234cc42015-08-31 15:26:47 -0700419 the case result accordingly.
420 msg: Message to be printed when the case is skipped in the reports.
421 """
422 result = result.upper().strip()
423 if result == "PASS":
424 self.CASERESULT = self.TRUE
425 elif result == "FAIL":
426 self.CASERESULT = self.FALSE
427 self.onFailMsg = "Skipping the rest of this case. "
428 if msg:
429 self.onFailMsg += str( msg )
430 raise StopIteration
kelvin-onlabf70fd542015-05-07 18:41:40 -0700431
Jon Hall714eeba2015-09-29 17:53:10 -0700432 def addCaseHeader( self ):
433 caseHeader = "\n" + "*" * 30 + "\n Result summary for Testcase" +\
434 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 30 + "\n"
435 self.log.exact( caseHeader )
436 caseHeader = "\n" + "*" * 40 + "\nStart of Test Case" +\
437 str( self.CurrentTestCaseNumber ) + " : "
adminbae64d82013-08-01 10:50:15 -0700438 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700439 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700440
Jon Hall714eeba2015-09-29 17:53:10 -0700441 def addCaseFooter( self ):
Jon Hall5a72b712015-09-28 12:20:59 -0700442 stepNo = self.stepResults[0][-2]
Jon Hall714eeba2015-09-29 17:53:10 -0700443 if stepNo > 0:
444 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
445 str( stepNo ) + ": " + str( self.stepName )
446 stepHeader = "\n" + "*" * 40 + "\nEnd of Step " + previousStep +\
447 "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700448
Jon Hall714eeba2015-09-29 17:53:10 -0700449 caseFooter = "\n" + "*" * 40 + "\nEnd of Test case " +\
450 str( self.CurrentTestCaseNumber ) + "\n" + "*" * 40 + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700451
adminbae64d82013-08-01 10:50:15 -0700452 for driver in self.driversList:
Jon Hall714eeba2015-09-29 17:53:10 -0700453 vars( self )[driver].write( stepHeader + "\n" + caseFooter )
adminbae64d82013-08-01 10:50:15 -0700454
Jon Hall714eeba2015-09-29 17:53:10 -0700455 def cleanup( self ):
adminbae64d82013-08-01 10:50:15 -0700456 '''
Jon Hall5b586732015-06-11 11:39:39 -0700457 Print a summary of the current test's results then attempt to release
458 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700459
Jon Hall5b586732015-06-11 11:39:39 -0700460 This function shouldbe threadsafe such that cleanup will only be
461 executed once per test.
462
463 This will return TRUE if all the component handles and log handles
464 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700465 '''
466 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700467 lock = self.cleanupLock
468 if lock.acquire( False ):
469 try:
470 if self.cleanupFlag is False: # First thread to run this
471 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700472 if self.initiated:
Jon Hall714eeba2015-09-29 17:53:10 -0700473 self.logger.testSummary( self )
Jon Hall892818c2015-10-20 17:58:34 -0700474 components = self.componentDictionary
475 for component in sorted( components,
476 key=lambda item: components[item]['connect_order'],
477 reverse=True ):
Jon Hall714eeba2015-09-29 17:53:10 -0700478 try:
479 tempObject = vars( self )[component]
480 print "Disconnecting from " + str( tempObject.name ) +\
481 ": " + str( tempObject.__class__)
Jon Hall5b586732015-06-11 11:39:39 -0700482 tempObject.disconnect()
Jon Hall1306a562015-09-04 11:21:24 -0700483 except KeyboardInterrupt:
484 pass
485 except KeyError:
486 # Component not created yet
487 self.log.warn( "Could not find the component " +
488 str( component ) )
489 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700490 self.log.exception( "Exception while disconnecting from " +
491 str( component ) )
492 result = self.FALSE
493 # Closing all the driver's session files
494 for driver in self.componentDictionary.keys():
495 try:
Jon Hall714eeba2015-09-29 17:53:10 -0700496 vars( self )[driver].close_log_handles()
Jon Hall1306a562015-09-04 11:21:24 -0700497 except KeyboardInterrupt:
498 pass
499 except KeyError:
500 # Component not created yet
501 self.log.warn( "Could not find the component " +
502 str( driver ) + " while trying to" +
503 " close log file" )
504 except StandardError:
Jon Hall5b586732015-06-11 11:39:39 -0700505 self.log.exception( "Exception while closing log files for " +
506 str( driver ) )
507 result = self.FALSE
508 else:
509 pass # Someone else already ran through this function
510 finally:
511 lock.release()
512 else: # Someone already has a lock
513 # NOTE: This could cause problems if we don't release the lock
514 # correctly
515 lock.acquire() # Wait for the other thread to finish
516 # NOTE: If we don't wait, exit could be called while the thread
517 # with the lock is still cleaning up
518 lock.release()
adminbae64d82013-08-01 10:50:15 -0700519 return result
Jon Halld61331b2015-02-17 16:35:47 -0800520
Jon Hall714eeba2015-09-29 17:53:10 -0700521 def pause( self ):
adminbae64d82013-08-01 10:50:15 -0700522 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700523 This function will pause the test's execution, and will continue after
524 user provide 'resume' command.
adminbae64d82013-08-01 10:50:15 -0700525 '''
526 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700527
Jon Hall714eeba2015-09-29 17:53:10 -0700528 def onfail( self, *components ):
adminbae64d82013-08-01 10:50:15 -0700529 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700530 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700531 '''
adminbae64d82013-08-01 10:50:15 -0700532 if not components:
Jon Hall714eeba2015-09-29 17:53:10 -0700533 try:
adminbae64d82013-08-01 10:50:15 -0700534 for component in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700535 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700536 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700537 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700538 print str( e )
adminbae64d82013-08-01 10:50:15 -0700539 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700540 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700541 try:
adminbae64d82013-08-01 10:50:15 -0700542 for component in components:
Jon Hall714eeba2015-09-29 17:53:10 -0700543 tempObject = vars( self )[component]
adminbae64d82013-08-01 10:50:15 -0700544 result = tempObject.onfail()
Jon Hall1306a562015-09-04 11:21:24 -0700545 except StandardError as e:
Jon Hall714eeba2015-09-29 17:53:10 -0700546 print str( e )
adminbae64d82013-08-01 10:50:15 -0700547 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700548
Jon Hall714eeba2015-09-29 17:53:10 -0700549 def getDriverPath( self, driverName ):
adminbae64d82013-08-01 10:50:15 -0700550 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700551 Based on the component 'type' specified in the params , this method
552 will find the absolute path, by recursively searching the name of
553 the component.
554
555 NOTE: This function requires the linux 'find' command.
adminbae64d82013-08-01 10:50:15 -0700556 '''
557 import commands
558
Jon Hall714eeba2015-09-29 17:53:10 -0700559 cmd = "find " + drivers_path + " -name " + driverName + ".py"
560 result = commands.getoutput( cmd )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700561
Jon Hall714eeba2015-09-29 17:53:10 -0700562 result_array = str( result ).split( '\n' )
adminbae64d82013-08-01 10:50:15 -0700563 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700564
adminbae64d82013-08-01 10:50:15 -0700565 for drivers_list in result_array:
Jon Hall714eeba2015-09-29 17:53:10 -0700566 result_count = result_count + 1
567 if result_count > 1:
568 print "Found " + driverName + " " + str( result_count ) + " times:"
569 print str( result_array )
adminbae64d82013-08-01 10:50:15 -0700570 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700571
Jon Hall714eeba2015-09-29 17:53:10 -0700572 result = re.sub( "(.*)drivers", "", result )
573 result = re.sub( "\/\/", "/", result )
574 result = re.sub( "\.py", "", result )
575 result = re.sub( "\.pyc", "", result )
576 result = re.sub( "\/", ".", result )
577 result = "drivers" + result
adminbae64d82013-08-01 10:50:15 -0700578 return result
adminbae64d82013-08-01 10:50:15 -0700579
Jon Hall714eeba2015-09-29 17:53:10 -0700580 def step( self, stepDesc ):
adminbae64d82013-08-01 10:50:15 -0700581 '''
582 The step information of the test-case will append to the logs.
583 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700584 previousStep = " " + str( self.CurrentTestCaseNumber ) + "." +\
585 str( self.stepNumber ) + ": " + str( self.stepName )
adminbae64d82013-08-01 10:50:15 -0700586 self.stepName = stepDesc
Jon Hall5a72b712015-09-28 12:20:59 -0700587 self.stepNumber += 1
588 self.stepResults[0].append( self.stepNumber )
589 self.stepResults[1].append( stepDesc )
590 self.stepResults[2].append( self.NORESULT )
591 self.stepResults[3].append( "No on fail message given" )
adminbae64d82013-08-01 10:50:15 -0700592
Jon Hall714eeba2015-09-29 17:53:10 -0700593 stepName = " " + str( self.CurrentTestCaseNumber ) + "." +\
594 str( self.stepNumber ) + ": " + str( stepDesc )
adminbae64d82013-08-01 10:50:15 -0700595 self.log.step(stepName)
596 stepHeader = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700597 line = "\n" + "-" * 45 + "\n"
598 if self.stepNumber > 1:
599 stepHeader = line + "End of Step " + previousStep + line
600 stepHeader += line + "Start of Step" + stepName + line
adminbae64d82013-08-01 10:50:15 -0700601 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700602 vars( self )[driver + 'log'].info( stepHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700603
Jon Hall714eeba2015-09-29 17:53:10 -0700604 def case( self, testCaseName ):
adminbae64d82013-08-01 10:50:15 -0700605 '''
606 Test's each test-case information will append to the logs.
607 '''
Jon Halld61331b2015-02-17 16:35:47 -0800608 self.CurrentTestCase = testCaseName
Jon Hall714eeba2015-09-29 17:53:10 -0700609 testCaseName = " " + str( testCaseName )
610 self.log.case( testCaseName )
611 caseHeader = testCaseName + "\n" + "*" * 40 + "\n"
adminbae64d82013-08-01 10:50:15 -0700612 for driver in self.componentDictionary.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700613 vars( self )[driver + 'log'].info( caseHeader )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700614
Jon Hall714eeba2015-09-29 17:53:10 -0700615 def testDesc( self, description ):
adminbae64d82013-08-01 10:50:15 -0700616 '''
617 Test description will append to the logs.
618 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700619 description = "Test Description : " + str( description )
620 self.log.info( description )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700621
Jon Hall714eeba2015-09-29 17:53:10 -0700622 def _getTest( self ):
adminbae64d82013-08-01 10:50:15 -0700623 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700624 This method will parse the test script to find required test
625 information.
adminbae64d82013-08-01 10:50:15 -0700626 '''
Jon Hall53c5e662016-04-13 16:06:56 -0700627 testFileHandler = open( main.testFile, 'r' )
adminbae64d82013-08-01 10:50:15 -0700628 testFileList = testFileHandler.readlines()
629 testFileHandler.close()
adminbae64d82013-08-01 10:50:15 -0700630 counter = 0
Jon Hall714eeba2015-09-29 17:53:10 -0700631 for index in range( len( testFileList ) ):
632 lineMatch = re.match( '\s+def CASE(\d+)(.*):',
633 testFileList[index],
634 0 )
adminbae64d82013-08-01 10:50:15 -0700635 if lineMatch:
Jon Hall714eeba2015-09-29 17:53:10 -0700636 counter = counter + 1
637 self.TC_PLANNED = len( self.testcases_list )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700638
Jon Hall714eeba2015-09-29 17:53:10 -0700639 def response_parser( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700640 ''' It will load the default response parser '''
641 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700642 response_dict = self.response_to_dict( response, return_format )
643 return_format_string = self.dict_to_return_format( response,
644 return_format,
645 response_dict )
adminbae64d82013-08-01 10:50:15 -0700646 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700647
Jon Hall714eeba2015-09-29 17:53:10 -0700648 def response_to_dict( self, response, return_format ):
adminbae64d82013-08-01 10:50:15 -0700649 response_dict = {}
Jon Hall714eeba2015-09-29 17:53:10 -0700650 json_match = re.search( '^\s*{', response )
651 xml_match = re.search( '^\s*\<', response )
652 ini_match = re.search( '^\s*\[', response )
653 if json_match:
654 self.log.info( "Response is in 'JSON' format, converting to '" +
655 return_format + "' format" )
Jon Halld61331b2015-02-17 16:35:47 -0800656 # Formatting the json string
Jon Hall714eeba2015-09-29 17:53:10 -0700657 response = re.sub( r"{\s*'?(\w)", r'{"\1', response )
658 response = re.sub( r",\s*'?(\w)", r',"\1', response )
659 response = re.sub( r"(\w)'?\s*:", r'\1":', response )
660 response = re.sub( r":\s*'(\w)'\s*([,}])", r':"\1"\2', response )
661 try:
adminbae64d82013-08-01 10:50:15 -0700662 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700663 response_dict = json.loads( response )
Jon Hall1306a562015-09-04 11:21:24 -0700664 except StandardError:
Jon Hall2a5002c2015-08-21 16:49:11 -0700665 self.log.exception( "Json Parser is unable to parse the string" )
adminbae64d82013-08-01 10:50:15 -0700666 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700667 elif ini_match:
668 self.log.info( "Response is in 'INI' format, converting to '" +
669 return_format + "' format" )
adminbae64d82013-08-01 10:50:15 -0700670 from configobj import ConfigObj
Jon Hall714eeba2015-09-29 17:53:10 -0700671 response_file = open( "respnse_file.temp", 'w' )
672 response_file.write( response )
Jon Halld61331b2015-02-17 16:35:47 -0800673 response_file.close()
Jon Hall714eeba2015-09-29 17:53:10 -0700674 response_dict = ConfigObj( "respnse_file.temp" )
adminbae64d82013-08-01 10:50:15 -0700675 return response_dict
Jon Hall714eeba2015-09-29 17:53:10 -0700676 elif xml_match:
677 self.log.info( "Response is in 'XML' format, converting to '" +
678 return_format + "' format" )
679 try:
680 response_dict = xmldict.xml_to_dict( "<response> " +
681 str( response ) +
682 " </response>" )
Jon Hall1306a562015-09-04 11:21:24 -0700683 except StandardError:
684 self.log.exception()
adminbae64d82013-08-01 10:50:15 -0700685 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700686
Jon Hall714eeba2015-09-29 17:53:10 -0700687 def dict_to_return_format( self, response, return_format, response_dict ):
688 if return_format == 'table':
adminbae64d82013-08-01 10:50:15 -0700689 ''' Will return in table format'''
690 to_do = "Call the table output formatter"
691 global response_table
692 response_table = '\n'
Jon Hall714eeba2015-09-29 17:53:10 -0700693 response_table = response_table + '\t'.join( response_dict ) + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700694
Jon Hall714eeba2015-09-29 17:53:10 -0700695 def get_table( value_to_convert ):
696 ''' This will parse the dictionary recusrsively and print as
697 table format'''
adminbae64d82013-08-01 10:50:15 -0700698 table_data = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700699 if isinstance( value_to_convert, dict ):
700 table_data = table_data + '\t'.join( value_to_convert ) +\
701 "\n"
702 for temp_val in value_to_convert.values():
703 table_data = table_data + get_table( temp_val )
704 else:
705 table_data = table_data + str( value_to_convert ) + "\t"
Jon Halld61331b2015-02-17 16:35:47 -0800706 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700707
Jon Hall714eeba2015-09-29 17:53:10 -0700708 for value in response_dict.values():
709 response_table = response_table + get_table( value )
adminbae64d82013-08-01 10:50:15 -0700710 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700711
Jon Hall714eeba2015-09-29 17:53:10 -0700712 elif return_format == 'config':
adminbae64d82013-08-01 10:50:15 -0700713 ''' Will return in config format'''
714 to_do = 'Call dict to config coverter'
Jon Hall714eeba2015-09-29 17:53:10 -0700715 response_string = str( response_dict )
adminbae64d82013-08-01 10:50:15 -0700716 print response_string
Jon Hall714eeba2015-09-29 17:53:10 -0700717 response_config = re.sub( ",", "\n\t", response_string )
718 response_config = re.sub( "u\'", "\'", response_config )
719 response_config = re.sub( "{", "", response_config )
720 response_config = re.sub( "}", "\n", response_config )
721 response_config = re.sub( ":", " =", response_config )
722 return "[response]\n\t " + response_config
adminbae64d82013-08-01 10:50:15 -0700723 elif return_format == 'xml':
724 ''' Will return in xml format'''
Jon Hall714eeba2015-09-29 17:53:10 -0700725 response_xml = xmldict.dict_to_xml( response_dict )
726 response_xml = re.sub( ">\s*<", ">\n<", response_xml )
727 return "\n" + response_xml
adminbae64d82013-08-01 10:50:15 -0700728 elif return_format == 'json':
729 ''' Will return in json format'''
730 to_do = 'Call dict to xml coverter'
731 import json
Jon Hall714eeba2015-09-29 17:53:10 -0700732 response_json = json.dumps( response_dict )
adminbae64d82013-08-01 10:50:15 -0700733 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700734
Jon Hall714eeba2015-09-29 17:53:10 -0700735 def get_random( self ):
adminbae64d82013-08-01 10:50:15 -0700736 self.random_order = self.random_order + 1
737 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700738
Jon Hall714eeba2015-09-29 17:53:10 -0700739 def exit( self ):
adminbae64d82013-08-01 10:50:15 -0700740 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700741 for thread in threading.enumerate():
742 if thread.isAlive():
743 try:
744 thread._Thread__stop()
745 except:
Jon Hall1306a562015-09-04 11:21:24 -0700746 # NOTE: We should catch any exceptions while trying to
747 # close the thread so that we can try to close the other
748 # threads as well
Jon Hall714eeba2015-09-29 17:53:10 -0700749 print str( thread.getName() ) +\
750 ' could not be terminated'
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700751 os.system( "stty sane" ) # fix format if necessary
adminbae64d82013-08-01 10:50:15 -0700752 sys.exit()
753
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700754
Jon Hallcd3d2a32015-10-01 11:07:28 -0700755 def stop( self, email=False ):
756 """
757 Stop the test until Ctrl-D is entered.
758 Ctrl-C will kill the test
Jon Hall25079782015-10-13 13:54:39 -0700759
760 Optional arguments:
761 email can either be a bool, or you can specify the email address
762 to send the email to
Jon Hallcd3d2a32015-10-01 11:07:28 -0700763 """
764 try:
765 if email:
Jon Hall25079782015-10-13 13:54:39 -0700766 if '@' in email:
767 main.mail = email
768 utilities.send_warning_email()
Jeremy Ronquillo15ff1072017-07-17 10:55:46 -0700769 self.log.debug( "Test execution suspended. \n"
770 "- Type 'c' to resume the test.\n"
771 "- Type Ctrl-C to exit the test.\n"
772 "- Enter interactive python interpreter commands.\n"
773 "\t- ie: main.Mininet1.pingall()\n"
774 "- Type 'help' for help with pdb." )
775 pdb.set_trace()
Jon Hallcd3d2a32015-10-01 11:07:28 -0700776 except EOFError:
777 return
778 # Pass all other exceptions up to caller
779
780
Jon Hall714eeba2015-09-29 17:53:10 -0700781def verifyOptions( options ):
adminbae64d82013-08-01 10:50:15 -0700782 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700783 This will verify the command line options and set to default values,
784 if any option not given in command line.
adminbae64d82013-08-01 10:50:15 -0700785 '''
Jon Hall714eeba2015-09-29 17:53:10 -0700786 verifyTest( options )
787 verifyExample( options )
788 verifyTestScript( options )
YPZhang1c89e762016-06-29 10:43:58 -0700789 verifyParams( options )
Jon Hall714eeba2015-09-29 17:53:10 -0700790 verifyLogdir( options )
791 verifyMail( options )
792 verifyTestCases( options )
793 verifyOnosCell( options )
adminbae64d82013-08-01 10:50:15 -0700794
Jon Hall714eeba2015-09-29 17:53:10 -0700795def verifyTest( options ):
Jon Hall44506242015-07-29 17:40:26 -0700796 try:
797 if options.testname:
798 main.TEST = options.testname
Jon Hall714eeba2015-09-29 17:53:10 -0700799 main.classPath = "tests." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700800 main.tests_path = tests_path
801 elif options.example:
802 main.TEST = options.example
Jon Hall714eeba2015-09-29 17:53:10 -0700803 main.tests_path = path + "/examples/"
804 main.classPath = "examples." + main.TEST + "." + main.TEST
Jon Hall44506242015-07-29 17:40:26 -0700805 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700806 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700807 main.exit()
adminbae64d82013-08-01 10:50:15 -0700808
Jon Hall714eeba2015-09-29 17:53:10 -0700809def verifyExample( options ):
adminbae64d82013-08-01 10:50:15 -0700810 if options.example:
Jon Hall714eeba2015-09-29 17:53:10 -0700811 main.testDir = path + '/examples/'
812 main.tests_path = path + "/examples/"
813 main.classPath = "examples." + main.TEST + "." + main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700814
Jon Hall714eeba2015-09-29 17:53:10 -0700815def verifyLogdir( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800816 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700817 if options.logdir:
818 main.logdir = options.logdir
Jon Hall714eeba2015-09-29 17:53:10 -0700819 else:
Jon Halld61331b2015-02-17 16:35:47 -0800820 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700821
Jon Hall714eeba2015-09-29 17:53:10 -0700822def verifyMail( options ):
Jon Hall25079782015-10-13 13:54:39 -0700823 # Mail-To: field
824 if options.mail: # Test run specific
adminbae64d82013-08-01 10:50:15 -0700825 main.mail = options.mail
Jon Hall25079782015-10-13 13:54:39 -0700826 elif main.params.get('mail'): # Test suite specific
827 main.mail = main.params.get( 'mail' )
828 else: # TestON specific
829 main.mail = main.config['config'].get( 'mail_to' )
830 # Mail-From: field
831 main.sender = main.config['config'].get( 'mail_from' )
832 # Mail smtp server
833 main.smtp = main.config['config'].get( 'mail_server' )
834 # Mail-From account password
835 main.senderPwd = main.config['config'].get( 'mail_pass' )
836
Devin Lim2df68a12017-06-30 15:39:05 -0700837def evalTestCase( tempList ):
838 tList = []
839 for tcase in tempList:
840 if isinstance( tcase, list ):
841 tList.extend( evalTestCase( tcase ) )
842 else:
843 tList.extend( [tcase] )
844 return tList
adminbae64d82013-08-01 10:50:15 -0700845
Jon Hall714eeba2015-09-29 17:53:10 -0700846def verifyTestCases( options ):
Jon Hall88e498c2015-03-06 09:54:35 -0800847 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700848 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800849 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800850 # sys.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700851 testcases_list = re.sub( "(\[|\])", "", options.testcases )
852 main.testcases_list = eval( testcases_list + "," )
853 else:
adminbae64d82013-08-01 10:50:15 -0700854 if 'testcases' in main.params.keys():
Jon Hall714eeba2015-09-29 17:53:10 -0700855 temp = eval( main.params['testcases'] + "," )
Devin Lim2df68a12017-06-30 15:39:05 -0700856 main.testcases_list = evalTestCase( list( temp ) )
Jon Hall714eeba2015-09-29 17:53:10 -0700857 else:
858 print "Testcases not specifed in params, please provide in " +\
859 "params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800860 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700861
Jon Hall714eeba2015-09-29 17:53:10 -0700862def verifyOnosCell( options ):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700863 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700864 if options.onoscell:
865 main.onoscell = options.onoscell
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700866 main.onosIPs = []
867 main.mnIP = ""
Jon Hall714eeba2015-09-29 17:53:10 -0700868 cellCMD = ". ~/.profile; cell " + main.onoscell
869 output = subprocess.check_output( ["bash", '-c', cellCMD] )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700870 splitOutput = output.splitlines()
Jon Hall714eeba2015-09-29 17:53:10 -0700871 for i in range( len( splitOutput ) ):
872 if re.match( "OCN", splitOutput[i] ):
873 mnNode = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700874 main.mnIP = mnNode[1]
Jon Hall714eeba2015-09-29 17:53:10 -0700875 # cell already sorts OC variables in bash, so no need to
876 # sort in TestON
877 if re.match( "OC[1-9]", splitOutput[i] ):
878 onosNodes = splitOutput[i].split( "=" )
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700879 main.onosIPs.append( onosNodes[1] )
Jon Hall714eeba2015-09-29 17:53:10 -0700880 else:
Hari Krishna03f530e2015-07-10 17:28:27 -0700881 main.onoscell = main.FALSE
882
Jon Hall714eeba2015-09-29 17:53:10 -0700883def verifyTestScript( options ):
adminbae64d82013-08-01 10:50:15 -0700884 '''
885 Verifyies test script.
886 '''
Jon Halld61331b2015-02-17 16:35:47 -0800887 main.openspeak = openspeak.OpenSpeak()
Jon Hall53c5e662016-04-13 16:06:56 -0700888 directory = main.testDir + "/" + main.TEST
889 if os.path.exists( directory ):
890 pass
891 else:
892 directory = ""
893 for root, dirs, files in os.walk( main.testDir, topdown=True):
894 if not directory:
895 for name in dirs:
896 if name == main.TEST:
897 directory = ( os.path.join( root, name ) )
898 index = directory.find( "/tests/" ) + 1
899 main.classPath = directory[index:].replace( '/', '.' ) + "." + main.TEST
900 break
901 openspeakfile = directory + "/" + main.TEST + ".ospk"
902 main.testFile = directory + "/" + main.TEST + ".py"
Jon Hall714eeba2015-09-29 17:53:10 -0700903 if os.path.exists( openspeakfile ):
Jon Hall44506242015-07-29 17:40:26 -0700904 # Openspeak file found, compiling to python
Jon Hall714eeba2015-09-29 17:53:10 -0700905 main.openspeak.compiler( openspeakfile=openspeakfile, writetofile=1 )
Jon Hall53c5e662016-04-13 16:06:56 -0700906 elif os.path.exists( main.testFile ):
Jon Hall44506242015-07-29 17:40:26 -0700907 # No openspeak found, using python file instead
908 pass
adminbae64d82013-08-01 10:50:15 -0700909 else:
Jon Hall714eeba2015-09-29 17:53:10 -0700910 print "\nThere is no \"" + main.TEST + "\" test script.\nPlease provide a " +\
Jon Hall44506242015-07-29 17:40:26 -0700911 "Python or OpenSpeak test script in the tests folder: " +\
Jon Hall714eeba2015-09-29 17:53:10 -0700912 main.testDir + "/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700913 __builtin__.testthread = None
914 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700915 try:
916 testModule = __import__( main.classPath,
917 globals(),
918 locals(),
919 [main.TEST],
920 -1 )
Jon Hall1306a562015-09-04 11:21:24 -0700921 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -0700922 print "There was an import error, it might mean that there is " +\
923 "no test named " + main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800924 main.exit()
adminbae64d82013-08-01 10:50:15 -0700925
Jon Hall714eeba2015-09-29 17:53:10 -0700926 testClass = getattr( testModule, main.TEST )
adminbae64d82013-08-01 10:50:15 -0700927 main.testObject = testClass()
928 load_parser()
Jon Hall714eeba2015-09-29 17:53:10 -0700929 main.params = main.parser.parseParams( main.classPath )
930 main.topology = main.parser.parseTopology( main.classPath )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700931
YPZhang1c89e762016-06-29 10:43:58 -0700932def verifyParams( options ):
Jon Hall714eeba2015-09-29 17:53:10 -0700933 try:
adminbae64d82013-08-01 10:50:15 -0700934 main.params = main.params['PARAMS']
Jon Hall1306a562015-09-04 11:21:24 -0700935 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700936 print "Error with the params file: Either the file not specified " +\
937 "or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800938 main.exit()
Jon Hall714eeba2015-09-29 17:53:10 -0700939 try:
adminbae64d82013-08-01 10:50:15 -0700940 main.topology = main.topology['TOPOLOGY']
Jon Hall1306a562015-09-04 11:21:24 -0700941 except KeyError:
Jon Hall714eeba2015-09-29 17:53:10 -0700942 print "Error with the Topology file: Either the file not specified " +\
943 "or the format is not correct"
adminbae64d82013-08-01 10:50:15 -0700944 main.exit()
YPZhang1c89e762016-06-29 10:43:58 -0700945 # Overwrite existing params variables if they are specified from command line
946 if len(options.params) > 0:
947 # Some params variables are specified from command line
948 for param in options.params:
949 if not re.search( ".=.", param ):
950 print( "Error when parsing params: params should follow key=value format" )
951 continue
Jon Hall7c4f4302016-07-15 14:39:02 -0700952 # Split the param string to catch nested keys and the value
YPZhang1c89e762016-06-29 10:43:58 -0700953 [ keys, value ] = param.split( "=" )
954 # Split the nested keys according to its hierarchy
955 keyList = keys.split( "/" )
956 # Get the outermost dictionary
957 paramDict = main.params
958 # Get the innermost dictionary
959 try:
960 while len( keyList ) > 1:
961 key = keyList.pop(0)
962 assert isinstance( paramDict[ key ], dict )
963 paramDict = paramDict[ key ]
964 except KeyError:
965 print( "Error when parsing params: key \"" + key + "\" not found in main.params" )
966 main.exit()
967 except AssertionError:
968 print( "Error when parsing params: \"" + key + "\" is already the innermost level in main.params" )
969 main.exit()
970 # Change the value
971 if not paramDict.has_key( keyList[0] ):
972 print( "Error when parsing params: key \"" + keyList[0] + "\" not found in main.params" )
973 main.exit()
974 elif isinstance( paramDict[ keyList[0] ], dict ):
975 print( "Error when parsing params: more levels under key \"" + keyList[0] + "\" in main.params" )
976 main.exit()
977 else:
978 paramDict[ keyList[0] ] = value
kelvin-onlabf70fd542015-05-07 18:41:40 -0700979
Jon Hall714eeba2015-09-29 17:53:10 -0700980def load_parser():
adminbae64d82013-08-01 10:50:15 -0700981 '''
982 It facilitates the loading customised parser for topology and params file.
983 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -0700984 It also loads default xmlparser if no parser have specified in teston.cfg
985 file.
adminbae64d82013-08-01 10:50:15 -0700986
987 '''
988 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -0700989 if 'file' in confighash['config']['parser'] and\
990 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -0700991 path = confighash['config']['parser']['file']
Jon Hall714eeba2015-09-29 17:53:10 -0700992 if path is not None or\
993 confighash['config']['parser']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -0700994 try:
995 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700996 moduleList = module.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -0700997 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -0700998 parsingClass = confighash['config']['parser']['class']
Jon Hall714eeba2015-09-29 17:53:10 -0700999 parsingModule = __import__( newModule,
1000 globals(),
1001 locals(),
1002 [parsingClass],
1003 -1 )
1004 parsingClass = getattr( parsingModule, parsingClass )
Jon Hall44506242015-07-29 17:40:26 -07001005 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001006 if hasattr( main.parser, "parseParams" ) and\
1007 hasattr( main.parser, "parseTopology" ) and\
1008 hasattr( main.parser, "parse" ):
Jon Hall44506242015-07-29 17:40:26 -07001009 pass
1010 else:
1011 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -07001012 main.exit()
Jon Hall44506242015-07-29 17:40:26 -07001013 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001014 print "Could not find the file " + path +\
1015 " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -08001016 load_defaultParser()
Jon Hall714eeba2015-09-29 17:53:10 -07001017 elif confighash['config']['parser']['file'] is None or\
1018 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001019 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -07001020 else:
1021 load_defaultParser()
1022
1023def load_defaultParser():
1024 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001025 It will load the default parser which is xml parser to parse the params and
1026 topology file.
adminbae64d82013-08-01 10:50:15 -07001027 '''
1028 moduleList = main.parserPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001029 newModule = ".".join( moduleList[-2:] )
1030 try:
Jon Halld61331b2015-02-17 16:35:47 -08001031 parsingClass = main.parsingClass
Jon Hall714eeba2015-09-29 17:53:10 -07001032 parsingModule = __import__( newModule,
1033 globals(),
1034 locals(),
1035 [parsingClass],
1036 -1 )
1037 parsingClass = getattr( parsingModule, parsingClass )
adminbae64d82013-08-01 10:50:15 -07001038 main.parser = parsingClass()
Jon Hall714eeba2015-09-29 17:53:10 -07001039 if hasattr( main.parser, "parseParams" ) and\
1040 hasattr( main.parser, "parseTopology" ) and\
1041 hasattr( main.parser, "parse" ):
adminbae64d82013-08-01 10:50:15 -07001042 pass
1043 else:
1044 main.exit()
adminbae64d82013-08-01 10:50:15 -07001045 except ImportError:
1046 print sys.exc_info()[1]
1047
Jon Hall714eeba2015-09-29 17:53:10 -07001048def load_logger():
adminbae64d82013-08-01 10:50:15 -07001049 '''
1050 It facilitates the loading customised parser for topology and params file.
1051 It loads parser mentioned in tab named parser of teston.cfg file.
Jon Hall714eeba2015-09-29 17:53:10 -07001052 It also loads default xmlparser if no parser have specified in teston.cfg
1053 file.
adminbae64d82013-08-01 10:50:15 -07001054 '''
1055 confighash = main.configDict
Jon Hall714eeba2015-09-29 17:53:10 -07001056 if 'file' in confighash['config']['logger'] and\
1057 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -07001058 path = confighash['config']['logger']['file']
Jon Hall714eeba2015-09-29 17:53:10 -07001059 if path is not None or\
1060 confighash['config']['logger']['class'] is not None:
Jon Hall44506242015-07-29 17:40:26 -07001061 try:
1062 module = re.sub( r".py\s*$", "", path )
Jon Hall714eeba2015-09-29 17:53:10 -07001063 moduleList = module.split( "/" )
1064 newModule = ".".join( moduleList[-2:] )
Jon Hall44506242015-07-29 17:40:26 -07001065 loggerClass = confighash['config']['logger']['class']
Jon Hall714eeba2015-09-29 17:53:10 -07001066 loggerModule = __import__( newModule,
1067 globals(),
1068 locals(),
1069 [loggerClass],
1070 -1 )
1071 loggerClass = getattr( loggerModule, loggerClass )
Jon Hall44506242015-07-29 17:40:26 -07001072 main.logger = loggerClass()
Jon Hall44506242015-07-29 17:40:26 -07001073 except ImportError:
Jon Hall714eeba2015-09-29 17:53:10 -07001074 print "Could not find the file " + path +\
1075 " using default logger."
adminbae64d82013-08-01 10:50:15 -07001076 load_defaultlogger()
Jon Hall714eeba2015-09-29 17:53:10 -07001077 elif confighash['config']['parser']['file'] is None or\
1078 confighash['config']['parser']['class'] is None:
Jon Halld61331b2015-02-17 16:35:47 -08001079 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -07001080 else:
1081 load_defaultlogger()
1082
1083def load_defaultlogger():
1084 '''
Jon Hall714eeba2015-09-29 17:53:10 -07001085 It will load the default parser which is xml parser to parse the params and
1086 topology file.
adminbae64d82013-08-01 10:50:15 -07001087 '''
1088 moduleList = main.loggerPath.split("/")
Jon Hall714eeba2015-09-29 17:53:10 -07001089 newModule = ".".join( moduleList[-2:] )
1090 try:
Jon Halld61331b2015-02-17 16:35:47 -08001091 loggerClass = main.loggerClass
Jon Hall714eeba2015-09-29 17:53:10 -07001092 loggerModule = __import__( newModule,
1093 globals(),
1094 locals(),
1095 [loggerClass],
1096 -1 )
1097 loggerClass = getattr( loggerModule, loggerClass )
adminbae64d82013-08-01 10:50:15 -07001098 main.logger = loggerClass()
1099
1100 except ImportError:
1101 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -08001102 main.exit()
adminbae64d82013-08-01 10:50:15 -07001103
Jon Hall714eeba2015-09-29 17:53:10 -07001104def _echo( self ):
adminbae64d82013-08-01 10:50:15 -07001105 print "THIS IS ECHO"