1#!/usr/bin/env python3
2
3r"""
4PEL functions.
5"""
6
7import json
8import os
9import sys
10from datetime import datetime
11
12import bmc_ssh_utils as bsu
13import func_args as fa
14
15base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16sys.path.append(base_path + "/data/")
17
18import pel_variables  # NOQA
19
20
21class PeltoolException(Exception):
22    r"""
23    Base class for peltool related exceptions.
24    """
25
26    def __init__(self, message):
27        self.message = message
28        super().__init__(self.message)
29
30
31def peltool(option_string, parse_json=True, **bsu_options):
32    r"""
33    Run peltool on the BMC with the caller's option string and return the result.
34
35    Example:
36
37    ${pel_results}=  Peltool  -l
38    Rprint Vars  pel_results
39
40    pel_results:
41      [0x50000031]:
42        [CompID]:                       0x1000
43        [PLID]:                         0x50000031
44        [Subsystem]:                    BMC Firmware
45        [Message]:                      An application had an internal failure
46        [SRC]:                          BD8D1002
47        [Commit Time]:                  02/25/2020  04:51:31
48        [Sev]:                          Unrecoverable Error
49        [CreatorID]:                    BMC
50
51    Description of argument(s):
52    option_string                   A string of options which are to be
53                                    processed by the peltool command.
54    parse_json                      Indicates that the raw JSON data should
55                                    parsed into a list of dictionaries.
56    bsu_options                     Options to be passed directly to
57                                    bmc_execute_command. See its prolog for
58                                    details.
59    """
60
61    bsu_options = fa.args_to_objects(bsu_options)
62    out_buf, _, _ = bsu.bmc_execute_command(
63        "peltool " + option_string, **bsu_options
64    )
65    if parse_json:
66        try:
67            return json.loads(out_buf)
68        except ValueError as e:
69            if type(out_buf) is str:
70                return out_buf
71            else:
72                print(str(e))
73                return {}
74    return out_buf
75
76
77def get_pel_data_from_bmc(
78    include_hidden_pels=False, include_informational_pels=False
79):
80    r"""
81    Returns PEL data from BMC else throws exception.
82
83    Description of arguments:
84    include_hidden_pels           True/False (default: False).
85                                  Set True to get hidden PELs else False.
86    include_informational_pels    True/False (default: False).
87                                  Set True to get informational PELs else False.
88    """
89    try:
90        pel_cmd = " -l"
91        if include_hidden_pels:
92            pel_cmd = pel_cmd + " -h"
93        if include_informational_pels:
94            pel_cmd = pel_cmd + " -f"
95        pel_data = peltool(pel_cmd)
96        if not pel_data:
97            print("No PEL data present in BMC ...")
98    except Exception as exception:
99        raise PeltoolException(
100            "Failed to get PEL data from BMC : " + str(exception)
101        ) from exception
102    return pel_data
103
104
105def verify_no_pel_exists_on_bmc():
106    r"""
107    Verify that no PEL exists in BMC. Raise an exception if it does.
108    """
109
110    try:
111        pel_data = get_pel_data_from_bmc()
112
113        if len(pel_data) == 0:
114            return True
115
116        print("PEL data present. \n", pel_data)
117        raise PeltoolException("PEL data present in BMC")
118    except Exception as exception:
119        raise PeltoolException(
120            "Failed to get PEL data from BMC : " + str(exception)
121        ) from exception
122
123
124def compare_pel_and_redfish_event_log(pel_record, event_record):
125    r"""
126    Compare PEL log attributes like "SRC", "Created at" with Redfish
127    event log attributes like "EventId", "Created".
128    Return False if they do not match.
129
130    Description of arguments:
131    pel_record     PEL record.
132    event_record   Redfish event which is equivalent of PEL record.
133    """
134
135    try:
136        # Below is format of PEL record / event record and following
137        # i.e. "SRC", "Created at" from
138        # PEL record is compared with "EventId", "Created" from event record.
139
140        # PEL Log attributes
141        # SRC        : XXXXXXXX
142        # Created at : 11/14/2022 12:38:04
143
144        # Event log attributes
145        # EventId : XXXXXXXX XXXXXXXX XXXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
146
147        # Created : 2022-11-14T12:38:04+00:00
148
149        print(f"\nPEL records : {pel_record}")
150        print(f"\nEvent records : {event_record}")
151
152        pel_src = pel_record["pel_data"]["SRC"]
153        pel_created_time = pel_record["pel_detail_data"]["Private Header"][
154            "Created at"
155        ]
156
157        event_ids = (event_record["EventId"]).split(" ")
158
159        event_time_format = (event_record["Created"]).split("T")
160        event_date = (event_time_format[0]).split("-")
161        event_date = datetime(
162            int(event_date[0]), int(event_date[1]), int(event_date[2])
163        )
164        event_date = event_date.strftime("%m/%d/%Y")
165        event_sub_time_format = (event_time_format[1]).split("+")
166        event_date_time = event_date + " " + event_sub_time_format[0]
167
168        event_created_time = event_date_time.replace("-", "/")
169
170        print(f"\nPEL SRC : {pel_src} | PEL Created Time : {pel_created_time}")
171
172        print(
173            f"\nError event ID : {event_ids[0]} | Error Log Created Time "
174            + ": {event_created_time}"
175        )
176
177        if pel_src == event_ids[0] and pel_created_time == event_created_time:
178            print(
179                "\nPEL SRC and created date time match with "
180                "event ID, created time"
181            )
182        else:
183            raise PeltoolException(
184                "\nPEL SRC and created date time did not "
185                "match with event ID, created time"
186            )
187    except Exception as exception:
188        raise PeltoolException(
189            "Exception occurred during PEL and Event log "
190            "comparison for SRC or event ID and created "
191            "time : " + str(exception)
192        ) from exception
193
194
195def fetch_all_pel_ids_for_src(src_id, severity, include_hidden_pels=False):
196    r"""
197    Fetch all PEL IDs for the input SRC ID based on the severity type
198    in the list format.
199
200    Description of arguments:
201    src_id                SRC ID (e.g. BCXXYYYY).
202    severity              PEL severity (e.g. "Predictive Error"
203                                             "Recovered Error").
204    include_hidden_pels   True/False (default: False).
205                          Set True to get hidden PELs else False.
206    """
207
208    try:
209        src_pel_ids = []
210        pel_data = get_pel_data_from_bmc(include_hidden_pels)
211        pel_id_list = pel_data.keys()
212        for pel_id in pel_id_list:
213            # Check if required SRC ID with severity is present
214            if src_id in pel_data[pel_id]["SRC"]:
215                if pel_data[pel_id]["Sev"] == severity:
216                    src_pel_ids.append(pel_id)
217
218        if not src_pel_ids:
219            raise PeltoolException(
220                src_id + " with severity " + severity + " not present"
221            )
222    except Exception as exception:
223        raise PeltoolException(
224            "Failed to fetch PEL ID for required SRC : " + str(exception)
225        ) from exception
226    return src_pel_ids
227
228
229def fetch_all_src(include_hidden_pels=False):
230    r"""
231    Fetch all SRC IDs from peltool in the list format.
232
233    include_hidden_pels       True/False (default: False).
234                              Set True to get hidden PELs else False.
235    """
236    try:
237        src_id = []
238        pel_data = get_pel_data_from_bmc(include_hidden_pels)
239        if pel_data:
240            pel_id_list = pel_data.keys()
241            for pel_id in pel_id_list:
242                src_id.append(pel_data[pel_id]["SRC"])
243        print("SRC IDs: " + str(src_id))
244    except Exception as exception:
245        raise PeltoolException(
246            "Failed to fetch all SRCs : " + str(exception)
247        ) from exception
248    return src_id
249
250
251def check_for_unexpected_src(
252    unexpected_src_list=None, include_hidden_pels=False
253):
254    r"""
255    From the given unexpected SRC list, check if any unexpected SRC created
256    on the BMC. Returns 0 if no SRC found else throws exception.
257
258    Description of arguments:
259    unexpected_src_list       Give unexpected SRCs in the list format.
260                              e.g.: ["BBXXYYYY", "AAXXYYYY"].
261
262    include_hidden_pels       True/False (default: False).
263                              Set True to get hidden PELs else False.
264    """
265    try:
266        unexpected_src_count = 0
267        if not unexpected_src_list:
268            print("Unexpected SRC list is empty.")
269        src_data = fetch_all_src(include_hidden_pels)
270        for src in unexpected_src_list:
271            if src in src_data:
272                print("Found an unexpected SRC : " + src)
273                unexpected_src_count = unexpected_src_count + 1
274        if unexpected_src_count >= 1:
275            raise PeltoolException("Unexpected SRC found.")
276
277    except Exception as exception:
278        raise PeltoolException(
279            "Failed to verify unexpected SRC list : " + str(exception)
280        ) from exception
281    return unexpected_src_count
282
283
284def filter_unexpected_srcs(expected_srcs=None):
285    r"""
286    Return list of SRCs found in BMC after filtering expected SRCs.
287    If expected_srcs is None then all SRCs found in system are returned.
288
289    Description of arguments:
290    expected_srcs       List of expected SRCs. E.g. ["BBXXYYYY", "AAXXYYYY"].
291    """
292
293    srcs_found = fetch_all_src()
294    if not expected_srcs:
295        expected_srcs = []
296    print(expected_srcs)
297    return list(set(srcs_found) - set(expected_srcs))
298
299
300def get_bmc_event_log_id_for_pel(pel_id):
301    r"""
302    Return BMC event log ID for the given PEL ID.
303
304    Description of arguments:
305    pel_id       PEL ID. E.g. 0x50000021.
306    """
307
308    pel_data = peltool("-i " + pel_id)
309    print(pel_data)
310    bmc_id_for_pel = pel_data["Private Header"]["BMC Event Log Id"]
311    return bmc_id_for_pel
312
313
314def get_latest_pels(number_of_pels=1):
315    r"""
316    Return latest PEL IDs.
317
318    Description of arguments:
319    number_of_pels       Number of PELS to be returned.
320    """
321
322    pel_data = peltool("-lr")
323    pel_ids = list(pel_data.keys())
324    return pel_ids[:number_of_pels]
325