1#!/usr/bin/env python3 2import argparse 3import os 4import re 5import sys 6 7import yaml 8from inflection import underscore 9 10 11def is_dbus_safe(name: str) -> bool: 12 """ 13 Check if a string is safe for use on D-Bus. 14 15 Args: 16 name (str): The input string. 17 18 Returns: 19 bool: True if the string is safe for D-Bus, False otherwise. 20 """ 21 if not name: 22 return False # Empty names are not allowed 23 24 # Check for invalid characters 25 if not re.match(r"^[a-zA-Z0-9_]+$", name): 26 return False 27 28 # Check for leading or trailing dots/underscores 29 if name[0] in "._" or name[-1] in "._": 30 return False 31 32 # Check for consecutive dots or underscores 33 if ".." in name or "__" in name: 34 return False 35 36 # Check length restriction 37 if len(name) > 255: 38 return False 39 40 return True 41 42 43def convert_dbus_name_error(name: str) -> str: 44 45 if not is_dbus_safe(name): 46 print("WARNING: '" + name + "' is not dbus safe", file=sys.stderr) 47 raise ValueError("WARNING: '" + name + "' is not dbus safe") 48 49 return name 50 51 52def convert_dbus_name_warn(name: str) -> str: 53 54 if not is_dbus_safe(name): 55 print("WARNING: '" + name + "' is not dbus safe", file=sys.stderr) 56 57 result = underscore(name) 58 59 if name != result: 60 print( 61 "WARNING: converted '" + name + "' to '" + result + "'", 62 file=sys.stderr, 63 ) 64 65 return result 66 67 68def config_error(ofile, led_name, group_name, message): 69 ofile.close() 70 os.remove(ofile.name) 71 raise ValueError( 72 "Invalid Configuration for LED [" 73 + led_name 74 + "] in Group [" 75 + group_name 76 + "]: " 77 + message 78 ) 79 80 81def check_led_priority(led_name, group, value, priority_dict): 82 83 if led_name in priority_dict: 84 if value != priority_dict[led_name]: 85 # Priority for a particular LED needs to stay SAME 86 # across all groups 87 config_error( 88 ofile, 89 led_name, 90 group, 91 "Priority is NOT same across all groups", 92 ) 93 else: 94 priority_dict[led_name] = value 95 96 return 0 97 98 99def led_action_literal(action): 100 if action == "": 101 return "std::nullopt" 102 103 return "phosphor::led::Layout::Action::" + str(action) 104 105 106def generate_file_single_led( 107 ifile, led_name, list_dict, priority_dict, ofile, has_group_priority, group 108): 109 110 has_led_priority = "Priority" in list_dict 111 112 if has_group_priority and has_led_priority: 113 config_error( 114 ofile, 115 led_name, 116 group, 117 "cannot mix group priority with led priority", 118 ) 119 120 if (not has_group_priority) and (not has_led_priority): 121 config_error( 122 ofile, led_name, group, "no group priority or led priority defined" 123 ) 124 125 led_priority = list_dict.get("Priority", "") 126 127 if has_led_priority: 128 check_led_priority(led_name, group, led_priority, priority_dict) 129 130 action = led_action_literal(list_dict.get("Action", "Off")) 131 dutyOn = str(list_dict.get("DutyOn", 50)) 132 period = str(list_dict.get("Period", 0)) 133 priority = led_action_literal(led_priority) 134 135 ofile.write(' {"' + convert_dbus_name_warn(led_name) + '",') 136 ofile.write(action + ",") 137 ofile.write(dutyOn + ",") 138 ofile.write(period + ",") 139 ofile.write(priority + ",") 140 141 ofile.write("},\n") 142 143 return 0 144 145 146def generate_file_single_group(ifile, group, priority_dict, ofile): 147 # This section generates an std::unordered_map of LedGroupNames to 148 # std::set of LEDs containing the name and properties 149 led_dict = ifile[group] 150 151 group_priority = 0 152 has_group_priority = led_dict and "Priority" in led_dict 153 154 if has_group_priority: 155 group_priority = led_dict["Priority"] 156 # we do not want to enumerate this as a led group 157 del led_dict["Priority"] 158 159 ofile.write( 160 ' {"' 161 + "/xyz/openbmc_project/led/groups/" 162 + convert_dbus_name_error(group) 163 + '"' 164 + ",{ " 165 + str(group_priority) 166 + ",\n" 167 + "{\n" 168 ) 169 170 # Some LED groups could be empty 171 if not led_dict: 172 led_dict = {} 173 174 for led_name, list_dict in list(led_dict.items()): 175 generate_file_single_led( 176 ifile, 177 led_name, 178 list_dict, 179 priority_dict, 180 ofile, 181 has_group_priority, 182 group, 183 ) 184 185 ofile.write(" }}},\n") 186 187 return 0 188 189 190def generate_file(ifile, ofile): 191 # Dictionary having [Name:Priority] 192 priority_dict = {} 193 194 ofile.write("/* !!! WARNING: This is a GENERATED Code..") 195 ofile.write("Please do NOT Edit !!! */\n\n") 196 197 ofile.write("static const phosphor::led::GroupMap") 198 ofile.write(" systemLedMap = {\n\n") 199 200 for group in list(ifile.keys()): 201 generate_file_single_group(ifile, group, priority_dict, ofile) 202 ofile.write("};\n") 203 204 return 0 205 206 207if __name__ == "__main__": 208 script_dir = os.path.dirname(os.path.realpath(__file__)) 209 parser = argparse.ArgumentParser() 210 parser.add_argument( 211 "-f", "--filename", default="led.yaml", help="Input File Name" 212 ) 213 parser.add_argument( 214 "-l", 215 "--output-filename", 216 dest="outputfilename", 217 default="led-gen.hpp", 218 help="Output File Name", 219 ) 220 parser.add_argument( 221 "-i", 222 "--input-dir", 223 dest="inputdir", 224 default=script_dir, 225 help="Input directory", 226 ) 227 parser.add_argument( 228 "-o", 229 "--output-dir", 230 dest="outputdir", 231 default=".", 232 help="Output directory.", 233 ) 234 235 args = parser.parse_args() 236 237 # Default to the one that is in the current. 238 yaml_dir = script_dir 239 yaml_file = os.path.join(yaml_dir, "led.yaml") 240 241 if args.inputdir: 242 yaml_dir = args.inputdir 243 244 if args.filename: 245 yaml_file = os.path.join(yaml_dir, args.filename) 246 247 with open(yaml_file, "r") as f: 248 ifile = yaml.safe_load(f) 249 250 with open(os.path.join(args.outputdir, args.outputfilename), "w") as ofile: 251 generate_file(ifile, ofile) 252