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