pymtt
 All Classes Namespaces Files Functions Variables Groups
pymtt.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #
3 # Copyright (c) 2015-2018 Intel, Inc. All rights reserved.
4 # Copyright (c) 2018 Cisco Systems, Inc. All rights reserved.
5 # $COPYRIGHT$
6 #
7 # Additional copyrights may follow
8 #
9 # $HEADER$
10 #
11 
12 
13 import os
14 import sys
15 import configparser
16 import importlib
17 import logging
18 import imp
19 from yapsy.PluginManager import PluginManager
20 import argparse
21 import shlex
22 
23 # First check for bozo error - we need to be given
24 # at least a cmd line option, so no params at all
25 # sounds like a plea for "help"
26 #if 1 == len(sys.argv):
27 # sys.exit('MTT usage error: add -h for help')
28 
29 # define the cmd line arguments
30 parser = argparse.ArgumentParser(
31  formatter_class=argparse.RawDescriptionHelpFormatter,
32  description='''\
33 Environment Variables:
34  MTT_HOME - this must be set to the top-level directory of your MTT installation.
35  MTT_ARGS - list of commandline arguments that you want set for each invocation.
36  Example: export MTT_ARGS="--verbose --log=/tmp/out.log"
37 ''')
38 
39 parser.add_argument('ini_files', action='append', metavar='FILE', nargs='*', help = ".ini file to be used")
40 
41 infoGroup = parser.add_argument_group('InfoGroup','Informational Options')
42 infoGroup.add_argument("-v", "--version",
43  action="store_true", dest="version", default=False,
44  help="Print version")
45 infoGroup.add_argument("--list-stages",
46  action="store_true", dest="listsections", default=False,
47  help="List section names understood by this client")
48 infoGroup.add_argument("--list-stage-plugins",
49  action="store", dest="listplugins", metavar="STAGE",
50  help="List available plugins for SECTION (* => all)")
51 infoGroup.add_argument("--list-stage-options",
52  action="store", dest="liststageoptions", metavar="STAGE",
53  help="List available options for STAGE (* => all)")
54 infoGroup.add_argument("--list-tools",
55  action="store_true", dest="listtools", default=False,
56  help="List tools available to this client")
57 infoGroup.add_argument("--list-tool-plugins",
58  action="store", dest="listtoolmodules", metavar="TYPE",
59  help="List available modules for TYPE (* => all)")
60 infoGroup.add_argument("--list-tool-options",
61  action="store", dest="listtooloptions", metavar="TOOL",
62  help="List available options for TOOL (* => all)")
63 infoGroup.add_argument("--list-utilities",
64  action="store_true", dest="listutils", default=False,
65  help="List utilities available to this client")
66 infoGroup.add_argument("--list-utility-plugins",
67  action="store", dest="listutilmodules", metavar="TYPE",
68  help="List available modules for TYPE (* => all)")
69 infoGroup.add_argument("--list-utility-options",
70  action="store", dest="listutiloptions", metavar="UTILITY",
71  help="List available options for UTILITY (* => all)")
72 
73 execGroup = parser.add_argument_group('execGroup', "Execution Options")
74 execGroup.add_argument("--description", dest="description",
75  help="Provide a brief title/description to be included in the log for this test")
76 execGroup.add_argument("-e", "--executor", dest="executor",
77  help="Use the specified execution STRATEGY module", metavar="STRATEGY")
78 execGroup.add_argument("--base-dir", dest="basedir",
79  help="Specify the DIRECTORY where we can find the TestDef class (checks DIRECTORY, DIRECTORY/Utilities, and DIRECTORY/pylib/Utilities locations) - also serves as default plugin-dir", metavar="DIRECTORY")
80 execGroup.add_argument("--plugin-dir", dest="plugindir",
81  help="Specify the DIRECTORY where additional plugins can be found (or comma-delimited list of DIRECTORYs)", metavar="DIRECTORY")
82 execGroup.add_argument("--ignore-loadpath-errors", action="store_true", dest="ignoreloadpatherrs", default=False,
83  help="Ignore errors in plugin paths")
84 execGroup.add_argument("--scratch-dir", dest="scratchdir", default=None,
85  help="Specify the DIRECTORY under which scratch files are to be stored", metavar="DIRECTORY")
86 execGroup.add_argument("--print-section-time", dest="sectime",
87  action="store_true", default=True,
88  help="Display section timestamps and execution time")
89 execGroup.add_argument("--print-cmd-time", dest="cmdtime",
90  action="store_true", default=False,
91  help="Display stdout/stderr timestamps and cmd execution time")
92 execGroup.add_argument("--timestamp", dest="time",
93  action="store_true", default=False,
94  help="Alias for --print-section-time --print-cmd-time")
95 execGroup.add_argument("--clean-start", dest="clean",
96  action="store_true",
97  help="Clean the scratch directory from past MTT invocations before running")
98 execGroup.add_argument("-c", "--cleanup", dest="clean_after",
99  action="store_true",
100  help="Clean the scratch directory after a successful run")
101 execGroup.add_argument("-s", "--sections", dest="section",
102  help="Execute the specified SECTION (or comma-delimited list of SECTIONs)", metavar="SECTION")
103 execGroup.add_argument("--skip-sections", dest="skipsections",
104  help="Skip the specified SECTION (or comma-delimited list of SECTIONs)", metavar="SECTION")
105 execGroup.add_argument("-l", "--log", dest="logfile", default=None,
106  help="Log all output to FILE (defaults to stdout)", metavar="FILE")
107 execGroup.add_argument("--group-results", dest="submit_group_results", default=True,
108  help="Report results from each test section as it is completed")
109 execGroup.add_argument("--default-make-options", dest="default_make_options", default="-j10",
110  help="Default options when running the \"make\" command")
111 execGroup.add_argument("--env-module-wrapper", dest="env_module_wrapper", default=None,
112  help="Python environment module wrapper")
113 execGroup.add_argument("--stop-on-fail", dest="stop_on_fail",
114  action="store_true", default=False,
115  help="If a stage fails, exit and issue a non-zero return code")
116 execGroup.add_argument("--duration",
117  dest="duration", default=None,
118  help="Add a maximum duration for test before interrupting.")
119 execGroup.add_argument("--loop",
120  dest="loop", default=None,
121  help="Causes MTT to loop until provided number of seconds finishes")
122 execGroup.add_argument("--loopforever", dest="loopforever",
123  action="store_true", default=False,
124  help="Causes MTT to continue to loop forever running same set of tests")
125 execGroup.add_argument("--harass_trigger",
126  dest="harass_trigger_scripts", default=None,
127  help="Paths to scripts that are run to harass the system while the test is running.")
128 execGroup.add_argument("--harass_stop",
129  dest="harass_stop_scripts", default=None,
130  help="Paths to scripts that are run to stop harassing the system after the test finishes.")
131 execGroup.add_argument("--harass_join_timeout",
132  dest="harass_join_timeout", default=None,
133  help="Number of seconds to wait while ending harass scripts. Default is infinity.")
134 
135 debugGroup = parser.add_argument_group('debugGroup', 'Debug Options')
136 debugGroup.add_argument("-d", "--debug", dest="debug",
137  action="store_true", default=False,
138  help="Output lots of debug messages")
139 debugGroup.add_argument("--verbose",
140  action="store_true", dest="verbose", default=False,
141  help="Output some status/verbose messages while processing")
142 debugGroup.add_argument("--extraverbose",
143  action="store_true", dest="extraverbose", default=False,
144  help="Output timestamps with every verbose message")
145 debugGroup.add_argument("--dryrun",
146  action="store_true", dest="dryrun", default=False,
147  help="Show commands, but do not execute them")
148 debugGroup.add_argument("--trial",
149  action="store_true", dest="trial", default=False,
150  help="Use when testing your MTT client setup; results that are generated and submitted to the database are marked as \"trials\" and are not included in normal reporting.")
151 
152 elkGroup = parser.add_argument_group('elkGroup','ELK-friendly output options')
153 elkGroup.add_argument("--elk_testcase", dest="elk_testcase",
154  default=os.environ['MTT_ELK_TESTCASE'] if 'MTT_ELK_TESTCASE' in os.environ else None,
155  help="Specifies which testcase to log results as when using elk-friendly output. You can also set this through the enviroment variable MTT_ELK_TESTCASE")
156 elkGroup.add_argument("--elk_testcycle", dest="elk_testcycle",
157  default=os.environ['MTT_ELK_TESTCYCLE'] if 'MTT_ELK_TESTCYCLE' in os.environ else None,
158  help="Specifies which testcycle to log results as when using elk-friendly output. You can also set this through the enviroment variable MTT_ELK_TESTCYCLE")
159 elkGroup.add_argument("--elk_id", dest="elk_id",
160  default=os.environ['MTT_ELK_ID'] if 'MTT_ELK_ID' in os.environ else None,
161  help="Specifies which execution id to log results as when using elk-friendly output. You can also set this through the enviroment variable MTT_ELK_ID")
162 elkGroup.add_argument("--elk_head", dest="elk_head",
163  default=os.environ['MTT_ELK_HEAD'] if 'MTT_ELK_HEAD' in os.environ else None,
164  help="Specifies which location to log <caseid>_<elk_id>.elog files for elk-friendly output. You can also set this through the environment variable MTT_ELK_HEAD")
165 elkGroup.add_argument("--elk_chown", dest="elk_chown",
166  default=os.environ['MTT_ELK_CHOWN'] if 'MTT_ELK_CHOWN' in os.environ else None,
167  help="Specifies what user and group to set for *.elog file permissions. You can also set this through the environment variable MTT_ELK_CHOWN")
168 elkGroup.add_argument("--elk_maxsize", dest="elk_maxsize",
169  default=os.environ['MTT_ELK_MAXSIZE'] if 'MTT_ELK_MAXSIZE' in os.environ else None,
170  help="Specifies a max number of lines to log for stdout and stderr in elk-friendly output, and otherwise truncate. You can also set this through the environment variable MTT_ELK_MAXSIZE")
171 elkGroup.add_argument("--elk_nostdout", dest="elk_nostdout", action="store_true",
172  default=True if 'MTT_ELK_NOSTDOUT' in os.environ and os.environ['MTT_ELK_NOSTDOUT'].lower() != 'false' else False,
173  help="Specifies whether to include stdout in elk-friendly output. You can also set this through the environment variable MTT_ELK_NOSTDOUT")
174 elkGroup.add_argument("--elk_nostderr", dest="elk_nostderr", action="store_true",
175  default=True if 'MTT_ELK_NOSTDERR' in os.environ and os.environ['MTT_ELK_NOSTDERR'].lower() != 'false' else False,
176  help="Specifies whether to include stdout in elk-friendly output. You can also set this through the environment variable MTT_ELK_NOSTDERR")
177 elkGroup.add_argument("--elk_debug", dest="elk_debug", action="store_true",
178  default=True if 'MTT_ELK_DEBUG' in os.environ and os.environ['MTT_ELK_DEBUG'].lower() != 'false' else False,
179  help="Specifies whether to output everything logged to *.elog files to the screen as extra verbose output. You can also set this through the environment variable MTT_ELK_DEBUG")
180 elkGroup.add_argument("--elk_hide_execmd", dest="elk_hide_execmd",
181  default=True if 'MTT_ELK_HIDE_EXECID' in os.environ and os.environ['MTT_ELK_HIDE_EXECID'].lower() != 'false' else False,
182  help="Comma delimited list of plugins and/or sections to hide execmd output from. Also set through environment variable MTT_ELK_HIDE_EXECMD. Use \"Default\" to hide execmd output when no plugin is specified.")
183 
184 args = parser.parse_args()
185 
186 # get any arguments set in MTT_ARGS environment variable and combine them with
187 # any set on the command line. Environment will override the commandline.
188 mttArgs = []
189 if 'MTT_ARGS' in os.environ and os.environ.get('MTT_ARGS') != None:
190  mttArgs = shlex.split(os.environ['MTT_ARGS'])
191  args = parser.parse_args(sys.argv[1:] + mttArgs)
192 
193 # check to see if MTT_HOME has been set - we require it
194 try:
195  mtthome = os.environ['MTT_HOME']
196 except KeyError:
197  print("MTT_HOME could not be found in your environment")
198  print("Python client requires that this be set and point")
199  print("to the top-level directory of your MTT installation")
200  sys.exit(1)
201 
202 # check to see if it is an absolute path, as we require
203 if not os.path.isabs(mtthome):
204  print("MTT_HOME environment variable:")
205  print(" ", mtthome)
206  print("is not an absolute path")
207  sys.exit(1)
208 
209 # check to see if MTT_HOME exists
210 if not os.path.exists(mtthome):
211  print("MTT_HOME points to a non-existent location:")
212  print(" ", mtthome)
213  print("Please correct")
214  sys.exit(1)
215 
216 # set topdir and check for existence
217 topdir = os.path.join(mtthome, "pylib")
218 if not os.path.exists(topdir) or not os.path.isdir(topdir):
219  print("MTT_HOME points to a location that does not\ninclude the \"pylib\" subdirectory:")
220  print(" ", topdir)
221  print("does not exist. Please correct")
222  sys.exit(1)
223 
224 # set basedir and check for existence
225 if args.basedir and not os.path.isabs(args.basedir):
226  print("The basedir cmd line option is not an absolute path:")
227  print(" ", args.basedir)
228  print("Please correct")
229  sys.exit(1)
230 
231 basedir = args.basedir or os.path.join(mtthome, "pylib", "System")
232 if not os.path.exists(basedir) or not os.path.isdir(basedir):
233  if basedir == args.basedir:
234  print("The basedir cmd line option points to a location that does not exist:")
235  print(" ", basedir)
236  print("Please correct")
237  else:
238  print("MTT_HOME points to a location that does not\ninclude the \"pylib/System\" subdirectory:")
239  print(" ", basedir)
240  print("does not exist. Please correct")
241  sys.exit(1)
242 
243 # if they want debug, set the logging level
244 if (args.debug):
245  logging.basicConfig(level=logging.DEBUG)
246 
247 # load the "testdef" Test Definition class so we can
248 # begin building this test
249 try:
250  m = imp.load_source("TestDef", os.path.join(basedir, "TestDef.py"))
251 except ImportError:
252  print("ERROR: unable to load TestDef that must contain the Test Definition object")
253  exit(1)
254 cls = getattr(m, "TestDef")
255 a = cls()
256 
257 # create the Test Definition object and set the
258 # options and arguments
259 testDef = a.__class__();
260 testDef.setOptions(args)
261 
262 # load the plugins for this test
263 testDef.loadPlugins(basedir, topdir)
264 
265 # provide an opportunity to print various requested
266 # outputs before starting to process the test
267 testDef.printInfo()
268 
269 # if they didn't specify any files, then there is nothing
270 # for us to do
271 if not args.ini_files or not args.ini_files[0]:
272  sys.exit('MTT requires at least one test-specification file')
273 
274 # sanity check a couple of options
275 if args.section and args.skipsections:
276  print("ERROR: Cannot both execute specific sections and specify sections to be skipped")
277  sys.exit(1)
278 
279 # open the logging file if given - otherwise, we log
280 # to stdout
281 testDef.openLogger()
282 
283 # open the elk logger if given, otherwise don't log to *.elog files
284 testDef.openElkLogger()
285 
286 # Read the input test definition file(s)
287 testDef.configTest()
288 
289 # Cli specified executor takes precedent over INI
290 # If there is nothing defined in either use fallback
291 fallback = "sequential"
292 executor = args.executor or testDef.config.get('MTTDefaults', 'executor', fallback=fallback)
293 
294 # Do not verify that executor exists now
295 # When the executor is loaded it ensures it exists
296 testDef.config.set('MTTDefaults', 'executor', executor.lower())
297 
298 status = testDef.executeTest(executor=executor.lower())
299 sys.exit(status)
tuple cls
Definition: pymtt.py:254