blob: a91052c2746798c72df3953d9419fa68a63e6f7a [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 20-Dec-2012
Jon Hall5f15fef2015-07-17 14:22:14 -07004
adminbae64d82013-08-01 10:50:15 -07005@author: Anil Kumar (anilkumar.s@paxterrasolutions.com)
6
7
8
9 TestON is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 2 of the License, or
12 (at your option) any later version.
13
14 TestON is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
Jon Hall4ba53f02015-07-29 13:07:41 -070020 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070021
22
23'''
24
25
26"""
27cli will provide the CLI shell for teston framework.
28
29A simple command-line interface for TestON.
30
31The TestON CLI provides a simple console which
32makes it easy to launch the test. For example, the command run will execute the test.
33
34teston> run test DpctlTest
35Several useful commands are provided.
36"""
37
38from subprocess import call
39from cmd import Cmd
40from os import isatty
41import sys
42import re
43import os
44import time
45import threading
46import __builtin__
47import pprint
48dump = pprint.PrettyPrinter(indent=4)
49__builtin__.testthread = False
50introduction = "TestON is the testing framework \nDeveloped by Paxterra Solutions (www.paxterrasolutions.com)"
Jon Hall0bde9ba2015-03-19 11:32:57 -070051__builtin__.COLORS = False
adminbae64d82013-08-01 10:50:15 -070052
Jon Hall1dd5a0a2015-07-08 10:49:26 -070053path = re.sub( "/bin$", "", sys.path[0] )
54sys.path.insert( 1, path )
Jon Hall0bde9ba2015-03-19 11:32:57 -070055from core.teston import *
adminbae64d82013-08-01 10:50:15 -070056
57class CLI( threading.Thread,Cmd,object ):
58 "command-line interface to execute the test."
59
60 prompt = 'teston> '
61
62 def __init__( self, teston, stdin=sys.stdin ):
63 self.teston = teston
Jon Hall0bde9ba2015-03-19 11:32:57 -070064
adminbae64d82013-08-01 10:50:15 -070065 self._mainevent = threading.Event()
66 threading.Thread.__init__(self)
67 self.main_stop = False
68 self.locals = { 'test': teston }
69 self.stdin = stdin
70 Cmd.__init__( self )
71 self.pause = False
72 self.stop = False
73 __builtin__.cli = self
74
75 def emptyline( self ):
76 "Don't repeat last command when you hit return."
77 pass
78
79 helpStr = (
80 " teston help"
81 )
82
83 def do_help( self, line ):
84 "Describe available CLI commands."
85 Cmd.do_help( self, line )
86 if line is '':
87 output( self.helpStr )
88 def do_run(self,args):
89 '''
90 run command will execute the test with following optional command line arguments
91 logdir <directory to store logs in>
92 testcases <list of testcases separated by comma or range of testcases separated by hypen>
93 mail <mail-id or list of mail-ids seperated by comma>
94 example 1, to execute the examples specified in the ~/examples diretory.
95 '''
Jon Hall1306a562015-09-04 11:21:24 -070096 try:
97 args = args.split()
98 options = {}
99 options = self.parseArgs(args,options)
100 options = dictToObj(options)
101 if not testthread:
102 test = TestThread(options)
103 test.start()
104 while test.isAlive():
105 test.join(1)
106 else:
107 print main.TEST+ " test execution paused, please resume that before executing to another test"
108 except KeyboardInterrupt, SystemExit:
109 print "Interrupt called, Exiting."
110 test._Thread__stop()
111 main.cleanup()
112 main.exit()
Jon Hall4ba53f02015-07-29 13:07:41 -0700113
adminbae64d82013-08-01 10:50:15 -0700114 def do_resume(self, line):
115 '''
116 resume command will continue the execution of paused test.
117 teston>resume
118 [2013-01-07 23:03:44.640723] [PoxTest] [STEP] 1.1: Checking the host reachability using pingHost
119 2013-01-07 23:03:44,858 - PoxTest - INFO - Expected Prompt Found
120 ....
121 '''
122 if testthread:
123 testthread.play()
124 else :
125 print "There is no test to resume"
Jon Hall4ba53f02015-07-29 13:07:41 -0700126
adminbae64d82013-08-01 10:50:15 -0700127 def do_nextstep(self,line):
128 '''
Jon Hall4ba53f02015-07-29 13:07:41 -0700129 nextstep will execute the next-step of the paused test and
adminbae64d82013-08-01 10:50:15 -0700130 it will pause the test after finishing of step.
Jon Hall4ba53f02015-07-29 13:07:41 -0700131
adminbae64d82013-08-01 10:50:15 -0700132 teston> nextstep
133 Will pause the test's execution, after completion of this step.....
Jon Hall4ba53f02015-07-29 13:07:41 -0700134
adminbae64d82013-08-01 10:50:15 -0700135 teston> [2013-01-07 21:24:26.286601] [PoxTest] [STEP] 1.8: Checking the host reachability using pingHost
136 2013-01-07 21:24:26,455 - PoxTest - INFO - Expected Prompt Found
137 .....
138 teston>
Jon Hall4ba53f02015-07-29 13:07:41 -0700139
adminbae64d82013-08-01 10:50:15 -0700140 '''
141 if testthread:
142 main.log.info("Executing the nextstep, Will pause test execution, after completion of the step")
143 testthread.play()
144 time.sleep(.1)
145 testthread.pause()
146 else:
147 print "There is no paused test "
Jon Hall4ba53f02015-07-29 13:07:41 -0700148
adminbae64d82013-08-01 10:50:15 -0700149 def do_dumpvar(self,line):
150 '''
151 dumpvar will print all the test data in raw format.
Jon Hall4ba53f02015-07-29 13:07:41 -0700152 usgae :
adminbae64d82013-08-01 10:50:15 -0700153 teston>dumpvar main
154 Here 'main' will be the test object.
Jon Hall4ba53f02015-07-29 13:07:41 -0700155
156 teston>dumpvar params
adminbae64d82013-08-01 10:50:15 -0700157 here 'params' will be the parameters specified in the params file.
Jon Hall4ba53f02015-07-29 13:07:41 -0700158
adminbae64d82013-08-01 10:50:15 -0700159 teston>dumpvar topology
160 here 'topology' will be topology specification of the test specified in topo file.
161 '''
162 if testthread:
163 if line == "main":
164 dump.pprint(vars(main))
165 else :
166 try :
167 dump.pprint(vars(main)[line])
Jon Hall1306a562015-09-04 11:21:24 -0700168 except KeyError as e:
adminbae64d82013-08-01 10:50:15 -0700169 print e
170 else :
171 print "There is no paused test "
Jon Hall4ba53f02015-07-29 13:07:41 -0700172
adminbae64d82013-08-01 10:50:15 -0700173 def do_currentcase(self,line):
174 '''
175 currentcase will return the current case in the test execution.
Jon Hall4ba53f02015-07-29 13:07:41 -0700176
adminbae64d82013-08-01 10:50:15 -0700177 teston>currentcase
178 Currently executing test case is: 2
Jon Hall4ba53f02015-07-29 13:07:41 -0700179
adminbae64d82013-08-01 10:50:15 -0700180 '''
181 if testthread:
182 print "Currently executing test case is: "+str(main.CurrentTestCaseNumber)
183 else :
184 print "There is no paused test "
Jon Hall4ba53f02015-07-29 13:07:41 -0700185
186
adminbae64d82013-08-01 10:50:15 -0700187 def do_currentstep(self,line):
188 '''
189 currentstep will return the current step in the test execution.
Jon Hall4ba53f02015-07-29 13:07:41 -0700190
adminbae64d82013-08-01 10:50:15 -0700191 teston>currentstep
192 Currently executing test step is: 2.3
193 '''
194 if testthread:
195 print "Currently executing test step is: "+str(main.CurrentTestCaseNumber)+'.'+str(main.stepCount)
196 else :
197 print "There is no paused test "
Jon Hall4ba53f02015-07-29 13:07:41 -0700198
199
adminbae64d82013-08-01 10:50:15 -0700200 def do_stop(self,line):
201 '''
202 Will stop the paused test, if any !
203 '''
204 if testthread:
205 testthread.stop()
Jon Hall4ba53f02015-07-29 13:07:41 -0700206
adminbae64d82013-08-01 10:50:15 -0700207 return 'exited by user command'
Jon Hall4ba53f02015-07-29 13:07:41 -0700208
adminbae64d82013-08-01 10:50:15 -0700209 def do_gettest(self,line):
210 '''
211 gettest will return the test name which is under execution or recently executed.
Jon Hall4ba53f02015-07-29 13:07:41 -0700212
adminbae64d82013-08-01 10:50:15 -0700213 Test under execution:
Jon Hall4ba53f02015-07-29 13:07:41 -0700214 teston>gettest
adminbae64d82013-08-01 10:50:15 -0700215 Currently executing Test is: PoxTest
Jon Hall4ba53f02015-07-29 13:07:41 -0700216
adminbae64d82013-08-01 10:50:15 -0700217 Test recently executed:
218 Recently executed test is: MininetTest
219 '''
220 try :
221 if testthread :
222 print "Currently executing Test is: "+main.TEST
223 else :
224 print "Recently executed test is: "+main.TEST
Jon Hall4ba53f02015-07-29 13:07:41 -0700225
adminbae64d82013-08-01 10:50:15 -0700226 except NameError:
227 print "There is no previously executed Test"
Jon Hall4ba53f02015-07-29 13:07:41 -0700228
adminbae64d82013-08-01 10:50:15 -0700229 def do_showlog(self,line):
230 '''
231 showlog will show the test's Log
232 teston>showlog
233 Last executed test's log is : //home/openflow/TestON/logs/PoxTest_07_Jan_2013_21_42_11/PoxTest_07_Jan_2013_21_42_11.log
234 .....
235 teston>showlog
236 Currently executing Test's log is: /home/openflow/TestON/logs/PoxTest_07_Jan_2013_21_46_58/PoxTest_07_Jan_2013_21_46_58.log
237 .....
238 '''
239 try :
240 if testthread :
241 print "Currently executing Test's log is: "+main.LogFileName
Jon Hall4ba53f02015-07-29 13:07:41 -0700242
adminbae64d82013-08-01 10:50:15 -0700243 else :
244 print "Last executed test's log is : "+main.LogFileName
Jon Hall4ba53f02015-07-29 13:07:41 -0700245
adminbae64d82013-08-01 10:50:15 -0700246 logFile = main.LogFileName
247 logFileHandler = open(logFile, 'r')
248 for msg in logFileHandler.readlines() :
249 print msg,
Jon Hall4ba53f02015-07-29 13:07:41 -0700250
adminbae64d82013-08-01 10:50:15 -0700251 logFileHandler.close()
Jon Hall4ba53f02015-07-29 13:07:41 -0700252
adminbae64d82013-08-01 10:50:15 -0700253 except NameError:
254 print "There is no previously executed Test"
Jon Hall4ba53f02015-07-29 13:07:41 -0700255
256
257
adminbae64d82013-08-01 10:50:15 -0700258 def parseArgs(self,args,options):
259 '''
260 This will parse the command line arguments.
261 '''
262 options = self.initOptions(options)
263 try :
264 for index, option in enumerate(args):
265 if index > 0 :
Hari Krishna03f530e2015-07-10 17:28:27 -0700266 if re.match("logdir|mail|example|testdir|testcases|onoscell", option, flags = 0):
adminbae64d82013-08-01 10:50:15 -0700267 index = index+1
268 options[option] = args[index]
269 options = self.testcasesInRange(index,option,args,options)
270 else :
271 options['testname'] = option
Jon Hall1306a562015-09-04 11:21:24 -0700272 except IndexError as e:
adminbae64d82013-08-01 10:50:15 -0700273 print e
Jon Hall4ba53f02015-07-29 13:07:41 -0700274
adminbae64d82013-08-01 10:50:15 -0700275 return options
Jon Hall4ba53f02015-07-29 13:07:41 -0700276
adminbae64d82013-08-01 10:50:15 -0700277 def initOptions(self,options):
278 '''
279 This will initialize the commandline options.
280 '''
281 options['logdir'] = None
282 options['mail'] = None
283 options['example'] = None
284 options['testdir'] = None
285 options['testcases'] = None
Hari Krishna03f530e2015-07-10 17:28:27 -0700286 options['onoscell'] = None
Jon Hall4ba53f02015-07-29 13:07:41 -0700287 return options
288
adminbae64d82013-08-01 10:50:15 -0700289 def testcasesInRange(self,index,option,args,options):
290 '''
291 This method will handle testcases list,specified in range [1-10].
292 '''
293 if re.match("testcases",option,1):
294 testcases = []
295 args[index] = re.sub("\[|\]","",args[index],0)
296 m = re.match("(\d+)\-(\d+)",args[index],flags=0)
297 if m:
298 start_case = eval(m.group(1))
299 end_case = eval(m.group(2))
300 if (start_case <= end_case):
301 i = start_case
302 while i <= end_case:
303 testcases.append(i)
Jon Hall4ba53f02015-07-29 13:07:41 -0700304 i= i+1
adminbae64d82013-08-01 10:50:15 -0700305 else :
306 print "Please specify testcases properly like 1-5"
307 else :
308 options[option] = args[index]
309 return options
310 options[option] = str(testcases)
Jon Hall4ba53f02015-07-29 13:07:41 -0700311
adminbae64d82013-08-01 10:50:15 -0700312 return options
Jon Hall4ba53f02015-07-29 13:07:41 -0700313
adminbae64d82013-08-01 10:50:15 -0700314 def cmdloop(self, intro=introduction):
315 print introduction
316 while True:
317 try:
318 super(CLI, self).cmdloop(intro="")
319 self.postloop()
320 except KeyboardInterrupt:
Jon Hall1306a562015-09-04 11:21:24 -0700321 if testthread:
322 testthread.pause()
323 else:
324 print "KeyboardInterrupt, Exiting."
325 sys.exit()
adminbae64d82013-08-01 10:50:15 -0700326
327 def do_echo( self, line ):
328 '''
329 Echoing of given input.
330 '''
331 output(line)
332
333 def do_sh( self, line ):
334 '''
335 Run an external shell command
336 sh pwd
337 sh ifconfig etc.
338 '''
339 call( line, shell=True )
340
341
342 def do_py( self, line ):
343 '''
344 Evaluate a Python expression.
Jon Hall4ba53f02015-07-29 13:07:41 -0700345
adminbae64d82013-08-01 10:50:15 -0700346 py main.log.info("Sample Log Information")
347 2013-01-07 12:07:26,804 - PoxTest - INFO - Sample Log Information
Jon Hall4ba53f02015-07-29 13:07:41 -0700348
adminbae64d82013-08-01 10:50:15 -0700349 '''
350 try:
351 exec( line )
Jon Hall1306a562015-09-04 11:21:24 -0700352 except Exception as e:
adminbae64d82013-08-01 10:50:15 -0700353 output( str( e ) + '\n' )
Jon Hall4ba53f02015-07-29 13:07:41 -0700354
adminbae64d82013-08-01 10:50:15 -0700355 def do_interpret(self,line):
356 '''
357 interpret will translate the single line openspeak statement to equivalent python script.
Jon Hall4ba53f02015-07-29 13:07:41 -0700358
adminbae64d82013-08-01 10:50:15 -0700359 teston> interpret ASSERT result EQUALS main.TRUE ONPASS "Ping executed successfully" ONFAIL "Ping failed"
360 utilities.assert_equals(expect=main.TRUE,actual=result,onpass="Ping executed successfully",onfail="Ping failed")
Jon Hall4ba53f02015-07-29 13:07:41 -0700361
adminbae64d82013-08-01 10:50:15 -0700362 '''
363 from core import openspeak
364 ospk = openspeak.OpenSpeak()
365 try :
366 translated_code = ospk.interpret(text=line)
367 print translated_code
Jon Hall1306a562015-09-04 11:21:24 -0700368 except AttributeError as e:
adminbae64d82013-08-01 10:50:15 -0700369 print 'Dynamic params are not allowed in single statement translations'
Jon Hall4ba53f02015-07-29 13:07:41 -0700370
adminbae64d82013-08-01 10:50:15 -0700371 def do_do (self,line):
372 '''
373 Do will translate and execute the openspeak statement for the paused test.
374 do <OpenSpeak statement>
375 '''
376 if testthread:
377 from core import openspeak
378 ospk = openspeak.OpenSpeak()
379 try :
380 translated_code = ospk.interpret(text=line)
381 eval(translated_code)
Jon Hall1306a562015-09-04 11:21:24 -0700382 except ( AttributeError, SyntaxError ) as e:
383 print 'Dynamic params are not allowed in single statement translations:'
384 print e
adminbae64d82013-08-01 10:50:15 -0700385 else :
386 print "Do will translate and execute the openspeak statement for the paused test.\nPlease use interpret to translate the OpenSpeak statement."
Jon Hall4ba53f02015-07-29 13:07:41 -0700387
adminbae64d82013-08-01 10:50:15 -0700388 def do_compile(self,line):
389 '''
390 compile will translate the openspeak (.ospk) file into TestON test script (python).
Jon Hall4ba53f02015-07-29 13:07:41 -0700391 It will receive the openspeak file path as input and will generate
392 equivalent test-script file in the same directory.
393
adminbae64d82013-08-01 10:50:15 -0700394 usage:
395 -----
396 teston>compile /home/openflow/TestON/PoxTest.ospk
Jon Hall4ba53f02015-07-29 13:07:41 -0700397
adminbae64d82013-08-01 10:50:15 -0700398 Auto-generated test-script file is /home/openflow/TestON/PoxTest.py
399 '''
400 from core import openspeak
Jon Hall4ba53f02015-07-29 13:07:41 -0700401 openspeak = openspeak.OpenSpeak()
adminbae64d82013-08-01 10:50:15 -0700402 openspeakfile = line
403 if os.path.exists(openspeakfile) :
404 openspeak.compiler(openspeakfile=openspeakfile,writetofile=1)
405 print "Auto-generated test-script file is "+ re.sub("ospk","py",openspeakfile,0)
406 else:
407 print 'There is no such file : '+line
408
409 def do_exit( self, _line ):
410 "Exit"
411 if testthread:
412 testthread.stop()
Jon Hall4ba53f02015-07-29 13:07:41 -0700413
adminbae64d82013-08-01 10:50:15 -0700414 sys.exit()
415
416 return 'exited by user command'
417
418 def do_quit( self, line ):
419 "Exit"
420 return self.do_exit( line )
421
422 def do_EOF( self, line ):
423 "Exit"
424 output( '\n' )
425 return self.do_exit( line )
426
427 def isatty( self ):
428 "Is our standard input a tty?"
429 return isatty( self.stdin.fileno() )
430
431 def do_source( self, line ):
432 '''
433 Read shell commands from an input file and execute them sequentially.
434 cmdsource.txt :
Jon Hall4ba53f02015-07-29 13:07:41 -0700435
adminbae64d82013-08-01 10:50:15 -0700436 "pwd
437 ls "
Jon Hall4ba53f02015-07-29 13:07:41 -0700438
adminbae64d82013-08-01 10:50:15 -0700439 teston>source /home/openflow/cmdsource.txt
440 /home/openflow/TestON/bin/
441 cli.py __init__.py
Jon Hall4ba53f02015-07-29 13:07:41 -0700442
adminbae64d82013-08-01 10:50:15 -0700443 '''
Jon Hall4ba53f02015-07-29 13:07:41 -0700444
adminbae64d82013-08-01 10:50:15 -0700445 args = line.split()
446 if len(args) != 1:
447 error( 'usage: source <file>\n' )
448 return
449 try:
450 self.inputFile = open( args[ 0 ] )
451 while True:
452 line = self.inputFile.readline()
453 if len( line ) > 0:
454 call( line, shell=True )
455 else:
456 break
457 except IOError:
458 error( 'error reading file %s\n' % args[ 0 ] )
Jon Hall4ba53f02015-07-29 13:07:41 -0700459
adminbae64d82013-08-01 10:50:15 -0700460 def do_time( self, line ):
461 "Measure time taken for any command in TestON."
462 start = time.time()
463 self.onecmd(line)
464 elapsed = time.time() - start
465 self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
466
467 def default( self, line ):
468 """Called on an input line when the command prefix is not recognized."""
469 first, args, line = self.parseline( line )
470 if not args:
471 return
472 if args and len(args) > 0 and args[ -1 ] == '\n':
473 args = args[ :-1 ]
474 rest = args.split( ' ' )
475
476 error( '*** Unknown command: %s\n' % first )
477
adminbae64d82013-08-01 10:50:15 -0700478class TestThread(threading.Thread):
479 '''
480 TestThread class will handle the test execution and will communicate with the thread in the do_run.
481 '''
482 def __init__(self,options):
483 self._stopevent = threading.Event()
484 threading.Thread.__init__(self)
485 self.is_stop = False
486 self.options = options
487 __builtin__.testthread = self
488
489 def run(self):
490 '''
491 Will execute the test.
492 '''
493 while not self.is_stop :
494 if not self._stopevent.isSet():
495 self.test_on = TestON(self.options)
496 try :
497 if self.test_on.init_result:
498 result = self.test_on.run()
499 if not self.is_stop :
500 result = self.test_on.cleanup()
501 self.is_stop = True
Jon Hall1306a562015-09-04 11:21:24 -0700502 except KeyboardInterrupt:
503 print "Recevied Interrupt, cleaning-up the logs and drivers before exiting"
adminbae64d82013-08-01 10:50:15 -0700504 result = self.test_on.cleanup()
505 self.is_stop = True
506
Jon Hall4ba53f02015-07-29 13:07:41 -0700507 __builtin__.testthread = False
adminbae64d82013-08-01 10:50:15 -0700508
509 def pause(self):
510 '''
511 Will pause the test.
512 '''
Jon Hall1306a562015-09-04 11:21:24 -0700513 if not cli.pause:
514 print "Will pause the test's execution, after completion of this step.....\n\n\n\n"
515 cli.pause = True
516 self._stopevent.set()
517 elif cli.pause and self.is_stop:
518 print "KeyboardInterrupt, Exiting."
519 self.test_on.exit()
520 else:
521 print "Recevied Interrupt, cleaning-up the logs and drivers before exiting"
522 result = self.test_on.cleanup()
523 self.is_stop = True
adminbae64d82013-08-01 10:50:15 -0700524
525 def play(self):
526 '''
527 Will resume the paused test.
528 '''
529 self._stopevent.clear()
530 cli.pause = False
Jon Hall4ba53f02015-07-29 13:07:41 -0700531
adminbae64d82013-08-01 10:50:15 -0700532 def stop(self):
533 '''
534 Will stop the test execution.
535 '''
Jon Hall4ba53f02015-07-29 13:07:41 -0700536
adminbae64d82013-08-01 10:50:15 -0700537 print "Stopping the test"
538 self.is_stop = True
539 cli.stop = True
540 __builtin__.testthread = False
Jon Hall4ba53f02015-07-29 13:07:41 -0700541
adminbae64d82013-08-01 10:50:15 -0700542def output(msg):
543 '''
544 Simply, print the message in console
545 '''
546 print msg
547
548def error(msg):
549 '''
550 print the error message.
551 '''
552 print msg
553
554def dictToObj(dictionary):
555 '''
556 This will facilitates the converting of the dictionary to the object.
557 This method will help to send options as object format to the test.
558 '''
559 if isinstance(dictionary, list):
560 dictionary = [dictToObj(x) for x in dictionary]
561 if not isinstance(dictionary, dict):
562 return dictionary
563 class Convert(object):
564 pass
565 obj = Convert()
566 for k in dictionary:
567 obj.__dict__[k] = dictToObj(dictionary[k])
568 return obj
569
570
571if __name__ == '__main__':
572 if len(sys.argv) > 1:
Jon Hall0bde9ba2015-03-19 11:32:57 -0700573 __builtin__.COLORS = True
adminbae64d82013-08-01 10:50:15 -0700574 CLI("test").onecmd(' '.join(sys.argv[1:]))
575 else:
Jon Hall0bde9ba2015-03-19 11:32:57 -0700576 __builtin__.COLORS = False
adminbae64d82013-08-01 10:50:15 -0700577 CLI("test").cmdloop()