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