1#!/usr/bin/env python 2 3""" 4This script reads in fan definition and zone definition YAML 5files and generates a set of structures for use by the fan control code. 6""" 7 8import os 9import sys 10import yaml 11from argparse import ArgumentParser 12from mako.template import Template 13from mako.lookup import TemplateLookup 14 15 16def parse_cpp_type(typeName): 17 """ 18 Take a list of dbus types from YAML and convert it to a recursive cpp 19 formed data structure. Each entry of the original list gets converted 20 into a tuple consisting of the type name and a list with the params 21 for this type, 22 e.g. 23 ['dict', ['string', 'dict', ['string', 'int64']]] 24 is converted to 25 [('dict', [('string', []), ('dict', [('string', []), 26 ('int64', [])]]] 27 """ 28 29 if not typeName: 30 return None 31 32 # Type names are _almost_ valid YAML. Insert a , before each [ 33 # and then wrap it in a [ ] and it becomes valid YAML (assuming 34 # the user gave us a valid typename). 35 typeArray = yaml.safe_load("[" + ",[".join(typeName.split("[")) + "]") 36 typeTuple = preprocess_yaml_type_array(typeArray).pop(0) 37 return get_cpp_type(typeTuple) 38 39 40def preprocess_yaml_type_array(typeArray): 41 """ 42 Flattens an array type into a tuple list that can be used to get the 43 supported cpp type from each element. 44 """ 45 46 result = [] 47 48 for i in range(len(typeArray)): 49 # Ignore lists because we merge them with the previous element 50 if type(typeArray[i]) is list: 51 continue 52 53 # If there is a next element and it is a list, merge it with the 54 # current element. 55 if i < len(typeArray)-1 and type(typeArray[i+1]) is list: 56 result.append( 57 (typeArray[i], 58 preprocess_yaml_type_array(typeArray[i+1]))) 59 else: 60 result.append((typeArray[i], [])) 61 62 return result 63 64 65def get_cpp_type(typeTuple): 66 """ 67 Take a list of dbus types and perform validity checking, such as: 68 [ variant [ dict [ int32, int32 ], double ] ] 69 This function then converts the type-list into a C++ type string. 70 """ 71 72 propertyMap = { 73 'byte': {'cppName': 'uint8_t', 'params': 0}, 74 'boolean': {'cppName': 'bool', 'params': 0}, 75 'int16': {'cppName': 'int16_t', 'params': 0}, 76 'uint16': {'cppName': 'uint16_t', 'params': 0}, 77 'int32': {'cppName': 'int32_t', 'params': 0}, 78 'uint32': {'cppName': 'uint32_t', 'params': 0}, 79 'int64': {'cppName': 'int64_t', 'params': 0}, 80 'uint64': {'cppName': 'uint64_t', 'params': 0}, 81 'double': {'cppName': 'double', 'params': 0}, 82 'string': {'cppName': 'std::string', 'params': 0}, 83 'array': {'cppName': 'std::vector', 'params': 1}, 84 'dict': {'cppName': 'std::map', 'params': 2}} 85 86 if len(typeTuple) != 2: 87 raise RuntimeError("Invalid typeTuple %s" % typeTuple) 88 89 first = typeTuple[0] 90 entry = propertyMap[first] 91 92 result = entry['cppName'] 93 94 # Handle 0-entry parameter lists. 95 if (entry['params'] == 0): 96 if (len(typeTuple[1]) != 0): 97 raise RuntimeError("Invalid typeTuple %s" % typeTuple) 98 else: 99 return result 100 101 # Get the parameter list 102 rest = typeTuple[1] 103 104 # Confirm parameter count matches. 105 if (entry['params'] != -1) and (entry['params'] != len(rest)): 106 raise RuntimeError("Invalid entry count for %s : %s" % 107 (first, rest)) 108 109 # Parse each parameter entry, if appropriate, and create C++ template 110 # syntax. 111 result += '<' 112 if entry.get('noparse'): 113 # Do not parse the parameter list, just use the first element 114 # of each tuple and ignore possible parameters 115 result += ", ".join([e[0] for e in rest]) 116 else: 117 result += ", ".join(map(lambda e: get_cpp_type(e), 118 rest)) 119 result += '>' 120 121 return result 122 123 124def convertToMap(listOfDict): 125 """ 126 Converts a list of dictionary entries to a std::map initialization list. 127 """ 128 listOfDict = listOfDict.replace('\'', '\"') 129 listOfDict = listOfDict.replace('[', '{') 130 listOfDict = listOfDict.replace(']', '}') 131 listOfDict = listOfDict.replace(':', ',') 132 return listOfDict 133 134 135def genEvent(event): 136 """ 137 Generates the source code of an event and returns it as a string 138 """ 139 e = "SetSpeedEvent{\n" 140 141 e += "Group{\n" 142 for group in event['groups']: 143 for member in group['members']: 144 e += "{\n" 145 e += "\"" + member['object'] + "\",\n" 146 e += "{\"" + member['interface'] + "\",\n" 147 e += " \"" + member['property'] + "\"}\n" 148 e += "},\n" 149 e += "},\n" 150 151 e += "std::vector<Action>{\n" 152 for a in event['action']: 153 if len(a['parameters']) != 0: 154 e += "make_action(action::" + a['name'] + "(\n" 155 else: 156 e += "make_action(action::" + a['name'] + "\n" 157 for i, p in enumerate(a['parameters']): 158 if (i+1) != len(a['parameters']): 159 e += p + ",\n" 160 else: 161 e += p + ")\n" 162 e += "),\n" 163 e += "},\n" 164 165 e += "TimerConf{\n" 166 if ('timer' in event['triggers']) and \ 167 (event['triggers']['timer'] is not None): 168 e += "\t" + event['triggers']['timer']['interval'] + ",\n" 169 e += "\t" + event['triggers']['timer']['type'] + "\n" 170 e += "},\n" 171 172 e += "std::vector<Signal>{\n" 173 for s in event['triggers']['signals']: 174 e += "Signal{\n" 175 e += "match::" + s['match'] + "(\n" 176 for i, mp in enumerate(s['mparams']): 177 if (i+1) != len(s['mparams']): 178 e += "\"" + mp + "\",\n" 179 else: 180 e += "\"" + mp + "\"\n" 181 e += "),\n" 182 e += "make_handler(\n" 183 if ('type' in s['sparams']) and (s['sparams']['type'] is not None): 184 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n" 185 else: 186 e += s['signal'] + "(\n" 187 for sp in s['sparams']['params']: 188 e += s['sparams'][sp] + ",\n" 189 if ('type' in s['hparams']) and (s['hparams']['type'] is not None): 190 e += ("handler::" + s['handler'] + 191 "<" + s['hparams']['type'] + ">(\n") 192 else: 193 e += "handler::" + s['handler'] + "(\n)" 194 for i, hp in enumerate(s['hparams']['params']): 195 if (i+1) != len(s['hparams']['params']): 196 e += s['hparams'][hp] + ",\n" 197 else: 198 e += s['hparams'][hp] + "\n" 199 e += "))\n" 200 e += ")\n" 201 e += "},\n" 202 e += "}\n" 203 204 e += "}" 205 206 return e 207 208 209def getGroups(zNum, zCond, edata, events): 210 """ 211 Extract and construct the groups for the given event. 212 """ 213 groups = [] 214 for eGroups in edata['groups']: 215 if ('zone_conditions' in eGroups) and \ 216 (eGroups['zone_conditions'] is not None): 217 # Zone conditions are optional in the events yaml but skip 218 # if this event's condition is not in this zone's conditions 219 if all('name' in z and z['name'] is not None and 220 not any(c['name'] == z['name'] for c in zCond) 221 for z in eGroups['zone_conditions']): 222 continue 223 224 # Zone numbers are optional in the events yaml but skip if this 225 # zone's zone number is not in the event's zone numbers 226 if all('zones' in z and z['zones'] is not None and 227 zNum not in z['zones'] 228 for z in eGroups['zone_conditions']): 229 continue 230 eGroup = next(g for g in events['groups'] 231 if g['name'] == eGroups['name']) 232 233 group = {} 234 members = [] 235 group['name'] = eGroup['name'] 236 for m in eGroup['members']: 237 member = {} 238 member['path'] = eGroup['type'] 239 member['object'] = (eGroup['type'] + m) 240 member['interface'] = eGroups['interface'] 241 member['property'] = eGroups['property']['name'] 242 member['type'] = eGroups['property']['type'] 243 # Use defined service to note member on zone object 244 if ('service' in eGroup) and \ 245 (eGroup['service'] is not None): 246 member['service'] = eGroup['service'] 247 # Add expected group member's property value if given 248 if ('value' in eGroups['property']) and \ 249 (eGroups['property']['value'] is not None): 250 if isinstance(eGroups['property']['value'], str) or \ 251 "string" in str(member['type']).lower(): 252 member['value'] = ( 253 "\"" + eGroups['property']['value'] + "\"") 254 else: 255 member['value'] = eGroups['property']['value'] 256 members.append(member) 257 group['members'] = members 258 groups.append(group) 259 return groups 260 261 262def getSignal(eGrps, eTrig, events): 263 """ 264 Extracts and constructs for each group member a signal 265 subscription of each match listed in the trigger. 266 """ 267 signals = [] 268 for group in eGrps: 269 for member in group['members']: 270 for eMatches in eTrig['matches']: 271 signal = {} 272 eMatch = next(m for m in events['matches'] 273 if m['name'] == eMatches['name']) 274 # If service not given, subscribe to signal match 275 if ('service' not in member): 276 signal['match'] = eMatch['name'] 277 params = [] 278 if ('parameters' in eMatch) and \ 279 (eMatch['parameters'] is not None): 280 for p in eMatch['parameters']: 281 params.append(member[str(p)]) 282 signal['mparams'] = params 283 284 if ('parameters' in eMatch['signal']) and \ 285 (eMatch['signal']['parameters'] is not None): 286 eSignal = eMatch['signal'] 287 else: 288 eSignal = next(s for s in events['signals'] 289 if s['name'] == eMatch['signal']) 290 signal['signal'] = eSignal['name'] 291 sparams = {} 292 if ('parameters' in eSignal) and \ 293 (eSignal['parameters'] is not None): 294 splist = [] 295 for p in eSignal['parameters']: 296 sp = str(p) 297 if (sp != 'type'): 298 splist.append(sp) 299 if (sp != 'group'): 300 sparams[sp] = "\"" + member[sp] + "\"" 301 else: 302 sparams[sp] = "Group{\n" 303 for m in group['members']: 304 sparams[sp] += ( 305 "{\n" + 306 "\"" + str(m['object']) + "\",\n" + 307 "{\"" + str(m['interface']) + "\"," + 308 "\"" + str(m['property']) + "\"}\n" + 309 "},\n") 310 sparams[sp] += "}" 311 else: 312 sparams[sp] = member[sp] 313 sparams['params'] = splist 314 signal['sparams'] = sparams 315 # Add signal handler 316 eHandler = next(h for h in events['handlers'] 317 if h['name'] == eSignal['handler']) 318 signal['handler'] = eHandler['name'] 319 hparams = {} 320 if ('parameters' in eHandler) and \ 321 (eHandler['parameters'] is not None): 322 hplist = [] 323 for p in eHandler['parameters']: 324 hp = str(p) 325 if (hp != 'type'): 326 hplist.append(hp) 327 if (hp != 'group'): 328 hparams[hp] = "\"" + member[hp] + "\"" 329 else: 330 hparams[hp] = "Group{\n" 331 for m in group['members']: 332 hparams[hp] += ( 333 "{\n" + 334 "\"" + str(m['object']) + "\",\n" + 335 "{\"" + str(m['interface']) + "\"," + 336 "\"" + str(m['property']) + "\"}\n" + 337 "},\n") 338 hparams[hp] += "}" 339 else: 340 hparams[hp] = member[hp] 341 hparams['params'] = hplist 342 signal['hparams'] = hparams 343 signals.append(signal) 344 return signals 345 346 347def getTimer(eTrig): 348 """ 349 Extracts and constructs the required parameters for an 350 event timer. 351 """ 352 timer = {} 353 timer['interval'] = ( 354 "static_cast<std::chrono::microseconds>" + 355 "(" + str(eTrig['interval']) + ")") 356 timer['type'] = "TimerType::" + str(eTrig['type']) 357 return timer 358 359 360def getActions(zNum, zCond, edata, actions, events): 361 """ 362 Extracts and constructs the make_action function call for 363 all the actions within the given event. 364 """ 365 action = [] 366 for eActions in actions['actions']: 367 actions = {} 368 eAction = next(a for a in events['actions'] 369 if a['name'] == eActions['name']) 370 actions['name'] = eAction['name'] 371 params = [] 372 if ('parameters' in eAction) and \ 373 (eAction['parameters'] is not None): 374 for p in eAction['parameters']: 375 param = "static_cast<" 376 if type(eActions[p]) is not dict: 377 if p == 'actions': 378 param = "std::vector<Action>{" 379 pActs = getActions(zNum, 380 zCond, 381 edata, 382 eActions, 383 events) 384 for a in pActs: 385 if (len(a['parameters']) != 0): 386 param += ( 387 "make_action(action::" + 388 a['name'] + 389 "(\n") 390 for i, ap in enumerate(a['parameters']): 391 if (i+1) != len(a['parameters']): 392 param += (ap + ",") 393 else: 394 param += (ap + ")") 395 else: 396 param += ("make_action(action::" + a['name']) 397 param += ")," 398 param += "}" 399 elif p == 'defevents' or p == 'altevents': 400 param = "std::vector<SetSpeedEvent>{\n" 401 for i, e in enumerate(eActions[p]): 402 aEvent = getEvent(zNum, zCond, e, events) 403 if not aEvent: 404 continue 405 if (i+1) != len(eActions[p]): 406 param += genEvent(aEvent) + ",\n" 407 else: 408 param += genEvent(aEvent) + "\n" 409 param += "\t}" 410 elif p == 'property': 411 if isinstance(eActions[p], str) or \ 412 "string" in str(eActions[p]['type']).lower(): 413 param += ( 414 str(eActions[p]['type']).lower() + 415 ">(\"" + str(eActions[p]) + "\")") 416 else: 417 param += ( 418 str(eActions[p]['type']).lower() + 419 ">(" + str(eActions[p]['value']).lower() + ")") 420 else: 421 # Default type to 'size_t' when not given 422 param += ("size_t>(" + str(eActions[p]).lower() + ")") 423 else: 424 if p == 'timer': 425 t = getTimer(eActions[p]) 426 param = ( 427 "TimerConf{" + t['interval'] + "," + 428 t['type'] + "}") 429 else: 430 param += (str(eActions[p]['type']).lower() + ">(") 431 if p != 'map': 432 if isinstance(eActions[p]['value'], str) or \ 433 "string" in str(eActions[p]['type']).lower(): 434 param += \ 435 "\"" + str(eActions[p]['value']) + "\")" 436 else: 437 param += \ 438 str(eActions[p]['value']).lower() + ")" 439 else: 440 param += ( 441 str(eActions[p]['type']).lower() + 442 convertToMap(str(eActions[p]['value'])) + ")") 443 params.append(param) 444 actions['parameters'] = params 445 action.append(actions) 446 return action 447 448 449def getEvent(zone_num, zone_conditions, e, events_data): 450 """ 451 Parses the sections of an event and populates the properties 452 that construct an event within the generated source. 453 """ 454 event = {} 455 456 # Add set speed event groups 457 grps = getGroups(zone_num, zone_conditions, e, events_data) 458 if not grps: 459 return 460 event['groups'] = grps 461 462 # Add optional set speed actions and function parameters 463 event['action'] = [] 464 if ('actions' in e) and \ 465 (e['actions'] is not None): 466 event['action'] = getActions(zone_num, 467 zone_conditions, 468 e, 469 e, 470 events_data) 471 472 # Add event triggers 473 event['triggers'] = {} 474 for trig in e['triggers']: 475 triggers = [] 476 if (trig['name'] == "timer"): 477 event['triggers']['timer'] = getTimer(trig) 478 elif (trig['name'] == "signal"): 479 if ('signals' not in event['triggers']): 480 event['triggers']['signals'] = [] 481 triggers = getSignal(event['groups'], trig, events_data) 482 event['triggers']['signals'].extend(triggers) 483 484 return event 485 486 487def addPrecondition(zNum, zCond, event, events_data): 488 """ 489 Parses the precondition section of an event and populates the necessary 490 structures to generate a precondition for a set speed event. 491 """ 492 precond = {} 493 # Add set speed event precondition group 494 grps = getGroups(zNum, zCond, event['precondition'], events_data) 495 if not grps: 496 return 497 precond['pcgrps'] = grps 498 499 # Add set speed event precondition actions 500 pc = [] 501 pcs = {} 502 pcs['name'] = event['precondition']['name'] 503 epc = next(p for p in events_data['preconditions'] 504 if p['name'] == event['precondition']['name']) 505 params = [] 506 for p in epc['parameters']: 507 param = {} 508 if p == 'groups': 509 param['type'] = "std::vector<PrecondGroup>" 510 param['open'] = "{" 511 param['close'] = "}" 512 values = [] 513 for group in precond['pcgrps']: 514 for pcgrp in group['members']: 515 value = {} 516 value['value'] = ( 517 "PrecondGroup{\"" + 518 str(pcgrp['object']) + "\",\"" + 519 str(pcgrp['interface']) + "\",\"" + 520 str(pcgrp['property']) + "\"," + 521 "static_cast<" + 522 str(pcgrp['type']).lower() + ">") 523 if isinstance(pcgrp['value'], str) or \ 524 "string" in str(pcgrp['type']).lower(): 525 value['value'] += ("(" + str(pcgrp['value']) + ")}") 526 else: 527 value['value'] += \ 528 ("(" + str(pcgrp['value']).lower() + ")}") 529 values.append(value) 530 param['values'] = values 531 params.append(param) 532 pcs['params'] = params 533 pc.append(pcs) 534 precond['pcact'] = pc 535 536 pcevents = [] 537 for pce in event['precondition']['events']: 538 pcevent = getEvent(zNum, zCond, pce, events_data) 539 if not pcevent: 540 continue 541 pcevents.append(pcevent) 542 precond['pcevts'] = pcevents 543 544 # Add precondition event triggers 545 precond['triggers'] = {} 546 for trig in event['precondition']['triggers']: 547 triggers = [] 548 if (trig['name'] == "timer"): 549 precond['triggers']['pctime'] = getTimer(trig) 550 elif (trig['name'] == "signal"): 551 if ('pcsigs' not in precond['triggers']): 552 precond['triggers']['pcsigs'] = [] 553 triggers = getSignal(precond['pcgrps'], trig, events_data) 554 precond['triggers']['pcsigs'].extend(triggers) 555 556 return precond 557 558 559def getEventsInZone(zone_num, zone_conditions, events_data): 560 """ 561 Constructs the event entries defined for each zone using the events yaml 562 provided. 563 """ 564 events = [] 565 566 if 'events' in events_data: 567 for e in events_data['events']: 568 event = {} 569 # Add precondition if given 570 if ('precondition' in e) and \ 571 (e['precondition'] is not None): 572 event['pc'] = addPrecondition(zone_num, 573 zone_conditions, 574 e, 575 events_data) 576 else: 577 event = getEvent(zone_num, zone_conditions, e, events_data) 578 if not event: 579 continue 580 events.append(event) 581 582 return events 583 584 585def getFansInZone(zone_num, profiles, fan_data): 586 """ 587 Parses the fan definition YAML files to find the fans 588 that match both the zone passed in and one of the 589 cooling profiles. 590 """ 591 592 fans = [] 593 594 for f in fan_data['fans']: 595 596 if zone_num != f['cooling_zone']: 597 continue 598 599 # 'cooling_profile' is optional (use 'all' instead) 600 if f.get('cooling_profile') is None: 601 profile = "all" 602 else: 603 profile = f['cooling_profile'] 604 605 if profile not in profiles: 606 continue 607 608 fan = {} 609 fan['name'] = f['inventory'] 610 fan['sensors'] = f['sensors'] 611 fan['target_interface'] = f.get( 612 'target_interface', 613 'xyz.openbmc_project.Control.FanSpeed') 614 fans.append(fan) 615 616 return fans 617 618 619def getIfacesInZone(zone_ifaces): 620 """ 621 Parse given interfaces for a zone for associating a zone with an interface 622 and set any properties listed to defined values upon fan control starting 623 on the zone. 624 """ 625 626 ifaces = [] 627 for i in zone_ifaces: 628 iface = {} 629 # Interface name not needed yet for fan zones but 630 # may be necessary as more interfaces are extended by the zones 631 iface['name'] = i['name'] 632 633 if ('properties' in i) and \ 634 (i['properties'] is not None): 635 props = [] 636 for p in i['properties']: 637 prop = {} 638 prop['name'] = p['name'] 639 prop['func'] = str(p['name']).lower() 640 prop['type'] = parse_cpp_type(p['type']) 641 if ('persist' in p): 642 persist = p['persist'] 643 if (persist is not None): 644 if (isinstance(persist, bool)): 645 prop['persist'] = 'true' if persist else 'false' 646 else: 647 prop['persist'] = 'false' 648 vals = [] 649 for v in p['values']: 650 val = v['value'] 651 if (val is not None): 652 if (isinstance(val, bool)): 653 # Convert True/False to 'true'/'false' 654 val = 'true' if val else 'false' 655 elif (isinstance(val, str)): 656 # Wrap strings with double-quotes 657 val = "\"" + val + "\"" 658 vals.append(val) 659 prop['values'] = vals 660 props.append(prop) 661 iface['props'] = props 662 ifaces.append(iface) 663 664 return ifaces 665 666 667def getConditionInZoneConditions(zone_condition, zone_conditions_data): 668 """ 669 Parses the zone conditions definition YAML files to find the condition 670 that match both the zone condition passed in. 671 """ 672 673 condition = {} 674 675 for c in zone_conditions_data['conditions']: 676 677 if zone_condition != c['name']: 678 continue 679 condition['type'] = c['type'] 680 properties = [] 681 for p in c['properties']: 682 property = {} 683 property['property'] = p['property'] 684 property['interface'] = p['interface'] 685 property['path'] = p['path'] 686 property['type'] = p['type'].lower() 687 property['value'] = str(p['value']).lower() 688 properties.append(property) 689 condition['properties'] = properties 690 691 return condition 692 693 694def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data): 695 """ 696 Combines the zone definition YAML and fan 697 definition YAML to create a data structure defining 698 the fan cooling zones. 699 """ 700 701 zone_groups = [] 702 703 for group in zone_data: 704 conditions = [] 705 # zone conditions are optional 706 if 'zone_conditions' in group and group['zone_conditions'] is not None: 707 for c in group['zone_conditions']: 708 709 if not zone_conditions_data: 710 sys.exit("No zone_conditions YAML file but " + 711 "zone_conditions used in zone YAML") 712 713 condition = getConditionInZoneConditions(c['name'], 714 zone_conditions_data) 715 716 if not condition: 717 sys.exit("Missing zone condition " + c['name']) 718 719 conditions.append(condition) 720 721 zone_group = {} 722 zone_group['conditions'] = conditions 723 724 zones = [] 725 for z in group['zones']: 726 zone = {} 727 728 # 'zone' is required 729 if ('zone' not in z) or (z['zone'] is None): 730 sys.exit("Missing fan zone number in " + zone_yaml) 731 732 zone['num'] = z['zone'] 733 734 zone['full_speed'] = z['full_speed'] 735 736 zone['default_floor'] = z['default_floor'] 737 738 # 'increase_delay' is optional (use 0 by default) 739 key = 'increase_delay' 740 zone[key] = z.setdefault(key, 0) 741 742 # 'decrease_interval' is optional (use 0 by default) 743 key = 'decrease_interval' 744 zone[key] = z.setdefault(key, 0) 745 746 # 'cooling_profiles' is optional (use 'all' instead) 747 if ('cooling_profiles' not in z) or \ 748 (z['cooling_profiles'] is None): 749 profiles = ["all"] 750 else: 751 profiles = z['cooling_profiles'] 752 753 # 'interfaces' is optional (no default) 754 ifaces = [] 755 if ('interfaces' in z) and \ 756 (z['interfaces'] is not None): 757 ifaces = getIfacesInZone(z['interfaces']) 758 759 fans = getFansInZone(z['zone'], profiles, fan_data) 760 events = getEventsInZone(z['zone'], group['zone_conditions'], 761 events_data) 762 763 if len(fans) == 0: 764 sys.exit("Didn't find any fans in zone " + str(zone['num'])) 765 766 if (ifaces): 767 zone['ifaces'] = ifaces 768 zone['fans'] = fans 769 zone['events'] = events 770 zones.append(zone) 771 772 zone_group['zones'] = zones 773 zone_groups.append(zone_group) 774 775 return zone_groups 776 777 778if __name__ == '__main__': 779 parser = ArgumentParser( 780 description="Phosphor fan zone definition parser") 781 782 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml', 783 default="example/zones.yaml", 784 help='fan zone definitional yaml') 785 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml', 786 default="example/fans.yaml", 787 help='fan definitional yaml') 788 parser.add_argument('-e', '--events_yaml', dest='events_yaml', 789 help='events to set speeds yaml') 790 parser.add_argument('-c', '--zone_conditions_yaml', 791 dest='zone_conditions_yaml', 792 help='conditions to determine zone yaml') 793 parser.add_argument('-o', '--output_dir', dest='output_dir', 794 default=".", 795 help='output directory') 796 args = parser.parse_args() 797 798 if not args.zone_yaml or not args.fan_yaml: 799 parser.print_usage() 800 sys.exit(1) 801 802 with open(args.zone_yaml, 'r') as zone_input: 803 zone_data = yaml.safe_load(zone_input) or {} 804 805 with open(args.fan_yaml, 'r') as fan_input: 806 fan_data = yaml.safe_load(fan_input) or {} 807 808 events_data = {} 809 if args.events_yaml: 810 with open(args.events_yaml, 'r') as events_input: 811 events_data = yaml.safe_load(events_input) or {} 812 813 zone_conditions_data = {} 814 if args.zone_conditions_yaml: 815 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input: 816 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {} 817 818 zone_config = buildZoneData(zone_data.get('zone_configuration', {}), 819 fan_data, events_data, zone_conditions_data) 820 821 manager_config = zone_data.get('manager_configuration', {}) 822 823 if manager_config.get('power_on_delay') is None: 824 manager_config['power_on_delay'] = 0 825 826 tmpls_dir = os.path.join( 827 os.path.dirname(os.path.realpath(__file__)), 828 "templates") 829 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp") 830 if sys.version_info < (3, 0): 831 lkup = TemplateLookup( 832 directories=tmpls_dir.split(), 833 disable_unicode=True) 834 else: 835 lkup = TemplateLookup( 836 directories=tmpls_dir.split()) 837 tmpl = lkup.get_template('fan_zone_defs.mako.cpp') 838 with open(output_file, 'w') as output: 839 output.write(tmpl.render(zones=zone_config, 840 mgr_data=manager_config)) 841