1#!/usr/bin/env python3
2r"""
3#    @file     openbmc_ffdc_list.py
4#    @brief    List for FFDC ( First failure data capture )
5#              commands and files to be collected as a part
6#              of the test case failure.
7"""
8import os
9
10from robot.libraries.BuiltIn import BuiltIn
11
12# -------------------
13# FFDC default list
14# -------------------
15# -----------------------------------------------------------------
16# Dict Name {  Index string : { Key String :  Command string} }
17# -----------------------------------------------------------------
18# Add cmd's needed to be part of the ffdc report manifest file
19FFDC_BMC_CMD = {
20    "DRIVER INFO": {
21        # String Name         Command
22        "FW Level": "cat /etc/os-release",
23        "FW Timestamp": "cat /etc/timestamp",
24    },
25    "BMC DATA": {
26        "BMC OS": "uname -a",
27        "BMC Uptime": "uptime;cat /proc/uptime",
28        "BMC File System Disk Space Usage": "df -hT",
29        "BMC Date Time": "date;/sbin/hwclock --show;/usr/bin/timedatectl",
30    },
31    "APPLICATION DATA": {
32        "BMC state": "/usr/bin/obmcutil state",
33    },
34}
35# Add file name and corresponding command needed for BMC
36FFDC_BMC_FILE = {
37    "BMC FILES": {
38        # File Name         Command
39        "BMC_flash_side.txt": (
40            "cat /sys/class/watchdog/watchdog1/bootstatus"
41            " >/tmp/BMC_flash_side.txt 2>&1"
42        ),
43        "BMC_hwmon.txt": (
44            "grep -r . /sys/class/hwmon/* >/tmp/BMC_hwmon.txt 2>&1"
45        ),
46        "BMC_proc_list.txt": "top -n 1 -b >/tmp/BMC_proc_list.txt 2>&1",
47        "BMC_proc_fd_active_list.txt": (
48            "ls -Al /proc/*/fd/ >/tmp/BMC_proc_fd_active_list.txt 2>&1"
49        ),
50        "BMC_journalctl_nopager.txt": (
51            "journalctl --no-pager >/tmp/BMC_journalctl_nopager.txt 2>&1"
52        ),
53        "BMC_journalctl_pretty.json": (
54            "journalctl -o json-pretty >/tmp/BMC_journalctl_pretty.json 2>&1"
55        ),
56        "BMC_dmesg.txt": "dmesg >/tmp/BMC_dmesg.txt 2>&1",
57        "BMC_procinfo.txt": "cat /proc/cpuinfo >/tmp/BMC_procinfo.txt 2>&1",
58        "BMC_meminfo.txt": "cat /proc/meminfo >/tmp/BMC_meminfo.txt 2>&1",
59        "BMC_systemd.txt": "systemctl status --all >/tmp/BMC_systemd.txt 2>&1",
60        "BMC_failed_service.txt": (
61            "systemctl list-units --failed >/tmp/BMC_failed_service.txt 2>&1"
62        ),
63        "BMC_list_service.txt": (
64            "systemctl list-jobs >/tmp/BMC_list_service.txt 2>&1"
65        ),
66        "BMC_obmc_console.txt": (
67            "cat /var/log/obmc-console.log >/tmp/BMC_obmc_console.txt 2>&1"
68        ),
69        "BMC_obmc_console1.txt": (
70            "cat /var/log/obmc-console1.log >/tmp/BMC_obmc_console1.txt 2>&1"
71        ),
72        "PEL_logs_list.json": "peltool -l >/tmp/PEL_logs_list.json 2>&1",
73        "PEL_logs_complete_list.json": (
74            "peltool -l -a -f >/tmp/PEL_logs_complete_list.json 2>&1"
75        ),
76        "PEL_logs_display.json": "peltool -a >/tmp/PEL_logs_display.json 2>&1",
77        "PEL_logs_complete_display.json": (
78            "peltool -a -f -h>/tmp/PEL_logs_complete_display.json 2>&1"
79        ),
80        "PEL_logs_badPEL.txt": "hexdump -C"
81        + " /var/lib/phosphor-logging/extensions/pels/badPEL>/tmp/PEL_logs_badPEL.txt"
82        " 2>&1",
83        "PLDM_fru_record.txt": (
84            "pldmtool fru getfrurecordtable>/tmp/PLDM_fru_record.txt 2>&1"
85        ),
86        "BMC_pldm_flight_recorder.txt": (
87            "rm -rf /tmp/pldm_flight_recorder; killall -s SIGUSR1 pldmd;"
88        )
89        + " sleep 5; cat /tmp/pldm_flight_recorder >"
90        " /tmp/BMC_pldm_flight_recorder.txt 2>&1;",
91        "OCC_state.txt": 'echo "OCC state check";for i in {0..3};'
92        + " do (echo /org/open_power/control/occ$i;"
93        + " busctl get-property org.open_power.OCC.Control"
94        " /org/open_power/control/occ$i"
95        + " org.open_power.OCC.Status OccActive) done > /tmp/OCC_state.txt"
96        " 2>&1",
97        "bmcweb_persistent_data.json": (
98            "cat /home/root/bmcweb_persistent_data.json"
99        )
100        + " > /tmp/bmcweb_persistent_data.json",
101        "GUARD_list.txt": "guard -l > /tmp/GUARD_list.txt 2>&1",
102        "fan_control_dump.json": "fanctl dump; sleep 5",
103        "DEVTREE": (
104            "cat /var/lib/phosphor-software-manager/pnor/rw/DEVTREE >"
105            " /tmp/DEVTREE 2>&1"
106        ),
107    },
108}
109# Add file name and corresponding command needed for all Linux distributions
110FFDC_OS_ALL_DISTROS_FILE = {
111    "OS FILES": {
112        # File Name         Command
113        "OS_msglog.txt": (
114            "cat /sys/firmware/opal/msglog >/tmp/OS_msglog.txt 2>&1"
115        ),
116        "OS_cpufrequency.txt": "ppc64_cpu --frequency "
117        + ">/tmp/OS_cpufrequency.txt 2>&1",
118        "OS_dmesg.txt": "dmesg >/tmp/OS_dmesg.txt 2>&1",
119        "OS_opal_prd.txt": "cat /var/log/opal-prd* >/tmp/OS_opal_prd.txt 2>&1",
120        "OS_boot.txt": "cat /var/log/boot.log >/tmp/OS_boot.txt 2>&1",
121        "OS_procinfo.txt": "cat /proc/cpuinfo >/tmp/OS_procinfo.txt 2>&1",
122        "OS_meminfo.txt": "cat /proc/meminfo >/tmp/OS_meminfo.txt 2>&1",
123        "OS_netstat.txt": "netstat -a >/tmp/OS_netstat.txt 2>&1",
124        "OS_lspci.txt": "lspci >/tmp/OS_lspci.txt 2>&1",
125        "OS_lscpu.txt": "lscpu >/tmp/OS_lscpu.txt 2>&1",
126        "OS_lscfg.txt": "lscfg >/tmp/OS_lscfg.txt 2>&1",
127        "OS_journalctl_nopager.txt": "journalctl --no-pager -b "
128        + "> /tmp/OS_journalctl_nopager.txt  2>&1",
129    },
130}
131# Add file name and corresponding command needed for Ubuntu Linux
132FFDC_OS_UBUNTU_FILE = {
133    "OS FILES": {
134        # File Name         Command
135        "OS_isusb.txt": "{ lsusb -t ; lsusb -v ; } >/tmp/OS_isusb.txt 2>&1",
136        "OS_kern.txt": (
137            "tail -n 50000 /var/log/kern.log >/tmp/OS_kern.txt 2>&1"
138        ),
139        "OS_authlog.txt": (
140            "{ cat /var/log/auth.log; cat /var/log/auth.log.1 ; } "
141        )
142        + ">/tmp/OS_authlog.txt 2>&1",
143        "OS_syslog.txt": (
144            "tail -n 200000 /var/log/syslog >/tmp/OS_syslog.txt 2>&1"
145        ),
146        "OS_info.txt": "{ uname -a; dpkg -s opal-prd; dpkg -s ipmitool ; } "
147        + ">/tmp/OS_info.txt 2>&1",
148        "OS_sosreport.txt": (
149            "{ rm -rf /tmp/sosreport*FFDC* ; sosreport --batch --tmp-dir "
150        )
151        + "/tmp --ticket-number FFDC ; } >/tmp/OS_sosreport.txt 2>&1",
152    },
153}
154# Add file name and corresponding command needed for RHEL Linux
155FFDC_OS_RHEL_FILE = {
156    "OS FILES": {
157        # File Name         Command
158        "OS_rsct.txt": "/usr/bin/ctversion -bv >/tmp/OS_rsct.txt 2>&1",
159        "OS_secure.txt": "cat /var/log/secure >/tmp/OS_secure.txt 2>&1",
160        "OS_syslog.txt": "tail -n 200000 /var/log/messages "
161        + ">/tmp/OS_syslog.txt 2>&1",
162        "OS_info.txt": "{ lsb_release -a; cat /etc/redhat-release; "
163        + "uname -a; rpm -qa ; } >/tmp/OS_info.txt 2>&1",
164        "OS_sosreport.txt": (
165            "{ rm -rf /tmp/sosreport*FFDC* ; sosreport --batch --tmp-dir "
166        )
167        + "/tmp --label FFDC ; } >/tmp/OS_sosreport.txt 2>&1",
168    },
169}
170# Add file name and corresponding command needed for AIX.
171FFDC_OS_AIX_FILE = {
172    "OS FILES": {
173        # File Name         Command
174        "OS_errpt.txt": "errpt >/tmp/OS_errpt.txt 2>&1 ; errclear 0;",
175        "OS_processors.txt": "bindprocessor -q >/tmp/OS_processors.txt 2>&1",
176    },
177}
178
179try:
180    redfish_support_trans_state = os.environ.get(
181        "REDFISH_SUPPORT_TRANS_STATE", 0
182    ) or int(
183        BuiltIn().get_variable_value(
184            "${REDFISH_SUPPORT_TRANS_STATE}", default=0
185        )
186    )
187except RobotNotRunningError:
188    pass
189
190OPENBMC_BASE = "/xyz/openbmc_project/"
191OPENPOWER_BASE = "/org/open_power/"
192ENUMERATE_SENSORS = OPENBMC_BASE + "sensors/enumerate"
193ENUMERATE_INVENTORY = OPENBMC_BASE + "inventory/enumerate"
194ENUMERATE_ELOG = OPENBMC_BASE + "logging/entry/enumerate"
195ENUMERATE_LED = OPENBMC_BASE + "led/enumerate"
196ENUMERATE_SW = OPENBMC_BASE + "software/enumerate"
197ENUMERATE_CONTROL = OPENBMC_BASE + "control/enumerate"
198ENUMERATE_STATE = OPENBMC_BASE + "state/enumerate"
199ENUMERATE_OCC = OPENPOWER_BASE + "/enumerate"
200ENUMERATE_DUMPS = OPENBMC_BASE + "dumps/enumerate"
201ENUMERATE_USER = OPENBMC_BASE + "user/enumerate"
202
203# Add file name and corresponding Get Request
204FFDC_GET_REQUEST = {
205    "GET REQUESTS": {
206        # File Name         Command
207        "FIRMWARE_list.txt": ENUMERATE_SW,
208        "BMC_sensor_list.txt": ENUMERATE_SENSORS,
209        "BMC_control_list.txt": ENUMERATE_CONTROL,
210        "BMC_inventory.txt": ENUMERATE_INVENTORY,
211        "BMC_elog.txt": ENUMERATE_ELOG,
212        "BMC_led.txt": ENUMERATE_LED,
213        "BMC_state.txt": ENUMERATE_STATE,
214        "OCC_state.txt": ENUMERATE_OCC,
215        "BMC_dumps.txt": ENUMERATE_DUMPS,
216        "BMC_USER.txt": ENUMERATE_USER,
217    },
218}
219
220# Remove the REST dictionary elements.
221if redfish_support_trans_state == 1:
222    for key in list(FFDC_GET_REQUEST):
223        del FFDC_GET_REQUEST[key]
224
225REDFISH_BASE = "/redfish/v1/"
226REDFISH_ELOG = REDFISH_BASE + "Systems/system/LogServices/EventLog/Entries"
227REDFISH_FIRMWARE = REDFISH_BASE + "UpdateService/FirmwareInventory"
228
229# Add file name and corresponding Get Request
230FFDC_GET_REDFISH_REQUEST = {
231    "GET REQUESTS": {
232        # File Name         Command
233        "BMC_redfish_elog.txt": REDFISH_ELOG,
234    },
235}
236
237# Define your keywords in method/utils and call here
238FFDC_METHOD_CALL = {
239    "BMC LOGS": {
240        # Description               Keyword name
241        "Start ffdc cleanup": "BMC FFDC Cleanup",
242        "FFDC Generic Report": "BMC FFDC Manifest",
243        "BMC Specific Files": "BMC FFDC Files",
244        "Get Request FFDC": "BMC FFDC Get Requests",
245        "Get Redfish Request FFDC": "BMC FFDC Get Redfish Requests",
246        "OS FFDC": "OS FFDC Files",
247        "Core Files": "SCP Coredump Files",
248        "SEL Log": "Collect eSEL Log",
249        "Sys Inventory Files": "System Inventory Files",
250        "Dump Files": "SCP Dump Files",
251        "PEL Files": "Collect PEL Log",
252        "Redfish Log": "Enumerate Redfish Resources",
253        "Firmware Log": "Enumerate Redfish Resources  "
254        + " enum_uri=/redfish/v1/UpdateService/FirmwareInventory  "
255        + " file_enum_name=redfish_FIRMWARE_list.txt",
256        "Redfish OEM Log": "Enumerate Redfish OEM Resources",
257        "End ffdc cleanup": "BMC FFDC Cleanup",
258    },
259}
260
261try:
262    platform_arch_type = os.environ.get(
263        "PLATFORM_ARCH_TYPE", ""
264    ) or BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
265except RobotNotRunningError:
266    pass
267
268# Filter the logs based on platform type.
269if platform_arch_type == "x86":
270    del FFDC_BMC_FILE["BMC FILES"]["PEL_logs_list.json"]
271    del FFDC_BMC_FILE["BMC FILES"]["PEL_logs_display.json"]
272    del FFDC_METHOD_CALL["BMC LOGS"]["PEL Files"]
273
274# -----------------------------------------------------------------
275# base class for FFDC default list
276
277
278class openbmc_ffdc_list:
279    def get_ffdc_bmc_cmd(self, i_type):
280        r"""
281        #######################################################################
282        #   @brief    This method returns the list from the dictionary for cmds
283        #   @param    i_type: @type string: string index lookup
284        #   @return   List of key pair from the dictionary
285        #######################################################################
286        """
287        return FFDC_BMC_CMD[i_type].items()
288
289    def get_ffdc_bmc_file(self, i_type):
290        r"""
291        #######################################################################
292        #   @brief    This method returns the list from the dictionary for scp
293        #   @param    i_type: @type string: string index lookup
294        #   @return   List of key pair from the dictionary
295        #######################################################################
296        """
297        return FFDC_BMC_FILE[i_type].items()
298
299    def get_ffdc_get_request(self, i_type):
300        r"""
301        #######################################################################
302        #   @brief    This method returns the list from the dictionary for scp
303        #   @param    i_type: @type string: string index lookup
304        #   @return   List of key pair from the dictionary
305        #######################################################################
306        """
307        return FFDC_GET_REQUEST[i_type].items()
308
309    def get_ffdc_get_redfish_request(self, i_type):
310        r"""
311        #######################################################################
312        #   @brief    This method returns the list from the dictionary for scp
313        #   @param    i_type: @type string: string index lookup
314        #   @return   List of key pair from the dictionary
315        #######################################################################
316        """
317        return FFDC_GET_REDFISH_REQUEST[i_type].items()
318
319    def get_ffdc_cmd_index(self):
320        r"""
321        #######################################################################
322        #   @brief    This method returns the list index from dictionary
323        #   @return   List of index to the dictionary
324        #######################################################################
325        """
326        return FFDC_BMC_CMD.keys()
327
328    def get_ffdc_get_request_index(self):
329        r"""
330        #######################################################################
331        #   @brief    This method returns the list index from dictionary
332        #   @return   List of index to the dictionary
333        #######################################################################
334        """
335        return FFDC_GET_REQUEST.keys()
336
337    def get_ffdc_get_redfish_request_index(self):
338        r"""
339        #######################################################################
340        #   @brief    This method returns the list index from dictionary
341        #   @return   List of index to the dictionary
342        #######################################################################
343        """
344        return FFDC_GET_REDFISH_REQUEST.keys()
345
346    def get_ffdc_file_index(self):
347        r"""
348        #######################################################################
349        #   @brief    This method returns the list index from dictionary
350        #   @return   List of index to the dictionary
351        #######################################################################
352        """
353        return FFDC_BMC_FILE.keys()
354
355    def get_ffdc_method_index(self):
356        r"""
357        #######################################################################
358        #   @brief    This method returns the key pair from the dictionary
359        #   @return   Index of the method dictionary
360        #######################################################################
361        """
362        return FFDC_METHOD_CALL.keys()
363
364    def get_ffdc_method_desc(self, index):
365        r"""
366        #######################################################################
367        #   @brief   This method returns the just the keys from the dictionary.
368        #   @return  List of ffdc descriptions.
369        #######################################################################
370        """
371        return FFDC_METHOD_CALL[index].keys()
372
373    def get_ffdc_method_call(self, i_type):
374        r"""
375        #######################################################################
376        #   @brief    This method returns the key pair from the dictionary
377        #   @return   List of key pair keywords
378        #######################################################################
379        """
380        return FFDC_METHOD_CALL[i_type].items()
381
382    def get_ffdc_os_all_distros_index(self):
383        r"""
384        #######################################################################
385        #   @brief    This method returns the key pair from the dictionary
386        #   @return   Index of the method dictionary
387        #######################################################################
388        """
389        return FFDC_OS_ALL_DISTROS_FILE.keys()
390
391    def get_ffdc_os_all_distros_call(self, i_type):
392        r"""
393        #######################################################################
394        #   @brief    This method returns the key pair from the dictionary
395        #   @return   List of key pair keywords
396        #######################################################################
397        """
398        return FFDC_OS_ALL_DISTROS_FILE[i_type].items()
399
400    def get_ffdc_os_distro_index(self, distro):
401        r"""
402        #######################################################################
403        #   @brief    This method returns the key pair from the dictionary
404        #   @return   Index of the method dictionary
405        #######################################################################
406        """
407        distro_file = "FFDC_OS_" + str(distro).upper() + "_FILE"
408        return eval(distro_file).keys()
409
410    def get_ffdc_os_distro_call(self, i_type, distro):
411        r"""
412        #######################################################################
413        #   @brief    This method returns the key pair from the dictionary
414        #   @return   List of key pair keywords
415        #######################################################################
416        """
417        distro_file = "FFDC_OS_" + str(distro).upper() + "_FILE"
418        return eval(distro_file)[i_type].items()
419
420    def get_strip_string(self, i_str):
421        r"""
422        #######################################################################
423        #   @brief    Returns the stripped strings
424        #   @param    i_str: @type string: string name
425        #   @return   Remove all special chars and return the string
426        #######################################################################
427        """
428        return "".join(e for e in i_str if e.isalnum())
429
430    def get_esel_index(self, esel_list):
431        r"""
432        #######################################################################
433        #   @brief    Returns the eSEL binary index.
434        #   @param    esel_ist: @type list: eSEL list.
435        #   @return   Index of "ESEL=" in the list.
436        #######################################################################
437        """
438        index = [i for i, str in enumerate(esel_list) if "ESEL=" in str]
439        return index[0]
440
441    def get_dump_index(self, dump_list):
442        r"""
443        #######################################################################
444        #   @brief    Returns the eSEL binary index.
445        #   @param    esel_ist: @type list: eSEL list.
446        #   @return   Index of "ESEL=" in the list.
447        #######################################################################
448        """
449        index = [i for i, str in enumerate(dump_list) if "DUMP=" in str]
450        return index[0]
451