blob: f29a8ec97e23c9c8c5d222ae38ff65e6ec14e3bf [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)
YPZhang1c89e762016-06-29 10:43:58 -0700270 elif re.match("--params", option, flags=0):
271 # check if there is a params
272 index = index + 1
273 options['params'].append(args[index])
adminbae64d82013-08-01 10:50:15 -0700274 else :
275 options['testname'] = option
Jon Hall1306a562015-09-04 11:21:24 -0700276 except IndexError as e:
adminbae64d82013-08-01 10:50:15 -0700277 print e
Jon Hall4ba53f02015-07-29 13:07:41 -0700278
adminbae64d82013-08-01 10:50:15 -0700279 return options
Jon Hall4ba53f02015-07-29 13:07:41 -0700280
adminbae64d82013-08-01 10:50:15 -0700281 def initOptions(self,options):
282 '''
283 This will initialize the commandline options.
284 '''
285 options['logdir'] = None
286 options['mail'] = None
287 options['example'] = None
288 options['testdir'] = None
289 options['testcases'] = None
Hari Krishna03f530e2015-07-10 17:28:27 -0700290 options['onoscell'] = None
YPZhang1c89e762016-06-29 10:43:58 -0700291 # init params as a empty list
292 options['params'] = []
Jon Hall4ba53f02015-07-29 13:07:41 -0700293 return options
294
adminbae64d82013-08-01 10:50:15 -0700295 def testcasesInRange(self,index,option,args,options):
296 '''
297 This method will handle testcases list,specified in range [1-10].
298 '''
299 if re.match("testcases",option,1):
300 testcases = []
301 args[index] = re.sub("\[|\]","",args[index],0)
302 m = re.match("(\d+)\-(\d+)",args[index],flags=0)
303 if m:
304 start_case = eval(m.group(1))
305 end_case = eval(m.group(2))
306 if (start_case <= end_case):
307 i = start_case
308 while i <= end_case:
309 testcases.append(i)
Jon Hall4ba53f02015-07-29 13:07:41 -0700310 i= i+1
adminbae64d82013-08-01 10:50:15 -0700311 else :
312 print "Please specify testcases properly like 1-5"
313 else :
314 options[option] = args[index]
315 return options
316 options[option] = str(testcases)
Jon Hall4ba53f02015-07-29 13:07:41 -0700317
adminbae64d82013-08-01 10:50:15 -0700318 return options
Jon Hall4ba53f02015-07-29 13:07:41 -0700319
adminbae64d82013-08-01 10:50:15 -0700320 def cmdloop(self, intro=introduction):
321 print introduction
322 while True:
323 try:
324 super(CLI, self).cmdloop(intro="")
325 self.postloop()
326 except KeyboardInterrupt:
Jon Hall1306a562015-09-04 11:21:24 -0700327 if testthread:
328 testthread.pause()
329 else:
330 print "KeyboardInterrupt, Exiting."
331 sys.exit()
adminbae64d82013-08-01 10:50:15 -0700332
333 def do_echo( self, line ):
334 '''
335 Echoing of given input.
336 '''
337 output(line)
338
339 def do_sh( self, line ):
340 '''
341 Run an external shell command
342 sh pwd
343 sh ifconfig etc.
344 '''
345 call( line, shell=True )
346
347
348 def do_py( self, line ):
349 '''
350 Evaluate a Python expression.
Jon Hall4ba53f02015-07-29 13:07:41 -0700351
adminbae64d82013-08-01 10:50:15 -0700352 py main.log.info("Sample Log Information")
353 2013-01-07 12:07:26,804 - PoxTest - INFO - Sample Log Information
Jon Hall4ba53f02015-07-29 13:07:41 -0700354
adminbae64d82013-08-01 10:50:15 -0700355 '''
356 try:
357 exec( line )
Jon Hall1306a562015-09-04 11:21:24 -0700358 except Exception as e:
adminbae64d82013-08-01 10:50:15 -0700359 output( str( e ) + '\n' )
Jon Hall4ba53f02015-07-29 13:07:41 -0700360
adminbae64d82013-08-01 10:50:15 -0700361 def do_interpret(self,line):
362 '''
363 interpret will translate the single line openspeak statement to equivalent python script.
Jon Hall4ba53f02015-07-29 13:07:41 -0700364
adminbae64d82013-08-01 10:50:15 -0700365 teston> interpret ASSERT result EQUALS main.TRUE ONPASS "Ping executed successfully" ONFAIL "Ping failed"
366 utilities.assert_equals(expect=main.TRUE,actual=result,onpass="Ping executed successfully",onfail="Ping failed")
Jon Hall4ba53f02015-07-29 13:07:41 -0700367
adminbae64d82013-08-01 10:50:15 -0700368 '''
369 from core import openspeak
370 ospk = openspeak.OpenSpeak()
371 try :
372 translated_code = ospk.interpret(text=line)
373 print translated_code
Jon Hall1306a562015-09-04 11:21:24 -0700374 except AttributeError as e:
adminbae64d82013-08-01 10:50:15 -0700375 print 'Dynamic params are not allowed in single statement translations'
Jon Hall4ba53f02015-07-29 13:07:41 -0700376
adminbae64d82013-08-01 10:50:15 -0700377 def do_do (self,line):
378 '''
379 Do will translate and execute the openspeak statement for the paused test.
380 do <OpenSpeak statement>
381 '''
382 if testthread:
383 from core import openspeak
384 ospk = openspeak.OpenSpeak()
385 try :
386 translated_code = ospk.interpret(text=line)
387 eval(translated_code)
Jon Hall1306a562015-09-04 11:21:24 -0700388 except ( AttributeError, SyntaxError ) as e:
389 print 'Dynamic params are not allowed in single statement translations:'
390 print e
adminbae64d82013-08-01 10:50:15 -0700391 else :
392 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 -0700393
adminbae64d82013-08-01 10:50:15 -0700394 def do_compile(self,line):
395 '''
396 compile will translate the openspeak (.ospk) file into TestON test script (python).
Jon Hall4ba53f02015-07-29 13:07:41 -0700397 It will receive the openspeak file path as input and will generate
398 equivalent test-script file in the same directory.
399
adminbae64d82013-08-01 10:50:15 -0700400 usage:
401 -----
402 teston>compile /home/openflow/TestON/PoxTest.ospk
Jon Hall4ba53f02015-07-29 13:07:41 -0700403
adminbae64d82013-08-01 10:50:15 -0700404 Auto-generated test-script file is /home/openflow/TestON/PoxTest.py
405 '''
406 from core import openspeak
Jon Hall4ba53f02015-07-29 13:07:41 -0700407 openspeak = openspeak.OpenSpeak()
adminbae64d82013-08-01 10:50:15 -0700408 openspeakfile = line
409 if os.path.exists(openspeakfile) :
410 openspeak.compiler(openspeakfile=openspeakfile,writetofile=1)
411 print "Auto-generated test-script file is "+ re.sub("ospk","py",openspeakfile,0)
412 else:
413 print 'There is no such file : '+line
414
415 def do_exit( self, _line ):
416 "Exit"
417 if testthread:
418 testthread.stop()
Jon Hall4ba53f02015-07-29 13:07:41 -0700419
adminbae64d82013-08-01 10:50:15 -0700420 sys.exit()
421
422 return 'exited by user command'
423
424 def do_quit( self, line ):
425 "Exit"
426 return self.do_exit( line )
427
428 def do_EOF( self, line ):
429 "Exit"
430 output( '\n' )
431 return self.do_exit( line )
432
433 def isatty( self ):
434 "Is our standard input a tty?"
435 return isatty( self.stdin.fileno() )
436
437 def do_source( self, line ):
438 '''
439 Read shell commands from an input file and execute them sequentially.
440 cmdsource.txt :
Jon Hall4ba53f02015-07-29 13:07:41 -0700441
adminbae64d82013-08-01 10:50:15 -0700442 "pwd
443 ls "
Jon Hall4ba53f02015-07-29 13:07:41 -0700444
adminbae64d82013-08-01 10:50:15 -0700445 teston>source /home/openflow/cmdsource.txt
446 /home/openflow/TestON/bin/
447 cli.py __init__.py
Jon Hall4ba53f02015-07-29 13:07:41 -0700448
adminbae64d82013-08-01 10:50:15 -0700449 '''
Jon Hall4ba53f02015-07-29 13:07:41 -0700450
adminbae64d82013-08-01 10:50:15 -0700451 args = line.split()
452 if len(args) != 1:
453 error( 'usage: source <file>\n' )
454 return
455 try:
456 self.inputFile = open( args[ 0 ] )
457 while True:
458 line = self.inputFile.readline()
459 if len( line ) > 0:
460 call( line, shell=True )
461 else:
462 break
463 except IOError:
464 error( 'error reading file %s\n' % args[ 0 ] )
Jon Hall4ba53f02015-07-29 13:07:41 -0700465
adminbae64d82013-08-01 10:50:15 -0700466 def do_time( self, line ):
467 "Measure time taken for any command in TestON."
468 start = time.time()
469 self.onecmd(line)
470 elapsed = time.time() - start
471 self.stdout.write("*** Elapsed time: %0.6f secs\n" % elapsed)
472
473 def default( self, line ):
474 """Called on an input line when the command prefix is not recognized."""
475 first, args, line = self.parseline( line )
476 if not args:
477 return
478 if args and len(args) > 0 and args[ -1 ] == '\n':
479 args = args[ :-1 ]
480 rest = args.split( ' ' )
481
482 error( '*** Unknown command: %s\n' % first )
483
adminbae64d82013-08-01 10:50:15 -0700484class TestThread(threading.Thread):
485 '''
486 TestThread class will handle the test execution and will communicate with the thread in the do_run.
487 '''
488 def __init__(self,options):
489 self._stopevent = threading.Event()
490 threading.Thread.__init__(self)
491 self.is_stop = False
492 self.options = options
493 __builtin__.testthread = self
494
495 def run(self):
496 '''
497 Will execute the test.
498 '''
499 while not self.is_stop :
500 if not self._stopevent.isSet():
501 self.test_on = TestON(self.options)
502 try :
503 if self.test_on.init_result:
504 result = self.test_on.run()
505 if not self.is_stop :
506 result = self.test_on.cleanup()
507 self.is_stop = True
Jon Hall1306a562015-09-04 11:21:24 -0700508 except KeyboardInterrupt:
509 print "Recevied Interrupt, cleaning-up the logs and drivers before exiting"
adminbae64d82013-08-01 10:50:15 -0700510 result = self.test_on.cleanup()
511 self.is_stop = True
512
Jon Hall4ba53f02015-07-29 13:07:41 -0700513 __builtin__.testthread = False
adminbae64d82013-08-01 10:50:15 -0700514
515 def pause(self):
516 '''
517 Will pause the test.
518 '''
Jon Hall1306a562015-09-04 11:21:24 -0700519 if not cli.pause:
520 print "Will pause the test's execution, after completion of this step.....\n\n\n\n"
521 cli.pause = True
522 self._stopevent.set()
523 elif cli.pause and self.is_stop:
524 print "KeyboardInterrupt, Exiting."
525 self.test_on.exit()
526 else:
527 print "Recevied Interrupt, cleaning-up the logs and drivers before exiting"
528 result = self.test_on.cleanup()
529 self.is_stop = True
adminbae64d82013-08-01 10:50:15 -0700530
531 def play(self):
532 '''
533 Will resume the paused test.
534 '''
535 self._stopevent.clear()
536 cli.pause = False
Jon Hall4ba53f02015-07-29 13:07:41 -0700537
adminbae64d82013-08-01 10:50:15 -0700538 def stop(self):
539 '''
540 Will stop the test execution.
541 '''
Jon Hall4ba53f02015-07-29 13:07:41 -0700542
adminbae64d82013-08-01 10:50:15 -0700543 print "Stopping the test"
544 self.is_stop = True
545 cli.stop = True
546 __builtin__.testthread = False
Jon Hall4ba53f02015-07-29 13:07:41 -0700547
adminbae64d82013-08-01 10:50:15 -0700548def output(msg):
549 '''
550 Simply, print the message in console
551 '''
552 print msg
553
554def error(msg):
555 '''
556 print the error message.
557 '''
558 print msg
559
560def dictToObj(dictionary):
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
577if __name__ == '__main__':
578 if len(sys.argv) > 1:
Jon Hall0bde9ba2015-03-19 11:32:57 -0700579 __builtin__.COLORS = True
adminbae64d82013-08-01 10:50:15 -0700580 CLI("test").onecmd(' '.join(sys.argv[1:]))
581 else:
Jon Hall0bde9ba2015-03-19 11:32:57 -0700582 __builtin__.COLORS = False
adminbae64d82013-08-01 10:50:15 -0700583 CLI("test").cmdloop()