xref: /openbmc/openbmc-test-automation/lib/open_power_utils.robot (revision 9eb919e96946294a075723ea7e4328b5796cbcd8)
1*** Settings ***
2Documentation  Open power domain keywords.
3
4Variables      ../data/variables.py
5Resource       ../lib/utils.robot
6Resource       ../lib/connection_client.robot
7Library        utilities.py
8
9*** Variables ***
10${functional_cpu_count}       ${0}
11${active_occ_count}           ${0}
12${OCC_WAIT_TIMEOUT}           8 min
13${fan_json_msg}               Unable to create dump on non-JSON config based system
14
15*** Keywords ***
16
17Get OCC Objects
18    [Documentation]  Get the OCC objects and return as a list.
19
20    # Example:
21    # {
22    #     "/org/open_power/control/occ0": {
23    #          "OccActive": 0
24    # },
25    #     "/org/open_power/control/occ1": {
26    #          "OccActive": 1
27    # }
28
29    ${occ_list}=  Get Endpoint Paths  ${OPENPOWER_CONTROL}  occ*
30
31    RETURN  ${occ_list}
32
33
34Get OCC Active State
35    [Documentation]  Get the OCC "OccActive" and return the attribute value.
36    [Arguments]  ${value}
37
38    # Description of argument(s):
39    # value       CPU position (e.g. "0, 1, 2").
40
41    ${cmd}=  Catenate  busctl get-property org.open_power.OCC.Control
42    ...   /org/open_power/control/occ${value} org.open_power.OCC.Status OccActive
43
44    ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
45    ...  print_out=1  print_err=1  ignore_err=1
46
47    # The command returns format  'b true'
48    IF  '${cmd_output.split(' ')[-1]}' == 'true'
49        RETURN  ${1}
50    END
51
52    RETURN  ${0}
53
54
55Count Object Entries
56    [Documentation]  Count the occurrence number of a given object.
57    [Arguments]  ${object_base_uri_path}  ${object_name}
58
59    # Description of argument(s):
60    # object_base_uri_path    Object base path
61    #                         (e.g. "/org/open_power/control/").
62    # object_name             Object name (e.g. "occ", "cpu" etc).
63
64    ${object_list}=  Get Endpoint Paths
65    ...  ${object_base_uri_path}  ${object_name}
66    ${list_count}=  Get Length  ${object_list}
67    RETURN  ${list_count}
68
69
70Read Object Attribute
71    [Documentation]  Return object attribute data.
72    [Arguments]  ${object_base_uri_path}  ${attribute_name}
73
74    # Description of argument(s):
75    # object_base_uri_path       Object path.
76    #                   (e.g. "/org/open_power/control/occ0").
77    # attribute_name    Object attribute name.
78
79    ${resp}=  OpenBMC Get Request
80    ...  ${object_base_uri_path}/attr/${attribute_name}  quiet=${1}
81
82    IF  ${resp.status_code} != ${HTTP_OK}
83        RETURN
84    END
85
86    RETURN  ${resp.json()["data"]}
87
88
89Get Functional Processor Count
90    [Documentation]  Get functional processor count.
91
92    ${cpu_list}=  Redfish.Get Members List  /redfish/v1/Systems/${SYSTEM_ID}/Processors/  *cpu*
93
94    FOR  ${endpoint_path}  IN  @{cpu_list}
95       # {'Health': 'OK', 'State': 'Enabled'} get only matching status good.
96       ${cpu_status}=  Redfish.Get Attribute  ${endpoint_path}  Status
97
98       IF  '${cpu_status['Health']}' != 'OK' or '${cpu_status['State']}' != 'Enabled'
99           CONTINUE
100       END
101       ${functional_cpu_count} =  Evaluate   ${functional_cpu_count} + 1
102    END
103
104    RETURN  ${functional_cpu_count}
105
106
107Get Active OCC State Count
108    [Documentation]  Get active OCC state count.
109
110    ${cpu_list}=  Redfish.Get Members List  /redfish/v1/Systems/${SYSTEM_ID}/Processors/  *cpu*
111
112    FOR  ${endpoint_path}  IN  @{cpu_list}
113       ${num}=  Set Variable  ${endpoint_path[-1]}
114       ${cmd}=  Catenate  busctl get-property org.open_power.OCC.Control
115       ...   /org/open_power/control/occ${num} org.open_power.OCC.Status OccActive
116
117       ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
118       ...  print_out=1  print_err=1  ignore_err=1
119
120       # The command returns format  'b true'
121       IF   '${cmd_output.split(' ')[-1]}' != 'true'  CONTINUE
122       ${active_occ_count} =  Evaluate   ${active_occ_count} + 1
123    END
124
125    RETURN  ${active_occ_count}
126
127
128Match OCC And CPU State Count
129    [Documentation]  Get CPU functional count and verify OCC count active matches.
130
131    ${cpu_count}=  Get Functional Processor Count
132    Log To Console  Functional Processor count: ${cpu_count}
133
134    FOR  ${num}  IN RANGE  ${0}  ${cpu_count}
135       ${cmd}=  Catenate  busctl get-property org.open_power.OCC.Control
136       ...   /org/open_power/control/occ${num} org.open_power.OCC.Status OccActive
137
138       ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
139       ...  print_out=1  print_err=1  ignore_err=1
140
141       # The command returns format  'b true'
142       IF   '${cmd_output.split(' ')[-1]}' != 'true'  CONTINUE
143       ${active_occ_count} =  Evaluate   ${active_occ_count} + 1
144    END
145
146    Log To Console  OCC Active count: ${active_occ_count}
147
148    Should Be Equal  ${active_occ_count}  ${cpu_count}
149    ...  msg=OCC count ${active_occ_count} and CPU Count ${cpu_count} mismatched.
150
151
152Verify OCC State
153    [Documentation]  Check OCC active state.
154    [Arguments]  ${expected_occ_active}=${1}
155    # Description of Argument(s):
156    # expected_occ_active  The expected occ_active value (i.e. 1/0).
157
158    # Example cpu_list data output:
159    #  /redfish/v1/Systems/system/Processors/cpu0
160    #  /redfish/v1/Systems/system/Processors/cpu1
161
162    ${cpu_list}=  Redfish.Get Members List  /redfish/v1/Systems/${SYSTEM_ID}/Processors/  cpu*
163
164    FOR  ${endpoint_path}  IN  @{cpu_list}
165       # {'Health': 'OK', 'State': 'Enabled'} get only matching status good.
166       ${cpu_status}=  Redfish.Get Attribute  ${endpoint_path}  Status
167       IF  '${cpu_status['Health']}' != 'OK' or '${cpu_status['State']}' != 'Enabled'  CONTINUE
168       Log To Console  ${cpu_status}
169       ${num}=  Set Variable  ${endpoint_path[-1]}
170       ${occ_active}=  Get OCC Active State  ${num}
171       Should Be Equal  ${occ_active}  ${expected_occ_active}
172       ...  msg=OCC not in right state
173    END
174
175
176Get Sensors Aggregation Data
177    [Documentation]  Return open power sensors aggregation value list.
178    [Arguments]  ${object_base_uri_path}
179
180    # Description of argument(s):
181    # object_base_uri_path  An object path such as one of the elements
182    #                       returned by 'Get Sensors Aggregation URL List'
183    #                       (e.g. "/org/open_power/sensors/aggregation/per_30s/ps0_input_power/average").
184
185    # Example of aggregation [epoch,time] data:
186    # "Values": [
187    #    [
188    #        1517815708479,  <-- EPOCH
189    #        282             <-- Power value in watts
190    #    ],
191    #    [
192    #        1517815678238,
193    #        282
194    #    ],
195    #    [
196    #        1517815648102,
197    #        282
198    #    ],
199    # ],
200
201    ${resp}=  Read Attribute  ${object_base_uri_path}  Values  quiet=${1}
202    ${power_sensors_value_list}=  Create List
203    FOR  ${entry}  IN  @{resp}
204       Append To List  ${power_sensors_value_list}  ${entry[1]}
205    END
206    RETURN  ${power_sensors_value_list}
207
208
209Get Sensors Aggregation URL List
210    [Documentation]  Return the open power aggregation maximum list and the
211    ...  average list URIs.
212    [Arguments]  ${object_base_uri_path}
213
214    # Example of the 2 lists returned by this keyword:
215    # avgs:
216    #   avgs[0]: /org/open_power/sensors/aggregation/per_30s/ps0_input_power/average
217    #   avgs[1]: /org/open_power/sensors/aggregation/per_30s/ps1_input_power/average
218    # maxs:
219    #   maxs[0]: /org/open_power/sensors/aggregation/per_30s/ps1_input_power/maximum
220    #   maxs[1]: /org/open_power/sensors/aggregation/per_30s/ps0_input_power/maximum
221
222    # Description of argument(s):
223    # object_base_uri_path  Object path.
224    #                       base path "/org/open_power/sensors/"
225    #        (e.g. "base path + aggregation/per_30s/ps0_input_power/average")
226
227    # Example of open power sensor aggregation data as returned by the get
228    # request:
229    # /org/open_power/sensors/list
230    # [
231    #    "/org/open_power/sensors/aggregation/per_30s/ps0_input_power/average",
232    #    "/org/open_power/sensors/aggregation/per_30s/ps1_input_power/maximum",
233    #    "/org/open_power/sensors/aggregation/per_30s/ps0_input_power/maximum",
234    #    "/org/open_power/sensors/aggregation/per_30s/ps1_input_power/average"
235    # ]
236
237    ${resp}=  OpenBMC Get Request  ${object_base_uri_path}list  quiet=${1}
238
239    ${power_supply_avg_list}=  Create List
240    ${power_supply_max_list}=  Create List
241
242    FOR  ${entry}  IN  @{resp.json()["data"]}
243        IF  'average' in '${entry}'  Append To List  ${power_supply_avg_list}  ${entry}
244        IF  'maximum' in '${entry}'  Append To List  ${power_supply_max_list}  ${entry}
245    END
246
247    RETURN  ${power_supply_avg_list}  ${power_supply_max_list}
248
249
250REST Verify No Gard Records
251    [Documentation]  Verify no gard records are present.
252
253    ${resp}=  Read Properties  ${OPENPOWER_CONTROL}gard/enumerate
254    Log Dictionary  ${resp}
255    Should Be Empty  ${resp}  msg=Found gard records.
256
257
258Inject OPAL TI
259    [Documentation]  OPAL terminate immediate procedure.
260    [Arguments]      ${stable_branch}=master
261    ...              ${repo_dir_path}=/tmp/repository
262    ...              ${repo_github_url}=https://github.com/open-power/op-test
263
264    # Description of arguments:
265    # stable_branch    Git branch to clone. (default: master)
266    # repo_dir_path    Directory path for repo tool (e.g. "op-test").
267    # repo_github_url  Github URL link (e.g. "https://github.com/open-power/op-test").
268
269    ${value}=  Generate Random String  4  [NUMBERS]
270
271    ${cmd_buf}=  Catenate  git clone --branch ${stable_branch} ${repo_github_url} ${repo_dir_path}/${value}
272    Shell Cmd  ${cmd_buf}
273
274    Open Connection for SCP
275    scp.Put File  ${repo_dir_path}/${value}/test_binaries/deadbeef  /tmp
276    Pdbg  -a putmem 0x300000f8 < /tmp/deadbeef
277
278    # Clean up the repo once done.
279    ${cmd_buf}=  Catenate  rm -rf ${repo_dir_path}${/}${value}
280    Shell Cmd  ${cmd_buf}
281
282
283Trigger OCC Reset
284    [Documentation]  Trigger OCC reset request on an active OCC.
285    [Arguments]  ${occ_target}=${0}
286
287    # Description of Argument(s):
288    # occ_target   Target a valid given OCC number 0,1, etc.
289
290    Log To Console   OCC Reset Triggered on OCC ${occ_target}
291
292    ${cmd}=  Catenate  busctl call org.open_power.OCC.Control
293    ...  /org/open_power/control/occ${occ_target} org.open_power.OCC.PassThrough
294    ...  Send ai 8 64 0 5 20 82 83 84 0
295
296    ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}  print_out=1  print_err=1
297
298    Log To Console  OCC wait check for disabled state.
299    Wait Until Keyword Succeeds  30 sec  5 sec  Verify OCC Target State  ${occ_target}
300
301
302Verify OCC Target State
303    [Documentation]  Verify that the user given state matches th current OCC state.
304    [Arguments]  ${occ_target}=${0}  ${expected_state}=${0}
305
306    # Description of Argument(s):
307    # occ_target       Target a valid given OCC number 0,1, etc.
308    # expected_state   For OCC either 0 or 1. Default is 0.
309
310    ${occ_active}=  Get OCC Active State  ${occ_target}
311    Should Be Equal  ${occ_active}  ${expected_state}
312    Log To Console  Target OCC ${occ_target} state is ${occ_active}.
313
314
315Trigger OCC Reset And Wait For OCC Active State
316    [Documentation]  Trigger OCC reset request and wait for OCC to reset back to active state.
317
318    Trigger OCC Reset
319
320    Log To Console  OCC wait check for active state.
321    Wait Until Keyword Succeeds  ${OCC_WAIT_TIMEOUT}  20 sec   Match OCC And CPU State Count
322
323
324Get Sensors Dbus Tree List
325    [Documentation]  Get the list dbus path of the given sensor object and
326    ...              return the populatedlist.
327
328    ${dbus_obj_var}=  Set Variable
329    ...  xyz.openbmc_project.HwmonTempSensor
330    ...  xyz.openbmc_project.ADCSensor
331    ...  xyz.openbmc_project.VirtualSensor
332
333    # Filter only the dbus paths service by the sensor obj.
334    ${sensors_dbus_tree_dict}=  Create Dictionary
335    FOR  ${dbus_obj}  IN  @{dbus_obj_var}
336        ${cmd}=  Catenate  busctl tree ${dbus_obj} --list | grep /sensors/
337        ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
338        ...  print_out=0  print_err=0  ignore_err=1
339        Set To Dictionary  ${sensors_dbus_tree_dict}  ${dbus_obj}  ${cmd_output.splitlines()}
340    END
341
342    Rprint Vars  sensors_dbus_tree_dict
343    # Key Pair: 'sensor obj":[list of obj URI]
344    # Example:
345    # sensors_dbus_tree_dict:
346    # [xyz.openbmc_project.HwmonTempSensor]:
347    #    [0]:     /xyz/openbmc_project/sensors/temperature/Ambient_0_Temp
348    #    [1]:     /xyz/openbmc_project/sensors/temperature/PCIE_0_Temp
349    # [xyz.openbmc_project.ADCSensor]:
350    #    [0]:     /xyz/openbmc_project/sensors/voltage/Battery_Voltage
351    # [xyz.openbmc_project.VirtualSensor]:
352    #    [0]:     /xyz/openbmc_project/sensors/temperature/Ambient_Virtual_Temp
353
354    RETURN  ${sensors_dbus_tree_dict}
355
356
357Get Populated Sensors Dbus List
358    [Documentation]  Perform GET operation on the attribute list and confirm it is
359    ...              populated and does not error out during GET request..
360
361    ${sensor_dict}=  Get Sensors Dbus Tree List
362
363    # Loop through the dictionary and iterate item entries.
364    ${valid_dbus_list}=  Create List
365    FOR  ${key}  IN  @{sensor_dict.keys()}
366        FOR  ${val}  IN  @{sensor_dict["${key}"]}
367           ${cmd}=  Catenate
368           ...  busctl get-property ${key} ${val} xyz.openbmc_project.Sensor.Value Value
369           ${cmd_output}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
370           ...  print_out=0  print_err=0  ignore_err=1
371           # Skip failed to get property command on Dbus object.
372           IF  ${rc} == 0   Append To List  ${valid_dbus_list}  ${val}
373        END
374    END
375
376    RETURN  ${valid_dbus_list}
377
378
379Verify Runtime Sensors Dbus List
380    [Documentation]  Load pre-defined sensor JSON Dbus data and validate against
381    ...              runtime sensor list generated.
382
383    # Default path data/sensor_dbus.json else takes
384    # user CLI input -v SENSOR_DBUS_JSON_FILE_PATH:<path>
385    ${SENSOR_DBUS_JSON_FILE_PATH}=
386    ...  Get Variable Value  ${SENSOR_DBUS_JSON_FILE_PATH}   data/sensor_dbus.json
387
388    ${json_data}=  OperatingSystem.Get File  ${SENSOR_DBUS_JSON_FILE_PATH}
389    ${json_sensor_data}=  Evaluate  json.loads('''${json_data}''')  json
390
391    ${runtime_sensor_list}=  Get Populated Sensors Dbus List
392
393    ${system_model}=  Get BMC System Model
394    Rprint Vars  system_model
395    Rprint Vars  runtime_sensor_list
396
397    ${status}=  Run Keyword And Return Status
398    ...  Dictionary Should Contain Value   ${json_sensor_data}  ${runtime_sensor_list}
399
400    IF  ${status} == ${False}  Log And Fail  ${json_sensor_data}
401
402    Log To Console  Runtime Dbus sensor list matches.
403
404
405Log And Fail
406    [Documentation]  Log detailed failure log on the console.
407    [Arguments]  ${json_sensor_data}
408
409    # Description of Argument(s):
410    # json_sensor_data   Sensor JSON data from data/sensor_dbus.json.
411
412    Rprint Vars  json_sensor_data
413    Fail  Runtime generated Dbus sensors does not match
414
415
416Dump Fan Control JSON
417    [Documentation]  Execute fan control on BMC to dump config with 'fanctl dump',
418    ...              which makes it write a /tmp/fan_control_dump.json file.
419
420    ${output}  ${stderr}  ${rc} =  BMC Execute Command  test -f /usr/bin/fanctl
421    ...  print_err=1  ignore_err=1
422
423    IF   ${rc} == 1  RETURN  fanctl application doesn't exist.
424
425    # This command will force a fan_control_dump.json file in temp path and
426    # takes few seconds to complete..
427    BMC Execute Command  fanctl dump
428    Sleep  10s
429
430
431Get Fan JSON Data
432    [Documentation]  Read the JSON string file from BMC and return.
433
434    # Check for the generated file and return the file data as JSON and fails if
435    # it doesn't find file generated.
436    ${cmd}=  Catenate  test -f /tmp/fan_control_dump.json; cat /tmp/fan_control_dump.json
437    ${json_string}  ${stderr}  ${rc} =  BMC Execute Command  ${cmd}
438    ...  print_out=1  print_err=1  ignore_err=1
439
440    Should Be True  ${rc} == 0  msg=No Fan control config JSON file is generated.
441    ${fan_json}=  Evaluate  json.loads('''${json_string}''')  json
442
443    RETURN  ${fan_json}
444
445
446Get Fan Attribute Value
447    [Documentation]  Return the specified value of the matched search key in
448    ...              nested dictionary data.
449    [Arguments]  ${fan_dict}  ${key_value}
450
451    # Description of Argument(s):
452    # key_value      User input attribute value in the dictionary.
453
454    ${empty_dicts}=  Create Dictionary
455
456    # Check for JSON response data.
457    # {
458    #   "msg":   "Unable to create dump on non-JSON config based system"
459    # }
460
461    ${status}=  Run Keyword And Return Status
462    ...   Should Be Equal  ${fan_dict["msg"]}  ${fan_json_msg}
463    IF  ${status}
464        Log To Console  Skipping attribute ${key_value} check.
465        Return From Keyword  ${empty_dicts}
466    END
467
468    # Python module:  get_value_from_nested_dict(key,dict)
469    ${value_list}=  utilities.Get Value From Nested Dict  ${key_value}  ${fan_dict}
470
471    Should Not Be Empty  ${value_list}  msg=${key_value} key attribute not found.
472
473    RETURN  ${value_list[0]}
474