blob: 96e7b5ff7dcc741090f1a14432a800dbecdf2500 [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).
79 pipeline = map(str.strip, re.split(r' ([\|>]) ', str(command_string)))
Michael Walshb973f9b2018-09-19 16:00:06 -050080 # The "tail" command below prevents a "egrep: write error: Broken pipe"
81 # error if the user is piping the output to a sub-process.
82 # Use "egrep -v" to get rid of editorial output from openbmctool.py.
Michael Walsh1793aeb2018-09-27 16:32:53 -050083 pipeline.insert(1, "| tail -n +1 | egrep -v 'Attempting login|User [^ ]+"
84 " has been logged out'")
Michael Walshb973f9b2018-09-19 16:00:06 -050085
Michael Walsh7384b9a2018-09-26 17:22:54 -050086 command_string = "set -o pipefail ; python3 $(which openbmctool.py) -H "\
87 + openbmc_host + " -U " + openbmc_username + " -P " + openbmc_password\
Michael Walsh1793aeb2018-09-27 16:32:53 -050088 + " " + " ".join(pipeline)
Michael Walshb973f9b2018-09-19 16:00:06 -050089
90 return gc.shell_cmd(command_string, *args, **kwargs)
Michael Walshd064ac92018-09-21 16:42:03 -050091
92
Michael Walsh0e7f3012018-10-11 17:05:46 -050093def openbmctool_execute_command_json(command_string,
94 *args,
95 **kwargs):
96 r"""
97 Run the command string as an argument to the openbmctool.py program, parse
98 the JSON output into a dictionary and return the dictionary.
99
100 This function is a wrapper for openbmctool_execute_command (defined
101 above). The caller may provide any command string where the output will
102 be JSON data. This function will convert the JSON data to a python
103 object, verify that the 'status' field = "ok" and return the 'data'
104 sub-field to the caller.
105
106 See openbmctool_execute_command (above) for all field descriptions.
107 """
108
109 rc, output = openbmctool_execute_command(command_string,
110 *args,
111 **kwargs)
112 json_object = utils.to_json_ordered(output)
113 if json_object['status'] != "ok":
114 err_msg = "Error found in JSON data returned by the openbmctool.py "
115 err_msg += "command. Expected a 'status' field value of \"ok\":\n"
116 err_msg += gp.sprint_var(json_object, 1)
117 BuiltIn().fail(gp.sprint_error(err_msg))
118
119 return json_object['data']
120
121
Michael Walshd064ac92018-09-21 16:42:03 -0500122def get_fru_status():
123 r"""
124 Get the fru status and return as a list of dictionaries.
125
126 Example robot code:
127
128 ${fru_status}= Get Fru Status
129 Rprint Vars 1 fru_status
130
131 Example result (excerpt):
132
133 fru_status:
134 fru_status[0]:
135 [component]: cpu0
136 [is_a]: Yes
137 [fru]: Yes
138 [present]: Yes
139 [functional]: No
140 fru_status[1]:
141 [component]: cpu0-core0
142 [is_a]: No
143 [fru]: Yes
144 [present]: Yes
145 [functional]: No
146 ...
147 """
148 rc, output = openbmctool_execute_command("fru status", print_output=False,
149 ignore_err=False)
150 # Example value for output (partial):
151 # Component | Is a FRU | Present | Functional | Has Logs
152 # cpu0 | Yes | Yes | Yes | No
153 # cpu0-core0 | No | Yes | Yes | No
154 # ...
155
156 # Replace spaces with underscores in field names (e.g. "Is a FRU" becomes
157 # "Is_a_FRU").
158 output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
159 output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
160
161 return vf.outbuf_to_report(output, field_delim="|")
162
163
164def get_fru_print(parse_json=True):
165 r"""
166 Get the output of the fru print command and return it either as raw JSON
167 data or as a list of dictionaries.
168
169 Example robot code:
170
171 ${fru_print}= Get Fru Print parse_json=${False}
172 Log to Console ${fru_print}
173
174 Example result (excerpt):
175
176 {
177 "data": {
178 "/xyz/openbmc_project/inventory/system": {
179 "AssetTag": "",
180 "BuildDate": "",
181 "Cached": false,
182 "FieldReplaceable": false,
183 "Manufacturer": "",
184 "Model": "xxxxxxxx",
185 "PartNumber": "",
186 "Present": true,
187 "PrettyName": "",
188 "SerialNumber": "13183FA"
189 },
190 "/xyz/openbmc_project/inventory/system/chassis": {
191 "AirCooled": true,
192 "WaterCooled": false
193 },
194 ...
195
196 Example robot code:
197
198 ${fru_print}= Get Fru Print
199 Rprint Vars 1 fru_print
200
201 Example result (excerpt):
202
203 fru_print:
204 fru_print[0]:
205 [data]:
206 [/xyz/openbmc_project/inventory/system]:
207 [AssetTag]: <blank>
208 [BuildDate]: <blank>
209 [Cached]: False
210 [FieldReplaceable]: False
211 [Manufacturer]: <blank>
212 [Model]: xxxxxxxx
213 [PartNumber]: <blank>
214 [Present]: True
215 [PrettyName]: <blank>
216 [SerialNumber]: 13183FA
217 [/xyz/openbmc_project/inventory/system/chassis]:
218 [AirCooled]: True
219 [WaterCooled]: False
220 ...
221
222 Description of argument(s):
223 parse_json Indicates that the raw JSON data should
224 parsed into a list of dictionaries.
225 """
226
227 rc, output = openbmctool_execute_command("fru print", print_output=False,
228 ignore_err=False)
229 if parse_json:
230 return gm.json_loads_multiple(output)
231 else:
232 return output
233
234
235def get_fru_list(parse_json=True):
236 r"""
237 Get the output of the fru list command and return it either as raw JSON
238 data or as a list of dictionaries.
239
240 Example robot code:
241
242 ${fru_list}= Get Fru List parse_json=${False}
243 Log to Console ${fru_list}
244
245 Example result (excerpt):
246
247 {
248 "data": {
249 "/xyz/openbmc_project/inventory/system": {
250 "AssetTag": "",
251 "BuildDate": "",
252 "Cached": false,
253 "FieldReplaceable": false,
254 "Manufacturer": "",
255 "Model": "xxxxxxxx",
256 "PartNumber": "",
257 "Present": true,
258 "PrettyName": "",
259 "SerialNumber": "13183FA"
260 },
261 "/xyz/openbmc_project/inventory/system/chassis": {
262 "AirCooled": true,
263 "WaterCooled": false
264 },
265 ...
266
267 Example robot code:
268
269 ${fru_list}= Get Fru List
270 Rprint Vars 1 fru_list
271
272 Example result (excerpt):
273
274 fru_list:
275 fru_list[0]:
276 [data]:
277 [/xyz/openbmc_project/inventory/system]:
278 [AssetTag]: <blank>
279 [BuildDate]: <blank>
280 [Cached]: False
281 [FieldReplaceable]: False
282 [Manufacturer]: <blank>
283 [Model]: xxxxxxxx
284 [PartNumber]: <blank>
285 [Present]: True
286 [PrettyName]: <blank>
287 [SerialNumber]: 13183FA
288 [/xyz/openbmc_project/inventory/system/chassis]:
289 [AirCooled]: True
290 [WaterCooled]: False
291 ...
292
293 Description of argument(s):
294 parse_json Indicates that the raw JSON data should
295 parsed into a list of dictionaries.
296 """
297
298 rc, output = openbmctool_execute_command("fru list", print_output=False,
299 ignore_err=False)
300 if parse_json:
301 return gm.json_loads_multiple(output)
302 else:
303 return output
304
305
306def get_sensors_print():
307
308 r"""
309 Get the output of the sensors print command and return as a list of
310 dictionaries.
311
312 Example robot code:
313
314 ${sensors_print}= Get Sensors Print
315 Rprint Vars 1 sensors_print
316
317 Example result (excerpt):
318
319 sensors_print:
320 sensors_print[0]:
321 [sensor]: OCC0
322 [type]: Discrete
323 [units]: N/A
324 [value]: Active
325 [target]: Active
326 sensors_print[1]:
327 [sensor]: OCC1
328 [type]: Discrete
329 [units]: N/A
330 [value]: Active
331 [target]: Active
332 ...
333 """
334 rc, output = openbmctool_execute_command("sensors print",
335 print_output=False,
336 ignore_err=False)
337 # Example value for output (partial):
338 # sensor | type | units | value | target
339 # OCC0 | Discrete | N/A | Active | Active
340 # OCC1 | Discrete | N/A | Active | Active
341
342 return vf.outbuf_to_report(output, field_delim="|")
343
344
345def get_sensors_list():
346
347 r"""
348 Get the output of the sensors list command and return as a list of
349 dictionaries.
350
351 Example robot code:
352
353 ${sensors_list}= Get Sensors List
354 Rprint Vars 1 sensors_list
355
356 Example result (excerpt):
357
358 sensors_list:
359 sensors_list[0]:
360 [sensor]: OCC0
361 [type]: Discrete
362 [units]: N/A
363 [value]: Active
364 [target]: Active
365 sensors_list[1]:
366 [sensor]: OCC1
367 [type]: Discrete
368 [units]: N/A
369 [value]: Active
370 [target]: Active
371 ...
372 """
373 rc, output = openbmctool_execute_command("sensors list",
374 print_output=False,
375 ignore_err=False)
376 # Example value for output (partial):
377 # sensor | type | units | value | target
378 # OCC0 | Discrete | N/A | Active | Active
379 # OCC1 | Discrete | N/A | Active | Active
380
381 return vf.outbuf_to_report(output, field_delim="|")
382
383
384def get_openbmctool_version():
385 r"""
386 Get the openbmctool.py version and return it.
387
388 Example robot code:
389 ${openbmctool_version}= Get Openbmctool Version
390 Rprint Vars openbmctool_version
391
392 Example result (excerpt):
393 openbmctool_version: 1.06
394 """
395 rc, output = openbmctool_execute_command("-V | cut -f 2 -d ' '",
396 print_output=False,
397 ignore_err=False)
398 return output
Michael Walsh1793aeb2018-09-27 16:32:53 -0500399
400
401def service_data_files():
402 r"""
403 Return a complete list of file names that are expected to be created by
404 the collect_service_data command.
405 """
406
407 return\
408 [
409 "inventory.txt",
410 "sensorReadings.txt",
411 "ledStatus.txt",
412 "SELshortlist.txt",
413 "parsedSELs.txt",
414 "bmcFullRaw.txt"
415 ]
416
417
418def collect_service_data(verify=False):
419 r"""
420 Run the collect_service_data command and return a list of files generated
421 by the command.
422
423 Description of argument(s):
424 verify If set, verify that all files which can be
425 created by collect_service_data did, in
426 fact, get created.
427 """
428
429 # Route the output of collect_service_data to a file for easier parsing.
430 temp = tempfile.NamedTemporaryFile()
431 temp_file_path = temp.name
432 openbmctool_execute_command("collect_service_data > " + temp_file_path,
433 ignore_err=False)
434 # Isolate the file paths in the collect_service_data output. We're
435 # looking for output lines like this from which to extract the file paths:
436 # Inventory collected and stored in /tmp/dummy--2018-09-26_17.59.18/inventory.txt
437 rc, file_paths = gc.shell_cmd("egrep 'collected and' " + temp_file_path
438 # + " | sed -re 's#.*/tmp#/tmp#g'",
439 + " | sed -re 's#[^/]*/#/#'",
440 quiet=1, print_output=0)
441 # Example file_paths value:
442 # /tmp/dummy--2018-09-26_17.59.18/inventory.txt
443 # /tmp/dummy--2018-09-26_17.59.18/sensorReadings.txt
444 # etc.
445 # Convert from output to list.
446 collect_service_data_file_paths =\
447 list(filter(None, file_paths.split("\n")))
448 if int(verify):
449 # Create a list of files by stripping the dir names from the elements
450 # of collect_service_data_file_paths.
451 files_obtained = [re.sub(r".*/", "", file_path)
452 for file_path in collect_service_data_file_paths]
453 files_expected = service_data_files()
454 files_missing = list(set(files_expected) - set(files_obtained))
455 if len(files_missing) > 0:
456 gp.printn("collect_service_data output:\n"
457 + gm.file_to_str(temp_file_path))
458 err_msg = "The following files are missing from the list of files"
459 err_msg += " returned by collect_service_data:\n"
460 err_msg += gp.sprint_var(files_missing)
461 err_msg += gp.sprint_var(collect_service_data_file_paths)
462 BuiltIn().fail(gp.sprint_error(err_msg))
463
464 return collect_service_data_file_paths
465
466
467def health_check_fields():
468 r"""
469 Return a complete list of field names returned by the health_check command.
470 """
471
472 return\
473 [
474 "hardware_status",
475 "performance"
476 ]
477
478
479def get_health_check(verify=False):
480 r"""
481 Get the health_check information and return as a dictionary.
482
483 Example robot code:
484
485 ${health_check}= Get Health Check
486 Rpvars 1 health_check
487
488 Example result:
489
490 health_check:
491 [hardware_status]: OK
492 [performance]: OK
493
494 Description of argument(s):
495 verify If set, verify that all all expected
496 field_names are generated by the
497 health_check command.
498 """
499
500 rc, output = openbmctool_execute_command("health_check",
501 print_output=False,
502 ignore_err=False)
503 health_check = vf.key_value_outbuf_to_dict(output, delim=":")
504 if int(verify):
Michael Walsh0e7f3012018-10-11 17:05:46 -0500505 err_msg = gv.svalid_dict(health_check, health_check_fields())
506 if err_msg != "":
Michael Walsh1793aeb2018-09-27 16:32:53 -0500507 BuiltIn().fail(gp.sprint_error(err_msg))
508
509 return health_check
Michael Walsh0e7f3012018-10-11 17:05:46 -0500510
511
512def remote_logging_view_fields():
513 r"""
514 Return a complete list of field names returned by the logging
515 remote_logging view command.
516 """
517
518 return\
519 [
520 "Address",
521 "AddressFamily",
522 "Port"
523 ]
524
525
526def get_remote_logging_view(verify=False):
527 r"""
528 Get the remote_logging view information and return as a dictionary.
529
530 Example robot code:
531
532 ${remote_logging_view}= Get Remote Logging View
533 Rpvars 1 remote_logging_view
534
535 Example result:
536
537 remote_logging_view:
538 [Address]: <blank>
539 [AddressFamily]: xyz.openbmc_project.Network.Client.IPProtocol.IPv4
540 [Port]: 0
541
542 Description of argument(s):
543 verify If set, verify that all all expected field
544 names are generated by the 'logging
545 remote_logging view' command.
546 """
547
548 remote_logging_view =\
549 openbmctool_execute_command_json("logging remote_logging view",
550 print_output=False,
551 ignore_err=False)
552
553 if int(verify):
554 err_msg = gv.svalid_dict(remote_logging_view,
555 remote_logging_view_fields())
556 if err_msg != "":
557 BuiltIn().fail(gp.sprint_error(err_msg))
558
559 return remote_logging_view