blob: ddc3517e4a1904e41f41dc733c35923057a4113b [file] [log] [blame]
Michael Walsh3ba8ecd2018-04-24 11:33:25 -05001#!/usr/bin/env python
2
3r"""
4This module provides functions which are useful to plug-ins call-point
5programs that wish to make external robot program calls.
6"""
7
8import sys
9import os
10import subprocess
11import re
12import time
13import imp
14
15import gen_print as gp
16import gen_valid as gv
17import gen_misc as gm
18import gen_cmd as gc
19
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050020base_path = \
21 os.path.dirname(os.path.dirname(imp.find_module("gen_robot_print")[1])) +\
22 os.sep
23
24
25def init_robot_out_parms(extra_prefix=""):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050026 r"""
27 Initialize robot output parms such as outputdir, output, etc.
28
29 This function will set global values for the following robot output parms.
30
31 outputdir, output, log, report, loglevel
32
33 This function would typically be called prior to calling
34 create_robot_cmd_string.
35 """
36
Michael Walsha0ce75a2018-07-31 13:54:29 -050037 gp.dprint_executing()
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050038 AUTOBOOT_OPENBMC_NICKNAME = gm.get_mod_global("AUTOBOOT_OPENBMC_NICKNAME")
39
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050040 # Set values for call to create_robot_cmd_string.
Michael Walsha0ce75a2018-07-31 13:54:29 -050041 # Environment variable TMP_ROBOT_DIR_PATH can be set by the user to
42 # indicate that robot-generated output should initially be written to the
43 # specified temporary directory and then moved to the normal output
44 # location after completion.
45 outputdir =\
46 os.environ.get("TMP_ROBOT_DIR_PATH",
47 os.environ.get("STATUS_DIR_PATH",
48 os.environ.get("HOME", ".")
49 + "/autoipl/status"))
50 outputdir = gm.add_trailing_slash(outputdir)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050051 seconds = time.time()
52 loc_time = time.localtime(seconds)
53 time_string = time.strftime("%y%m%d.%H%M%S", loc_time)
54 file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "." + extra_prefix +\
55 time_string + "."
Michael Walsha0ce75a2018-07-31 13:54:29 -050056 # Environment variable SAVE_STATUS_POLICY governs when robot-generated
57 # output files (e.g. the log.html) will be moved from TMP_ROBOT_DIR_PATH
58 # to FFDC_DIR_PATH. Valid values are "ALWAYS", "NEVER" and "FAIL".
59 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS")
60 if SAVE_STATUS_POLICY == "NEVER":
61 output = "NONE"
62 log = "NONE"
63 report = "NONE"
64 else:
65 output = file_prefix + "output.xml"
66 log = file_prefix + "log.html"
67 report = file_prefix + "report.html"
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050068 loglevel = "TRACE"
69
70 # Make create_robot_cmd_string values global.
71 gm.set_mod_global(outputdir)
72 gm.set_mod_global(output)
73 gm.set_mod_global(log)
74 gm.set_mod_global(report)
75 gm.set_mod_global(loglevel)
76
77
78def init_robot_test_base_dir_path():
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050079 r"""
80 Initialize and validate the environment variable, ROBOT_TEST_BASE_DIR_PATH
81 and set corresponding global variable ROBOT_TEST_RUNNING_FROM_SB.
82
83 If ROBOT_TEST_BASE_DIR_PATH is already set, this function will merely
84 validate it. This function will also set environment variable
85 ROBOT_TEST_RUNNING_FROM_SB when ROBOT_TEST_BASE_DIR_PATH is not pre-set.
86 """
87
88 # ROBOT_TEST_BASE_DIR_PATH will be set as follows:
89 # This function will determine whether we are running in a user sandbox
90 # or from a standard apolloxxx environment.
91 # - User sandbox:
92 # If there is a <developer's home dir>/git/openbmc-test-automation/,
93 # ROBOT_TEST_BASE_DIR_PATH will be set to that path. Otherwise, we set it
94 # to <program dir path>/git/openbmc-test-automation/
95 # - Not in user sandbox:
96 # ROBOT_TEST_BASE_DIR_PATH will be set to <program dir
97 # path>/git/openbmc-test-automation/
98
99 ROBOT_TEST_BASE_DIR_PATH = os.environ.get('ROBOT_TEST_BASE_DIR_PATH', "")
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500100 ROBOT_TEST_RUNNING_FROM_SB = \
101 int(os.environ.get('ROBOT_TEST_RUNNING_FROM_SB', "0"))
102 if ROBOT_TEST_BASE_DIR_PATH == "":
103 # ROBOT_TEST_BASE_DIR_PATH was not set by user/caller.
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500104 AUTOIPL_VERSION = os.environ.get('AUTOIPL_VERSION', '')
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500105 if AUTOIPL_VERSION == "":
106 ROBOT_TEST_BASE_DIR_PATH = base_path
107 else:
108 suffix = "git/openbmc-test-automation/"
109
110 # Determine whether we're running out of a developer sandbox or
111 # simply out of an apolloxxx/bin path.
Michael Walshbffaa1d2018-06-08 15:09:27 -0500112 shell_rc, out_buf = gc.shell_cmd('dirname $(which gen_print.py)',
113 quiet=(not debug), print_output=0)
114 executable_base_dir_path = os.path.realpath(out_buf.rstrip()) + "/"
115 apollo_dir_path = os.environ['AUTO_BASE_PATH'] + AUTOIPL_VERSION +\
116 "/bin/"
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500117 developer_home_dir_path = re.sub('/sandbox.*', '',
118 executable_base_dir_path)
119 developer_home_dir_path = \
120 gm.add_trailing_slash(developer_home_dir_path)
121 gp.dprint_vars(executable_base_dir_path, developer_home_dir_path,
122 apollo_dir_path)
123
124 ROBOT_TEST_RUNNING_FROM_SB = 0
125 if executable_base_dir_path != apollo_dir_path:
126 ROBOT_TEST_RUNNING_FROM_SB = 1
127 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB)
128 ROBOT_TEST_BASE_DIR_PATH = developer_home_dir_path + suffix
129 if not os.path.isdir(ROBOT_TEST_BASE_DIR_PATH):
Michael Walsha0ce75a2018-07-31 13:54:29 -0500130 gp.dprint_timen("NOTE: Sandbox directory "
131 + ROBOT_TEST_BASE_DIR_PATH + " does not"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500132 + " exist.")
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500133 # Fall back to the apollo dir path.
134 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
135 else:
136 # Use to the apollo dir path.
137 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
138
139 if not gv.valid_value(ROBOT_TEST_BASE_DIR_PATH):
140 return False
141 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB, ROBOT_TEST_BASE_DIR_PATH)
142 if not gv.valid_dir_path(ROBOT_TEST_BASE_DIR_PATH):
143 return False
144
145 ROBOT_TEST_BASE_DIR_PATH = gm.add_trailing_slash(ROBOT_TEST_BASE_DIR_PATH)
146 gm.set_mod_global(ROBOT_TEST_BASE_DIR_PATH)
147 os.environ['ROBOT_TEST_BASE_DIR_PATH'] = ROBOT_TEST_BASE_DIR_PATH
148
149 gm.set_mod_global(ROBOT_TEST_RUNNING_FROM_SB)
150 os.environ['ROBOT_TEST_RUNNING_FROM_SB'] = str(ROBOT_TEST_RUNNING_FROM_SB)
151
152
153raw_robot_file_search_path = "${ROBOT_TEST_BASE_DIR_PATH}:" +\
154 "${ROBOT_TEST_BASE_DIR_PATH}tests:${ROBOT_TEST_BASE_DIR_PATH}extended:" +\
155 "${ROBOT_TEST_BASE_DIR_PATH}scratch:${PATH}"
156
157
158def init_robot_file_path(robot_file_path):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500159 r"""
160 Determine full path name for the file path passed in robot_file_path and
161 return it.
162
163 If robot_file_path contains a fully qualified path name, this function
164 will verify that the file exists. If robot_file_path contains a relative
165 path, this function will search for the file and set robot_file_path so
166 that it contains the absolute path to the robot file. This function will
167 search for the robot file using the raw_robot_file_search_path (defined
168 above). Note that if ROBOT_TEST_BASE_DIR_PATH is not set, this function
169 will call init_robot_test_base_dir_path to set it.
170
171 Description of arguments:
172 robot_file_path The absolute or relative path to a robot
173 file.
174 """
175
176 if not gv.valid_value(robot_file_path):
177 raise ValueError('Programmer error.')
178
179 try:
180 if ROBOT_TEST_BASE_DIR_PATH is NONE:
181 init_robot_test_base_dir_path()
182 except NameError:
183 init_robot_test_base_dir_path()
184
185 if not re.match(r".*\.(robot|py)$", robot_file_path):
186 # No suffix so we'll assign one of "\.robot".
187 robot_file_path = robot_file_path + ".robot"
188
189 abs_path = 0
190 if robot_file_path[0:1] == "/":
191 abs_path = 1
192
193 gp.dprint_vars(abs_path, robot_file_path)
194
195 if not abs_path:
196 cmd_buf = "echo -n \"" + raw_robot_file_search_path + "\""
Michael Walshbffaa1d2018-06-08 15:09:27 -0500197 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=(not debug),
198 print_output=0)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500199 robot_file_search_paths = out_buf
Michael Walsha0ce75a2018-07-31 13:54:29 -0500200 gp.dprint_var(robot_file_search_paths)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500201 robot_file_search_paths_list = robot_file_search_paths.split(':')
202 for search_path in robot_file_search_paths_list:
203 search_path = gm.add_trailing_slash(search_path)
204 candidate_file_path = search_path + robot_file_path
205 gp.dprint_var(candidate_file_path)
206 if os.path.isfile(candidate_file_path):
207 gp.dprint_timen("Found full path to " + robot_file_path + ".")
208 robot_file_path = candidate_file_path
209 break
210
211 gp.dprint_var(robot_file_path)
212 if not gv.valid_file_path(robot_file_path):
213 raise ValueError('Programmer error.')
214
215 return robot_file_path
216
217
218def get_robot_parm_names():
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500219 r"""
220 Return a list containing all of the long parm names (e.g. --outputdir)
221 supported by the robot program. Double dashes are not included in the
222 names returned.
223 """
224
225 cmd_buf = "robot -h | egrep " +\
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500226 "'^([ ]\\-[a-zA-Z0-9])?[ ]+--[a-zA-Z0-9]+[ ]+' | sed -re" +\
227 " s'/.*\\-\\-//g' -e s'/ .*//g' | sort -u"
Michael Walshbffaa1d2018-06-08 15:09:27 -0500228 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500229
230 return out_buf.split("\n")
231
232
233def create_robot_cmd_string(robot_file_path, *parms):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500234 r"""
235 Create a robot command string and return it. On failure, return an empty
236 string.
237
238 Description of arguments:
239 robot_file_path The path to the robot file to be run.
240 parms The list of parms to be included in the
241 command string. The name of each variable
242 in this list must be the same as the name
243 of the corresponding parm. This function
244 figures out that name. This function is
245 also able to distinguish robot parms (e.g.
246 --outputdir) from robot program parms (all
247 other parms which will be passed as "-v
248 PARM_NAME:parm_value")..
249
250 Example:
251
252 The following call to this function...
253 cmd_buf = create_robot_cmd_string("tools/start_sol_console.robot",
254 OPENBMC_HOST, quiet, test_mode, debug, outputdir, output, log, report)
255
256 Would return a string something like this.
257 robot -v OPENBMC_HOST:beye6 -v quiet:0 -v test_mode:1 -v debug:1
258 --outputdir=/gsa/ausgsa/projects/a/autoipl/status
259 --output=beye6.OS_Console.output.xml --log=beye6.OS_Console.log.html
260 --report=beye6.OS_Console.report.html tools/start_sol_console.robot
261 """
262
263 robot_file_path = init_robot_file_path(robot_file_path)
264
265 robot_parm_names = get_robot_parm_names()
266
267 robot_parm_list = []
268
269 stack_frame = 2
270 ix = 2
271 for arg in parms:
272 parm = arg
273 parm = gm.quote_bash_parm(gm.escape_bash_quotes(str(parm)))
274 var_name = gp.get_arg_name(None, ix, stack_frame)
275 if var_name in robot_parm_names:
276 p_string = "--" + var_name + "=" + str(parm)
277 robot_parm_list.append(p_string)
278 else:
279 p_string = "-v " + var_name + ":" + str(parm)
280 robot_parm_list.append(p_string)
281 ix += 1
282
283 robot_cmd_buf = "robot " + ' '.join(robot_parm_list) + " " +\
284 robot_file_path
285
286 return robot_cmd_buf
287
288
Michael Walsha0ce75a2018-07-31 13:54:29 -0500289# Global variables to aid in cleanup after running robot_cmd_fnc.
290gcr_last_robot_cmd_buf = ""
291gcr_last_robot_rc = 0
292
293
294def process_robot_output_files(robot_cmd_buf=None,
295 robot_rc=None,
296 gzip=1):
297 r"""
298 Process robot output files which can involve several operations:
299 - If the files are in a temporary location, using SAVE_STATUS_POLICY to
300 decide whether to move them to a permanent location or to delete them.
301 - Gzipping them.
302
303 Description of argument(s):
304 robot_cmd_buf The complete command string used to invoke
305 robot.
306 robot_rc The return code from running the robot
307 command string.
308 gzip Indicates whether robot-generated output
309 should be gzipped.
310 """
311
312 robot_cmd_buf = gm.dft(robot_cmd_buf, gcr_last_robot_cmd_buf)
313 robot_rc = gm.dft(robot_rc, gcr_last_robot_rc)
314
315 if robot_cmd_buf == "":
316 # This can legitimately occur if this function is called from an
317 # exit_function without the program having ever run robot_cmd_fnc.
318 return
319
320 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS")
321 gp.qprint_vars(SAVE_STATUS_POLICY)
322
323 # When SAVE_STATUS_POLICY is "NEVER" robot output files don't even get
324 # generated.
325 if SAVE_STATUS_POLICY == "NEVER":
326 return
327
328 # Compose file_list based on robot command buffer passed in.
329 robot_cmd_buf_dict = gc.parse_command_string(robot_cmd_buf)
330 outputdir = robot_cmd_buf_dict['outputdir']
331 outputdir = gm.add_trailing_slash(outputdir)
332 file_list = outputdir + robot_cmd_buf_dict['output'] + " " + outputdir\
333 + robot_cmd_buf_dict['log'] + " " + outputdir\
334 + robot_cmd_buf_dict['report']
335
336 # Double checking that files are present.
337 shell_rc, out_buf = gc.shell_cmd("ls -1 " + file_list + " 2>/dev/null",
338 show_err=0)
339 file_list = re.sub("\n", " ", out_buf.rstrip("\n"))
340
341 if file_list == "":
342 gp.qprint_timen("No robot output files were found in " + outputdir
343 + ".")
344 return
345 gp.qprint_var(robot_rc, 1)
346 if SAVE_STATUS_POLICY == "FAIL" and robot_rc == 0:
347 gp.qprint_timen("The call to robot produced no failures."
348 + " Deleting robot output files.")
349 gc.shell_cmd("rm -rf " + file_list)
350 return
351
352 if gzip:
353 gc.shell_cmd("gzip " + file_list)
354 # Update the values in file_list.
355 file_list = re.sub(" ", ".gz ", file_list) + ".gz"
356
357 # It TMP_ROBOT_DIR_PATH is set, it means the caller wanted the robot
358 # output initially directed to TMP_ROBOT_DIR_PATH but later moved to
359 # FFDC_DIR_PATH. Otherwise, we're done.
360
361 if os.environ.get("TMP_ROBOT_DIR_PATH", "") is "":
362 return
363
364 # We're directing these to the FFDC dir path so that they'll be subjected
365 # to FFDC cleanup.
366 target_dir_path = os.environ.get("FFDC_DIR_PATH",
367 os.environ.get("HOME", ".")
368 + "/autoipl/ffdc")
369 target_dir_path = gm.add_trailing_slash(target_dir_path)
370
371 targ_file_list = [re.sub(".*/", target_dir_path, x)
372 for x in file_list.split(" ")]
373
374 gc.shell_cmd("mv " + file_list + " " + target_dir_path + " >/dev/null",
375 time_out=600)
376
377 gp.qprint_timen("New robot log file locations:")
378 gp.qprintn('\n'.join(targ_file_list))
379
380
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500381def robot_cmd_fnc(robot_cmd_buf,
382 robot_jail=os.environ.get('ROBOT_JAIL', ''),
383 gzip=1):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500384 r"""
385 Run the robot command string.
386
387 This function will set the various PATH variables correctly so that you
388 are running the proper version of all imported files, etc.
389
390 Description of argument(s):
391 robot_cmd_buf The complete robot command string.
392 robot_jail Indicates that this is to run in "robot
393 jail" meaning without visibility to any
394 apolloxxx import files, programs, etc.
395 gqip This indicates that the log, report and
396 output files produced by robot should be
397 gzipped to save space.
398 """
399
400 if not gv.valid_value(robot_cmd_buf):
401 return False
402
Michael Walsha0ce75a2018-07-31 13:54:29 -0500403 # Set global variables to aid in cleanup with process_robot_output_files.
404 global gcr_last_robot_cmd_buf
405 global gcr_last_robot_rc
406 gcr_last_robot_cmd_buf = robot_cmd_buf
407
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500408 # Get globals set by init_robot_test_base_dir_path().
409 module = sys.modules["__main__"]
410 try:
411 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
412 except NameError:
413 init_robot_test_base_dir_path()
414 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
415
416 ROBOT_TEST_RUNNING_FROM_SB = \
417 gm.get_mod_global("ROBOT_TEST_RUNNING_FROM_SB")
418
419 if robot_jail == "":
420 if ROBOT_TEST_RUNNING_FROM_SB:
421 robot_jail = 0
422 else:
423 robot_jail = 1
424
425 robot_jail = int(robot_jail)
426 ROBOT_JAIL = os.environ.get('ROBOT_JAIL', '')
427 gp.dprint_vars(ROBOT_TEST_BASE_DIR_PATH, ROBOT_TEST_RUNNING_FROM_SB,
428 ROBOT_JAIL, robot_jail)
429
430 # Save PATH and PYTHONPATH to be restored later.
431 os.environ["SAVED_PYTHONPATH"] = os.environ.get("PYTHONPATH", "")
432 os.environ["SAVED_PATH"] = os.environ.get("PATH", "")
433
434 if robot_jail:
435 PYTHONPATH = ROBOT_TEST_BASE_DIR_PATH + "lib"
436 NEW_PATH_LIST = [ROBOT_TEST_BASE_DIR_PATH + "bin"]
437 # Coding special case to preserve python27_path.
438 python27_path = "/opt/rh/python27/root/usr/bin"
439 PATH_LIST = os.environ.get("PATH", "").split(":")
440 if python27_path in PATH_LIST:
441 NEW_PATH_LIST.append(python27_path)
442 NEW_PATH_LIST.extend(["/usr/local/sbin", "/usr/local/bin", "/usr/sbin",
443 "/usr/bin", "/sbin", "/bin"])
444 PATH = ":".join(NEW_PATH_LIST)
445 else:
446 PYTHONPATH = os.environ.get('PYTHONPATH', '') + ":" +\
447 ROBOT_TEST_BASE_DIR_PATH + "lib/"
448 PATH = os.environ.get('PATH', '') + ":" + ROBOT_TEST_BASE_DIR_PATH +\
449 "bin/"
450
451 os.environ['PYTHONPATH'] = PYTHONPATH
452 os.environ['PATH'] = PATH
453 gp.dprint_vars(PATH, PYTHONPATH)
454
455 os.environ['FFDC_DIR_PATH_STYLE'] = os.environ.get('FFDC_DIR_PATH_STYLE',
456 '1')
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500457 test_mode = getattr(module, "test_mode")
458
459 gp.qpissuing(robot_cmd_buf, test_mode)
460 if test_mode:
461 os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
462 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
463 return True
464
465 if quiet:
466 DEVNULL = open(os.devnull, 'wb')
467 stdout = DEVNULL
468 else:
469 stdout = None
470 sub_proc = subprocess.Popen(robot_cmd_buf, stdout=stdout, shell=True)
471 sub_proc.communicate()
472 shell_rc = sub_proc.returncode
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500473 os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
474 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
Michael Walsha0ce75a2018-07-31 13:54:29 -0500475 gcr_last_robot_rc = shell_rc
476 process_robot_output_files()
477 if shell_rc != 0:
478 hex = 1
479 gp.print_var(shell_rc, hex)
480 return False
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500481
482 return True