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