1#!/usr/bin/env python
2
3r"""
4This module is the python counterpart to openbmc_ffdc.robot..
5"""
6
7import os
8
9import gen_print as gp
10import gen_valid as gv
11import gen_robot_keyword as grk
12import state as st
13
14from robot.libraries.BuiltIn import BuiltIn
15
16
17def ffdc(ffdc_dir_path=None,
18         ffdc_prefix=None,
19         ffdc_function_list="",
20         comm_check=True):
21    r"""
22    Gather First Failure Data Capture (FFDC).
23
24    This includes:
25    - Set global FFDC_TIME.
26    - Create FFDC work space directory.
27    - Write test info details.
28    - Call BMC methods to write/collect FFDC data.
29
30    Description of arguments:
31    ffdc_dir_path                   The dir path where FFDC data should be put.
32    ffdc_prefix                     The prefix to be given to each FFDC file name generated.
33    ffdc_function_list              A colon-delimited list of all the types of FFDC data you wish to have
34                                    collected.  A blank value means that all possible kinds of FFDC are to be
35                                    collected.  See FFDC_METHOD_CALL object in lib/openbmc_ffdc_list.py for
36                                    possible choices.
37    comm_check                      Do a communications check prior to collecting FFDC.  If commincation to
38                                    the BMC can't be established, abort the FFDC collection.
39    """
40
41    ffdc_file_list = []
42
43    # Check if Ping and SSH connection is alive
44    OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}")
45
46    if comm_check:
47        state = st.get_state(req_states=['ping', 'uptime', 'rest'])
48        gp.qprint_var(state)
49        if not int(state['ping']):
50            gp.print_error("BMC is not ping-able.  Terminating FFDC collection.\n")
51            return ffdc_file_list
52
53        if not int(state['rest']):
54            gp.print_error("REST commands to the BMC are failing."
55                           + "  Terminating FFDC collection.\n")
56            return ffdc_file_list
57
58        if state['uptime'] == "":
59            gp.print_error("BMC is not communicating via ssh.  Terminating FFDC"
60                           + " collection.\n")
61            return ffdc_file_list
62
63    gp.qprint_timen("Collecting FFDC.")
64
65    # Get default values for arguments.
66    ffdc_dir_path, ffdc_prefix = set_ffdc_defaults(ffdc_dir_path, ffdc_prefix)
67    gp.qprint_var(ffdc_dir_path)
68    gp.qprint_var(ffdc_prefix)
69
70    # LOG_PREFIX is used by subordinate functions.
71    LOG_PREFIX = ffdc_dir_path + ffdc_prefix
72    BuiltIn().set_global_variable("${LOG_PREFIX}", LOG_PREFIX)
73
74    cmd_buf = ["Create Directory", ffdc_dir_path]
75    gp.qprint_issuing(cmd_buf)
76    status, output = BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
77    if status != "PASS":
78        error_message = gp.sprint_error_report("Create Directory failed"
79                                               + " with the following"
80                                               + " error:\n" + output)
81        BuiltIn().fail(error_message)
82
83    # FFDC_FILE_PATH is used by Header Message.
84    FFDC_FILE_PATH = ffdc_dir_path + ffdc_prefix + "BMC_general.txt"
85    BuiltIn().set_global_variable("${FFDC_FILE_PATH}", FFDC_FILE_PATH)
86
87    status, ffdc_file_list = grk.run_key_u("Header Message")
88    status, ffdc_file_sub_list = \
89        grk.run_key_u("Call FFDC Methods  ffdc_function_list="
90                      + ffdc_function_list)
91
92    # Combine lists, remove duplicates and sort.
93    ffdc_file_list = sorted(set(ffdc_file_list + ffdc_file_sub_list))
94
95    gp.qprint_timen("Finished collecting FFDC.")
96
97    return ffdc_file_list
98
99
100def set_ffdc_defaults(ffdc_dir_path=None,
101                      ffdc_prefix=None):
102    r"""
103    Set a default value for ffdc_dir_path and ffdc_prefix if they don't
104    already have values.  Return both values.
105
106    Description of arguments:
107    ffdc_dir_path  The dir path where FFDC data should be put.
108    ffdc_prefix    The prefix to be given to each FFDC file name generated.
109
110    NOTE: If global variable ffdc_dir_path_style is set to ${1}, this function
111    will create default values in a newer way.  Otherwise, its behavior
112    will remain unchanged.
113    """
114
115    # Note: Several subordinate functions like 'Get Test Dir and Name' and
116    # 'Header Message' expect global variable FFDC_TIME to be set.
117    cmd_buf = ["Get Current Time Stamp"]
118    gp.dprint_issuing(cmd_buf)
119    FFDC_TIME = BuiltIn().run_keyword(*cmd_buf)
120    BuiltIn().set_global_variable("${FFDC_TIME}", FFDC_TIME)
121
122    ffdc_dir_path_style = BuiltIn().get_variable_value(
123        "${ffdc_dir_path_style}")
124
125    if ffdc_dir_path is None:
126        if ffdc_dir_path_style:
127            try:
128                ffdc_dir_path = os.environ['FFDC_DIR_PATH']
129            except KeyError:
130                ffdc_dir_path = os.path.dirname(
131                    BuiltIn().get_variable_value("${LOG_FILE}")) + "/"
132        else:
133            FFDC_LOG_PATH = os.getcwd() + "/logs/"
134            if FFDC_LOG_PATH is None:
135                FFDC_LOG_PATH = ""
136            if FFDC_LOG_PATH == "":
137                FFDC_LOG_PATH = os.path.dirname(
138                    BuiltIn().get_variable_value("${LOG_FILE}")) + "/"
139            error_message = gv.valid_value(FFDC_LOG_PATH,
140                                           var_name="FFDC_LOG_PATH")
141            if error_message != "":
142                error_message = gp.sprint_error_report(error_message)
143                BuiltIn().fail(error_message)
144            FFDC_LOG_PATH = os.path.normpath(FFDC_LOG_PATH) + os.sep
145
146            cmd_buf = ["Get Test Dir and Name"]
147            gp.print_issuing(cmd_buf)
148            suitename, testname = BuiltIn().run_keyword(*cmd_buf)
149
150            ffdc_dir_path = FFDC_LOG_PATH + suitename + "/" + testname + "/"
151
152    # Add trailing slash.
153    ffdc_dir_path = os.path.normpath(ffdc_dir_path) + os.sep
154
155    if ffdc_prefix is None:
156        FFDC_TIME = BuiltIn().get_variable_value("${FFDC_TIME}")
157        if ffdc_prefix is None:
158            if ffdc_dir_path_style:
159                OPENBMC_HOST = BuiltIn().get_variable_value("${OPENBMC_HOST}")
160                OPENBMC_NICKNAME = BuiltIn().get_variable_value(
161                    "${OPENBMC_NICKNAME}", default=OPENBMC_HOST)
162                ffdc_prefix = OPENBMC_NICKNAME + "." + FFDC_TIME[2:8] + "." +\
163                    FFDC_TIME[8:14] + "."
164            else:
165                ffdc_prefix = FFDC_TIME + "_"
166
167    BuiltIn().set_global_variable("${FFDC_DIR_PATH}", ffdc_dir_path)
168    BuiltIn().set_global_variable("${FFDC_PREFIX}", ffdc_prefix)
169
170    return ffdc_dir_path, ffdc_prefix
171