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