1*** Settings ***
2Documentation  This module provides general keywords for dump.
3
4Library         bmc_ssh_utils.py
5
6*** Variables ***
7
8*** Keywords ***
9
10Create User Initiated Dump
11    [Documentation]  Generate user initiated dump and return
12    ...  the dump id number (e.g., "5").  Optionally return EMPTY
13    ...  if out of dump space.
14    [Arguments]   ${check_out_of_space}=${False}
15
16    # Description of Argument(s):
17    # check_out_of_space   If ${False}, a dump will be created and
18    #                      its dump_id will be returned.
19    #                      If ${True}, either the dump_id will be
20    #                      returned, or the value ${EMPTY} will be
21    #                      returned if out of dump space was
22    #                      detected when creating the dump.
23
24    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
25    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
26    ...  Set Global Variable  ${REST_DUMP_URI}  /xyz/openbmc_project/dump/
27
28    ${data}=  Create Dictionary  data=@{EMPTY}
29    ${resp}=  OpenBMC Post Request
30    ...  ${REST_DUMP_URI}action/CreateDump  data=${data}  quiet=${1}
31
32    Run Keyword If  '${check_out_of_space}' == '${False}'
33    ...      Run Keyword And Return  Get The Dump Id  ${resp}
34    ...  ELSE
35    ...      Run Keyword And Return  Check For Too Many Dumps  ${resp}
36
37
38Get The Dump Id
39    [Documentation]  Wait for the dump to be created. Return the
40    ...  dump id number (e.g., "5").
41    [Arguments]  ${resp}
42
43    # Description of Argument(s):
44    # resp   Response object from action/Create Dump attempt.
45    #        Example object:
46    #        {
47    #           "data": 5,
48    #           "message": "200 OK",
49    #           "status": "ok"
50    #        },
51    #        The "data" field conveys the id number of the created dump.
52
53    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_OK}
54
55    Run Keyword If  ${resp.json()["data"]} == ${None}
56    ...  Fail  Dump id returned null.
57
58    ${dump_id}=  Set Variable  ${json["data"]}
59
60    Wait Until Keyword Succeeds  3 min  15 sec  Check Dump Existence
61    ...  ${dump_id}
62
63    [Return]  ${dump_id}
64
65
66Check For Too Many Dumps
67    [Documentation]  Return the dump_id number, or return ${EMPTY} if dump
68    ...  creation failed due to too many dumps.
69    [Arguments]  ${resp}
70
71    # Description of Argument(s):
72    # resp   Response object from action/Create Dump attempt.
73    #        Example object if there are too many dumps:
74    #       {
75    #           "data": {
76    #               "description": "xyz.openbmc_project.Dump.Create.Error.QuotaExceeded"
77    #           },
78    #           "message": "Dump not captured due to a cap.",
79    #           "status": "error"
80    #       }
81
82    # If dump was created normally, return the dump_id number.
83    Run Keyword If  '${resp.status_code}' == '${HTTP_OK}'
84    ...  Run Keyword And Return  Get The Dump Id  ${resp}
85
86    ${exception}=  Set Variable  ${resp.json()["message"]}
87    ${at_capacity}=  Set Variable  Dump not captured due to a cap
88    ${too_many_dumps}=  Evaluate  $at_capacity in $exception
89    Printn
90    Rprint Vars   exception  too_many_dumps
91    # If there are too many dumps, return ${EMPTY}, otherwise Fail.
92    ${status}=  Run Keyword If  ${too_many_dumps}  Set Variable  ${EMPTY}
93    ...  ELSE  Fail  msg=${exception}.
94
95    [Return]  ${status}
96
97
98Verify No Dump In Progress
99    [Documentation]  Verify no dump in progress.
100
101    ${dump_progress}  ${stderr}  ${rc}=  BMC Execute Command  ls /tmp
102    Should Not Contain  ${dump_progress}  obmcdump
103
104
105Check Dump Existence
106    [Documentation]  Verify if given dump exist.
107    [Arguments]  ${dump_id}
108
109    # Description of Argument(s):
110    # dump_id  An integer value that identifies a particular dump
111    #          object(e.g. 1, 3, 5).
112
113    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
114    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
115    ...  Set Global Variable  ${DUMP_ENTRY_URI}  /xyz/openbmc_project/dump/entry/
116
117    ${resp}=  OpenBMC Get Request  ${DUMP_ENTRY_URI}${dump_id}
118    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_OK}
119
120
121Delete BMC Dump
122    [Documentation]  Deletes a given bmc dump.
123    [Arguments]  ${dump_id}
124
125    # Description of Argument(s):
126    # dump_id  An integer value that identifies a particular dump (e.g. 1, 3).
127
128    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
129    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
130    ...  Set Global Variable  ${DUMP_ENTRY_URI}  /xyz/openbmc_project/dump/entry/
131
132    ${args}=  Set Variable   {"data": []}
133    ${resp}=  OpenBMC Post Request
134    ...  ${DUMP_ENTRY_URI}${dump_id}/action/Delete  data=${args}
135
136    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_OK}
137
138Delete All Dumps
139    [Documentation]  Delete all dumps.
140
141    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
142    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
143    ...  Set Global Variable  ${DUMP_ENTRY_URI}  /xyz/openbmc_project/dump/entry/
144
145    # Check if dump entries exist, if not return.
146    ${resp}=  OpenBMC Get Request  ${DUMP_ENTRY_URI}list  quiet=${1}
147    Return From Keyword If  ${resp.status_code} == ${HTTP_NOT_FOUND}
148
149    # Get the list of dump entries and delete them all.
150    ${dump_entries}=  Get URL List  ${DUMP_ENTRY_URI}
151    FOR  ${entry}  IN  @{dump_entries}
152        ${dump_id}=  Fetch From Right  ${entry}  /
153        Delete BMC Dump  ${dump_id}
154    END
155
156
157Redfish Delete BMC Dump
158    [Documentation]  Deletes a given BMC dump via Redfish..
159    [Arguments]  ${dump_id}
160
161    # Description of Argument(s):
162    # dump_id  An integer value that identifies a particular dump (e.g. 1, 3).
163
164    Redfish.Delete  /redfish/v1/Managers/bmc/LogServices/Dump/Entries/${dump_id}
165
166
167Redfish Delete All BMC Dumps
168    [Documentation]  Delete all BMC dumps via Redfish.
169
170    # Check if dump entries exist, if not return.
171    ${resp}=  Redfish.Get  /redfish/v1/Managers/bmc/LogServices/Dump/Entries
172    Return From Keyword If  ${resp.dict["Members@odata.count"]} == ${0}
173
174    Redfish.Post  /redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog
175
176
177Redfish Delete All System Dumps
178    [Documentation]  Delete all system  dumps via Redfish.
179
180    Redfish.Post  /redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog
181
182
183Delete All BMC Dump
184    [Documentation]  Delete all BMC dump entries using "DeleteAll" interface.
185
186    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
187    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
188    ...  Set Global Variable  ${REST_DUMP_URI}  /xyz/openbmc_project/dump/
189
190    ${args}=  Set Variable   {"data": []}
191    ${resp}=  Openbmc Post Request  ${REST_DUMP_URI}action/DeleteAll  data=${args}
192    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_OK}
193
194Dump Should Not Exist
195    [Documentation]  Verify that BMC dumps do not exist.
196
197    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
198    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
199    ...  Set Global Variable  ${DUMP_ENTRY_URI}  /xyz/openbmc_project/dump/entry/
200
201    ${resp}=  OpenBMC Get Request  ${DUMP_ENTRY_URI}list  quiet=${1}
202    Should Be Equal As Strings  ${resp.status_code}  ${HTTP_NOT_FOUND}
203
204Check Existence Of BMC Dump File
205    [Documentation]  Verify existence of BMC dump file.
206    [Arguments]  ${dump_id}
207
208    # Description of argument(s):
209    # dump_id  BMC dump identifier
210
211    ${dump_check_cmd}=  Set Variable
212    ...  ls /var/lib/phosphor-debug-collector/dumps
213
214    # Output of sample BMC Execute command with '2' as dump id is as follows
215    # ls /var/lib/phosphor-debug-collector/dumps/2
216    # obmcdump_2_XXXXXXXXXX.tar.xz
217    ${file_there}  ${stderr}  ${rc}=  BMC Execute Command
218    ...  ${dump_check_cmd}/${dump_id}
219    Should End With  ${file_there}  tar.xz  msg=BMC dump file not found.
220
221Get Dump Entries
222    [Documentation]  Return dump entries list.
223
224    ${resp}=  OpenBMC Get Request  ${REST_DUMP_URI}
225    Run Keyword If  '${resp.status_code}' == '${HTTP_NOT_FOUND}'
226    ...  Set Global Variable  ${DUMP_ENTRY_URI}  /xyz/openbmc_project/dump/entry/
227
228    ${dump_entries}=  Get URL List  ${DUMP_ENTRY_URI}
229    [Return]  ${dump_entries}
230
231Trigger Core Dump
232    [Documentation]  Trigger core dump.
233
234    # Find the pid of the active ipmid and kill it.
235    ${cmd_buf}=  Catenate  kill -s SEGV $(ps | egrep ' ipmid$' |
236    ...  egrep -v grep | \ cut -c1-6)
237
238    ${cmd_output}  ${stderr}  ${rc}=  BMC Execute Command  ${cmd_buf}
239    Should Be Empty  ${stderr}  msg=BMC execute command error.
240    Should Be Equal As Integers  ${rc}  ${0}
241    ...  msg=BMC execute command return code is not zero.
242
243Initiate BMC Dump Using Redfish And Return Task Id
244     [Documentation]  Initiate BMC dump via Redfish and return its task ID.
245
246     ${payload}=  Create Dictionary  DiagnosticDataType=Manager
247     ${resp}=  Redfish.Post
248     ...  /redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData
249     ...  body=${payload}  valid_status_codes=[${HTTP_ACCEPTED}]
250
251     # Example of response from above Redfish POST request.
252     # "@odata.id": "/redfish/v1/TaskService/Tasks/0",
253     # "@odata.type": "#Task.v1_4_3.Task",
254     # "Id": "0",
255     # "TaskState": "Running",
256     # "TaskStatus": "OK"
257
258     [Return]  ${resp.dict['Id']}
259
260Create User Initiated BMC Dump Via Redfish
261    [Documentation]  Generate user initiated BMC dump via Redfish and return the dump id number (e.g., "5").
262
263    ${payload}=  Create Dictionary  DiagnosticDataType=Manager
264    ${resp}=  Redfish.Post  /redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData
265    ...  body=${payload}  valid_status_codes=[${HTTP_ACCEPTED}]
266
267    # Example of response from above Redfish POST request.
268    # "@odata.id": "/redfish/v1/TaskService/Tasks/0",
269    # "@odata.type": "#Task.v1_4_3.Task",
270    # "Id": "0",
271    # "TaskState": "Running",
272    # "TaskStatus": "OK"
273
274    Wait Until Keyword Succeeds  5 min  15 sec  Check Task Completion  ${resp.dict['Id']}
275    ${task_id}=  Set Variable  ${resp.dict['Id']}
276
277    ${task_dict}=  Redfish.Get Properties  /redfish/v1/TaskService/Tasks/${task_id}
278
279    # Example of HttpHeaders field of task details.
280    # "Payload": {
281    #   "HttpHeaders": [
282    #     "Host: <BMC_IP>",
283    #      "Accept-Encoding: identity",
284    #      "Connection: Keep-Alive",
285    #      "Accept: */*",
286    #      "Content-Length: 33",
287    #      "Location: /redfish/v1/Managers/bmc/LogServices/Dump/Entries/2"]
288    #    ],
289    #    "HttpOperation": "POST",
290    #    "JsonBody": "{\"DiagnosticDataType\":\"Manager\"}",
291    #     "TargetUri": "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"
292    # }
293
294    [Return]  ${task_dict["Payload"]["HttpHeaders"][-1].split("/")[-1]}
295
296Auto Generate BMC Dump
297    [Documentation]  Auto generate BMC dump.
298
299    ${cmd}=  Catenate  busctl --verbose call xyz.openbmc_project.Dump.Manager
300    ...  /xyz/openbmc_project/dump/bmc xyz.openbmc_project.Dump.Create CreateDump a{sv} 0
301    ${stdout}  ${stderr}  ${rc}=
302    ...  BMC Execute Command  ${cmd}
303    [Return]  ${stdout}  ${stderr}  ${rc}
304
305Get Dump Size
306    [Documentation]  Get dump size.
307    [Arguments]  ${dump_uri}
308
309    # Description of argument(s):
310    # dump_uri        Dump URI
311    #                 (Eg. 	/xyz/openbmc_project/dump/bmc/entry/1).
312
313    # Example of Dump entry.
314    # "data": {
315    #   "CompletedTime": 1616760931,
316    #   "Elapsed": 1616760931,
317    #   "OffloadUri": "",
318    #   "Offloaded": false,
319    #   "Password": "",
320    #   "Size": 3056,
321    #   "SourceDumpId": 117440513,
322    #   "StartTime": 1616760931,
323    #   "Status": "xyz.openbmc_project.Common.Progress.OperationStatus.Completed",
324    #   "VSPString": ""
325    #  },
326
327    Log  ${dump_uri}
328    ${dump_data}=  Redfish.Get Properties  ${dump_uri}
329    [Return]  ${dump_data["data"]["Size"]}
330
331Get Dump ID
332    [Documentation]  Return dump ID.
333    [Arguments]   ${task_id}
334
335    # Description of argument(s):
336    # task_id        Task ID.
337
338    # Example of HttpHeaders field of task details.
339    # "Payload": {
340    #   "HttpHeaders": [
341    #     "Host: <BMC_IP>",
342    #      "Accept-Encoding: identity",
343    #      "Connection: Keep-Alive",
344    #      "Accept: */*",
345    #      "Content-Length: 33",
346    #      "Location: /redfish/v1/Managers/bmc/LogServices/Dump/Entries/2"]
347    #    ],
348    #    "HttpOperation": "POST",
349    #    "JsonBody": "{\"DiagnosticDataType\":\"Manager\"}",
350    #     "TargetUri":
351    # "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"
352    # }
353
354    ${task_dict}=  Redfish.Get Properties  /redfish/v1/TaskService/Tasks/${task_id}
355    ${key}  ${value}=  Set Variable  ${task_dict["Payload"]["HttpHeaders"][-1].split(":")}
356    Run Keyword If  '${key}' != 'Location'  Fail
357    [Return]  ${value.strip('/').split('/')[-1]}
358
359Get Task Status
360    [Documentation]  Return task status.
361    [Arguments]   ${task_id}
362
363    # Description of argument(s):
364    # task_id        Task ID.
365
366    ${resp}=  Redfish.Get Properties  /redfish/v1/TaskService/Tasks/${task_id}
367    [Return]  ${resp['TaskState']}
368
369Check Task Completion
370    [Documentation]  Check if the task is complete.
371    [Arguments]   ${task_id}
372
373    # Description of argument(s):
374    # task_id        Task ID.
375
376    ${task_dict}=  Redfish.Get Properties  /redfish/v1/TaskService/Tasks/${task_id}
377    Should Be Equal As Strings  ${task_dict['TaskState']}  Completed
378
379Get Dump ID And Status
380    [Documentation]  Return dump ID and status.
381    [Arguments]   ${task_id}
382
383    # Description of argument(s):
384    # task_id        Task ID.
385
386    Wait Until Keyword Succeeds  10 min  15 sec  Check Task Completion  ${task_id}
387    ${dump_id}=  Get Dump ID  ${task_id}
388    [Return]  ${dump_id}  Completed
389
390
391Create BMC User Dump
392    [Documentation]  Generate user initiated BMC dump via Redfish and return
393    ...  the task instance Id and response object (e.g., "5").
394
395    ${payload}=  Create Dictionary  DiagnosticDataType=Manager
396    ${resp}=  Redfish.Post  /redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData
397    ...  body=${payload}  valid_status_codes=[${HTTP_ACCEPTED}]
398
399    ${ip_resp}=  Evaluate  json.loads(r'''${resp.text}''')  json
400
401    Return From Keyword  ${ip_resp["Id"]}  ${resp}
402
403
404Wait For Task Completion
405    [Documentation]  Check whether the state of task instance matches any of the
406    ...  expected completion states before maximum number of retries exceeds and
407    ...  exit loop in case completion state is met.
408    [Arguments]  ${task_id}  ${expected_status}  ${retry_max_count}=300
409    ...  ${check_state}=${FALSE}
410
411    # Description of argument(s):
412    # task_id                     the task id for which completion is
413    #                             to be monitored.
414    # expected_status             the task state which is to be considered as the
415    #                             end of task life cycle.
416    # retry_max_count             the maximum number of retry count to wait for
417    #                             task to reach its completion state. Default
418    #                             value of retry_max_count is 300.
419    # check_state                 if set as TRUE, the task state will be
420    #                             monitored whether the task state value is
421    #                             valid throughout task life cycle until
422    #                             expected completion state is reached.
423    #                             Default value of check_state is FALSE.
424
425    FOR  ${retry}  IN RANGE  ${retry_max_count}
426        ${resp}=  Redfish.Get Properties  /redfish/v1/TaskService/Tasks/${task_id}
427        ${current_task_state}=  Set Variable  ${resp["TaskState"]}
428        Rprint Vars  current_task_state
429
430        Run Keyword If  ${check_state} == ${TRUE}  Should Be True
431        ...  '${resp["TaskState"]}' in ${allowed_task_state}
432        ...  msg=Verify task state is valid
433
434        Exit For Loop If
435        ...  '${resp["TaskState"]}' in ${expected_status}
436
437        Sleep  5s
438    END