xref: /openbmc/openbmc-test-automation/ipmi/test_ipmi_fru_device.robot (revision 49c1b53931191184251a4d1d9ba9ab127ee9f9fb)
1*** Settings ***
2Documentation  Test IPMI FRU data.
3
4Resource               ../lib/ipmi_client.robot
5Resource               ../lib/bmc_dbus.robot
6Variables              ../data/ipmi_raw_cmd_table.py
7Library                ../lib/ipmi_utils.py
8
9Test Tags             IPMI_FRU_Device
10
11*** Variables ***
12
13${FRU_NAME}       dimm01 dimm02 cpu0 cpu1  motherboard
14${BUSCTL_FRU}                xyz.openbmc_project.FruDevice
15${FRU_DBUS_URL}              /xyz/openbmc_project/FruDevice
16${fru_device_id}             0x00
17${fru_device_id_invalid}     0xff
18${read_write_offset}         0x00 0x00
19&{dbus_dict}
20&{ipmi_dbus_name_mapping}    Chassis Part Number=.CHASSIS_PART_NUMBER
21...  Board Mfg Date=.BOARD_MANUFACTURE_DATE  Board Mfg=.BOARD_MANUFACTURER
22...  Board Product=.BOARD_PRODUCT_NAME  Board Serial=.BOARD_SERIAL_NUMBER
23...  Board Part Number=.BOARD_PART_NUMBER  Product Manufacturer=.PRODUCT_MANUFACTURER
24...  Product Name=.PRODUCT_PRODUCT_NAME  Product Part Number=.PRODUCT_PART_NUMBER
25...  Product Version=.PRODUCT_VERSION  Product Serial=.PRODUCT_SERIAL_NUMBER
26
27*** Test Cases ***
28Test FRU Device Name
29    [Documentation]  Search FRU for device name
30    [Tags]  Test_FRU_Device_Name
31
32    ${output}=  Run IPMI Standard Command  fru
33    Should Contain  ${output}  ${FRU_NAME}  msg=Fail: Given FRU device ${FRU_NAME} not found
34
35
36Verify Fru Device Configuration
37    [Documentation]  Read the FRU device configuration of each device
38    ...  and compare with DBUS data.
39    [Tags]  Verify_Fru_Device_Configuration
40
41    # IPMI FRU print.
42    ${ipmi_output}=  Run IPMI Standard Command  fru
43
44    # Create dictionary with FRU device serial number as key and details as value from IPMI.
45    ${ipmi_fru}=  Get IPMI FRU Devices Data  ${ipmi_output}
46
47    # Returns all the available FRU dbus uri.
48    ${dbus_fru_uri}=  Get DBUS URI List From BMC  ${BUSCTL_FRU}  ${FRU_DBUS_URL}
49
50    # Returns all the FRU device uri with special characters removed.
51    ${dbus_fru_uri_list}=  Fetch DBUS URI List Without Unicode  ${dbus_fru_uri}
52
53    # Creates dictionary with serial number as key, and corresponding FRU device uri as value from dbus.
54    Get DBUS Dictionary For FRU Devices  ${dbus_fru_uri_list}  ${ipmi_fru}
55
56    # Compare dbus dictionary each field, with IPMI FRU device fields for each FRU device.
57    Compare IPMI FRU with DBUS  ${ipmi_fru}
58
59
60Verify Get FRU Inventory Area Info
61    [Documentation]  Verify IPMI get FRU inventory area info command.
62    [Tags]  Verify_Get_FRU_Inventory_Area_Info
63
64    # IPMI read FRU data command.
65    ${resp}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
66    ${bytes_read}=  Set Variable  ${resp.split()[0]}
67
68    # IPMI get FRU inventory area info command.
69    ${bytes_inventory}=  Get FRU Inventory Area Info
70
71    # Compare read FRU data Count returned -- count is ‘1’ based, with inventory area count.
72    Should Be Equal  ${bytes_inventory}  ${bytes_read}
73
74
75Verify Get FRU Inventory Area Info For Invalid Device Data
76    [Documentation]  Verify IPMI get FRU inventory area info command for Invalid Device Data.
77    [Tags]  Verify_Get_FRU_Inventory_Area_Info_For_Invalid_Device_Data
78
79    # Verify response for invalid FRU device id.
80    Run Keyword and Expect Error  *${IPMI_RAW_CMD['FRU']['Inventory_Area_Info'][1]}*
81    ...  Run IPMI Command  ${IPMI_RAW_CMD['FRU']['Inventory_Area_Info'][0]} ${fru_device_id_invalid}
82
83
84Verify Get FRU Inventory Area Info For Invalid Data Request
85    [Documentation]  Verify IPMI get FRU inventory area info command for Invalid Data Request.
86    [Tags]  Verify_Get_FRU_Inventory_Area_Info_For_Invalid_Data_Request
87
88    # Verify response for invalid response data - extra bytes.
89    Run Keyword and Expect Error  *${IPMI_RAW_CMD['FRU']['Inventory_Area_Info'][2]}*
90    ...  Run IPMI Command  ${IPMI_RAW_CMD['FRU']['Inventory_Area_Info'][0]} ${fru_device_id} 0x00
91
92
93Verify IPMI Write FRU Data
94    [Documentation]  Verify write data in FRU and compare data from read FRU data command via IPMI.
95    [Tags]  Verify_IPMI_Write_FRU_Data
96    [Setup]  Get Default FRU Data
97    [Teardown]  Restore Default FRU Data
98
99    # Generate random data to write in FRU device.
100    ${write_data_prefixed}  ${write_data}=  Generate Random Data For FRU
101
102    # Get the length of the data generated and convert to hex.
103    ${write_data_length}=  Get Length  ${write_data}
104    ${write_data_length}=  Convert To Hex  ${write_data_length}  lowercase=yes
105
106    # Write the data to FRU device.
107    Write FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}  ${write_data_prefixed}  ${write_data_length}
108
109    # Read the FRU data.
110    ${resp}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
111    ${resp_data}=  Set Variable  ${resp.split()[1:]}
112
113    # Verify if the data written and read are same.
114    Should Be Equal  ${write_data}  ${resp_data}
115
116
117Verify IPMI Write FRU Data With BMC Reboot
118    [Documentation]  Verify IPMI write data in FRU and compare data from read FRU data command after BMC reboot.
119    [Tags]  Verify_IPMI_Write_FRU_Data_With_BMC_Reboot
120    [Setup]  Get Default FRU Data
121    [Teardown]  Restore Default FRU Data
122
123    # Generate random data to write in FRU device.
124    ${write_data_prefixed}  ${write_data}=  Generate Random Data For FRU
125
126    # Get the length of the data generated and convert to hex.
127    ${write_data_length}=  Get Length  ${write_data}
128    ${write_data_length}=  Convert To Hex  ${write_data_length}  lowercase=yes
129
130    # Write the data to FRU device.
131    Write FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}  ${write_data_prefixed}  ${write_data_length}
132
133    # Read the FRU data.
134    ${resp}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
135    ${resp_data}=  Set Variable  ${resp.split()[1:]}
136
137    # Verify if the data written and read are same.
138    Should Be Equal  ${write_data}  ${resp_data}
139
140    # Reboot BMC and verify if the data written and read are same.
141    IPMI MC Reset Cold (run)
142    ${resp}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
143    Should Not Be Equal  ${resp}  ${initial_fru_data}
144    Should Be Equal  ${resp[1:]}  ${write_data}
145
146
147*** Keywords ***
148
149Get IPMI FRU Devices Data
150    [Documentation]  Get response from IPMI FRU command and format data
151    ...  with Board or Product serial as key and corresponding data as value.
152    [Arguments]  ${ipmi_output}
153
154    # Description of Argument(s):
155    # ipmi_output        All the FRU devices listed in IPMI FRU command.
156
157    # Get the FRU list and return as a dictionary with serial number as key.
158    # Example:
159    # fru_data = {
160    # "123456789012345XYZ":
161    #  {
162    #     FRU Device Description : Builtin FRU Device (ID 0),
163    #     Chassis Type          : Rack Mount Chassis,
164    #     Chassis Part Number   : xxx-xxxxx-xxxx-xxx,
165    #     Board Mfg Date        : Fri Oct 16 06:34:00 2020 UTC,
166    #     Board Mfg             : XXXXX,
167    #     Board Product         : XXXXX,
168    #     Board Serial          : 123456789012345XYZ,
169    #     Board Part Number     : xxx.xxxxx.xxxx
170    #     Board Extra           : 01
171    #     Product Manufacturer  : XXXXX
172    #     Product Name          : XXXXX
173    #     Product Part Number   : xxx-xxxx-xxxx-xxx
174    #     Product Version       : v1.0
175    #     Product Serial        : 1234567890XYZ
176    #  },....}
177
178    # Gets response from FRU data and split each device.
179    ${output}=  Set Variable  ${ipmi_output.strip("\n")}
180    ${output}=  Split String  ${output}  \n\n
181    &{fru}=  Create Dictionary
182    ${num}=  Set Variable  0
183
184    # For each device, identify either Board Serial/Product Serial (whichever is available).
185    FOR  ${devices}  IN  @{output}
186        &{tmp}=  Create Dictionary
187        ${dev}=  Split String  ${devices}  \n
188        FOR  ${device}  IN  @{dev}
189            ${ipmi_fru_board_serial_status}=  Run Keyword And Return Status  Should Contain  ${device}  Board Serial
190            IF  '${ipmi_fru_board_serial_status}' == 'True'  BREAK
191        END
192        ${frudata}=  Get From List  ${output}  ${num}
193
194        ${serial_str}=  Set Variable If  '${ipmi_fru_board_serial_status}' == 'True'
195        ...  Board Serial  Product Serial
196
197        ${serial_no}=  Get Lines Containing String  ${frudata}  ${serial_str}
198
199        # Get each device and split field as key and value and append to a dictionary.
200        ${serial_nos}=  Set Variable  ${serial_no.strip()}
201        ${data}=  Split String  ${serial_nos}  :
202        ${serial_number}=  Get From List  ${data}  1
203        ${num}=  Evaluate  int(${num}) + 1
204        FOR  ${entry}  IN  @{dev}
205            ${entry}=  Split String  ${entry}  ${SPACE}:${SPACE}
206            ${entry1}=  Set Variable  ${entry[0].strip()}
207            ${entry2}=  Set Variable  ${entry[1].strip()}
208            Set To Dictionary  ${tmp}  ${entry1}  ${entry2}
209        END
210        ${serial_number}=  Set Variable  ${serial_number.strip()}
211        # Assign serial number as key for main dictionary and a each device detail as value.
212        Set To Dictionary  ${fru}  ${serial_number}  ${tmp}
213    END
214
215    RETURN  ${fru}
216
217
218Get DBUS Dictionary For FRU Devices
219    [Documentation]  Provides the dictionary of DBUS FRU devices from DBUS FRU.
220    [Arguments]  ${dbus_fru}  ${ipmi_fru}
221
222    # Description of Argument(s):
223    # dbus_fru    FRU dbus uri list.
224    # ipmi_fru    IPMI FRU details.
225
226    # Execute DBUS Introspect Command for each device,
227    # Appends dictionary with serial number as key and FRU dbus uri as value,
228    # if the IPMI FRU key matches the serial number of each device dbus response.
229    # Example :
230    #    ${dbus_output} = { "123456789012345XYZ" : "xyz.openbmc_project.FruDevice/xyz/openbmc_project/FruDevice/Device_0" }
231    FOR  ${fru}  IN  @{dbus_fru}
232        ${cmd}=  Catenate  ${BUSCTL_FRU} ${fru}
233        ${dbus_output}=  Execute DBUS Introspect Command  ${cmd}
234        ${dbus_fru_board_serial_status}=  Run Keyword And Return Status  Should Contain  ${dbus_output}  .BOARD_SERIAL
235        ${dbus_fru_product_serial_status}=  Run Keyword And Return Status  Should Contain  ${dbus_output}  .PRODUCT_SERIAL
236        IF  '${dbus_fru_board_serial_status}' == 'True' or '${dbus_fru_product_serial_status}' == 'True'
237            Create Dictionary For DBUS URI  ${dbus_output}  ${ipmi_fru}  ${dbus_fru_board_serial_status}  ${cmd}
238        END
239    END
240
241
242Create Dictionary For DBUS URI
243    [Documentation]  Create Dictionary For DBUS URI
244    [Arguments]  ${dbus_output}  ${ipmi_fru}  ${dbus_fru_board_serial_status}  ${fru_command}
245
246    # Description of Argument(s):
247    # dbus_output                             Dbus response got from BMC console.
248    # ipmi_fru                                IPMI FRU details.
249    # dbus_fru_board_serial_status            FRU devices may have either BOARD_SERIAL or PRODUCT_SERIAL
250    # ...                                     if status was true value of BOARD_SERIAL will be taken for dictionary as an key
251    # ...                                     otherwise value of PRODUCT_SERIAL will be taken as an key for dictionary.
252    # fru_command                             FRU command to map into dictionary as value.
253
254    # Validates the IPMI FRU dictionary key with dbus uri response serial number.
255    # If matches then, sets the serial number as key and FRU uri as value.
256    # ${dbus_dict} defined under variable section.
257    FOR  ${ipmi_fru_serial_no}  IN  @{ipmi_fru.keys()}
258        ${serial_str}=  Set Variable If  '${ipmi_fru_board_serial_status}' == 'True'
259        ...  Board Serial  Product Serial
260
261        ${serial_no}=  Get Lines Containing String  ${frudata}  ${serial_str}
262
263        ${serial_no}=  Split String  ${serial_no}  "
264        ${dbus_serial_no}=  Set Variable  ${serial_no[1].strip()}
265        ${serial_no_status}=  Run Keyword And Return Status  Should Be Equal As Strings
266        ...  ${ipmi_fru_serial_no}  ${dbus_serial_no}
267
268        IF  '${serial_no_status}' == 'True'
269            Set To Dictionary  ${dbus_dict}  ${dbus_serial_no}  ${fru_command}
270            BREAK
271        END
272    END
273
274
275Compare IPMI FRU with DBUS
276    [Documentation]  Compare the IPMI FRU dictionary values with DBUS dictionary values,
277    ...  if the serial number is present in both FRU and dbus dictionaries.
278    [Arguments]  ${ipmi_fru}
279
280   # Description of Argument(s):
281   # ipmi_fru    IPMI FRU device details.
282
283    # With each IPMI FRU key, get the corresponding valid from dbus dictionary,
284    # Execute the value which is dbus uri and,
285    # validate each dbus field value with IPMI FRU field value.
286    # Values are validated for keys present in the ipmi_dbus_name_mapping dictionary.
287    # Example :
288    #    IPMI FRU field :
289    #             Board Part Number     : 111.22222.0000
290    #    DBUS FRU field :
291    #            .BOARD_PART_NUMBER      property  s     "111.22222.0000"        emits-change
292    FOR  ${key}  ${ipmi_fru_value}  IN  &{ipmi_fru}
293        ${dbus_resp}=  Execute DBUS Introspect Command  ${dbus_dict}[${key}]
294        ${ipmi_fru_subkeys}=  Get Dictionary Keys  ${ipmi_fru_value}
295        FOR  ${subkeys}  IN  @{ipmi_fru_subkeys}
296            ${key_status}=  Run Keyword And Return Status  Dictionary Should Contain Key
297            ...  ${ipmi_dbus_name_mapping}  ${subkeys}
298            IF  '${key_status}' == 'False'  CONTINUE
299            ${property_name}=  Get From Dictionary  ${ipmi_dbus_name_mapping}  ${subkeys}
300            ${dbus_data}=  Get Lines Containing String  ${dbus_resp}  ${property_name}
301            ${dbus_value}=  Set Variable  ${dbus_data.split('"')[1].strip()}
302            ${ipmi_response}=  Get From Dictionary  ${ipmi_fru_value}  ${subkeys}
303            ${status}=  Run Keyword And Return Status  Should Contain  ${property_name}  DATE
304            IF  ${status}
305                # If the required IPMI field has date field, the IPMI FRU value is converted to
306                # format = %Y-%m-%d - %H:%M:%S and validated against dbus FRU data.
307                ${ipmi_date}=  Convert Date  ${ipmi_response}  date_format=%a %b %d %H:%M:%S %Y
308                ...  result_format=%Y-%m-%d - %H:%M:%S
309                Run Keyword And Continue On Failure  Should Be Equal As Strings  ${ipmi_date}  ${dbus_value}
310                ...  message=${property_name} Property value mismatch with IPMI and DBUS
311            ELSE
312                Run Keyword And Continue On Failure  Should Be Equal As Strings  ${ipmi_response}  ${dbus_value}
313                ...  message=${property_name} Property value mismatch with IPMI and DBUS
314            END
315        END
316    END
317
318
319Get FRU Inventory Area Info
320    [Documentation]  IPMI Get FRU Inventory Area Info and returns FRU Inventory area size in bytes.
321
322    ${resp}=  Run IPMI Command  ${IPMI_RAW_CMD['FRU']['Inventory_Area_Info'][0]} ${fru_device_id}
323    ${resp}=  Split String  ${resp}
324
325    RETURN  ${resp[0]}
326
327
328Read FRU Data Via IPMI
329    [Documentation]  Read FRU data using IPMI raw command.
330    [Arguments]  ${fru_id}  ${offset}
331
332    # Description of Argument(s):
333    # fru_id        FRU id.
334    # offset        Offset byte for read FRU command.
335
336    # IPMI Read FRU Data Command.
337    # 0xff - Count to read --- count is ‘1’ based
338    ${resp}=  Run IPMI Command
339    ...  ${IPMI_RAW_CMD['FRU']['Read'][0]} ${fru_id} ${offset} 0xff
340
341    RETURN  ${resp}
342
343
344Write FRU Data Via IPMI
345    [Documentation]  Write FRU data using IPMI raw command.
346    [Arguments]  ${fru_id}  ${offset}  ${data}  ${length}
347
348    # Description of Argument(s):
349    # fru_id        FRU id.
350    # offset        Offset byte for read FRU command.
351    # data          Data to write for write FRU command.
352    # length        Count of bytes that gets written in write FRU command.
353
354    # IPMI Write FRU Data Command.
355    ${resp}=  Run IPMI Command
356    ...  ${IPMI_RAW_CMD['FRU']['Write'][0]} ${fru_id} ${offset} ${data}
357
358    Should Be Equal As Strings  ${resp}  ${length}
359
360
361Generate Random Data For FRU
362    [Documentation]  Generate random data for write in FRU.
363
364    # Description:
365    #  Generates string of bytes and convert to hexadecimal data.
366    #  Gets the length of initial FRU data read with IPMI read FRU device command.
367
368    # ${frudata_prefixed}    string with bytes prefixed 0x by default
369    # ${fru_data}            string with only hexadecimal bytes without prefix
370
371    ${string}=  Generate Random String  ${initial_fru_length}  [LETTERS]
372    ${frudata_prefixed}  ${fru_data}=  Identify Request Data  ${string}
373
374    RETURN  ${frudata_prefixed}  ${fru_data}
375
376
377Get Default FRU Data
378    [Documentation]  Get default data via read FRU data IPMI command.
379
380    # Read the default FRU device data.
381    # split the response and identify length of Requested data.
382    ${initial_fru_data}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
383    ${initial_fru_list}=  Split String  ${initial_fru_data}
384    ${initial_fru_list}=  Set Variable  ${initial_fru_list[1:]}
385    ${initial_fru_length}=  Get Length  ${initial_fru_list}
386    Set Test Variable  ${initial_fru_data}
387    Set Test Variable  ${initial_fru_list}
388    Set Test Variable  ${initial_fru_length}
389
390
391Restore Default FRU Data
392    [Documentation]  Restore default FRU data.
393
394    # Prefix 0x to initial request data.
395    ${fru_list}=  Prefix Bytes  ${initial_fru_list}
396    ${fru_byte}=  Evaluate  " ".join(${fru_list})
397    ${initial_frulength_hex}=  Convert To Hex  ${initial_fru_length}  lowercase=yes
398    # Write the initial FRU data to restore.
399    Write FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}  ${fru_byte}  ${initial_frulength_hex}
400    # Verify whether initial FRU data is restored.
401    ${fru_data}=  Read FRU Data Via IPMI  ${fru_device_id}  ${read_write_offset}
402    Should Be Equal  ${fru_data}  ${initial_fru_data}
403