xref: /openbmc/openbmc-test-automation/lib/gen_call_robot.py (revision 35aa8d53f99891f7fb7c8a1b02b6fb72ffd04727)
1#!/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
20base_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=""):
26
27    r"""
28    Initialize robot output parms such as outputdir, output, etc.
29
30    This function will set global values for the following robot output parms.
31
32    outputdir, output, log, report, loglevel
33
34    This function would typically be called prior to calling
35    create_robot_cmd_string.
36    """
37
38    AUTOBOOT_OPENBMC_NICKNAME = gm.get_mod_global("AUTOBOOT_OPENBMC_NICKNAME")
39
40    FFDC_DIR_PATH_STYLE = os.environ.get('FFDC_DIR_PATH_STYLE', '0')
41    if FFDC_DIR_PATH_STYLE == '1':
42        default_ffdc_dir_path = "/tmp/"
43    else:
44        default_ffdc_dir_path = base_path
45    # Set values for call to create_robot_cmd_string.
46    outputdir = gm.add_trailing_slash(os.environ.get("FFDC_DIR_PATH",
47                                                     default_ffdc_dir_path))
48    seconds = time.time()
49    loc_time = time.localtime(seconds)
50    time_string = time.strftime("%y%m%d.%H%M%S", loc_time)
51    file_prefix = AUTOBOOT_OPENBMC_NICKNAME + "." + extra_prefix +\
52        time_string + "."
53    output = file_prefix + "output.xml"
54    log = file_prefix + "log.html"
55    report = file_prefix + "report.html"
56    loglevel = "TRACE"
57
58    # Make create_robot_cmd_string values global.
59    gm.set_mod_global(outputdir)
60    gm.set_mod_global(output)
61    gm.set_mod_global(log)
62    gm.set_mod_global(report)
63    gm.set_mod_global(loglevel)
64
65
66def init_robot_test_base_dir_path():
67
68    r"""
69    Initialize and validate the environment variable, ROBOT_TEST_BASE_DIR_PATH
70    and set corresponding global variable ROBOT_TEST_RUNNING_FROM_SB.
71
72    If ROBOT_TEST_BASE_DIR_PATH is already set, this function will merely
73    validate it.  This function will also set environment variable
74    ROBOT_TEST_RUNNING_FROM_SB when ROBOT_TEST_BASE_DIR_PATH is not pre-set.
75    """
76
77    # ROBOT_TEST_BASE_DIR_PATH will be set as follows:
78    # This function will determine whether we are running in a user sandbox
79    # or from a standard apolloxxx environment.
80    # - User sandbox:
81    # If there is a <developer's home dir>/git/openbmc-test-automation/,
82    # ROBOT_TEST_BASE_DIR_PATH will be set to that path.  Otherwise, we set it
83    # to <program dir path>/git/openbmc-test-automation/
84    # - Not in user sandbox:
85    # ROBOT_TEST_BASE_DIR_PATH will be set to <program dir
86    # path>/git/openbmc-test-automation/
87
88    ROBOT_TEST_BASE_DIR_PATH = os.environ.get('ROBOT_TEST_BASE_DIR_PATH', "")
89    ROBOT_TEST_RUNNING_FROM_SB = \
90        int(os.environ.get('ROBOT_TEST_RUNNING_FROM_SB', "0"))
91    if ROBOT_TEST_BASE_DIR_PATH == "":
92        # ROBOT_TEST_BASE_DIR_PATH was not set by user/caller.
93        AUTOIPL_VERSION = os.environ.get('AUTOIPL_VERSION', '')
94        if AUTOIPL_VERSION == "":
95            ROBOT_TEST_BASE_DIR_PATH = base_path
96        else:
97            suffix = "git/openbmc-test-automation/"
98
99            # Determine whether we're running out of a developer sandbox or
100            # simply out of an apolloxxx/bin path.
101            shell_rc, out_buf = gc.shell_cmd('dirname $(which gen_print.py)',
102                                             quiet=(not debug), print_output=0)
103            executable_base_dir_path = os.path.realpath(out_buf.rstrip()) + "/"
104            apollo_dir_path = os.environ['AUTO_BASE_PATH'] + AUTOIPL_VERSION +\
105                "/bin/"
106            developer_home_dir_path = re.sub('/sandbox.*', '',
107                                             executable_base_dir_path)
108            developer_home_dir_path = \
109                gm.add_trailing_slash(developer_home_dir_path)
110            gp.dprint_vars(executable_base_dir_path, developer_home_dir_path,
111                           apollo_dir_path)
112
113            ROBOT_TEST_RUNNING_FROM_SB = 0
114            if executable_base_dir_path != apollo_dir_path:
115                ROBOT_TEST_RUNNING_FROM_SB = 1
116                gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB)
117                ROBOT_TEST_BASE_DIR_PATH = developer_home_dir_path + suffix
118                if not os.path.isdir(ROBOT_TEST_BASE_DIR_PATH):
119                    gp.dprint_timen("NOTE: Sandbox directory" +
120                                    " ${ROBOT_TEST_BASE_DIR_PATH} does not" +
121                                    " exist.")
122                    # Fall back to the apollo dir path.
123                    ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
124            else:
125                # Use to the apollo dir path.
126                ROBOT_TEST_BASE_DIR_PATH = apollo_dir_path + suffix
127
128    if not gv.valid_value(ROBOT_TEST_BASE_DIR_PATH):
129        return False
130    gp.dprint_vars(ROBOT_TEST_RUNNING_FROM_SB, ROBOT_TEST_BASE_DIR_PATH)
131    if not gv.valid_dir_path(ROBOT_TEST_BASE_DIR_PATH):
132        return False
133
134    ROBOT_TEST_BASE_DIR_PATH = gm.add_trailing_slash(ROBOT_TEST_BASE_DIR_PATH)
135    gm.set_mod_global(ROBOT_TEST_BASE_DIR_PATH)
136    os.environ['ROBOT_TEST_BASE_DIR_PATH'] = ROBOT_TEST_BASE_DIR_PATH
137
138    gm.set_mod_global(ROBOT_TEST_RUNNING_FROM_SB)
139    os.environ['ROBOT_TEST_RUNNING_FROM_SB'] = str(ROBOT_TEST_RUNNING_FROM_SB)
140
141
142raw_robot_file_search_path = "${ROBOT_TEST_BASE_DIR_PATH}:" +\
143    "${ROBOT_TEST_BASE_DIR_PATH}tests:${ROBOT_TEST_BASE_DIR_PATH}extended:" +\
144    "${ROBOT_TEST_BASE_DIR_PATH}scratch:${PATH}"
145
146
147def init_robot_file_path(robot_file_path):
148
149    r"""
150    Determine full path name for the file path passed in robot_file_path and
151    return it.
152
153    If robot_file_path contains a fully qualified path name, this function
154    will verify that the file exists.  If robot_file_path contains a relative
155    path, this function will search for the file and set robot_file_path so
156    that it contains the absolute path to the robot file.  This function will
157    search for the robot file using the raw_robot_file_search_path (defined
158    above).  Note that if ROBOT_TEST_BASE_DIR_PATH is not set, this function
159    will call init_robot_test_base_dir_path to set it.
160
161    Description of arguments:
162    robot_file_path                 The absolute or relative path to a robot
163                                    file.
164    """
165
166    if not gv.valid_value(robot_file_path):
167        raise ValueError('Programmer error.')
168
169    try:
170        if ROBOT_TEST_BASE_DIR_PATH is NONE:
171            init_robot_test_base_dir_path()
172    except NameError:
173        init_robot_test_base_dir_path()
174
175    if not re.match(r".*\.(robot|py)$", robot_file_path):
176        # No suffix so we'll assign one of "\.robot".
177        robot_file_path = robot_file_path + ".robot"
178
179    abs_path = 0
180    if robot_file_path[0:1] == "/":
181        abs_path = 1
182
183    gp.dprint_vars(abs_path, robot_file_path)
184
185    if not abs_path:
186        cmd_buf = "echo -n \"" + raw_robot_file_search_path + "\""
187        shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=(not debug),
188                                         print_output=0)
189        robot_file_search_paths = out_buf
190        gp.dpvar(robot_file_search_paths)
191        robot_file_search_paths_list = robot_file_search_paths.split(':')
192        for search_path in robot_file_search_paths_list:
193            search_path = gm.add_trailing_slash(search_path)
194            candidate_file_path = search_path + robot_file_path
195            gp.dprint_var(candidate_file_path)
196            if os.path.isfile(candidate_file_path):
197                gp.dprint_timen("Found full path to " + robot_file_path + ".")
198                robot_file_path = candidate_file_path
199                break
200
201    gp.dprint_var(robot_file_path)
202    if not gv.valid_file_path(robot_file_path):
203        raise ValueError('Programmer error.')
204
205    return robot_file_path
206
207
208def get_robot_parm_names():
209
210    r"""
211    Return a list containing all of the long parm names (e.g. --outputdir)
212    supported by the robot program.  Double dashes are not included in the
213    names returned.
214    """
215
216    cmd_buf = "robot -h | egrep " +\
217        "'^([ ]\-[a-zA-Z0-9])?[ ]+--[a-zA-Z0-9]+[ ]+' | sed -re" +\
218        " s'/.*\-\-//g' -e s'/ .*//g' | sort -u"
219    shell_rc, out_buf = gc.shell_cmd(cmd_buf, quiet=1, print_output=0)
220
221    return out_buf.split("\n")
222
223
224def create_robot_cmd_string(robot_file_path, *parms):
225
226    r"""
227    Create a robot command string and return it.  On failure, return an empty
228    string.
229
230    Description of arguments:
231    robot_file_path                 The path to the robot file to be run.
232    parms                           The list of parms to be included in the
233                                    command string.  The name of each variable
234                                    in this list must be the same as the name
235                                    of the corresponding parm.  This function
236                                    figures out that name.  This function is
237                                    also able to distinguish robot parms (e.g.
238                                    --outputdir) from robot program parms (all
239                                    other parms which will be passed as "-v
240                                    PARM_NAME:parm_value")..
241
242    Example:
243
244    The following call to this function...
245    cmd_buf = create_robot_cmd_string("tools/start_sol_console.robot",
246    OPENBMC_HOST, quiet, test_mode, debug, outputdir, output, log, report)
247
248    Would return a string something like this.
249    robot -v OPENBMC_HOST:beye6 -v quiet:0 -v test_mode:1 -v debug:1
250    --outputdir=/gsa/ausgsa/projects/a/autoipl/status
251    --output=beye6.OS_Console.output.xml --log=beye6.OS_Console.log.html
252    --report=beye6.OS_Console.report.html tools/start_sol_console.robot
253    """
254
255    robot_file_path = init_robot_file_path(robot_file_path)
256
257    robot_parm_names = get_robot_parm_names()
258
259    robot_parm_list = []
260
261    stack_frame = 2
262    ix = 2
263    for arg in parms:
264        parm = arg
265        parm = gm.quote_bash_parm(gm.escape_bash_quotes(str(parm)))
266        var_name = gp.get_arg_name(None, ix, stack_frame)
267        if var_name in robot_parm_names:
268            p_string = "--" + var_name + "=" + str(parm)
269            robot_parm_list.append(p_string)
270        else:
271            p_string = "-v " + var_name + ":" + str(parm)
272            robot_parm_list.append(p_string)
273        ix += 1
274
275    robot_cmd_buf = "robot " + ' '.join(robot_parm_list) + " " +\
276        robot_file_path
277
278    return robot_cmd_buf
279
280
281def robot_cmd_fnc(robot_cmd_buf,
282                  robot_jail=os.environ.get('ROBOT_JAIL', ''),
283                  gzip=1):
284
285    r"""
286    Run the robot command string.
287
288    This function will set the various PATH variables correctly so that you
289    are running the proper version of all imported files, etc.
290
291    Description of argument(s):
292    robot_cmd_buf                   The complete robot command string.
293    robot_jail                      Indicates that this is to run in "robot
294                                    jail" meaning without visibility to any
295                                    apolloxxx import files, programs, etc.
296    gqip                            This indicates that the log, report and
297                                    output files produced by robot should be
298                                    gzipped to save space.
299    """
300
301    if not gv.valid_value(robot_cmd_buf):
302        return False
303
304    # Get globals set by init_robot_test_base_dir_path().
305    module = sys.modules["__main__"]
306    try:
307        ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
308    except NameError:
309        init_robot_test_base_dir_path()
310        ROBOT_TEST_BASE_DIR_PATH = getattr(module, "ROBOT_TEST_BASE_DIR_PATH")
311
312    ROBOT_TEST_RUNNING_FROM_SB = \
313        gm.get_mod_global("ROBOT_TEST_RUNNING_FROM_SB")
314
315    if robot_jail == "":
316        if ROBOT_TEST_RUNNING_FROM_SB:
317            robot_jail = 0
318        else:
319            robot_jail = 1
320
321    robot_jail = int(robot_jail)
322    ROBOT_JAIL = os.environ.get('ROBOT_JAIL', '')
323    gp.dprint_vars(ROBOT_TEST_BASE_DIR_PATH, ROBOT_TEST_RUNNING_FROM_SB,
324                   ROBOT_JAIL, robot_jail)
325
326    # Save PATH and PYTHONPATH to be restored later.
327    os.environ["SAVED_PYTHONPATH"] = os.environ.get("PYTHONPATH", "")
328    os.environ["SAVED_PATH"] = os.environ.get("PATH", "")
329
330    if robot_jail:
331        PYTHONPATH = ROBOT_TEST_BASE_DIR_PATH + "lib"
332        NEW_PATH_LIST = [ROBOT_TEST_BASE_DIR_PATH + "bin"]
333        # Coding special case to preserve python27_path.
334        python27_path = "/opt/rh/python27/root/usr/bin"
335        PATH_LIST = os.environ.get("PATH", "").split(":")
336        if python27_path in PATH_LIST:
337            NEW_PATH_LIST.append(python27_path)
338        NEW_PATH_LIST.extend(["/usr/local/sbin", "/usr/local/bin", "/usr/sbin",
339                              "/usr/bin", "/sbin", "/bin"])
340        PATH = ":".join(NEW_PATH_LIST)
341    else:
342        PYTHONPATH = os.environ.get('PYTHONPATH', '') + ":" +\
343            ROBOT_TEST_BASE_DIR_PATH + "lib/"
344        PATH = os.environ.get('PATH', '') + ":" + ROBOT_TEST_BASE_DIR_PATH +\
345            "bin/"
346
347    os.environ['PYTHONPATH'] = PYTHONPATH
348    os.environ['PATH'] = PATH
349    gp.dprint_vars(PATH, PYTHONPATH)
350
351    os.environ['FFDC_DIR_PATH_STYLE'] = os.environ.get('FFDC_DIR_PATH_STYLE',
352                                                       '1')
353
354    test_mode = getattr(module, "test_mode")
355
356    gp.qpissuing(robot_cmd_buf, test_mode)
357    if test_mode:
358        os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
359        os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
360        return True
361
362    if quiet:
363        DEVNULL = open(os.devnull, 'wb')
364        stdout = DEVNULL
365    else:
366        stdout = None
367    sub_proc = subprocess.Popen(robot_cmd_buf, stdout=stdout, shell=True)
368    sub_proc.communicate()
369    shell_rc = sub_proc.returncode
370    if shell_rc != 0:
371        hex = 1
372        gp.pvar(shell_rc, hex)
373        os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
374        os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
375        return False
376
377    os.environ["PATH"] = os.environ.get("SAVED_PATH", "")
378    os.environ["PYTHONPATH"] = os.environ.get("SAVED_PYTHONPATH", "")
379
380    if not gzip:
381        return True
382
383    # gzip the output files.
384    # Retrieve the parms from the robot command buffer.
385    robot_cmd_buf_dict = gc.parse_command_string(robot_cmd_buf)
386    # Get prefix from the log parm.
387    prefix = re.sub('log\.html$', '', robot_cmd_buf_dict['log'])
388    gp.qprintn()
389    rc, outbuf = gc.cmd_fnc("cd " + robot_cmd_buf_dict['outputdir'] +
390                            " ; gzip " + robot_cmd_buf_dict['output'] +
391                            " " + robot_cmd_buf_dict['log'] +
392                            " " + robot_cmd_buf_dict['report'])
393
394    outputdir = gm.add_trailing_slash(robot_cmd_buf_dict['outputdir'])
395    Output = outputdir + robot_cmd_buf_dict['output'] + ".gz"
396    Log = outputdir + robot_cmd_buf_dict['log'] + ".gz"
397    Report = outputdir + robot_cmd_buf_dict['report'] + ".gz"
398    gp.qprintn("\ngzipped output:")
399    gp.qpvars(0, 9, Output, Log, Report)
400
401    return True
402