1
2 '''
3 Created on 20-Dec-2012
4
5 @author: Anil Kumar (anilkumar.s@paxterrasolutions.com)
6
7 cli will provide the CLI shell for teston framework.
8
9 '''
10
11
12 """
13 A simple command-line interface for TestON.
14
15 The TestON CLI provides a simple console which
16 makes it easy to launch the test. For example, the command run will execute the test.
17
18 teston> run test DpctlTest
19 Several useful commands are provided.
20 """
21
22 from subprocess import call
23 from cmd import Cmd
24 from os import isatty
25 import sys
26 import re
27 import os
28 import time
29 import threading
30 import __builtin__
31 import pprint
32 dump = pprint.PrettyPrinter(indent=4)
33 __builtin__.testthread = False
34 introduction = "TestON is the testing framework \nDeveloped by Paxterra Solutions (www.paxterrasolutions.com)"
35
36 path = re.sub("teston$", "", os.getcwd())
37 sys.path.append(path+"/Core")
38 sys.path.append("../")
39 from core.teston import *
40
41 -class CLI( threading.Thread,Cmd,object ):
42 "command-line interface to execute the test."
43
44 prompt = 'teston> '
45
46 - def __init__( self, teston, stdin=sys.stdin ):
47 self.teston = teston
48
49 self._mainevent = threading.Event()
50 threading.Thread.__init__(self)
51 self.main_stop = False
52 self.locals = { 'test': teston }
53 self.stdin = stdin
54 Cmd.__init__( self )
55 self.pause = False
56 self.stop = False
57 __builtin__.cli = self
58
60 "Don't repeat last command when you hit return."
61 pass
62
63 helpStr = (
64 " teston help"
65 )
66
68 "Describe available CLI commands."
69 Cmd.do_help( self, line )
70 if line is '':
71 output( self.helpStr )
73 '''
74 run command will execute the test with following optional command line arguments
75 logdir <directory to store logs in>
76 testcases <list of testcases separated by comma or range of testcases separated by hypen>
77 mail <mail-id or list of mail-ids seperated by comma>
78 example 1, to execute the examples specified in the ~/examples diretory.
79 '''
80 args = args.split()
81 options = {}
82 options = self.parseArgs(args,options)
83 options = dictToObj(options)
84 if not testthread:
85 test = TestThread(options)
86 test.start()
87 else :
88 print main.TEST+ " test execution paused, please resume that before executing to another test"
89
91 '''
92 resume command will continue the execution of paused test.
93 teston>resume
94 [2013-01-07 23:03:44.640723] [PoxTest] [STEP] 1.1: Checking the host reachability using pingHost
95 2013-01-07 23:03:44,858 - PoxTest - INFO - Expected Prompt Found
96 ....
97 '''
98 if testthread:
99 testthread.play()
100 else :
101 print "There is no test to resume"
102
104 '''
105 nextstep will execute the next-step of the paused test and
106 it will pause the test after finishing of step.
107
108 teston> nextstep
109 Will pause the test's execution, after completion of this step.....
110
111 teston> [2013-01-07 21:24:26.286601] [PoxTest] [STEP] 1.8: Checking the host reachability using pingHost
112 2013-01-07 21:24:26,455 - PoxTest - INFO - Expected Prompt Found
113 .....
114 teston>
115
116 '''
117 if testthread:
118 main.log.info("Executing the nextstep, Will pause test execution, after completion of the step")
119 testthread.play()
120 time.sleep(.1)
121 testthread.pause()
122 else:
123 print "There is no paused test "
124
126 '''
127 dumpvar will print all the test data in raw format.
128 usgae :
129 teston>dumpvar main
130 Here 'main' will be the test object.
131
132 teston>dumpvar params
133 here 'params' will be the parameters specified in the params file.
134
135 teston>dumpvar topology
136 here 'topology' will be topology specification of the test specified in topo file.
137 '''
138 if testthread:
139 if line == "main":
140 dump.pprint(vars(main))
141 else :
142 try :
143 dump.pprint(vars(main)[line])
144 except KeyError,e:
145 print e
146 else :
147 print "There is no paused test "
148
150 '''
151 currentcase will return the current case in the test execution.
152
153 teston>currentcase
154 Currently executing test case is: 2
155
156 '''
157 if testthread:
158 print "Currently executing test case is: "+str(main.CurrentTestCaseNumber)
159 else :
160 print "There is no paused test "
161
162
164 '''
165 currentstep will return the current step in the test execution.
166
167 teston>currentstep
168 Currently executing test step is: 2.3
169 '''
170 if testthread:
171 print "Currently executing test step is: "+str(main.CurrentTestCaseNumber)+'.'+str(main.stepCount)
172 else :
173 print "There is no paused test "
174
175
177 '''
178 Will stop the paused test, if any !
179 '''
180 if testthread:
181 testthread.stop()
182
183 return 'exited by user command'
184
186 '''
187 gettest will return the test name which is under execution or recently executed.
188
189 Test under execution:
190 teston>gettest
191 Currently executing Test is: PoxTest
192
193 Test recently executed:
194 Recently executed test is: MininetTest
195 '''
196 try :
197 if testthread :
198 print "Currently executing Test is: "+main.TEST
199 else :
200 print "Recently executed test is: "+main.TEST
201
202 except NameError:
203 print "There is no previously executed Test"
204
206 '''
207 showlog will show the test's Log
208 teston>showlog
209 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
210 .....
211 teston>showlog
212 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
213 .....
214 '''
215 try :
216 if testthread :
217 print "Currently executing Test's log is: "+main.LogFileName
218
219 else :
220 print "Last executed test's log is : "+main.LogFileName
221
222 logFile = main.LogFileName
223 logFileHandler = open(logFile, 'r')
224 for msg in logFileHandler.readlines() :
225 print msg,
226
227 logFileHandler.close()
228
229 except NameError:
230 print "There is no previously executed Test"
231
232
233
235 '''
236 This will parse the command line arguments.
237 '''
238 options = self.initOptions(options)
239 try :
240 for index, option in enumerate(args):
241 if index > 0 :
242 if re.match("logdir|mail|example|testdir|testcases", option, flags = 0):
243 index = index+1
244 options[option] = args[index]
245 options = self.testcasesInRange(index,option,args,options)
246 else :
247 options['testname'] = option
248 except IndexError,e:
249 print e
250
251 return options
252
254 '''
255 This will initialize the commandline options.
256 '''
257 options['logdir'] = None
258 options['mail'] = None
259 options['example'] = None
260 options['testdir'] = None
261 options['testcases'] = None
262 return options
263
265 '''
266 This method will handle testcases list,specified in range [1-10].
267 '''
268 if re.match("testcases",option,1):
269 testcases = []
270 args[index] = re.sub("\[|\]","",args[index],0)
271 m = re.match("(\d+)\-(\d+)",args[index],flags=0)
272 if m:
273 start_case = eval(m.group(1))
274 end_case = eval(m.group(2))
275 if (start_case <= end_case):
276 i = start_case
277 while i <= end_case:
278 testcases.append(i)
279 i= i+1
280 else :
281 print "Please specify testcases properly like 1-5"
282 else :
283 options[option] = args[index]
284 return options
285 options[option] = str(testcases)
286
287 return options
288
290 print introduction
291 while True:
292 try:
293 super(CLI, self).cmdloop(intro="")
294 self.postloop()
295 except KeyboardInterrupt:
296 testthread.pause()
297
299 '''
300 Echoing of given input.
301 '''
302 output(line)
303
304 - def do_sh( self, line ):
305 '''
306 Run an external shell command
307 sh pwd
308 sh ifconfig etc.
309 '''
310 call( line, shell=True )
311
312
313 - def do_py( self, line ):
314 '''
315 Evaluate a Python expression.
316
317 py main.log.info("Sample Log Information")
318 2013-01-07 12:07:26,804 - PoxTest - INFO - Sample Log Information
319
320 '''
321 try:
322 exec( line )
323 except Exception, e:
324 output( str( e ) + '\n' )
325
327 '''
328 interpret will translate the single line openspeak statement to equivalent python script.
329
330 teston> interpret ASSERT result EQUALS main.TRUE ONPASS "Ping executed successfully" ONFAIL "Ping failed"
331 utilities.assert_equals(expect=main.TRUE,actual=result,onpass="Ping executed successfully",onfail="Ping failed")
332
333 '''
334 from core import openspeak
335 ospk = openspeak.OpenSpeak()
336 try :
337 translated_code = ospk.interpret(text=line)
338 print translated_code
339 except AttributeError, e:
340 print 'Dynamic params are not allowed in single statement translations'
341
343 '''
344 Do will translate and execute the openspeak statement for the paused test.
345 do <OpenSpeak statement>
346 '''
347 if testthread:
348 from core import openspeak
349 ospk = openspeak.OpenSpeak()
350 try :
351 translated_code = ospk.interpret(text=line)
352 eval(translated_code)
353 except (AttributeError,SyntaxError), e:
354 print 'Dynamic params are not allowed in single statement translations'
355 else :
356 print "Do will translate and execute the openspeak statement for the paused test.\nPlease use interpret to translate the OpenSpeak statement."
357
359 '''
360 compile will translate the openspeak (.ospk) file into TestON test script (python).
361 It will receive the openspeak file path as input and will generate
362 equivalent test-script file in the same directory.
363
364 usage:
365 -----
366 teston>compile /home/openflow/TestON/PoxTest.ospk
367
368 Auto-generated test-script file is /home/openflow/TestON/PoxTest.py
369 '''
370 from core import openspeak
371 openspeak = openspeak.OpenSpeak()
372 openspeakfile = line
373 if os.path.exists(openspeakfile) :
374 openspeak.compiler(openspeakfile=openspeakfile,writetofile=1)
375 print "Auto-generated test-script file is "+ re.sub("ospk","py",openspeakfile,0)
376 else:
377 print 'There is no such file : '+line
378
380 "Exit"
381 if testthread:
382 testthread.stop()
383
384 sys.exit()
385
386 return 'exited by user command'
387
389 "Exit"
390 return self.do_exit( line )
391
396
398 "Is our standard input a tty?"
399 return isatty( self.stdin.fileno() )
400
402 '''
403 Read shell commands from an input file and execute them sequentially.
404 cmdsource.txt :
405
406 "pwd
407 ls "
408
409 teston>source /home/openflow/cmdsource.txt
410 /home/openflow/TestON/bin/
411 cli.py __init__.py
412
413 '''
414
415 args = line.split()
416 if len(args) != 1:
417 error( 'usage: source <file>\n' )
418 return
419 try:
420 self.inputFile = open( args[ 0 ] )
421 while True:
422 line = self.inputFile.readline()
423 if len( line ) > 0:
424 call( line, shell=True )
425 else:
426 break
427 except IOError:
428 error( 'error reading file %s\n' % args[ 0 ] )
429
431 '''
432 updatedriver will update the given driver name which exists into mentioned config file.
433 It will receive two optional arguments :
434
435 1. Config File Path
436 2. Drivers List to be updated.
437
438 Default : config file = "~/TestON/config/updatedriver" ,
439 Driver List = all drivers specified in config file .
440 '''
441 args = line.split()
442 config = ''
443 drivers = ''
444 try :
445 for index, option in enumerate(args):
446 if option == 'config':
447 index = index + 1
448 config = args[index]
449 elif option == 'drivers' :
450 index = index + 1
451 drivers = args[index]
452 except IndexError:
453 pass
454 import updatedriver
455 converter = updatedriver.UpdateDriver()
456
457 if config == '':
458 path = re.sub("(bin)$", "", os.getcwd())
459 config = path + "/config/updatedriver.cfg"
460 configDict = converter.configparser(config)
461
462 else :
463 converter.configparser(config)
464 configDict = converter.configparser(config)
465
466
467 converter.writeDriver(drivers)
468
469
470
471
473 "Measure time taken for any command in TestON."
474 start = time.time()
475 self.onecmd(line)
476 elapsed = time.time() - start
477 self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
478
480 """Called on an input line when the command prefix is not recognized."""
481 first, args, line = self.parseline( line )
482 if not args:
483 return
484 if args and len(args) > 0 and args[ -1 ] == '\n':
485 args = args[ :-1 ]
486 rest = args.split( ' ' )
487
488 error( '*** Unknown command: %s\n' % first )
489
490
491
493 '''
494 TestThread class will handle the test execution and will communicate with the thread in the do_run.
495 '''
497 self._stopevent = threading.Event()
498 threading.Thread.__init__(self)
499 self.is_stop = False
500 self.options = options
501 __builtin__.testthread = self
502
504 '''
505 Will execute the test.
506 '''
507 while not self.is_stop :
508 if not self._stopevent.isSet():
509 self.test_on = TestON(self.options)
510 try :
511 if self.test_on.init_result:
512 result = self.test_on.run()
513 if not self.is_stop :
514 result = self.test_on.cleanup()
515 self.is_stop = True
516 except(KeyboardInterrupt):
517 print "Recevied Interrupt,cleaning-up the logs and drivers before exiting"
518 result = self.test_on.cleanup()
519 self.is_stop = True
520
521 __builtin__.testthread = False
522
524 '''
525 Will pause the test.
526 '''
527 print "Will pause the test's execution, after completion of this step.....\n\n\n\n"
528 cli.pause = True
529 self._stopevent.set()
530
532 '''
533 Will resume the paused test.
534 '''
535 self._stopevent.clear()
536 cli.pause = False
537
539 '''
540 Will stop the test execution.
541 '''
542
543 print "Stopping the test"
544 self.is_stop = True
545 cli.stop = True
546 __builtin__.testthread = False
547
549 '''
550 Simply, print the message in console
551 '''
552 print msg
553
555 '''
556 print the error message.
557 '''
558 print msg
559
561 '''
562 This will facilitates the converting of the dictionary to the object.
563 This method will help to send options as object format to the test.
564 '''
565 if isinstance(dictionary, list):
566 dictionary = [dictToObj(x) for x in dictionary]
567 if not isinstance(dictionary, dict):
568 return dictionary
569 class Convert(object):
570 pass
571 obj = Convert()
572 for k in dictionary:
573 obj.__dict__[k] = dictToObj(dictionary[k])
574 return obj
575
576
577 if __name__ == '__main__':
578 if len(sys.argv) > 1:
579 CLI("test").onecmd(' '.join(sys.argv[1:]))
580 else:
581 CLI("test").cmdloop()
582