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