blob: 3944c7943d9b5293248cfa4956727b5e438010ca [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.
Michael Walshf33140f2018-11-01 14:05:56 -050035
36 Description of argument(s):
37 extra_prefix An extra prefix to be appended to the
38 default prefix for output file names.
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050039 """
40
Michael Walsha0ce75a2018-07-31 13:54:29 -050041 gp.dprint_executing()
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050042 AUTOBOOT_OPENBMC_NICKNAME = gm.get_mod_global("AUTOBOOT_OPENBMC_NICKNAME")
43
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050044 # Set values for call to create_robot_cmd_string.
Michael Walsha0ce75a2018-07-31 13:54:29 -050045 # Environment variable TMP_ROBOT_DIR_PATH can be set by the user to
46 # indicate that robot-generated output should initially be written to the
47 # specified temporary directory and then moved to the normal output
48 # location after completion.
49 outputdir =\
50 os.environ.get("TMP_ROBOT_DIR_PATH",
51 os.environ.get("STATUS_DIR_PATH",
52 os.environ.get("HOME", ".")
Michael Walsh0a3bdb42019-01-31 16:21:44 +000053 + "/status"))
Michael Walsha0ce75a2018-07-31 13:54:29 -050054 outputdir = gm.add_trailing_slash(outputdir)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050055 seconds = time.time()
56 loc_time = time.localtime(seconds)
57 time_string = time.strftime("%y%m%d.%H%M%S", loc_time)
58 file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "." + extra_prefix +\
59 time_string + "."
Michael Walsha0ce75a2018-07-31 13:54:29 -050060 # Environment variable SAVE_STATUS_POLICY governs when robot-generated
61 # output files (e.g. the log.html) will be moved from TMP_ROBOT_DIR_PATH
62 # to FFDC_DIR_PATH. Valid values are "ALWAYS", "NEVER" and "FAIL".
63 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS")
64 if SAVE_STATUS_POLICY == "NEVER":
65 output = "NONE"
66 log = "NONE"
67 report = "NONE"
68 else:
69 output = file_prefix + "output.xml"
70 log = file_prefix + "log.html"
71 report = file_prefix + "report.html"
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050072 loglevel = "TRACE"
73
74 # Make create_robot_cmd_string values global.
75 gm.set_mod_global(outputdir)
76 gm.set_mod_global(output)
77 gm.set_mod_global(log)
78 gm.set_mod_global(report)
79 gm.set_mod_global(loglevel)
80
81
82def init_robot_test_base_dir_path():
Michael Walsh3ba8ecd2018-04-24 11:33:25 -050083 r"""
84 Initialize and validate the environment variable, ROBOT_TEST_BASE_DIR_PATH
85 and set corresponding global variable ROBOT_TEST_RUNNING_FROM_SB.
86
87 If ROBOT_TEST_BASE_DIR_PATH is already set, this function will merely
88 validate it. This function will also set environment variable
89 ROBOT_TEST_RUNNING_FROM_SB when ROBOT_TEST_BASE_DIR_PATH is not pre-set.
90 """
91
92 # ROBOT_TEST_BASE_DIR_PATH will be set as follows:
93 # This function will determine whether we are running in a user sandbox
94 # or from a standard apolloxxx environment.
95 # - User sandbox:
96 # If there is a <developer's home dir>/git/openbmc-test-automation/,
97 # ROBOT_TEST_BASE_DIR_PATH will be set to that path. Otherwise, we set it
98 # to <program dir path>/git/openbmc-test-automation/
99 # - Not in user sandbox:
100 # ROBOT_TEST_BASE_DIR_PATH will be set to <program dir
101 # path>/git/openbmc-test-automation/
102
103 ROBOT_TEST_BASE_DIR_PATH = os.environ.get('ROBOT_TEST_BASE_DIR_PATH', "")
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500104 ROBOT_TEST_RUNNING_FROM_SB = \
105 int(os.environ.get('ROBOT_TEST_RUNNING_FROM_SB', "0"))
106 if ROBOT_TEST_BASE_DIR_PATH == "":
107 # ROBOT_TEST_BASE_DIR_PATH was not set by user/caller.
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500108 AUTOIPL_VERSION = os.environ.get('AUTOIPL_VERSION', '')
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500109 if AUTOIPL_VERSION == "":
110 ROBOT_TEST_BASE_DIR_PATH = base_path
111 else:
112 suffix = "git/openbmc-test-automation/"
113
114 # Determine whether we're running out of a developer sandbox or
115 # simply out of an apolloxxx/bin path.
Michael Walshbffaa1d2018-06-08 15:09:27 -0500116 shell_rc, out_buf = gc.shell_cmd('dirname $(which gen_print.py)',
117 quiet=(not debug), print_output=0)
118 executable_base_dir_path = os.path.realpath(out_buf.rstrip()) + "/"
119 apollo_dir_path = os.environ['AUTO_BASE_PATH'] + AUTOIPL_VERSION +\
120 "/bin/"
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500121 developer_home_dir_path = re.sub('/sandbox.*', '',
122 executable_base_dir_path)
123 developer_home_dir_path = \
124 gm.add_trailing_slash(developer_home_dir_path)
125 gp.dprint_vars(executable_base_dir_path, developer_home_dir_path,
126 apollo_dir_path)
127
128 ROBOT_TEST_RUNNING_FROM_SB = 0
129 if executable_base_dir_path != apollo_dir_path:
130 ROBOT_TEST_RUNNING_FROM_SB = 1
131 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB)
132 ROBOT_TEST_BASE_DIR_PATH = developer_home_dir_path + suffix
133 if not os.path.isdir(ROBOT_TEST_BASE_DIR_PATH):
Michael Walsha0ce75a2018-07-31 13:54:29 -0500134 gp.dprint_timen("NOTE: Sandbox directory "
135 + ROBOT_TEST_BASE_DIR_PATH + " does not"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500136 + " exist.")
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500137 # Fall back to the apollo dir path.
138 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
139 else:
140 # Use to the apollo dir path.
141 ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
142
143 if not gv.valid_value(ROBOT_TEST_BASE_DIR_PATH):
144 return False
145 gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB, ROBOT_TEST_BASE_DIR_PATH)
146 if not gv.valid_dir_path(ROBOT_TEST_BASE_DIR_PATH):
147 return False
148
149 ROBOT_TEST_BASE_DIR_PATH = gm.add_trailing_slash(ROBOT_TEST_BASE_DIR_PATH)
150 gm.set_mod_global(ROBOT_TEST_BASE_DIR_PATH)
151 os.environ['ROBOT_TEST_BASE_DIR_PATH'] = ROBOT_TEST_BASE_DIR_PATH
152
153 gm.set_mod_global(ROBOT_TEST_RUNNING_FROM_SB)
154 os.environ['ROBOT_TEST_RUNNING_FROM_SB'] = str(ROBOT_TEST_RUNNING_FROM_SB)
155
156
157raw_robot_file_search_path = "${ROBOT_TEST_BASE_DIR_PATH}:" +\
158 "${ROBOT_TEST_BASE_DIR_PATH}tests:${ROBOT_TEST_BASE_DIR_PATH}extended:" +\
159 "${ROBOT_TEST_BASE_DIR_PATH}scratch:${PATH}"
160
161
162def init_robot_file_path(robot_file_path):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500163 r"""
164 Determine full path name for the file path passed in robot_file_path and
165 return it.
166
167 If robot_file_path contains a fully qualified path name, this function
168 will verify that the file exists. If robot_file_path contains a relative
169 path, this function will search for the file and set robot_file_path so
170 that it contains the absolute path to the robot file. This function will
171 search for the robot file using the raw_robot_file_search_path (defined
172 above). Note that if ROBOT_TEST_BASE_DIR_PATH is not set, this function
173 will call init_robot_test_base_dir_path to set it.
174
175 Description of arguments:
176 robot_file_path The absolute or relative path to a robot
177 file.
178 """
179
180 if not gv.valid_value(robot_file_path):
181 raise ValueError('Programmer error.')
182
183 try:
184 if ROBOT_TEST_BASE_DIR_PATH is NONE:
185 init_robot_test_base_dir_path()
186 except NameError:
187 init_robot_test_base_dir_path()
188
189 if not re.match(r".*\.(robot|py)$", robot_file_path):
190 # No suffix so we'll assign one of "\.robot".
191 robot_file_path = robot_file_path + ".robot"
192
193 abs_path = 0
194 if robot_file_path[0:1] == "/":
195 abs_path = 1
196
197 gp.dprint_vars(abs_path, robot_file_path)
198
199 if not abs_path:
200 cmd_buf = "echo -n \"" + raw_robot_file_search_path + "\""
Michael Walshbffaa1d2018-06-08 15:09:27 -0500201 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=(not debug),
202 print_output=0)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500203 robot_file_search_paths = out_buf
Michael Walsha0ce75a2018-07-31 13:54:29 -0500204 gp.dprint_var(robot_file_search_paths)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500205 robot_file_search_paths_list = robot_file_search_paths.split(':')
206 for search_path in robot_file_search_paths_list:
207 search_path = gm.add_trailing_slash(search_path)
208 candidate_file_path = search_path + robot_file_path
209 gp.dprint_var(candidate_file_path)
210 if os.path.isfile(candidate_file_path):
211 gp.dprint_timen("Found full path to " + robot_file_path + ".")
212 robot_file_path = candidate_file_path
213 break
214
215 gp.dprint_var(robot_file_path)
216 if not gv.valid_file_path(robot_file_path):
217 raise ValueError('Programmer error.')
218
219 return robot_file_path
220
221
222def get_robot_parm_names():
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500223 r"""
224 Return a list containing all of the long parm names (e.g. --outputdir)
225 supported by the robot program. Double dashes are not included in the
226 names returned.
227 """
228
229 cmd_buf = "robot -h | egrep " +\
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500230 "'^([ ]\\-[a-zA-Z0-9])?[ ]+--[a-zA-Z0-9]+[ ]+' | sed -re" +\
231 " s'/.*\\-\\-//g' -e s'/ .*//g' | sort -u"
Michael Walshbffaa1d2018-06-08 15:09:27 -0500232 shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500233
234 return out_buf.split("\n")
235
236
237def create_robot_cmd_string(robot_file_path, *parms):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500238 r"""
239 Create a robot command string and return it. On failure, return an empty
240 string.
241
242 Description of arguments:
243 robot_file_path The path to the robot file to be run.
244 parms The list of parms to be included in the
245 command string. The name of each variable
246 in this list must be the same as the name
247 of the corresponding parm. This function
248 figures out that name. This function is
249 also able to distinguish robot parms (e.g.
250 --outputdir) from robot program parms (all
251 other parms which will be passed as "-v
252 PARM_NAME:parm_value")..
253
254 Example:
255
256 The following call to this function...
257 cmd_buf = create_robot_cmd_string("tools/start_sol_console.robot",
258 OPENBMC_HOST, quiet, test_mode, debug, outputdir, output, log, report)
259
260 Would return a string something like this.
261 robot -v OPENBMC_HOST:beye6 -v quiet:0 -v test_mode:1 -v debug:1
Michael Walsh0a3bdb42019-01-31 16:21:44 +0000262 --outputdir=/gsa/ausgsa/projects/a/status
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500263 --output=beye6.OS_Console.output.xml --log=beye6.OS_Console.log.html
264 --report=beye6.OS_Console.report.html tools/start_sol_console.robot
265 """
266
267 robot_file_path = init_robot_file_path(robot_file_path)
268
269 robot_parm_names = get_robot_parm_names()
270
271 robot_parm_list = []
272
273 stack_frame = 2
274 ix = 2
275 for arg in parms:
276 parm = arg
277 parm = gm.quote_bash_parm(gm.escape_bash_quotes(str(parm)))
278 var_name = gp.get_arg_name(None, ix, stack_frame)
279 if var_name in robot_parm_names:
280 p_string = "--" + var_name + "=" + str(parm)
281 robot_parm_list.append(p_string)
282 else:
283 p_string = "-v " + var_name + ":" + str(parm)
284 robot_parm_list.append(p_string)
285 ix += 1
286
287 robot_cmd_buf = "robot " + ' '.join(robot_parm_list) + " " +\
288 robot_file_path
289
290 return robot_cmd_buf
291
292
Michael Walsha0ce75a2018-07-31 13:54:29 -0500293# Global variables to aid in cleanup after running robot_cmd_fnc.
294gcr_last_robot_cmd_buf = ""
295gcr_last_robot_rc = 0
296
297
298def process_robot_output_files(robot_cmd_buf=None,
299 robot_rc=None,
Michael Walshf33140f2018-11-01 14:05:56 -0500300 gzip=None):
Michael Walsha0ce75a2018-07-31 13:54:29 -0500301 r"""
302 Process robot output files which can involve several operations:
303 - If the files are in a temporary location, using SAVE_STATUS_POLICY to
304 decide whether to move them to a permanent location or to delete them.
305 - Gzipping them.
306
307 Description of argument(s):
308 robot_cmd_buf The complete command string used to invoke
309 robot.
310 robot_rc The return code from running the robot
311 command string.
312 gzip Indicates whether robot-generated output
313 should be gzipped.
314 """
315
316 robot_cmd_buf = gm.dft(robot_cmd_buf, gcr_last_robot_cmd_buf)
317 robot_rc = gm.dft(robot_rc, gcr_last_robot_rc)
Michael Walshf33140f2018-11-01 14:05:56 -0500318 gzip = gm.dft(gzip, int(os.environ.get("GZIP_ROBOT", "1")))
Michael Walsha0ce75a2018-07-31 13:54:29 -0500319
320 if robot_cmd_buf == "":
321 # This can legitimately occur if this function is called from an
322 # exit_function without the program having ever run robot_cmd_fnc.
323 return
324
325 SAVE_STATUS_POLICY = os.environ.get("SAVE_STATUS_POLICY", "ALWAYS")
326 gp.qprint_vars(SAVE_STATUS_POLICY)
327
328 # When SAVE_STATUS_POLICY is "NEVER" robot output files don't even get
329 # generated.
330 if SAVE_STATUS_POLICY == "NEVER":
331 return
332
333 # Compose file_list based on robot command buffer passed in.
334 robot_cmd_buf_dict = gc.parse_command_string(robot_cmd_buf)
335 outputdir = robot_cmd_buf_dict['outputdir']
336 outputdir = gm.add_trailing_slash(outputdir)
337 file_list = outputdir + robot_cmd_buf_dict['output'] + " " + outputdir\
338 + robot_cmd_buf_dict['log'] + " " + outputdir\
339 + robot_cmd_buf_dict['report']
340
341 # Double checking that files are present.
342 shell_rc, out_buf = gc.shell_cmd("ls -1 " + file_list + " 2>/dev/null",
343 show_err=0)
344 file_list = re.sub("\n", " ", out_buf.rstrip("\n"))
345
346 if file_list == "":
347 gp.qprint_timen("No robot output files were found in " + outputdir
348 + ".")
349 return
350 gp.qprint_var(robot_rc, 1)
351 if SAVE_STATUS_POLICY == "FAIL" and robot_rc == 0:
352 gp.qprint_timen("The call to robot produced no failures."
353 + " Deleting robot output files.")
354 gc.shell_cmd("rm -rf " + file_list)
355 return
356
357 if gzip:
Michael Walshe53dfec2018-08-07 15:02:56 -0500358 gc.shell_cmd("gzip -f " + file_list)
Michael Walsha0ce75a2018-07-31 13:54:29 -0500359 # Update the values in file_list.
360 file_list = re.sub(" ", ".gz ", file_list) + ".gz"
361
362 # It TMP_ROBOT_DIR_PATH is set, it means the caller wanted the robot
363 # output initially directed to TMP_ROBOT_DIR_PATH but later moved to
364 # FFDC_DIR_PATH. Otherwise, we're done.
365
366 if os.environ.get("TMP_ROBOT_DIR_PATH", "") is "":
367 return
368
369 # We're directing these to the FFDC dir path so that they'll be subjected
370 # to FFDC cleanup.
371 target_dir_path = os.environ.get("FFDC_DIR_PATH",
372 os.environ.get("HOME", ".")
Michael Walsh0a3bdb42019-01-31 16:21:44 +0000373 + "/ffdc")
Michael Walsha0ce75a2018-07-31 13:54:29 -0500374 target_dir_path = gm.add_trailing_slash(target_dir_path)
375
376 targ_file_list = [re.sub(".*/", target_dir_path, x)
377 for x in file_list.split(" ")]
378
379 gc.shell_cmd("mv " + file_list + " " + target_dir_path + " >/dev/null",
380 time_out=600)
381
382 gp.qprint_timen("New robot log file locations:")
383 gp.qprintn('\n'.join(targ_file_list))
384
385
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500386def robot_cmd_fnc(robot_cmd_buf,
Michael Walshe53dfec2018-08-07 15:02:56 -0500387 robot_jail=os.environ.get('ROBOT_JAIL', '')):
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500388 r"""
389 Run the robot command string.
390
391 This function will set the various PATH variables correctly so that you
392 are running the proper version of all imported files, etc.
393
394 Description of argument(s):
395 robot_cmd_buf The complete robot command string.
396 robot_jail Indicates that this is to run in "robot
397 jail" meaning without visibility to any
398 apolloxxx import files, programs, etc.
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500399 """
400
401 if not gv.valid_value(robot_cmd_buf):
402 return False
403
Michael Walsha0ce75a2018-07-31 13:54:29 -0500404 # Set global variables to aid in cleanup with process_robot_output_files.
405 global gcr_last_robot_cmd_buf
406 global gcr_last_robot_rc
407 gcr_last_robot_cmd_buf = robot_cmd_buf
408
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500409 # Get globals set by init_robot_test_base_dir_path().
410 module = sys.modules["__main__"]
411 try:
412 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
413 except NameError:
414 init_robot_test_base_dir_path()
415 ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
416
417 ROBOT_TEST_RUNNING_FROM_SB = \
418 gm.get_mod_global("ROBOT_TEST_RUNNING_FROM_SB")
419
420 if robot_jail == "":
421 if ROBOT_TEST_RUNNING_FROM_SB:
422 robot_jail = 0
423 else:
424 robot_jail = 1
425
426 robot_jail = int(robot_jail)
427 ROBOT_JAIL = os.environ.get('ROBOT_JAIL', '')
428 gp.dprint_vars(ROBOT_TEST_BASE_DIR_PATH, ROBOT_TEST_RUNNING_FROM_SB,
429 ROBOT_JAIL, robot_jail)
430
431 # Save PATH and PYTHONPATH to be restored later.
432 os.environ["SAVED_PYTHONPATH"] = os.environ.get("PYTHONPATH", "")
433 os.environ["SAVED_PATH"] = os.environ.get("PATH", "")
434
435 if robot_jail:
436 PYTHONPATH = ROBOT_TEST_BASE_DIR_PATH + "lib"
437 NEW_PATH_LIST = [ROBOT_TEST_BASE_DIR_PATH + "bin"]
438 # Coding special case to preserve python27_path.
439 python27_path = "/opt/rh/python27/root/usr/bin"
440 PATH_LIST = os.environ.get("PATH", "").split(":")
441 if python27_path in PATH_LIST:
442 NEW_PATH_LIST.append(python27_path)
Michael Walsh0a3bdb42019-01-31 16:21:44 +0000443 # Make sure robot can be found in robot jail.
444 robot_dir_path = os.path.dirname(gm.which('robot'))
445 if robot_dir_path in PATH_LIST:
446 NEW_PATH_LIST.append(robot_dir_path)
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500447 NEW_PATH_LIST.extend(["/usr/local/sbin", "/usr/local/bin", "/usr/sbin",
448 "/usr/bin", "/sbin", "/bin"])
449 PATH = ":".join(NEW_PATH_LIST)
450 else:
451 PYTHONPATH = os.environ.get('PYTHONPATH', '') + ":" +\
452 ROBOT_TEST_BASE_DIR_PATH + "lib/"
453 PATH = os.environ.get('PATH', '') + ":" + ROBOT_TEST_BASE_DIR_PATH +\
454 "bin/"
455
456 os.environ['PYTHONPATH'] = PYTHONPATH
457 os.environ['PATH'] = PATH
458 gp.dprint_vars(PATH, PYTHONPATH)
459
460 os.environ['FFDC_DIR_PATH_STYLE'] = os.environ.get('FFDC_DIR_PATH_STYLE',
461 '1')
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500462 test_mode = getattr(module, "test_mode")
463
464 gp.qpissuing(robot_cmd_buf, test_mode)
465 if test_mode:
466 os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
467 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
468 return True
469
470 if quiet:
471 DEVNULL = open(os.devnull, 'wb')
472 stdout = DEVNULL
473 else:
474 stdout = None
475 sub_proc = subprocess.Popen(robot_cmd_buf, stdout=stdout, shell=True)
476 sub_proc.communicate()
477 shell_rc = sub_proc.returncode
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500478 os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
479 os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
Michael Walsha0ce75a2018-07-31 13:54:29 -0500480 gcr_last_robot_rc = shell_rc
481 process_robot_output_files()
482 if shell_rc != 0:
483 hex = 1
484 gp.print_var(shell_rc, hex)
485 return False
Michael Walsh3ba8ecd2018-04-24 11:33:25 -0500486
487 return True