blob: 5bc702bd60ef938fa06a96e78619dd35e1595308 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 22-Oct-2012
Jon Hall65844a32015-03-09 19:09:37 -07004
adminbae64d82013-08-01 10:50:15 -07005@author: Anil Kumar (anilkumar.s@paxterrasolutions.com)
6
7
8 TestON is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 TestON is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
Jon Hall65844a32015-03-09 19:09:37 -070019 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070020
21
22
23teston is the main module.
24
25'''
26
27import sys
28import getpass
29import os
30import re
31import __builtin__
32import new
33import xmldict
Jon Hall30b82fa2015-03-04 17:15:43 -080034import importlib
Jon Hall5b586732015-06-11 11:39:39 -070035import threading
adminbae64d82013-08-01 10:50:15 -070036module = new.module("test")
37import openspeak
Hari Krishnabe4b97b2015-07-15 12:19:43 -070038import subprocess
adminbae64d82013-08-01 10:50:15 -070039global path, drivers_path, core_path, tests_path,logs_path
Jon Hall44506242015-07-29 17:40:26 -070040location = os.path.abspath( os.path.dirname( __file__ ) )
41path = re.sub( "(core|bin)$", "", location )
adminbae64d82013-08-01 10:50:15 -070042drivers_path = path+"drivers/"
43core_path = path+"core"
44tests_path = path+"tests"
45logs_path = path+"logs/"
46config_path = path + "config/"
Jon Hall44506242015-07-29 17:40:26 -070047sys.path.append( path )
48sys.path.append( drivers_path )
49sys.path.append( core_path )
50sys.path.append( tests_path )
adminbae64d82013-08-01 10:50:15 -070051
52from core.utilities import Utilities
kelvin-onlabfb521662015-02-27 09:52:40 -080053from core.Thread import Thread
adminbae64d82013-08-01 10:50:15 -070054
adminbae64d82013-08-01 10:50:15 -070055
56class TestON:
57 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -070058 TestON will initiate the specified test.
59 The main tasks are :
60 * Initiate the required Component handles for the test.
adminbae64d82013-08-01 10:50:15 -070061 * Create Log file Handles.
adminbae64d82013-08-01 10:50:15 -070062 '''
63 def __init__(self,options):
64 '''
65 Initialise the component handles specified in the topology file of the specified test.
adminbae64d82013-08-01 10:50:15 -070066 '''
67 # Initialization of the variables.
68 __builtin__.main = self
adminbae64d82013-08-01 10:50:15 -070069 __builtin__.path = path
70 __builtin__.utilities = Utilities()
71 self.TRUE = 1
72 self.FALSE = 0
73 self.ERROR = -1
kelvin-onlabf70fd542015-05-07 18:41:40 -070074 self.NORESULT = 2
adminbae64d82013-08-01 10:50:15 -070075 self.FAIL = False
76 self.PASS = True
kelvin-onlabf70fd542015-05-07 18:41:40 -070077 self.CASERESULT = self.ERROR
78 self.STEPRESULT = self.NORESULT
79 self.stepResults = []
adminbae64d82013-08-01 10:50:15 -070080 self.init_result = self.TRUE
81 self.testResult = "Summary"
kelvin-onlabf70fd542015-05-07 18:41:40 -070082 self.stepName = ""
83 self.stepCache = ""
Jon Halld61331b2015-02-17 16:35:47 -080084 self.EXPERIMENTAL_MODE = False
adminbae64d82013-08-01 10:50:15 -070085 self.test_target = None
86 self.lastcommand = None
Jon Halld61331b2015-02-17 16:35:47 -080087 self.testDir = tests_path
88 self.configFile = config_path + "teston.cfg"
adminbae64d82013-08-01 10:50:15 -070089 self.parsingClass = "xmlparser"
90 self.parserPath = core_path + "/xmlparser"
91 self.loggerPath = core_path + "/logger"
92 self.loggerClass = "Logger"
93 self.logs_path = logs_path
94 self.driver = ''
kelvin-onlabfb521662015-02-27 09:52:40 -080095 self.Thread = Thread
Jon Hall5b586732015-06-11 11:39:39 -070096 self.cleanupFlag = False
97 self.cleanupLock = threading.Lock()
Jon Hall0fc0d452015-07-14 09:49:58 -070098 self.initiated = False
Jon Hall65844a32015-03-09 19:09:37 -070099
adminbae64d82013-08-01 10:50:15 -0700100 self.configparser()
101 verifyOptions(options)
102 load_logger()
103 self.componentDictionary = {}
104 self.componentDictionary = self.topology ['COMPONENT']
105 self.driversList=[]
106 if type(self.componentDictionary) == str :
107 self.componentDictionary = dict(self.componentDictionary)
Jon Hall65844a32015-03-09 19:09:37 -0700108
adminbae64d82013-08-01 10:50:15 -0700109 for component in self.componentDictionary :
110 self.driversList.append(self.componentDictionary[component]['type'])
Jon Hall65844a32015-03-09 19:09:37 -0700111
adminbae64d82013-08-01 10:50:15 -0700112 self.driversList = list(set(self.driversList)) # Removing duplicates.
113 # Checking the test_target option set for the component or not
114 if type(self.componentDictionary) == dict:
115 for component in self.componentDictionary.keys():
116 if 'test_target' in self.componentDictionary[component].keys():
117 self.test_target = component
Jon Hall65844a32015-03-09 19:09:37 -0700118
Jon Halld61331b2015-02-17 16:35:47 -0800119 # Checking for the openspeak file and test script
adminbae64d82013-08-01 10:50:15 -0700120 self.logger.initlog(self)
121
122 # Creating Drivers Handles
123 initString = "\n"+"*" * 30+"\n CASE INIT \n"+"*" * 30+"\n"
124 self.log.exact(initString)
125 self.driverObject = {}
126 self.random_order = 111 # Random order id to connect the components
127 components_connect_order = {}
128 #component_list.append()
129 if type(self.componentDictionary) == dict:
130 for component in self.componentDictionary.keys():
131 self.componentDictionary[component]['connect_order'] = self.componentDictionary[component]['connect_order'] if ('connect_order' in self.componentDictionary[component].keys()) else str(self.get_random())
132 components_connect_order[component] = eval(self.componentDictionary[component]['connect_order'])
133 #Ordering components based on the connect order.
134 ordered_component_list =sorted(components_connect_order, key=lambda key: components_connect_order[key])
135 print ordered_component_list
adminbae64d82013-08-01 10:50:15 -0700136 for component in ordered_component_list:
137 self.componentInit(component)
138
139 def configparser(self):
140 '''
141 It will parse the config file (teston.cfg) and return as dictionary
142 '''
143 matchFileName = re.match(r'(.*)\.cfg', self.configFile, re.M | re.I)
144 if matchFileName:
145 xml = open(self.configFile).read()
146 try :
147 self.configDict = xmldict.xml_to_dict(xml)
148 return self.configDict
Jon Hallfebb1c72015-03-05 13:30:09 -0800149 except Exception:
adminbae64d82013-08-01 10:50:15 -0700150 print "There is no such file to parse " + self.configFile
kelvin-onlabf70fd542015-05-07 18:41:40 -0700151
adminbae64d82013-08-01 10:50:15 -0700152 def componentInit(self,component):
153 '''
154 This method will initialize specified component
155 '''
156 global driver_options
Jon Hall0fc0d452015-07-14 09:49:58 -0700157 self.initiated = False
adminbae64d82013-08-01 10:50:15 -0700158 self.log.info("Creating component Handle: "+component)
Jon Halld61331b2015-02-17 16:35:47 -0800159 driver_options = {}
adminbae64d82013-08-01 10:50:15 -0700160 if 'COMPONENTS' in self.componentDictionary[component].keys():
161 driver_options =dict(self.componentDictionary[component]['COMPONENTS'])
162
163 driver_options['name']=component
164 driverName = self.componentDictionary[component]['type']
165 driver_options ['type'] = driverName
kelvin-onlabf70fd542015-05-07 18:41:40 -0700166
adminbae64d82013-08-01 10:50:15 -0700167 classPath = self.getDriverPath(driverName.lower())
Jon Hall30b82fa2015-03-04 17:15:43 -0800168 driverModule = importlib.import_module(classPath)
adminbae64d82013-08-01 10:50:15 -0700169 driverClass = getattr(driverModule, driverName)
170 driverObject = driverClass()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700171
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700172 if ( "OCN" in self.componentDictionary[component]['host'] and main.onoscell ):
173 self.componentDictionary[component]['host'] = main.mnIP
174
adminbae64d82013-08-01 10:50:15 -0700175 connect_result = driverObject.connect(user_name = self.componentDictionary[component]['user'] if ('user' in self.componentDictionary[component].keys()) else getpass.getuser(),
176 ip_address= self.componentDictionary[component]['host'] if ('host' in self.componentDictionary[component].keys()) else 'localhost',
177 pwd = self.componentDictionary[component]['password'] if ('password' in self.componentDictionary[component].keys()) else 'changeme',
178 port = self.componentDictionary[component]['port'] if ('port' in self.componentDictionary[component].keys()) else None,
179 options = driver_options)
cameron@onlab.us5cc6a372015-05-11 17:18:07 -0700180
adminbae64d82013-08-01 10:50:15 -0700181 if not connect_result:
182 self.log.error("Exiting form the test execution because the connecting to the "+component+" component failed.")
Jon Halld61331b2015-02-17 16:35:47 -0800183 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700184
adminbae64d82013-08-01 10:50:15 -0700185 vars(self)[component] = driverObject
Jon Hall0fc0d452015-07-14 09:49:58 -0700186 self.initiated = True
kelvin-onlabf70fd542015-05-07 18:41:40 -0700187
adminbae64d82013-08-01 10:50:15 -0700188 def run(self):
189 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700190 The Execution of the test script's cases listed in the Test params file will be done here.
191 And Update each test case result.
192 This method will return TRUE if it executed all the test cases successfully,
adminbae64d82013-08-01 10:50:15 -0700193 else will retun FALSE
194 '''
adminbae64d82013-08-01 10:50:15 -0700195 self.testCaseResult = {}
Jon Halla1185982014-09-15 14:55:10 -0700196 self.TOTAL_TC = 0
adminbae64d82013-08-01 10:50:15 -0700197 self.TOTAL_TC_RUN = 0
Jon Halld61331b2015-02-17 16:35:47 -0800198 self.TOTAL_TC_PLANNED = 0
adminbae64d82013-08-01 10:50:15 -0700199 self.TOTAL_TC_NORESULT = 0
200 self.TOTAL_TC_FAIL = 0
201 self.TOTAL_TC_PASS = 0
Jon Halla1185982014-09-15 14:55:10 -0700202 self.TEST_ITERATION = 0
adminbae64d82013-08-01 10:50:15 -0700203 self.stepCount = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700204 self.CASERESULT = self.NORESULT
205
Jon Halld61331b2015-02-17 16:35:47 -0800206 import testparser
adminbae64d82013-08-01 10:50:15 -0700207 testFile = self.tests_path + "/"+self.TEST + "/"+self.TEST + ".py"
208 test = testparser.TestParser(testFile)
209 self.testscript = test.testscript
210 self.code = test.getStepCode()
Jon Hallfebb1c72015-03-05 13:30:09 -0800211 repeat= int(self.params['repeat']) if ('repeat' in self.params) else 1
212 self.TOTAL_TC_PLANNED = len(self.testcases_list)*repeat
kelvin-onlabf70fd542015-05-07 18:41:40 -0700213
adminbae64d82013-08-01 10:50:15 -0700214 result = self.TRUE
Jon Hallfebb1c72015-03-05 13:30:09 -0800215 while(repeat):
Jon Halla1185982014-09-15 14:55:10 -0700216 for self.CurrentTestCaseNumber in self.testcases_list:
Jon Halld61331b2015-02-17 16:35:47 -0800217 result = self.runCase(self.CurrentTestCaseNumber)
Jon Hallfebb1c72015-03-05 13:30:09 -0800218 repeat-=1
adminbae64d82013-08-01 10:50:15 -0700219 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700220
adminbae64d82013-08-01 10:50:15 -0700221 def runCase(self,testCaseNumber):
222 self.CurrentTestCaseNumber = testCaseNumber
kelvin-onlabf70fd542015-05-07 18:41:40 -0700223 self.CurrentTestCase = ""
224 self.stepResults = []
225 self.stepName = ""
Jon Hall783bbf92015-07-23 14:33:19 -0700226 self.caseExplanation = ""
adminbae64d82013-08-01 10:50:15 -0700227 result = self.TRUE
228 self.stepCount = 0
229 self.EXPERIMENTAL_MODE = self.FALSE
230 self.addCaseHeader()
231 self.testCaseNumber = str(testCaseNumber)
232 stopped = False
233 try :
234 self.stepList = self.code[self.testCaseNumber].keys()
Jon Halld61331b2015-02-17 16:35:47 -0800235 except KeyError:
Jon Hallfebb1c72015-03-05 13:30:09 -0800236 self.log.error("There is no Test-Case "+ self.testCaseNumber)
237 return self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700238
adminbae64d82013-08-01 10:50:15 -0700239 self.stepCount = 0
240 while self.stepCount < len(self.code[self.testCaseNumber].keys()):
241 result = self.runStep(self.stepList,self.code,self.testCaseNumber)
Jon Hallfebb1c72015-03-05 13:30:09 -0800242 if result == self.FALSE:
adminbae64d82013-08-01 10:50:15 -0700243 break
Jon Hallfebb1c72015-03-05 13:30:09 -0800244 elif result == self.TRUE:
adminbae64d82013-08-01 10:50:15 -0700245 continue
adminbae64d82013-08-01 10:50:15 -0700246 if not stopped :
kelvin-onlabf70fd542015-05-07 18:41:40 -0700247 if all( self.TRUE == i for i in self.stepResults ):
248 # ALL PASSED
249 self.CASERESULT = self.TRUE
250 elif self.FALSE in self.stepResults:
251 # AT LEAST ONE FAILED
252 self.CASERESULT = self.FALSE
253 elif self.TRUE in self.stepResults:
254 # AT LEAST ONE PASSED
255 self.CASERESULT = self.TRUE
256 else:
257 self.CASERESULT = self.NORESULT
adminbae64d82013-08-01 10:50:15 -0700258 self.testCaseResult[str(self.CurrentTestCaseNumber)] = self.CASERESULT
259 self.logger.updateCaseResults(self)
Jon Hall783bbf92015-07-23 14:33:19 -0700260 self.log.wiki( "<p>" + self.caseExplanation + "</p>" )
261 self.log.summary( self.caseExplanation )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700262 self.log.wiki( "<ul>" )
263 for line in self.stepCache.splitlines():
264 if re.search( " - PASS$", line ):
265 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
266 elif re.search( " - FAIL$", line ):
267 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
268 elif re.search( " - No Result$", line ):
269 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700270 else: # Should only be on fail message
271 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700272 self.log.wiki( "</ul>" )
273 self.log.summary( self.stepCache )
274 self.stepCache = ""
adminbae64d82013-08-01 10:50:15 -0700275 return result
kelvin-onlabf70fd542015-05-07 18:41:40 -0700276
adminbae64d82013-08-01 10:50:15 -0700277 def runStep(self,stepList,code,testCaseNumber):
278 if not cli.pause:
279 try :
280 step = stepList[self.stepCount]
kelvin-onlabf70fd542015-05-07 18:41:40 -0700281 self.STEPRESULT = self.NORESULT
Jon Hall90627612015-06-09 14:57:02 -0700282 self.onFailMsg = "\t\tNo on fail message given"
adminbae64d82013-08-01 10:50:15 -0700283 exec code[testCaseNumber][step] in module.__dict__
284 self.stepCount = self.stepCount + 1
kelvin-onlabf70fd542015-05-07 18:41:40 -0700285 if step > 0:
286 self.stepCache += "\t"+str(testCaseNumber)+"."+str(step)+" "+self.stepName+" - "
287 if self.STEPRESULT == self.TRUE:
288 self.stepCache += "PASS\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700289 elif self.STEPRESULT == self.FALSE:
290 self.stepCache += "FAIL\n"
Jon Hall8ce34e82015-06-05 10:41:45 -0700291 # TODO: Print the on-fail statement here
Jon Hall90627612015-06-09 14:57:02 -0700292 self.stepCache += "\t\t" + self.onFailMsg + "\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700293 else:
294 self.stepCache += "No Result\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700295 self.stepResults.append(self.STEPRESULT)
Jon Hall5b586732015-06-11 11:39:39 -0700296 except StandardError:
Jon Hall40d2cbd2015-06-03 16:24:29 -0700297 self.log.exception( "\nException in the following section of" +
298 " code: " + str(testCaseNumber) + "." +
299 str(step) + ": " + self.stepName )
Jon Hall63604932015-02-26 17:09:50 -0800300 #print code[testCaseNumber][step]
adminbae64d82013-08-01 10:50:15 -0700301 self.stepCount = self.stepCount + 1
kelvin-onlabf70fd542015-05-07 18:41:40 -0700302 self.logger.updateCaseResults(self)
303 #WIKI results
304 self.log.wiki( "<ul>" )
305 for line in self.stepCache.splitlines():
306 if re.search( " - PASS$", line ):
307 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"tick\" /></li>\n" )
308 elif re.search( " - FAIL$", line ):
309 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"cross\" /></li>\n" )
310 elif re.search( " - No Result$", line ):
311 self.log.wiki( "<li>" + line + " <ac:emoticon ac:name=\"warning\" /></li>\n" )
Jon Hall90627612015-06-09 14:57:02 -0700312 else: # Should only be on fail message
313 self.log.wiki( "<ul><li>" + line + "</li></ul>\n" )
kelvin-onlabf70fd542015-05-07 18:41:40 -0700314 self.log.wiki( "</ul>" )
315 #summary results
316 self.log.summary( self.stepCache )
317 self.stepCache = ""
shahshreya957feaa2015-03-23 16:08:29 -0700318 self.cleanup()
Jon Hall00539b12015-04-03 13:55:46 -0700319 self.exit()
adminbae64d82013-08-01 10:50:15 -0700320 return main.TRUE
adminbae64d82013-08-01 10:50:15 -0700321 if cli.stop:
322 cli.stop = False
323 stopped = True
324 self.TOTAL_TC_NORESULT = self.TOTAL_TC_NORESULT + 1
325 self.testCaseResult[str(self.CurrentTestCaseNumber)] = "Stopped"
326 self.logger.updateCaseResults(self)
327 result = self.cleanup()
328 return main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700329
adminbae64d82013-08-01 10:50:15 -0700330 def addCaseHeader(self):
331 caseHeader = "\n"+"*" * 30+"\n Result summary for Testcase"+str(self.CurrentTestCaseNumber)+"\n"+"*" * 30+"\n"
Jon Halld61331b2015-02-17 16:35:47 -0800332 self.log.exact(caseHeader)
333 caseHeader = "\n"+"*" * 40 +"\nStart of Test Case"+str(self.CurrentTestCaseNumber)+" : "
adminbae64d82013-08-01 10:50:15 -0700334 for driver in self.componentDictionary.keys():
335 vars(self)[driver+'log'].info(caseHeader)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700336
adminbae64d82013-08-01 10:50:15 -0700337 def addCaseFooter(self):
338 if self.stepCount-1 > 0 :
339 previousStep = " "+str(self.CurrentTestCaseNumber)+"."+str(self.stepCount-1)+": "+ str(self.stepName) + ""
340 stepHeader = "\n"+"*" * 40+"\nEnd of Step "+previousStep+"\n"+"*" * 40+"\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700341
adminbae64d82013-08-01 10:50:15 -0700342 caseFooter = "\n"+"*" * 40+"\nEnd of Test case "+str(self.CurrentTestCaseNumber)+"\n"+"*" * 40+"\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700343
adminbae64d82013-08-01 10:50:15 -0700344 for driver in self.driversList:
345 vars(self)[driver].write(stepHeader+"\n"+caseFooter)
346
347 def cleanup(self):
348 '''
Jon Hall5b586732015-06-11 11:39:39 -0700349 Print a summary of the current test's results then attempt to release
350 all the component handles and the close opened file handles.
adminbae64d82013-08-01 10:50:15 -0700351
Jon Hall5b586732015-06-11 11:39:39 -0700352 This function shouldbe threadsafe such that cleanup will only be
353 executed once per test.
354
355 This will return TRUE if all the component handles and log handles
356 closed properly, else return FALSE.
adminbae64d82013-08-01 10:50:15 -0700357 '''
358 result = self.TRUE
Jon Hall5b586732015-06-11 11:39:39 -0700359 lock = self.cleanupLock
360 if lock.acquire( False ):
361 try:
362 if self.cleanupFlag is False: # First thread to run this
363 self.cleanupFlag = True
Jon Hall0fc0d452015-07-14 09:49:58 -0700364 if self.initiated:
365 self.logger.testSummary(self)
Jon Hall5b586732015-06-11 11:39:39 -0700366 for component in self.componentDictionary.keys():
367 try :
368 tempObject = vars(self)[component]
369 print "Disconnecting from " + str(tempObject.name) + ": " + \
370 str(tempObject)
371 tempObject.disconnect()
372 except Exception:
373 self.log.exception( "Exception while disconnecting from " +
374 str( component ) )
375 result = self.FALSE
376 # Closing all the driver's session files
377 for driver in self.componentDictionary.keys():
378 try:
379 vars(self)[driver].close_log_handles()
380 except Exception:
381 self.log.exception( "Exception while closing log files for " +
382 str( driver ) )
383 result = self.FALSE
384 else:
385 pass # Someone else already ran through this function
386 finally:
387 lock.release()
388 else: # Someone already has a lock
389 # NOTE: This could cause problems if we don't release the lock
390 # correctly
391 lock.acquire() # Wait for the other thread to finish
392 # NOTE: If we don't wait, exit could be called while the thread
393 # with the lock is still cleaning up
394 lock.release()
adminbae64d82013-08-01 10:50:15 -0700395 return result
Jon Halld61331b2015-02-17 16:35:47 -0800396
adminbae64d82013-08-01 10:50:15 -0700397 def pause(self):
398 '''
399 This function will pause the test's execution, and will continue after user provide 'resume' command.
400 '''
401 __builtin__.testthread.pause()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700402
adminbae64d82013-08-01 10:50:15 -0700403 def onfail(self,*components):
404 '''
kelvin-onlabf70fd542015-05-07 18:41:40 -0700405 When test step failed, calling all the components onfail.
adminbae64d82013-08-01 10:50:15 -0700406 '''
adminbae64d82013-08-01 10:50:15 -0700407 if not components:
408 try :
409 for component in self.componentDictionary.keys():
410 tempObject = vars(self)[component]
411 result = tempObject.onfail()
412 except(Exception),e:
413 print str(e)
414 result = self.FALSE
adminbae64d82013-08-01 10:50:15 -0700415 else:
416 try :
417 for component in components:
418 tempObject = vars(self)[component]
419 result = tempObject.onfail()
420 except(Exception),e:
421 print str(e)
422 result = self.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700423
adminbae64d82013-08-01 10:50:15 -0700424 def getDriverPath(self,driverName):
425 '''
426 Based on the component 'type' specified in the params , this method will find the absolute path ,
427 by recursively searching the name of the component.
428 '''
429 import commands
430
431 cmd = "find "+drivers_path+" -name "+driverName+".py"
432 result = commands.getoutput(cmd)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700433
adminbae64d82013-08-01 10:50:15 -0700434 result_array = str(result).split('\n')
435 result_count = 0
kelvin-onlabf70fd542015-05-07 18:41:40 -0700436
adminbae64d82013-08-01 10:50:15 -0700437 for drivers_list in result_array:
438 result_count = result_count+1
439 if result_count > 1 :
440 print "found "+driverName+" "+ str(result_count) + " times"+str(result_array)
441 self.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700442
adminbae64d82013-08-01 10:50:15 -0700443 result = re.sub("(.*)drivers","",result)
444 result = re.sub("\.py","",result)
445 result = re.sub("\.pyc","",result)
446 result = re.sub("\/",".",result)
447 result = "drivers"+result
448 return result
adminbae64d82013-08-01 10:50:15 -0700449
450 def step(self,stepDesc):
451 '''
452 The step information of the test-case will append to the logs.
453 '''
454 previousStep = " "+str(self.CurrentTestCaseNumber)+"."+str(self.stepCount-1)+": "+ str(self.stepName) + ""
455 self.stepName = stepDesc
456
457 stepName = " "+str(self.CurrentTestCaseNumber)+"."+str(self.stepCount)+": "+ str(stepDesc) + ""
458 try :
459 if self.stepCount == 0:
460 stepName = " INIT : Initializing the test case :"+self.CurrentTestCase
461 except AttributeError:
462 stepName = " INIT : Initializing the test case :"+str(self.CurrentTestCaseNumber)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700463
adminbae64d82013-08-01 10:50:15 -0700464 self.log.step(stepName)
465 stepHeader = ""
466 if self.stepCount > 1 :
467 stepHeader = "\n"+"-"*45+"\nEnd of Step "+previousStep+"\n"+"-"*45+"\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700468
Jon Halld61331b2015-02-17 16:35:47 -0800469 stepHeader += "\n"+"-"*45+"\nStart of Step"+stepName+"\n"+"-"*45+"\n"
adminbae64d82013-08-01 10:50:15 -0700470 for driver in self.componentDictionary.keys():
471 vars(self)[driver+'log'].info(stepHeader)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700472
adminbae64d82013-08-01 10:50:15 -0700473 def case(self,testCaseName):
474 '''
475 Test's each test-case information will append to the logs.
476 '''
Jon Halld61331b2015-02-17 16:35:47 -0800477 self.CurrentTestCase = testCaseName
adminbae64d82013-08-01 10:50:15 -0700478 testCaseName = " " + str(testCaseName) + ""
479 self.log.case(testCaseName)
Jon Halld61331b2015-02-17 16:35:47 -0800480 caseHeader = testCaseName+"\n"+"*" * 40+"\n"
adminbae64d82013-08-01 10:50:15 -0700481 for driver in self.componentDictionary.keys():
482 vars(self)[driver+'log'].info(caseHeader)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700483
adminbae64d82013-08-01 10:50:15 -0700484 def testDesc(self,description):
485 '''
486 Test description will append to the logs.
487 '''
488 description = "Test Description : " + str (description) + ""
489 self.log.info(description)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700490
adminbae64d82013-08-01 10:50:15 -0700491 def _getTest(self):
492 '''
493 This method will parse the test script to find required test information.
494 '''
495 testFile = self.tests_path + "/"+self.TEST + "/"+self.TEST + ".py"
496 testFileHandler = open(testFile, 'r')
497 testFileList = testFileHandler.readlines()
498 testFileHandler.close()
499 #self.TOTAL_TC_PLANNED = 0
500 counter = 0
501 for index in range(len(testFileList)):
502 lineMatch = re.match('\s+def CASE(\d+)(.*):',testFileList[index],0)
503 if lineMatch:
504 counter = counter + 1
Jon Halla1185982014-09-15 14:55:10 -0700505 self.TC_PLANNED = len(self.testcases_list)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700506
adminbae64d82013-08-01 10:50:15 -0700507 def response_parser(self,response, return_format):
508 ''' It will load the default response parser '''
509 response_dict = {}
510 response_dict = self.response_to_dict(response, return_format)
Jon Halld61331b2015-02-17 16:35:47 -0800511 return_format_string = self.dict_to_return_format(response,return_format,response_dict)
adminbae64d82013-08-01 10:50:15 -0700512 return return_format_string
kelvin-onlabf70fd542015-05-07 18:41:40 -0700513
adminbae64d82013-08-01 10:50:15 -0700514 def response_to_dict(self,response,return_format):
adminbae64d82013-08-01 10:50:15 -0700515 response_dict = {}
516 json_match = re.search('^\s*{', response)
517 xml_match = re.search('^\s*\<', response)
518 ini_match = re.search('^\s*\[', response)
519 if json_match :
Jon Hallfebb1c72015-03-05 13:30:09 -0800520 self.log.info(" Response is in 'JSON' format and Converting to '"+return_format+"' format")
Jon Halld61331b2015-02-17 16:35:47 -0800521 # Formatting the json string
adminbae64d82013-08-01 10:50:15 -0700522 response = re.sub(r"{\s*'?(\w)", r'{"\1', response)
523 response = re.sub(r",\s*'?(\w)", r',"\1', response)
524 response = re.sub(r"(\w)'?\s*:", r'\1":', response)
525 response = re.sub(r":\s*'(\w)'\s*([,}])", r':"\1"\2', response)
adminbae64d82013-08-01 10:50:15 -0700526 try :
527 import json
528 response_dict = json.loads(response)
Jon Hall30b82fa2015-03-04 17:15:43 -0800529 except Exception, e:
Jon Hallfebb1c72015-03-05 13:30:09 -0800530 self.log.exception( e )
531 self.log.error("Json Parser is unable to parse the string")
adminbae64d82013-08-01 10:50:15 -0700532 return response_dict
adminbae64d82013-08-01 10:50:15 -0700533 elif ini_match :
Jon Hallfebb1c72015-03-05 13:30:09 -0800534 self.log.info(" Response is in 'INI' format and Converting to '"+return_format+"' format")
adminbae64d82013-08-01 10:50:15 -0700535 from configobj import ConfigObj
536 response_file = open("respnse_file.temp",'w')
537 response_file.write(response)
Jon Halld61331b2015-02-17 16:35:47 -0800538 response_file.close()
adminbae64d82013-08-01 10:50:15 -0700539 response_dict = ConfigObj("respnse_file.temp")
540 return response_dict
adminbae64d82013-08-01 10:50:15 -0700541 elif xml_match :
Jon Hallfebb1c72015-03-05 13:30:09 -0800542 self.log.info(" Response is in 'XML' format and Converting to '"+return_format+"' format")
adminbae64d82013-08-01 10:50:15 -0700543 try :
adminbae64d82013-08-01 10:50:15 -0700544 response_dict = xmldict.xml_to_dict("<response> "+str(response)+" </response>")
545 except Exception, e:
Jon Hallfebb1c72015-03-05 13:30:09 -0800546 self.log.exception( e )
adminbae64d82013-08-01 10:50:15 -0700547 return response_dict
kelvin-onlabf70fd542015-05-07 18:41:40 -0700548
adminbae64d82013-08-01 10:50:15 -0700549 def dict_to_return_format(self,response,return_format,response_dict):
adminbae64d82013-08-01 10:50:15 -0700550 if return_format =='table' :
551 ''' Will return in table format'''
552 to_do = "Call the table output formatter"
553 global response_table
554 response_table = '\n'
555 response_table = response_table +'\t'.join(response_dict)+"\n"
kelvin-onlabf70fd542015-05-07 18:41:40 -0700556
adminbae64d82013-08-01 10:50:15 -0700557 def get_table(value_to_convert):
558 ''' This will parse the dictionary recusrsively and print as table format'''
559 table_data = ""
560 if type(value_to_convert) == dict :
561 table_data = table_data +'\t'.join(value_to_convert)+"\n"
562 for temp_val in value_to_convert.values() :
563 table_data = table_data + get_table(temp_val)
564 else :
565 table_data = table_data + str(value_to_convert) +"\t"
Jon Halld61331b2015-02-17 16:35:47 -0800566 return table_data
kelvin-onlabf70fd542015-05-07 18:41:40 -0700567
adminbae64d82013-08-01 10:50:15 -0700568 for value in response_dict.values() :
569 response_table = response_table + get_table(value)
Jon Hall88e498c2015-03-06 09:54:35 -0800570 # response_table = response_table + '\t'.join(response_dict.values())
adminbae64d82013-08-01 10:50:15 -0700571 return response_table
kelvin-onlabf70fd542015-05-07 18:41:40 -0700572
adminbae64d82013-08-01 10:50:15 -0700573 elif return_format =='config':
574 ''' Will return in config format'''
575 to_do = 'Call dict to config coverter'
576 response_string = str(response_dict)
577 print response_string
578 response_config = re.sub(",", "\n\t", response_string)
579 response_config = re.sub("u\'", "\'", response_config)
580 response_config = re.sub("{", "", response_config)
581 response_config = re.sub("}", "\n", response_config)
582 response_config = re.sub(":", " =", response_config)
583 return "[response]\n\t "+response_config
adminbae64d82013-08-01 10:50:15 -0700584 elif return_format == 'xml':
585 ''' Will return in xml format'''
adminbae64d82013-08-01 10:50:15 -0700586 response_xml = xmldict.dict_to_xml(response_dict)
587 response_xml = re.sub(">\s*<", ">\n<", response_xml)
588 return "\n"+response_xml
adminbae64d82013-08-01 10:50:15 -0700589 elif return_format == 'json':
590 ''' Will return in json format'''
591 to_do = 'Call dict to xml coverter'
592 import json
593 response_json = json.dumps(response_dict)
594 return response_json
kelvin-onlabf70fd542015-05-07 18:41:40 -0700595
adminbae64d82013-08-01 10:50:15 -0700596 def get_random(self):
597 self.random_order = self.random_order + 1
598 return self.random_order
kelvin-onlabf70fd542015-05-07 18:41:40 -0700599
adminbae64d82013-08-01 10:50:15 -0700600 def exit(self):
601 __builtin__.testthread = None
Jon Hall5b586732015-06-11 11:39:39 -0700602 for thread in threading.enumerate():
603 if thread.isAlive():
604 try:
605 thread._Thread__stop()
606 except:
607 print(str(thread.getName()) + ' could not be terminated' )
adminbae64d82013-08-01 10:50:15 -0700608 sys.exit()
609
610def verifyOptions(options):
611 '''
612 This will verify the command line options and set to default values, if any option not given in command line.
613 '''
614 import pprint
615 pp = pprint.PrettyPrinter(indent=4)
616
Jon Hall88e498c2015-03-06 09:54:35 -0800617 # pp.pprint(options)
adminbae64d82013-08-01 10:50:15 -0700618 verifyTest(options)
619 verifyExample(options)
620 verifyTestScript(options)
621 verifyParams()
622 verifyLogdir(options)
623 verifyMail(options)
624 verifyTestCases(options)
Hari Krishna03f530e2015-07-10 17:28:27 -0700625 verifyOnosCell(options)
adminbae64d82013-08-01 10:50:15 -0700626
627def verifyTest(options):
Jon Hall44506242015-07-29 17:40:26 -0700628 try:
629 if options.testname:
630 main.TEST = options.testname
631 main.classPath = "tests."+main.TEST+"."+main.TEST
632 main.tests_path = tests_path
633 elif options.example:
634 main.TEST = options.example
635 main.tests_path = path+"/examples/"
636 main.classPath = "examples."+main.TEST+"."+main.TEST
637 except AttributeError:
adminbae64d82013-08-01 10:50:15 -0700638 print "Test or Example not specified please specify the --test <test name > or --example <example name>"
Jon Hall5b586732015-06-11 11:39:39 -0700639 main.exit()
adminbae64d82013-08-01 10:50:15 -0700640
641def verifyExample(options):
642 if options.example:
643 main.testDir = path+'/examples/'
644 main.tests_path = path+"/examples/"
645 main.classPath = "examples."+main.TEST+"."+main.TEST
kelvin-onlabf70fd542015-05-07 18:41:40 -0700646
adminbae64d82013-08-01 10:50:15 -0700647def verifyLogdir(options):
Jon Hall88e498c2015-03-06 09:54:35 -0800648 # Verifying Log directory option
adminbae64d82013-08-01 10:50:15 -0700649 if options.logdir:
650 main.logdir = options.logdir
651 else :
Jon Halld61331b2015-02-17 16:35:47 -0800652 main.logdir = main.FALSE
kelvin-onlabf70fd542015-05-07 18:41:40 -0700653
adminbae64d82013-08-01 10:50:15 -0700654def verifyMail(options):
Jon Halld61331b2015-02-17 16:35:47 -0800655 # Checking the mailing list
adminbae64d82013-08-01 10:50:15 -0700656 if options.mail:
657 main.mail = options.mail
658 elif main.params.has_key('mail'):
659 main.mail = main.params['mail']
660 else :
661 main.mail = 'paxweb@paxterrasolutions.com'
662
663def verifyTestCases(options):
Jon Hall88e498c2015-03-06 09:54:35 -0800664 # Getting Test cases list
adminbae64d82013-08-01 10:50:15 -0700665 if options.testcases:
Jon Hallfebb1c72015-03-05 13:30:09 -0800666 testcases_list = options.testcases
Jon Hall88e498c2015-03-06 09:54:35 -0800667 # sys.exit()
adminbae64d82013-08-01 10:50:15 -0700668 testcases_list = re.sub("(\[|\])", "", options.testcases)
669 main.testcases_list = eval(testcases_list+",")
670 else :
671 if 'testcases' in main.params.keys():
Jon Halla1185982014-09-15 14:55:10 -0700672 temp = eval(main.params['testcases']+",")
673 list1=[]
674 if type(temp[0])==list:
Jon Hallfebb1c72015-03-05 13:30:09 -0800675 for test in temp:
676 for testcase in test:
677 if type(testcase)==int:
678 testcase=[testcase]
679 list1.extend(testcase)
680 else :
681 temp=list(temp)
682 for testcase in temp:
683 if type(testcase)==int:
684 testcase=[testcase]
685 list1.extend(testcase)
686 main.testcases_list=list1
adminbae64d82013-08-01 10:50:15 -0700687 else :
688 print "testcases not specifed in params, please provide in params file or 'testcases' commandline argument"
Jon Halld61331b2015-02-17 16:35:47 -0800689 sys.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700690
Hari Krishna03f530e2015-07-10 17:28:27 -0700691def verifyOnosCell(options):
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700692 # Verifying onoscell option
Hari Krishna03f530e2015-07-10 17:28:27 -0700693 if options.onoscell:
694 main.onoscell = options.onoscell
Hari Krishnabe4b97b2015-07-15 12:19:43 -0700695 main.onosIPs = []
696 main.mnIP = ""
697 cellCMD = ". ~/.profile; cell "+main.onoscell
698 output=subprocess.check_output( ["bash", '-c', cellCMD] )
699 splitOutput = output.splitlines()
700 for i in range( len(splitOutput) ):
701 if( re.match( "OCN", splitOutput[i] ) ):
702 mnNode=splitOutput[i].split("=")
703 main.mnIP = mnNode[1]
704 # cell already sorts OC variables in bash, so no need to sort in TestON
705 if( re.match( "OC[1-9]", splitOutput[i] ) ):
706 onosNodes = splitOutput[i].split("=")
707 main.onosIPs.append( onosNodes[1] )
Hari Krishna03f530e2015-07-10 17:28:27 -0700708 else :
709 main.onoscell = main.FALSE
710
adminbae64d82013-08-01 10:50:15 -0700711def verifyTestScript(options):
712 '''
713 Verifyies test script.
714 '''
Jon Halld61331b2015-02-17 16:35:47 -0800715 main.openspeak = openspeak.OpenSpeak()
adminbae64d82013-08-01 10:50:15 -0700716 openspeakfile = main.testDir+"/" + main.TEST + "/" + main.TEST + ".ospk"
717 testfile = main.testDir+"/" + main.TEST + "/" + main.TEST + ".py"
Jon Hall44506242015-07-29 17:40:26 -0700718 if os.path.exists(openspeakfile):
719 # Openspeak file found, compiling to python
adminbae64d82013-08-01 10:50:15 -0700720 main.openspeak.compiler(openspeakfile=openspeakfile,writetofile=1)
721 elif os.path.exists(testfile):
Jon Hall44506242015-07-29 17:40:26 -0700722 # No openspeak found, using python file instead
723 pass
adminbae64d82013-08-01 10:50:15 -0700724 else:
Jon Hall44506242015-07-29 17:40:26 -0700725 print "\nThere is no \""+main.TEST+"\" test script.\nPlease provide a " +\
726 "Python or OpenSpeak test script in the tests folder: " +\
727 main.testDir+"/" + main.TEST + "/"
adminbae64d82013-08-01 10:50:15 -0700728 __builtin__.testthread = None
729 main.exit()
adminbae64d82013-08-01 10:50:15 -0700730 try :
adminbae64d82013-08-01 10:50:15 -0700731 testModule = __import__(main.classPath, globals(), locals(), [main.TEST], -1)
732 except(ImportError):
Jon Hall44506242015-07-29 17:40:26 -0700733 print "There was an import error, it might mean that there is no test named "+main.TEST
Jon Halld61331b2015-02-17 16:35:47 -0800734 main.exit()
adminbae64d82013-08-01 10:50:15 -0700735
736 testClass = getattr(testModule, main.TEST)
737 main.testObject = testClass()
738 load_parser()
Jon Halld61331b2015-02-17 16:35:47 -0800739 main.params = main.parser.parseParams(main.classPath)
740 main.topology = main.parser.parseTopology(main.classPath)
kelvin-onlabf70fd542015-05-07 18:41:40 -0700741
adminbae64d82013-08-01 10:50:15 -0700742def verifyParams():
743 try :
744 main.params = main.params['PARAMS']
745 except(KeyError):
746 print "Error with the params file: Either the file not specified or the format is not correct"
Jon Halld61331b2015-02-17 16:35:47 -0800747 main.exit()
adminbae64d82013-08-01 10:50:15 -0700748 try :
749 main.topology = main.topology['TOPOLOGY']
750 except(KeyError):
751 print "Error with the Topology file: Either the file not specified or the format is not correct"
752 main.exit()
kelvin-onlabf70fd542015-05-07 18:41:40 -0700753
adminbae64d82013-08-01 10:50:15 -0700754def load_parser() :
755 '''
756 It facilitates the loading customised parser for topology and params file.
757 It loads parser mentioned in tab named parser of teston.cfg file.
758 It also loads default xmlparser if no parser have specified in teston.cfg file.
759
760 '''
761 confighash = main.configDict
762 if 'file' in confighash['config']['parser'] and 'class' in confighash['config']['parser']:
Jon Hall44506242015-07-29 17:40:26 -0700763 path = confighash['config']['parser']['file']
764 if path != None or confighash['config']['parser']['class']!= None:
765 try:
766 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700767 moduleList = module.split("/")
768 newModule = ".".join([moduleList[len(moduleList) - 2],moduleList[len(moduleList) - 1]])
Jon Hall44506242015-07-29 17:40:26 -0700769 parsingClass = confighash['config']['parser']['class']
770 parsingModule = __import__(newModule, globals(), locals(), [parsingClass], -1)
771 parsingClass = getattr(parsingModule, parsingClass)
772 main.parser = parsingClass()
773 #hashobj = main.parser.parseParams(main.classPath)
774 if hasattr(main.parser,"parseParams") and hasattr(main.parser,"parseTopology") and hasattr(main.parser,"parse"):
775 pass
776 else:
777 print "Invalid parser format"
adminbae64d82013-08-01 10:50:15 -0700778 main.exit()
Jon Hall44506242015-07-29 17:40:26 -0700779 except ImportError:
780 print "Could not find the file " + path + " using default parser."
Jon Halld61331b2015-02-17 16:35:47 -0800781 load_defaultParser()
Jon Hall44506242015-07-29 17:40:26 -0700782 elif confighash['config']['parser']['file'] == None or confighash['config']['parser']['class'] == None:
Jon Halld61331b2015-02-17 16:35:47 -0800783 load_defaultParser()
adminbae64d82013-08-01 10:50:15 -0700784 else:
785 load_defaultParser()
786
787def load_defaultParser():
788 '''
789 It will load the default parser which is xml parser to parse the params and topology file.
790 '''
791 moduleList = main.parserPath.split("/")
792 newModule = ".".join([moduleList[len(moduleList) - 2],moduleList[len(moduleList) - 1]])
793 try :
Jon Halld61331b2015-02-17 16:35:47 -0800794 parsingClass = main.parsingClass
adminbae64d82013-08-01 10:50:15 -0700795 parsingModule = __import__(newModule, globals(), locals(), [parsingClass], -1)
796 parsingClass = getattr(parsingModule, parsingClass)
797 main.parser = parsingClass()
798 if hasattr(main.parser,"parseParams") and hasattr(main.parser,"parseTopology") and hasattr(main.parser,"parse") :
799 pass
800 else:
801 main.exit()
802
803 except ImportError:
804 print sys.exc_info()[1]
805
adminbae64d82013-08-01 10:50:15 -0700806def load_logger() :
807 '''
808 It facilitates the loading customised parser for topology and params file.
809 It loads parser mentioned in tab named parser of teston.cfg file.
810 It also loads default xmlparser if no parser have specified in teston.cfg file.
811
812 '''
813 confighash = main.configDict
814 if 'file' in confighash['config']['logger'] and 'class' in confighash['config']['logger']:
Jon Hall44506242015-07-29 17:40:26 -0700815 path = confighash['config']['logger']['file']
816 if path != None or confighash['config']['logger']['class']!= None :
817 try:
818 module = re.sub( r".py\s*$", "", path )
adminbae64d82013-08-01 10:50:15 -0700819 moduleList = module.split("/")
820 newModule = ".".join([moduleList[len(moduleList) - 2],moduleList[len(moduleList) - 1]])
Jon Hall44506242015-07-29 17:40:26 -0700821 loggerClass = confighash['config']['logger']['class']
822 loggerModule = __import__(newModule, globals(), locals(), [loggerClass], -1)
823 loggerClass = getattr(loggerModule, loggerClass)
824 main.logger = loggerClass()
825 #hashobj = main.parser.parseParams(main.classPath)
826 except ImportError:
827 print "Could not find the file " + path + " using default logger."
adminbae64d82013-08-01 10:50:15 -0700828 load_defaultlogger()
Jon Halld61331b2015-02-17 16:35:47 -0800829 elif confighash['config']['parser']['file'] == None or confighash['config']['parser']['class'] == None :
830 load_defaultlogger()
adminbae64d82013-08-01 10:50:15 -0700831 else:
832 load_defaultlogger()
833
834def load_defaultlogger():
835 '''
836 It will load the default parser which is xml parser to parse the params and topology file.
837 '''
838 moduleList = main.loggerPath.split("/")
839 newModule = ".".join([moduleList[len(moduleList) - 2],moduleList[len(moduleList) - 1]])
840 try :
Jon Halld61331b2015-02-17 16:35:47 -0800841 loggerClass = main.loggerClass
adminbae64d82013-08-01 10:50:15 -0700842 loggerModule = __import__(newModule, globals(), locals(), [loggerClass], -1)
843 loggerClass = getattr(loggerModule, loggerClass)
844 main.logger = loggerClass()
845
846 except ImportError:
847 print sys.exc_info()[1]
Jon Halld61331b2015-02-17 16:35:47 -0800848 main.exit()
adminbae64d82013-08-01 10:50:15 -0700849
adminbae64d82013-08-01 10:50:15 -0700850def _echo(self):
851 print "THIS IS ECHO"