1*** Settings ***
2Documentation   This module is for IPMI client for copying ipmitool to
3...             openbmc box and execute ipmitool IPMI standard
4...             command. IPMI raw command will use dbus-send command
5Resource        ../lib/resource.robot
6Resource        ../lib/connection_client.robot
7Resource        ../lib/utils.robot
8Resource        ../lib/state_manager.robot
9
10Library         String
11Library         ipmi_client.py
12
13*** Variables ***
14${dbusHostIpmicmd1}=   dbus-send --system  ${OPENBMC_BASE_URI}HostIpmi/1
15${dbusHostIpmiCmdReceivedMsg}=   ${OPENBMC_BASE_DBUS}.HostIpmi.ReceivedMessage
16${netfnByte}=          ${EMPTY}
17${cmdByte}=            ${EMPTY}
18${arrayByte}=          array:byte:
19${IPMI_USER_OPTIONS}   ${EMPTY}
20${IPMI_INBAND_CMD}=    ipmitool -C ${IPMI_CIPHER_LEVEL}
21${HOST}=               -H
22${RAW}=                raw
23
24*** Keywords ***
25
26Run IPMI Command
27    [Documentation]  Run the raw IPMI command.
28    [Arguments]  ${command}  ${fail_on_err}=${1}  &{options}
29
30    # Description of argument(s):
31    # command                       The IPMI command string to be executed
32    #                               (e.g. "power status").
33    # fail_on_err                   Fail if the IPMI command execution fails.
34    # options                       Additional ipmitool command options (e.g.
35    #                               -C=3, -I=lanplus, etc.).  Currently, only
36    #                               used for external IPMI commands.
37
38    ${resp}=  Run Keyword If  '${IPMI_COMMAND}' == 'External'
39    ...    Run External IPMI Raw Command  ${command}  ${fail_on_err}  &{options}
40    ...  ELSE IF  '${IPMI_COMMAND}' == 'Inband'
41    ...    Run Inband IPMI Raw Command  ${command}
42    ...  ELSE IF  '${IPMI_COMMAND}' == 'Dbus'
43    ...    Run Dbus IPMI RAW Command  ${command}
44    ...  ELSE  Fail  msg=Invalid IPMI Command type provided: ${IPMI_COMMAND}
45    [Return]  ${resp}
46
47
48Run IPMI Standard Command
49    [Documentation]  Run the standard IPMI command.
50    [Arguments]  ${command}  ${fail_on_err}=${1}  &{options}
51
52    # Description of argument(s):
53    # command                       The IPMI command string to be executed
54    #                               (e.g. "0x06 0x36").
55    # fail_on_err                   Fail if the IPMI command execution fails.
56    # options                       Additional ipmitool command options (e.g.
57    #                               -C=3, -I=lanplus, etc.).  Currently, only
58    #                               used for external IPMI commands.
59
60    ${resp}=  Run Keyword If  '${IPMI_COMMAND}' == 'External'
61    ...    Run External IPMI Standard Command  ${command}  ${fail_on_err}  &{options}
62    ...  ELSE IF  '${IPMI_COMMAND}' == 'Inband'
63    ...    Run Inband IPMI Standard Command  ${command}  ${fail_on_err}
64    ...  ELSE IF  '${IPMI_COMMAND}' == 'Dbus'
65    ...    Run Dbus IPMI Standard Command  ${command}
66    ...  ELSE  Fail  msg=Invalid IPMI Command type provided : ${IPMI_COMMAND}
67
68    [Return]  ${resp}
69
70
71Run Dbus IPMI RAW Command
72    [Documentation]  Run the raw IPMI command through dbus.
73    [Arguments]    ${command}
74    ${valueinBytes}=   Byte Conversion  ${command}
75    ${cmd}=   Catenate   ${dbushostipmicmd1} ${dbusHostIpmiCmdReceivedMsg}
76    ${cmd}=   Catenate   ${cmd} ${valueinBytes}
77    ${output}   ${stderr}=  Execute Command  ${cmd}  return_stderr=True
78    Should Be Empty      ${stderr}
79    set test variable    ${OUTPUT}     "${output}"
80
81
82Run Dbus IPMI Standard Command
83    [Documentation]  Run the standard IPMI command through dbus.
84    [Arguments]    ${command}
85    Copy ipmitool
86    ${stdout}    ${stderr}    ${output}=  Execute Command
87    ...    /tmp/ipmitool -I dbus ${command}    return_stdout=True
88    ...    return_stderr= True    return_rc=True
89    Should Be Equal    ${output}    ${0}    msg=${stderr}
90    [Return]    ${stdout}
91
92
93Run Inband IPMI Raw Command
94    [Documentation]  Run the raw IPMI command in-band.
95    [Arguments]  ${command}  ${os_host}=${OS_HOST}  ${os_username}=${OS_USERNAME}
96    ...          ${os_password}=${OS_PASSWORD}
97
98    # Description of argument(s):
99    # command                       The IPMI command string to be executed
100    #                               (e.g. "0x06 0x36").
101    # os_host                       The host name or IP address of the OS Host.
102    # os_username                   The OS host user name.
103    # os_password                   The OS host passwrd.
104
105    Login To OS Host  ${os_host}  ${os_username}  ${os_password}
106    Check If IPMI Tool Exist
107
108    ${ipmi_cmd}=  Catenate  ${IPMI_INBAND_CMD}  ${RAW}  ${command}
109    Qprint Issuing  ${ipmi_cmd}
110    ${stdout}  ${stderr}=  Execute Command  ${ipmi_cmd}  return_stderr=True
111    Should Be Empty  ${stderr}  msg=${stdout}
112    [Return]  ${stdout}
113
114
115Run Inband IPMI Standard Command
116    [Documentation]  Run the standard IPMI command in-band.
117    [Arguments]  ${command}  ${fail_on_err}=${1}  ${os_host}=${OS_HOST}
118    ...          ${os_username}=${OS_USERNAME}  ${os_password}=${OS_PASSWORD}
119
120    # Description of argument(s):
121    # command                       The IPMI command string to be executed
122    #                               (e.g. "power status").
123    # os_host                       The host name or IP address of the OS Host.
124    # os_username                   The OS host user name.
125    # os_password                   The OS host passwrd.
126
127    Login To OS Host  ${os_host}  ${os_username}  ${os_password}
128    Check If IPMI Tool Exist
129
130    ${ipmi_cmd}=  Catenate  ${IPMI_INBAND_CMD}  ${command}
131    Qprint Issuing  ${ipmi_cmd}
132    ${stdout}  ${stderr}=  Execute Command  ${ipmi_cmd}  return_stderr=True
133    Return From Keyword If  ${fail_on_err} == ${0}  ${stderr}
134    Should Be Empty  ${stderr}  msg=${stdout}
135    [Return]  ${stdout}
136
137
138Run External IPMI Standard Command
139    [Documentation]  Run the external IPMI standard command.
140    [Arguments]  ${command}  ${fail_on_err}=${1}  &{options}
141
142    # Description of argument(s):
143    # command                       The IPMI command string to be executed
144    #                               (e.g. "power status").  Note that if
145    #                               ${IPMI_USER_OPTIONS} has a value (e.g.
146    #                               "-vvv"), it will be pre-pended to this
147    #                               command string.
148    # fail_on_err                   Fail if the IPMI command execution fails.
149    # options                       Additional ipmitool command options (e.g.
150    #                               -C=3, -I=lanplus, etc.).
151
152    ${command_string}=  Process IPMI User Options  ${command}
153    ${ipmi_cmd}=  Create IPMI Ext Command String  ${command_string}  &{options}
154    Qprint Issuing  ${ipmi_cmd}
155    ${rc}  ${output}=  Run And Return RC and Output  ${ipmi_cmd}
156    Return From Keyword If  ${fail_on_err} == ${0}  ${output}
157    Should Be Equal  ${rc}  ${0}  msg=${output}
158    [Return]  ${output}
159
160
161Run External IPMI Raw Command
162    [Documentation]  Run the external IPMI raw command.
163    [Arguments]  ${command}  ${fail_on_err}=${1}  &{options}
164
165    # This keyword is a wrapper for 'Run External IPMI Standard Command'. See
166    # that keyword's prolog for argument details.  This keyword will pre-pend
167    # the word "raw" plus a space to command prior to calling 'Run External
168    # IPMI Standard Command'.
169
170    ${output}=  Run External IPMI Standard Command
171    ...  raw ${command}  ${fail_on_err}  &{options}
172    [Return]  ${output}
173
174
175Check If IPMI Tool Exist
176    [Documentation]  Check if IPMI Tool installed or not.
177    ${output}=  Execute Command  which ipmitool
178    Should Not Be Empty  ${output}  msg=ipmitool not installed.
179
180
181Activate SOL Via IPMI
182    [Documentation]  Start SOL using IPMI and route output to a file.
183    [Arguments]  ${file_path}=/tmp/sol_${OPENBMC_HOST}
184
185    # Description of argument(s):
186    # file_path                     The file path on the local machine (vs.
187    #                               OBMC) to collect SOL output. By default
188    #                               SOL output is collected at
189    #                               /tmp/sol_<BMC_IP> else user input location.
190
191    ${ipmi_cmd}=  Create IPMI Ext Command String  sol activate usesolkeepalive
192    Qprint Issuing  ${ipmi_cmd}
193    Start Process  ${ipmi_cmd}  shell=True  stdout=${file_path}
194    ...  alias=sol_proc
195
196
197Deactivate SOL Via IPMI
198    [Documentation]  Stop SOL using IPMI and return SOL output.
199    [Arguments]  ${file_path}=/tmp/sol_${OPENBMC_HOST}
200
201    # Description of argument(s):
202    # file_path                     The file path on the local machine to copy
203    #                               SOL output collected by above "Activate
204    #                               SOL Via IPMI" keyword.  By default it
205    #                               copies log from /tmp/sol_<BMC_IP>.
206
207    ${ipmi_cmd}=  Create IPMI Ext Command String  sol deactivate
208    Qprint Issuing  ${ipmi_cmd}
209    ${rc}  ${output}=  Run and Return RC and Output  ${ipmi_cmd}
210    Run Keyword If  ${rc} > 0  Run Keywords
211    ...  Run Keyword And Ignore Error  Terminate Process  sol_proc
212    ...  AND  Return From Keyword  ${output}
213
214    ${rc}  ${output}=  Run and Return RC and Output  cat ${file_path}
215    Should Be Equal  ${rc}  ${0}  msg=${output}
216
217    # Logging SOL output for debug purpose.
218    Log  ${output}
219
220    [Return]  ${output}
221
222
223Byte Conversion
224    [Documentation]   Byte Conversion method receives IPMI RAW commands as
225    ...               argument in string format.
226    ...               Sample argument is as follows
227    ...               "0x04 0x30 9 0x01 0x00 0x35 0x00 0x00 0x00 0x00 0x00
228    ...               0x00"
229    ...               IPMI RAW command format is as follows
230    ...               <netfn Byte> <cmd Byte> <Data Bytes..>
231    ...               This method converts IPMI command format into
232    ...               dbus command format  as follows
233    ...               <byte:seq-id> <byte:netfn> <byte:lun> <byte:cmd>
234    ...               <array:byte:data>
235    ...               Sample dbus  Host IPMI Received Message argument
236    ...               byte:0x00 byte:0x04 byte:0x00 byte:0x30
237    ...               array:byte:9,0x01,0x00,0x35,0x00,0x00,0x00,0x00,0x00,0x00
238    [Arguments]     ${args}
239    ${argLength}=   Get Length  ${args}
240    Set Global Variable  ${arrayByte}   array:byte:
241    @{listargs}=   Split String  ${args}
242    ${index}=   Set Variable   ${0}
243    :FOR  ${word}  IN  @{listargs}
244    \    Run Keyword if   ${index} == 0   Set NetFn Byte  ${word}
245    \    Run Keyword if   ${index} == 1   Set Cmd Byte    ${word}
246    \    Run Keyword if   ${index} > 1    Set Array Byte  ${word}
247    \    ${index}=    Set Variable    ${index + 1}
248    ${length}=   Get Length  ${arrayByte}
249    ${length}=   Evaluate  ${length} - 1
250    ${arrayByteLocal}=  Get Substring  ${arrayByte}  0   ${length}
251    Set Global Variable  ${arrayByte}   ${arrayByteLocal}
252    ${valueinBytesWithArray}=   Catenate  byte:0x00   ${netfnByte}  byte:0x00
253    ${valueinBytesWithArray}=   Catenate  ${valueinBytesWithArray}  ${cmdByte}
254    ${valueinBytesWithArray}=   Catenate  ${valueinBytesWithArray} ${arrayByte}
255    ${valueinBytesWithoutArray}=   Catenate  byte:0x00 ${netfnByte}  byte:0x00
256    ${valueinBytesWithoutArray}=   Catenate  ${valueinBytesWithoutArray} ${cmdByte}
257    #   To Check scenario for smaller IPMI raw commands with only 2 arguments
258    #   instead of usual 12 arguments.
259    #   Sample small IPMI raw command: Run IPMI command 0x06 0x36
260    #   If IPMI raw argument length is only 9 then return value in bytes without
261    #   array population.
262    #   Equivalent dbus-send argument for smaller IPMI raw command:
263    #   byte:0x00 byte:0x06 byte:0x00 byte:0x36
264    Run Keyword if   ${argLength} == 9     Return from Keyword    ${valueinBytesWithoutArray}
265    [Return]    ${valueinBytesWithArray}
266
267
268Set NetFn Byte
269    [Documentation]  Set the network function byte.
270    [Arguments]    ${word}
271    ${netfnByteLocal}=  Catenate   byte:${word}
272    Set Global Variable  ${netfnByte}  ${netfnByteLocal}
273
274
275Set Cmd Byte
276    [Documentation]  Set the command byte.
277    [Arguments]    ${word}
278    ${cmdByteLocal}=  Catenate   byte:${word}
279    Set Global Variable  ${cmdByte}  ${cmdByteLocal}
280
281
282Set Array Byte
283    [Documentation]  Set the array byte.
284    [Arguments]    ${word}
285    ${arrayByteLocal}=   Catenate   SEPARATOR=  ${arrayByte}  ${word}
286    ${arrayByteLocal}=   Catenate   SEPARATOR=  ${arrayByteLocal}   ,
287    Set Global Variable  ${arrayByte}   ${arrayByteLocal}
288
289
290Copy ipmitool
291    [Documentation]  Copy the ipmitool to the BMC.
292    ${ipmitool_error}=  Catenate  The ipmitool program could not be found in the tools directory.
293    ...  It is not part of the automation code by default. You must manually copy or link the correct openbmc
294    ...  version of the tool in to the tools directory in order to run this test suite.
295
296    OperatingSystem.File Should Exist  tools/ipmitool  msg=${ipmitool_error}
297
298    Import Library      SCPLibrary      WITH NAME       scp
299    scp.Open connection     ${OPENBMC_HOST}     username=${OPENBMC_USERNAME}      password=${OPENBMC_PASSWORD}
300    scp.Put File    tools/ipmitool   /tmp
301    SSHLibrary.Open Connection     ${OPENBMC_HOST}
302    Login   ${OPENBMC_USERNAME}    ${OPENBMC_PASSWORD}
303    Execute Command     chmod +x /tmp/ipmitool
304
305
306Initiate Host Boot Via External IPMI
307    [Documentation]  Initiate host power on using external IPMI.
308    [Arguments]  ${wait}=${1}
309
310    # Description of argument(s):
311    # wait                          Indicates that this keyword should wait
312    #                               for host running state.
313
314    ${output}=  Run External IPMI Standard Command  chassis power on
315    Should Not Contain  ${output}  Error
316
317    Run Keyword If  '${wait}' == '${0}'  Return From Keyword
318    Wait Until Keyword Succeeds  10 min  10 sec  Is Host Running
319
320
321Initiate Host PowerOff Via External IPMI
322    [Documentation]  Initiate host power off using external IPMI.
323    [Arguments]  ${wait}=${1}
324
325    # Description of argument(s):
326    # wait                          Indicates that this keyword should wait
327    #                               for host off state.
328
329    ${output}=  Run External IPMI Standard Command  chassis power off
330    Should Not Contain  ${output}  Error
331
332    Run Keyword If  '${wait}' == '${0}'  Return From Keyword
333    Wait Until Keyword Succeeds  3 min  10 sec  Is Host Off
334
335
336Get Host State Via External IPMI
337    [Documentation]  Returns host state using external IPMI.
338
339    ${output}=  Run External IPMI Standard Command  chassis power status
340    Should Not Contain  ${output}  Error
341    ${output}=  Fetch From Right  ${output}  ${SPACE}
342
343    [Return]  ${output}
344
345
346Set BMC Network From Host
347    [Documentation]  Set BMC network from host.
348    [Arguments]  ${nw_info}
349
350    # Description of argument(s):
351    # nw_info                       A dictionary containing the network
352    #                               information to apply.
353
354    Run Inband IPMI Standard Command
355    ...  lan set 1 ipaddr ${nw_info['IP Address']}
356
357    Run Inband IPMI Standard Command
358    ...  lan set 1 netmask ${nw_info['Subnet Mask']}
359
360    Run Inband IPMI Standard Command
361    ...  lan set 1 defgw ipaddr ${nw_info['Default Gateway IP']}
362