| #!/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) |