blob: 7f989636b8f6c514ade83ee44742150e03926e88 [file] [log] [blame]
#!/usr/bin/env python
r"""
This module provides many valuable openbmctool.py functions such as
openbmctool_execute_command.
"""
import gen_print as gp
import gen_cmd as gc
import gen_valid as gv
import gen_misc as gm
import var_funcs as vf
import utils as utils
from robot.libraries.BuiltIn import BuiltIn
import re
import tempfile
import collections
import json
def openbmctool_execute_command(command_string,
*args,
**kwargs):
r"""
Run the command string as an argument to the openbmctool.py program and
return the stdout and the return code.
This function provides several benefits versus calling shell_cmd directly:
- This function will obtain the global values for OPENBMC_HOST,
OPENBMC_USERNAME, etc.
- This function will compose the openbmctool.py command string which
includes the caller's command_string.
- The openbmctool.py produces additional text that clutters the output.
This function will remove such text. Example:
Attempting login...
<actual output>
User root has been logged out
NOTE: If you have pipe symbols in your command_string, they must be
surrounded by a single space on each side (see example below).
Example code:
${rc} ${output}= Openbmctool Execute Command fru status | head -n 2
Example output:
#(CDT) 2018/09/19 15:16:58 - Issuing: set -o pipefail ; openbmctool.py -H hostname -U root -P ********
... fru status | tail -n +1 | egrep -v 'Attempting login|User [^ ]+ hasbeen logged out' | head -n 2
Component | Is a FRU | Present | Functional | Has Logs
cpu0 | Yes | Yes | Yes | No
Description of arguments:
command_string The command string to be passed to the
openbmctool.py program.
All remaining arguments are passed directly to shell_cmd. See the
shell_cmd prolog for details on allowable arguments. The caller may code
them directly as in this example:
openbmctool_execute_command("my command", quiet=1, max_attempts=2).
Python will do the work of putting these values into args/kwargs.
"""
if not gv.valid_value(command_string):
return "", "", 1
# Get global BMC variable values.
openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}", default="")
https_port = BuiltIn().get_variable_value("${HTTPS_PORT}", default="443")
openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}",
default="")
openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}",
default="")
if not gv.valid_value(openbmc_host):
return "", "", 1
if not gv.valid_value(openbmc_username):
return "", "", 1
if not gv.valid_value(openbmc_password):
return "", "", 1
if not gv.valid_value(https_port):
return "", "", 1
# Break the caller's command up into separate piped commands. For
# example, the user may have specified "fru status | head -n 2" which
# would be broken into 2 list elements. We will also break on ">"
# (re-direct).
pipeline = list(map(str.strip, re.split(r' ([\|>]) ',
str(command_string))))
# The "tail" command below prevents a "egrep: write error: Broken pipe"
# error if the user is piping the output to a sub-process.
# Use "egrep -v" to get rid of editorial output from openbmctool.py.
pipeline.insert(1, "| tail -n +1 | egrep -v 'Attempting login|User [^ ]+"
" has been logged out'")
command_string = "set -o pipefail ; python3 $(which openbmctool.py) -H "\
+ openbmc_host + ":" + https_port + " -U " + openbmc_username + " -P " + openbmc_password\
+ " " + " ".join(pipeline)
return gc.shell_cmd(command_string, *args, **kwargs)
def openbmctool_execute_command_json(command_string,
*args,
**kwargs):
r"""
Run the command string as an argument to the openbmctool.py program, parse
the JSON output into a dictionary and return the dictionary.
This function is a wrapper for openbmctool_execute_command (defined
above). The caller may provide any command string where the output will
be JSON data. This function will convert the JSON data to a python
object, verify that the 'status' field = "ok" and return the 'data'
sub-field to the caller.
See openbmctool_execute_command (above) for all field descriptions.
"""
rc, output = openbmctool_execute_command(command_string,
*args,
**kwargs)
try:
json_object = utils.to_json_ordered(output)
except json.JSONDecodeError:
BuiltIn().fail(gp.sprint_error(output))
if json_object['status'] != "ok":
err_msg = "Error found in JSON data returned by the openbmctool.py "
err_msg += "command. Expected a 'status' field value of \"ok\":\n"
err_msg += gp.sprint_var(json_object, 1)
BuiltIn().fail(gp.sprint_error(err_msg))
return json_object['data']
def get_fru_status():
r"""
Get the fru status and return as a list of dictionaries.
Example robot code:
${fru_status}= Get Fru Status
Rprint Vars fru_status fmt=1
Example result (excerpt):
fru_status:
fru_status[0]:
[component]: cpu0
[is_a]: Yes
[fru]: Yes
[present]: Yes
[functional]: No
fru_status[1]:
[component]: cpu0-core0
[is_a]: No
[fru]: Yes
[present]: Yes
[functional]: No
...
"""
rc, output = openbmctool_execute_command("fru status", print_output=False,
ignore_err=False)
# Example value for output (partial):
# Component | Is a FRU | Present | Functional | Has Logs
# cpu0 | Yes | Yes | Yes | No
# cpu0-core0 | No | Yes | Yes | No
# ...
# Replace spaces with underscores in field names (e.g. "Is a FRU" becomes
# "Is_a_FRU").
output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
output = re.sub("([^ \\|])[ ]([^ ])", "\\1_\\2", output)
return vf.outbuf_to_report(output, field_delim="|")
def get_fru_print(parse_json=True):
r"""
Get the output of the fru print command and return it either as raw JSON
data or as a list of dictionaries.
Example robot code:
${fru_print}= Get Fru Print parse_json=${False}
Log to Console ${fru_print}
Example result (excerpt):
{
"data": {
"/xyz/openbmc_project/inventory/system": {
"AssetTag": "",
"BuildDate": "",
"Cached": false,
"FieldReplaceable": false,
"Manufacturer": "",
"Model": "xxxxxxxx",
"PartNumber": "",
"Present": true,
"PrettyName": "",
"SerialNumber": "13183FA"
},
"/xyz/openbmc_project/inventory/system/chassis": {
"AirCooled": true,
"WaterCooled": false
},
...
Example robot code:
${fru_print}= Get Fru Print
Rprint Vars fru_print fmt=1
Example result (excerpt):
fru_print:
fru_print[0]:
[data]:
[/xyz/openbmc_project/inventory/system]:
[AssetTag]: <blank>
[BuildDate]: <blank>
[Cached]: False
[FieldReplaceable]: False
[Manufacturer]: <blank>
[Model]: xxxxxxxx
[PartNumber]: <blank>
[Present]: True
[PrettyName]: <blank>
[SerialNumber]: 13183FA
[/xyz/openbmc_project/inventory/system/chassis]:
[AirCooled]: True
[WaterCooled]: False
...
Description of argument(s):
parse_json Indicates that the raw JSON data should
parsed into a list of dictionaries.
"""
rc, output = openbmctool_execute_command("fru print", print_output=False,
ignore_err=False)
if parse_json:
return gm.json_loads_multiple(output)
else:
return output
def get_fru_list(parse_json=True):
r"""
Get the output of the fru list command and return it either as raw JSON
data or as a list of dictionaries.
Example robot code:
${fru_list}= Get Fru List parse_json=${False}
Log to Console ${fru_list}
Example result (excerpt):
{
"data": {
"/xyz/openbmc_project/inventory/system": {
"AssetTag": "",
"BuildDate": "",
"Cached": false,
"FieldReplaceable": false,
"Manufacturer": "",
"Model": "xxxxxxxx",
"PartNumber": "",
"Present": true,
"PrettyName": "",
"SerialNumber": "13183FA"
},
"/xyz/openbmc_project/inventory/system/chassis": {
"AirCooled": true,
"WaterCooled": false
},
...
Example robot code:
${fru_list}= Get Fru List
Rprint Vars fru_list fmt=1
Example result (excerpt):
fru_list:
fru_list[0]:
[data]:
[/xyz/openbmc_project/inventory/system]:
[AssetTag]: <blank>
[BuildDate]: <blank>
[Cached]: False
[FieldReplaceable]: False
[Manufacturer]: <blank>
[Model]: xxxxxxxx
[PartNumber]: <blank>
[Present]: True
[PrettyName]: <blank>
[SerialNumber]: 13183FA
[/xyz/openbmc_project/inventory/system/chassis]:
[AirCooled]: True
[WaterCooled]: False
...
Description of argument(s):
parse_json Indicates that the raw JSON data should
parsed into a list of dictionaries.
"""
rc, output = openbmctool_execute_command("fru list", print_output=False,
ignore_err=False)
if parse_json:
return gm.json_loads_multiple(output)
else:
return output
def get_sensors_print():
r"""
Get the output of the sensors print command and return as a list of
dictionaries.
Example robot code:
${sensors_print}= Get Sensors Print
Rprint Vars sensors_print fmt=1
Example result (excerpt):
sensors_print:
sensors_print[0]:
[sensor]: OCC0
[type]: Discrete
[units]: N/A
[value]: Active
[target]: Active
sensors_print[1]:
[sensor]: OCC1
[type]: Discrete
[units]: N/A
[value]: Active
[target]: Active
...
"""
rc, output = openbmctool_execute_command("sensors print",
print_output=False,
ignore_err=False)
# Example value for output (partial):
# sensor | type | units | value | target
# OCC0 | Discrete | N/A | Active | Active
# OCC1 | Discrete | N/A | Active | Active
return vf.outbuf_to_report(output, field_delim="|")
def get_sensors_list():
r"""
Get the output of the sensors list command and return as a list of
dictionaries.
Example robot code:
${sensors_list}= Get Sensors List
Rprint Vars sensors_list fmt=1
Example result (excerpt):
sensors_list:
sensors_list[0]:
[sensor]: OCC0
[type]: Discrete
[units]: N/A
[value]: Active
[target]: Active
sensors_list[1]:
[sensor]: OCC1
[type]: Discrete
[units]: N/A
[value]: Active
[target]: Active
...
"""
rc, output = openbmctool_execute_command("sensors list",
print_output=False,
ignore_err=False)
# Example value for output (partial):
# sensor | type | units | value | target
# OCC0 | Discrete | N/A | Active | Active
# OCC1 | Discrete | N/A | Active | Active
return vf.outbuf_to_report(output, field_delim="|")
def get_openbmctool_version():
r"""
Get the openbmctool.py version and return it.
Example robot code:
${openbmctool_version}= Get Openbmctool Version
Rprint Vars openbmctool_version
Example result (excerpt):
openbmctool_version: 1.06
"""
rc, output = openbmctool_execute_command("-V | cut -f 2 -d ' '",
print_output=False,
ignore_err=False)
return output
def service_data_files():
r"""
Return a complete list of file names that are expected to be created by
the collect_service_data command.
"""
return\
[
"inventory.txt",
"sensorReadings.txt",
"ledStatus.txt",
"SELshortlist.txt",
"parsedSELs.txt",
"bmcFullRaw.txt"
]
def collect_service_data(verify=False):
r"""
Run the collect_service_data command and return a list of files generated
by the command.
Description of argument(s):
verify If set, verify that all files which can be
created by collect_service_data did, in
fact, get created.
"""
# Route the output of collect_service_data to a file for easier parsing.
temp = tempfile.NamedTemporaryFile()
temp_file_path = temp.name
openbmctool_execute_command("collect_service_data > " + temp_file_path,
ignore_err=False)
# Isolate the file paths in the collect_service_data output. We're
# looking for output lines like this from which to extract the file paths:
# Inventory collected and stored in /tmp/dummy--2018-09-26_17.59.18/inventory.txt
rc, file_paths = gc.shell_cmd("egrep 'collected and' " + temp_file_path
# + " | sed -re 's#.*/tmp#/tmp#g'",
+ " | sed -re 's#[^/]*/#/#'",
quiet=1, print_output=0)
# Example file_paths value:
# /tmp/dummy--2018-09-26_17.59.18/inventory.txt
# /tmp/dummy--2018-09-26_17.59.18/sensorReadings.txt
# etc.
# Convert from output to list.
collect_service_data_file_paths =\
list(filter(None, file_paths.split("\n")))
if int(verify):
# Create a list of files by stripping the dir names from the elements
# of collect_service_data_file_paths.
files_obtained = [re.sub(r".*/", "", file_path)
for file_path in collect_service_data_file_paths]
files_expected = service_data_files()
files_missing = list(set(files_expected) - set(files_obtained))
if len(files_missing) > 0:
gp.printn("collect_service_data output:\n"
+ gm.file_to_str(temp_file_path))
err_msg = "The following files are missing from the list of files"
err_msg += " returned by collect_service_data:\n"
err_msg += gp.sprint_var(files_missing)
err_msg += gp.sprint_var(collect_service_data_file_paths)
BuiltIn().fail(gp.sprint_error(err_msg))
return collect_service_data_file_paths
def health_check_fields():
r"""
Return a complete list of field names returned by the health_check command.
"""
return\
[
"hardware_status",
"performance"
]
def get_health_check(verify=False):
r"""
Get the health_check information and return as a dictionary.
Example robot code:
${health_check}= Get Health Check
Rprint Vars health_check fmt=1
Example result:
health_check:
[hardware_status]: OK
[performance]: OK
Description of argument(s):
verify If set, verify that all all expected
field_names are generated by the
health_check command.
"""
rc, output = openbmctool_execute_command("health_check",
print_output=False,
ignore_err=False)
health_check = vf.key_value_outbuf_to_dict(output, delim=":")
if int(verify):
err_msg = gv.valid_dict(health_check, health_check_fields())
if err_msg != "":
BuiltIn().fail(gp.sprint_error(err_msg))
return health_check
def remote_logging_view_fields():
r"""
Return a complete list of field names returned by the logging
remote_logging view command.
"""
return\
[
"Address",
"Port"
]
def get_remote_logging_view(verify=False):
r"""
Get the remote_logging view information and return as a dictionary.
Example robot code:
${remote_logging_view}= Get Remote Logging View
Rprint Vars remote_logging_view fmt=1
Example result:
remote_logging_view:
[Address]: <blank>
[AddressFamily]: xyz.openbmc_project.Network.Client.IPProtocol.IPv4
[Port]: 0
Description of argument(s):
verify If set, verify that all all expected field
names are generated by the 'logging
remote_logging view' command.
"""
remote_logging_view =\
openbmctool_execute_command_json("logging remote_logging view",
print_output=False,
ignore_err=False)
if int(verify):
err_msg = gv.valid_dict(remote_logging_view,
remote_logging_view_fields())
if err_msg != "":
BuiltIn().fail(gp.sprint_error(err_msg))
return remote_logging_view
def network(sub_command, **options):
r"""
Run an openbmctool.py network command and return the results as a dictionary.
Note that any valid network argument may be specified as a function argument.
Example robot code:
${ip_records}= Network getIP I=eth0
Rprint Vars ip_records
Resulting output:
ip_records:
[/xyz/openbmc_project/network/eth0/ipv4/23d41d48]:
[Address]: n.n.n.n
[Gateway]:
[Origin]: xyz.openbmc_project.Network.IP.AddressOrigin.Static
[PrefixLength]: 24
[Type]: xyz.openbmc_project.Network.IP.Protocol.IPv4
[/xyz/openbmc_project/network/eth0/ipv4/24ba5feb]:
[Address]: n.n.n.n
(etc.)
Description of argument(s):
sub_command The sub-command accepted by the network
command (e.g. "view-config", "getIP",
etc.).
options Zero or more options accepted by the network command.
"""
if gm.python_version < gm.ordered_dict_version:
new_options = collections.OrderedDict(options)
else:
new_options = options
command_string = gc.create_command_string('network ' + sub_command,
new_options)
return openbmctool_execute_command_json(command_string,
print_output=False,
ignore_err=False)