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