blob: e845f3acb630db5dd582d270f664f54aa04258de [file] [log] [blame]
Michael Walshb973f9b2018-09-19 16:00:06 -05001#!/usr/bin/env python
2
3r"""
4This module provides many valuable openbmctool.py functions such as
5openbmctool_execute_command.
6"""
7
Michael Walshd064ac92018-09-21 16:42:03 -05008import gen_print as gp
Michael Walshb973f9b2018-09-19 16:00:06 -05009import gen_cmd as gc
10import gen_valid as gv
Michael Walshd064ac92018-09-21 16:42:03 -050011import gen_misc as gm
12import var_funcs as vf
Michael Walsh0e7f3012018-10-11 17:05:46 -050013import utils as utils
Michael Walshb973f9b2018-09-19 16:00:06 -050014from robot.libraries.BuiltIn import BuiltIn
15import re
Michael Walsh1793aeb2018-09-27 16:32:53 -050016import tempfile
Michael Walshb973f9b2018-09-19 16:00:06 -050017
18
19def openbmctool_execute_command(command_string,
20 *args,
21 **kwargs):
22 r"""
23 Run the command string as an argument to the openbmctool.py program and
24 return the stdout and the return code.
25
26 This function provides several benefits versus calling shell_cmd directly:
27 - This function will obtain the global values for OPENBMC_HOST,
28 OPENBMC_USERNAME, etc.
29 - This function will compose the openbmctool.py command string which
30 includes the caller's command_string.
31 - The openbmctool.py produces additional text that clutters the output.
32 This function will remove such text. Example:
33 Attempting login...
34 <actual output>
35 User root has been logged out
36
37 NOTE: If you have pipe symbols in your command_string, they must be
38 surrounded by a single space on each side (see example below).
39
40 Example code:
41 ${rc} ${output}= Openbmctool Execute Command fru status | head -n 2
42
43 Example output:
44 #(CDT) 2018/09/19 15:16:58 - Issuing: set -o pipefail ; openbmctool.py -H hostname -U root -P ********
45 ... fru status | tail -n +1 | egrep -v 'Attempting login|User [^ ]+ hasbeen logged out' | head -n 2
46 Component | Is a FRU | Present | Functional | Has Logs
47 cpu0 | Yes | Yes | Yes | No
48
49 Description of arguments:
50 command_string The command string to be passed to the
51 openbmctool.py program.
Michael Walsh58b11ac2018-09-20 15:24:37 -050052 All remaining arguments are passed directly to shell_cmd. See the
53 shell_cmd prolog for details on allowable arguments. The caller may code
54 them directly as in this example:
55 openbmctool_execute_command("my command", quiet=1, max_attempts=2).
56 Python will do the work of putting these values into args/kwargs.
Michael Walshb973f9b2018-09-19 16:00:06 -050057 """
58
59 if not gv.valid_value(command_string):
60 return "", "", 1
61
62 # Get global BMC variable values.
63 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}", default="")
64 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}",
65 default="")
66 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}",
67 default="")
68 if not gv.valid_value(openbmc_host):
69 return "", "", 1
70 if not gv.valid_value(openbmc_username):
71 return "", "", 1
72 if not gv.valid_value(openbmc_password):
73 return "", "", 1
74
75 # Break the caller's command up into separate piped commands. For
76 # example, the user may have specified "fru status | head -n 2" which
Michael Walsh1793aeb2018-09-27 16:32:53 -050077 # would be broken into 2 list elements. We will also break on ">"
78 # (re-direct).
Michael Walsh3343b5e2019-08-07 14:08:19 -050079 pipeline = list(map(str.strip, re.split(r' ([\|>]) ',
80 str(command_string))))
Michael Walshb973f9b2018-09-19 16:00:06 -050081 # The "tail" command below prevents a "egrep: write error: Broken pipe"
82 # error if the user is piping the output to a sub-process.
83 # Use "egrep -v" to get rid of editorial output from openbmctool.py.
Michael Walsh1793aeb2018-09-27 16:32:53 -050084 pipeline.insert(1, "| tail -n +1 | egrep -v 'Attempting login|User [^ ]+"
85 " has been logged out'")
Michael Walshb973f9b2018-09-19 16:00:06 -050086
Michael Walsh7384b9a2018-09-26 17:22:54 -050087 command_string = "set -o pipefail ; python3 $(which openbmctool.py) -H "\
88 + openbmc_host + " -U " + openbmc_username + " -P " + openbmc_password\
Michael Walsh1793aeb2018-09-27 16:32:53 -050089 + " " + " ".join(pipeline)
Michael Walshb973f9b2018-09-19 16:00:06 -050090
91 return gc.shell_cmd(command_string, *args, **kwargs)
Michael Walshd064ac92018-09-21 16:42:03 -050092
93
Michael Walsh0e7f3012018-10-11 17:05:46 -050094def openbmctool_execute_command_json(command_string,
95 *args,
96 **kwargs):
97 r"""
98 Run the command string as an argument to the openbmctool.py program, parse
99 the JSON output into a dictionary and return the dictionary.
100
101 This function is a wrapper for openbmctool_execute_command (defined
102 above). The caller may provide any command string where the output will
103 be JSON data. This function will convert the JSON data to a python
104 object, verify that the 'status' field = "ok" and return the 'data'
105 sub-field to the caller.
106
107 See openbmctool_execute_command (above) for all field descriptions.
108 """
109
110 rc, output = openbmctool_execute_command(command_string,
111 *args,
112 **kwargs)
113 json_object = utils.to_json_ordered(output)
114 if json_object['status'] != "ok":
115 err_msg = "Error found in JSON data returned by the openbmctool.py "
116 err_msg += "command. Expected a 'status' field value of \"ok\":\n"
117 err_msg += gp.sprint_var(json_object, 1)
118 BuiltIn().fail(gp.sprint_error(err_msg))
119
120 return json_object['data']
121
122
Michael Walshd064ac92018-09-21 16:42:03 -0500123def get_fru_status():
124 r"""
125 Get the fru status and return as a list of dictionaries.
126
127 Example robot code:
128
129 ${fru_status}= Get Fru Status
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500130 Rprint Vars fru_status fmt=1
Michael Walshd064ac92018-09-21 16:42:03 -0500131
132 Example result (excerpt):
133
134 fru_status:
135 fru_status[0]:
136 [component]: cpu0
137 [is_a]: Yes
138 [fru]: Yes
139 [present]: Yes
140 [functional]: No
141 fru_status[1]:
142 [component]: cpu0-core0
143 [is_a]: No
144 [fru]: Yes
145 [present]: Yes
146 [functional]: No
147 ...
148 """
149 rc, output = openbmctool_execute_command("fru status", print_output=False,
150 ignore_err=False)
151 # Example value for output (partial):
152 # Component | Is a FRU | Present | Functional | Has Logs
153 # cpu0 | Yes | Yes | Yes | No
154 # cpu0-core0 | No | Yes | Yes | No
155 # ...
156
157 # Replace spaces with underscores in field names (e.g. "Is a FRU" becomes
158 # "Is_a_FRU").
159 output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
160 output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
161
162 return vf.outbuf_to_report(output, field_delim="|")
163
164
165def get_fru_print(parse_json=True):
166 r"""
167 Get the output of the fru print command and return it either as raw JSON
168 data or as a list of dictionaries.
169
170 Example robot code:
171
172 ${fru_print}= Get Fru Print parse_json=${False}
173 Log to Console ${fru_print}
174
175 Example result (excerpt):
176
177 {
178 "data": {
179 "/xyz/openbmc_project/inventory/system": {
180 "AssetTag": "",
181 "BuildDate": "",
182 "Cached": false,
183 "FieldReplaceable": false,
184 "Manufacturer": "",
185 "Model": "xxxxxxxx",
186 "PartNumber": "",
187 "Present": true,
188 "PrettyName": "",
189 "SerialNumber": "13183FA"
190 },
191 "/xyz/openbmc_project/inventory/system/chassis": {
192 "AirCooled": true,
193 "WaterCooled": false
194 },
195 ...
196
197 Example robot code:
198
199 ${fru_print}= Get Fru Print
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500200 Rprint Vars fru_print fmt=1
Michael Walshd064ac92018-09-21 16:42:03 -0500201
202 Example result (excerpt):
203
204 fru_print:
205 fru_print[0]:
206 [data]:
207 [/xyz/openbmc_project/inventory/system]:
208 [AssetTag]: <blank>
209 [BuildDate]: <blank>
210 [Cached]: False
211 [FieldReplaceable]: False
212 [Manufacturer]: <blank>
213 [Model]: xxxxxxxx
214 [PartNumber]: <blank>
215 [Present]: True
216 [PrettyName]: <blank>
217 [SerialNumber]: 13183FA
218 [/xyz/openbmc_project/inventory/system/chassis]:
219 [AirCooled]: True
220 [WaterCooled]: False
221 ...
222
223 Description of argument(s):
224 parse_json Indicates that the raw JSON data should
225 parsed into a list of dictionaries.
226 """
227
228 rc, output = openbmctool_execute_command("fru print", print_output=False,
229 ignore_err=False)
230 if parse_json:
231 return gm.json_loads_multiple(output)
232 else:
233 return output
234
235
236def get_fru_list(parse_json=True):
237 r"""
238 Get the output of the fru list command and return it either as raw JSON
239 data or as a list of dictionaries.
240
241 Example robot code:
242
243 ${fru_list}= Get Fru List parse_json=${False}
244 Log to Console ${fru_list}
245
246 Example result (excerpt):
247
248 {
249 "data": {
250 "/xyz/openbmc_project/inventory/system": {
251 "AssetTag": "",
252 "BuildDate": "",
253 "Cached": false,
254 "FieldReplaceable": false,
255 "Manufacturer": "",
256 "Model": "xxxxxxxx",
257 "PartNumber": "",
258 "Present": true,
259 "PrettyName": "",
260 "SerialNumber": "13183FA"
261 },
262 "/xyz/openbmc_project/inventory/system/chassis": {
263 "AirCooled": true,
264 "WaterCooled": false
265 },
266 ...
267
268 Example robot code:
269
270 ${fru_list}= Get Fru List
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500271 Rprint Vars fru_list fmt=1
Michael Walshd064ac92018-09-21 16:42:03 -0500272
273 Example result (excerpt):
274
275 fru_list:
276 fru_list[0]:
277 [data]:
278 [/xyz/openbmc_project/inventory/system]:
279 [AssetTag]: <blank>
280 [BuildDate]: <blank>
281 [Cached]: False
282 [FieldReplaceable]: False
283 [Manufacturer]: <blank>
284 [Model]: xxxxxxxx
285 [PartNumber]: <blank>
286 [Present]: True
287 [PrettyName]: <blank>
288 [SerialNumber]: 13183FA
289 [/xyz/openbmc_project/inventory/system/chassis]:
290 [AirCooled]: True
291 [WaterCooled]: False
292 ...
293
294 Description of argument(s):
295 parse_json Indicates that the raw JSON data should
296 parsed into a list of dictionaries.
297 """
298
299 rc, output = openbmctool_execute_command("fru list", print_output=False,
300 ignore_err=False)
301 if parse_json:
302 return gm.json_loads_multiple(output)
303 else:
304 return output
305
306
307def get_sensors_print():
308
309 r"""
310 Get the output of the sensors print command and return as a list of
311 dictionaries.
312
313 Example robot code:
314
315 ${sensors_print}= Get Sensors Print
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500316 Rprint Vars sensors_print fmt=1
Michael Walshd064ac92018-09-21 16:42:03 -0500317
318 Example result (excerpt):
319
320 sensors_print:
321 sensors_print[0]:
322 [sensor]: OCC0
323 [type]: Discrete
324 [units]: N/A
325 [value]: Active
326 [target]: Active
327 sensors_print[1]:
328 [sensor]: OCC1
329 [type]: Discrete
330 [units]: N/A
331 [value]: Active
332 [target]: Active
333 ...
334 """
335 rc, output = openbmctool_execute_command("sensors print",
336 print_output=False,
337 ignore_err=False)
338 # Example value for output (partial):
339 # sensor | type | units | value | target
340 # OCC0 | Discrete | N/A | Active | Active
341 # OCC1 | Discrete | N/A | Active | Active
342
343 return vf.outbuf_to_report(output, field_delim="|")
344
345
346def get_sensors_list():
347
348 r"""
349 Get the output of the sensors list command and return as a list of
350 dictionaries.
351
352 Example robot code:
353
354 ${sensors_list}= Get Sensors List
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500355 Rprint Vars sensors_list fmt=1
Michael Walshd064ac92018-09-21 16:42:03 -0500356
357 Example result (excerpt):
358
359 sensors_list:
360 sensors_list[0]:
361 [sensor]: OCC0
362 [type]: Discrete
363 [units]: N/A
364 [value]: Active
365 [target]: Active
366 sensors_list[1]:
367 [sensor]: OCC1
368 [type]: Discrete
369 [units]: N/A
370 [value]: Active
371 [target]: Active
372 ...
373 """
374 rc, output = openbmctool_execute_command("sensors list",
375 print_output=False,
376 ignore_err=False)
377 # Example value for output (partial):
378 # sensor | type | units | value | target
379 # OCC0 | Discrete | N/A | Active | Active
380 # OCC1 | Discrete | N/A | Active | Active
381
382 return vf.outbuf_to_report(output, field_delim="|")
383
384
385def get_openbmctool_version():
386 r"""
387 Get the openbmctool.py version and return it.
388
389 Example robot code:
390 ${openbmctool_version}= Get Openbmctool Version
391 Rprint Vars openbmctool_version
392
393 Example result (excerpt):
394 openbmctool_version: 1.06
395 """
396 rc, output = openbmctool_execute_command("-V | cut -f 2 -d ' '",
397 print_output=False,
398 ignore_err=False)
399 return output
Michael Walsh1793aeb2018-09-27 16:32:53 -0500400
401
402def service_data_files():
403 r"""
404 Return a complete list of file names that are expected to be created by
405 the collect_service_data command.
406 """
407
408 return\
409 [
410 "inventory.txt",
411 "sensorReadings.txt",
412 "ledStatus.txt",
413 "SELshortlist.txt",
414 "parsedSELs.txt",
415 "bmcFullRaw.txt"
416 ]
417
418
419def collect_service_data(verify=False):
420 r"""
421 Run the collect_service_data command and return a list of files generated
422 by the command.
423
424 Description of argument(s):
425 verify If set, verify that all files which can be
426 created by collect_service_data did, in
427 fact, get created.
428 """
429
430 # Route the output of collect_service_data to a file for easier parsing.
431 temp = tempfile.NamedTemporaryFile()
432 temp_file_path = temp.name
433 openbmctool_execute_command("collect_service_data > " + temp_file_path,
434 ignore_err=False)
435 # Isolate the file paths in the collect_service_data output. We're
436 # looking for output lines like this from which to extract the file paths:
437 # Inventory collected and stored in /tmp/dummy--2018-09-26_17.59.18/inventory.txt
438 rc, file_paths = gc.shell_cmd("egrep 'collected and' " + temp_file_path
439 # + " | sed -re 's#.*/tmp#/tmp#g'",
440 + " | sed -re 's#[^/]*/#/#'",
441 quiet=1, print_output=0)
442 # Example file_paths value:
443 # /tmp/dummy--2018-09-26_17.59.18/inventory.txt
444 # /tmp/dummy--2018-09-26_17.59.18/sensorReadings.txt
445 # etc.
446 # Convert from output to list.
447 collect_service_data_file_paths =\
448 list(filter(None, file_paths.split("\n")))
449 if int(verify):
450 # Create a list of files by stripping the dir names from the elements
451 # of collect_service_data_file_paths.
452 files_obtained = [re.sub(r".*/", "", file_path)
453 for file_path in collect_service_data_file_paths]
454 files_expected = service_data_files()
455 files_missing = list(set(files_expected) - set(files_obtained))
456 if len(files_missing) > 0:
457 gp.printn("collect_service_data output:\n"
458 + gm.file_to_str(temp_file_path))
459 err_msg = "The following files are missing from the list of files"
460 err_msg += " returned by collect_service_data:\n"
461 err_msg += gp.sprint_var(files_missing)
462 err_msg += gp.sprint_var(collect_service_data_file_paths)
463 BuiltIn().fail(gp.sprint_error(err_msg))
464
465 return collect_service_data_file_paths
466
467
468def health_check_fields():
469 r"""
470 Return a complete list of field names returned by the health_check command.
471 """
472
473 return\
474 [
475 "hardware_status",
476 "performance"
477 ]
478
479
480def get_health_check(verify=False):
481 r"""
482 Get the health_check information and return as a dictionary.
483
484 Example robot code:
485
486 ${health_check}= Get Health Check
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500487 Rprint Vars health_check fmt=1
Michael Walsh1793aeb2018-09-27 16:32:53 -0500488
489 Example result:
490
491 health_check:
492 [hardware_status]: OK
493 [performance]: OK
494
495 Description of argument(s):
496 verify If set, verify that all all expected
497 field_names are generated by the
498 health_check command.
499 """
500
501 rc, output = openbmctool_execute_command("health_check",
502 print_output=False,
503 ignore_err=False)
504 health_check = vf.key_value_outbuf_to_dict(output, delim=":")
505 if int(verify):
Michael Walshec01a6f2019-08-01 12:43:20 -0500506 err_msg = gv.valid_dict(health_check, health_check_fields())
Michael Walsh0e7f3012018-10-11 17:05:46 -0500507 if err_msg != "":
Michael Walsh1793aeb2018-09-27 16:32:53 -0500508 BuiltIn().fail(gp.sprint_error(err_msg))
509
510 return health_check
Michael Walsh0e7f3012018-10-11 17:05:46 -0500511
512
513def remote_logging_view_fields():
514 r"""
515 Return a complete list of field names returned by the logging
516 remote_logging view command.
517 """
518
519 return\
520 [
521 "Address",
522 "AddressFamily",
523 "Port"
524 ]
525
526
527def get_remote_logging_view(verify=False):
528 r"""
529 Get the remote_logging view information and return as a dictionary.
530
531 Example robot code:
532
533 ${remote_logging_view}= Get Remote Logging View
Michael Walsh0d5f96a2019-05-20 10:09:57 -0500534 Rprint Vars remote_logging_view fmt=1
Michael Walsh0e7f3012018-10-11 17:05:46 -0500535
536 Example result:
537
538 remote_logging_view:
539 [Address]: <blank>
540 [AddressFamily]: xyz.openbmc_project.Network.Client.IPProtocol.IPv4
541 [Port]: 0
542
543 Description of argument(s):
544 verify If set, verify that all all expected field
545 names are generated by the 'logging
546 remote_logging view' command.
547 """
548
549 remote_logging_view =\
550 openbmctool_execute_command_json("logging remote_logging view",
551 print_output=False,
552 ignore_err=False)
553
554 if int(verify):
Michael Walshec01a6f2019-08-01 12:43:20 -0500555 err_msg = gv.valid_dict(remote_logging_view,
556 remote_logging_view_fields())
Michael Walsh0e7f3012018-10-11 17:05:46 -0500557 if err_msg != "":
558 BuiltIn().fail(gp.sprint_error(err_msg))
559
560 return remote_logging_view