1""" 2This module provides functions for retrieving and validating sensor information 3from D-Bus and Redfish endpoints. It includes functionalities for: 4- Validating sensor threshold values according to IPMI specifications. 5- Checking sensor reading value lengths. 6- Converting sensor names to a 16-byte format as required by IPMI. 7- Creating a list of sensors that do not have a single threshold value. 8These functions support automated sensor validations as part of the Robot 9Framework tests in an OpenBMC environment. 10""" 11 12from robot.libraries.BuiltIn import BuiltIn 13 14 15def validate_threshold_values(sensor_threshold_values, sensor_id): 16 r""" 17 Validate sensor thresholds per IPMI spec: 18 Lower thresholds: lnr < lcr < lnc 19 Upper thresholds: unc > ucr > unr 20 21 Description of arguments: 22 sensor_threshold_values: Dictionary of threshold values. 23 sensor_id: Sensor identifier (e.g. "fan_1"). 24 """ 25 try: 26 # Validate lower thresholds 27 # lnr = Lower Non-Recoverable 28 # lcr = Lower Critical 29 # lnc = Lower Non-Critical 30 lnr = float(sensor_threshold_values["lnr"]) 31 lcr = float(sensor_threshold_values["lcr"]) 32 lnc = float(sensor_threshold_values["lnc"]) 33 if not (lnr < lcr < lnc): 34 error_msg = ( 35 f"{sensor_id}: Lower thresholds violate IPMI spec\n" 36 f"lnr={lnr} lcr={lcr} lnc={lnc}" 37 ) 38 BuiltIn().fail(error_msg) 39 except Exception as e: 40 BuiltIn().fail( 41 f"- Error: {e}\n" 42 f"- Threshold Values:\n" 43 f" • Lower Non-Recoverable (lnr): {lnr}\n" 44 f" • Lower Critical (lcr): {lcr}\n" 45 f" • Lower Non-Critical (lnc): {lnc}\n" 46 ) 47 48 try: 49 # Validate upper thresholds 50 # unc = Upper Non-Critical 51 # ucr = Upper Critical 52 # unr = Upper Non-Recoverable 53 unc = float(sensor_threshold_values["unc"]) 54 ucr = float(sensor_threshold_values["ucr"]) 55 unr = float(sensor_threshold_values["unr"]) 56 if not (unc > ucr > unr): 57 error_msg = ( 58 f"{sensor_id}: Upper thresholds violate IPMI spec\n" 59 f"unc={unc} ucr={ucr} unr={unr}" 60 ) 61 BuiltIn().fail(error_msg) 62 except Exception as e: 63 BuiltIn().fail( 64 f"- Error: {e}\n" 65 f"- Threshold Values:\n" 66 f" • Upper Non-Critical (unc): {unc}\n" 67 f" • Upper Critical (ucr): {ucr}\n" 68 f" • Upper Non-Recoverable (unr): {unr}\n" 69 ) 70 71 72def check_reading_value_length(sensor_reading, sensor_id, sensor_unit): 73 r""" 74 Validate sensor reading length per IPMI spec. 75 76 Description of arguments: 77 sensor_reading: Reading value (e.g. "1234.567"). 78 sensor_id: Sensor identifier (e.g. "temp_ambient"). 79 sensor_unit: Sensor unit (e.g. "RPM"). 80 """ 81 max_int_len = 6 if sensor_unit == "RPM" else 4 82 max_frac_len = 3 if sensor_unit == "RPM" else 4 83 84 if "." in sensor_reading: 85 integer_part, fractional_part = sensor_reading.split(".", 1) 86 else: 87 integer_part = sensor_reading 88 fractional_part = "" 89 90 if len(integer_part) > max_int_len: 91 BuiltIn().fail( 92 f"{sensor_id}: Integer part exceeds {max_int_len} digits " 93 f"({integer_part})" 94 ) 95 96 if len(fractional_part) > max_frac_len: 97 BuiltIn().fail( 98 f"{sensor_id}: Fractional part exceeds {max_frac_len} digits " 99 f"({fractional_part})" 100 ) 101 102 103def convert_sensor_name_as_per_ipmi_spec(sensor_name): 104 r""" 105 Convert sensor name to 16-byte IPMI-compliant format. 106 107 Description of arguments: 108 sensor_name: Original sensor name. 109 110 Example: 111 Input: "very_long_sensor_name_12345" 112 Output: "very_long_sensor" (16-byte truncated UTF-8) 113 """ 114 READING_VALUE_BYTE_LIMIT = 16 115 encoded = sensor_name.encode("utf-8") 116 truncated = encoded[:READING_VALUE_BYTE_LIMIT] 117 padded = truncated.ljust(READING_VALUE_BYTE_LIMIT, b"\x00") 118 return padded.decode("utf-8", errors="ignore") 119 120 121def create_sensor_list_not_having_single_threshold( 122 ipmi_sensor_response, threshold_sensor_list 123): 124 r""" 125 Identify sensors with no valid thresholds. 126 127 Description of arguments: 128 ipmi_sensor_response: Raw IPMI sensor output. 129 threshold_sensor_list: List of expected threshold sensors. 130 131 Example IPMI response line: 132 "fan_1 | 1000 RPM | ok | 200.000 | 500.000 | 600.000 | 700.000 | " 133 "800.000 | 900.000 | na |" 134 - Splitting by "|" gives parts where `parts[4:10]` are thresholds: 135 [500.000, 600.000, 700.000, 800.000, 900.000, na] 136 - If any threshold is not "na", thresholds exist for that sensor. 137 """ 138 sensor_ids_missing_threshold = [] 139 140 for sensor_id in threshold_sensor_list: 141 thresholds_exist = False 142 for line in ipmi_sensor_response.splitlines(): 143 if sensor_id in line: 144 parts = [p.strip() for p in line.split("|")] 145 if len(parts) >= 10: 146 thresholds = parts[4:10] 147 if any(t != "na" for t in thresholds): 148 thresholds_exist = True 149 break 150 if not thresholds_exist: 151 sensor_ids_missing_threshold.append(sensor_id) 152 153 return sensor_ids_missing_threshold 154