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 += "std::vector<Trigger>{\n" 166 if ('timer' in event['triggers']) and \ 167 (event['triggers']['timer'] is not None): 168 e += "\tmake_trigger(trigger::timer(TimerConf{\n" 169 e += "\t" + event['triggers']['timer']['interval'] + ",\n" 170 e += "\t" + event['triggers']['timer']['type'] + "\n" 171 e += "\t}))\n" 172 e += "},\n" 173 174 e += "std::vector<Signal>{\n" 175 for s in event['triggers']['signals']: 176 e += "Signal{\n" 177 e += "match::" + s['match'] + "(\n" 178 for i, mp in enumerate(s['mparams']): 179 if (i+1) != len(s['mparams']): 180 e += "\"" + mp + "\",\n" 181 else: 182 e += "\"" + mp + "\"\n" 183 e += "),\n" 184 e += "make_handler(\n" 185 if ('type' in s['sparams']) and (s['sparams']['type'] is not None): 186 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n" 187 else: 188 e += s['signal'] + "(\n" 189 for sp in s['sparams']['params']: 190 e += s['sparams'][sp] + ",\n" 191 if ('type' in s['hparams']) and (s['hparams']['type'] is not None): 192 e += ("handler::" + s['handler'] + 193 "<" + s['hparams']['type'] + ">(\n") 194 else: 195 e += "handler::" + s['handler'] + "(\n)" 196 for i, hp in enumerate(s['hparams']['params']): 197 if (i+1) != len(s['hparams']['params']): 198 e += s['hparams'][hp] + ",\n" 199 else: 200 e += s['hparams'][hp] + "\n" 201 e += "))\n" 202 e += ")\n" 203 e += "},\n" 204 e += "}\n" 205 206 e += "}" 207 208 return e 209 210 211def getGroups(zNum, zCond, edata, events): 212 """ 213 Extract and construct the groups for the given event. 214 """ 215 groups = [] 216 for eGroups in edata['groups']: 217 if ('zone_conditions' in eGroups) and \ 218 (eGroups['zone_conditions'] is not None): 219 # Zone conditions are optional in the events yaml but skip 220 # if this event's condition is not in this zone's conditions 221 if all('name' in z and z['name'] is not None and 222 not any(c['name'] == z['name'] for c in zCond) 223 for z in eGroups['zone_conditions']): 224 continue 225 226 # Zone numbers are optional in the events yaml but skip if this 227 # zone's zone number is not in the event's zone numbers 228 if all('zones' in z and z['zones'] is not None and 229 zNum not in z['zones'] 230 for z in eGroups['zone_conditions']): 231 continue 232 eGroup = next(g for g in events['groups'] 233 if g['name'] == eGroups['name']) 234 235 group = {} 236 members = [] 237 group['name'] = eGroup['name'] 238 for m in eGroup['members']: 239 member = {} 240 member['path'] = eGroup['type'] 241 member['object'] = (eGroup['type'] + m) 242 member['interface'] = eGroups['interface'] 243 member['property'] = eGroups['property']['name'] 244 member['type'] = eGroups['property']['type'] 245 # Use defined service to note member on zone object 246 if ('service' in eGroup) and \ 247 (eGroup['service'] is not None): 248 member['service'] = eGroup['service'] 249 # Add expected group member's property value if given 250 if ('value' in eGroups['property']) and \ 251 (eGroups['property']['value'] is not None): 252 if isinstance(eGroups['property']['value'], str) or \ 253 "string" in str(member['type']).lower(): 254 member['value'] = ( 255 "\"" + eGroups['property']['value'] + "\"") 256 else: 257 member['value'] = eGroups['property']['value'] 258 members.append(member) 259 group['members'] = members 260 groups.append(group) 261 return groups 262 263 264def getSignal(eGrps, eTrig, events): 265 """ 266 Extracts and constructs for each group member a signal 267 subscription of each match listed in the trigger. 268 """ 269 signals = [] 270 for group in eGrps: 271 for member in group['members']: 272 for eMatches in eTrig['matches']: 273 signal = {} 274 eMatch = next(m for m in events['matches'] 275 if m['name'] == eMatches['name']) 276 # If service not given, subscribe to signal match 277 if ('service' not in member): 278 signal['match'] = eMatch['name'] 279 params = [] 280 if ('parameters' in eMatch) and \ 281 (eMatch['parameters'] is not None): 282 for p in eMatch['parameters']: 283 params.append(member[str(p)]) 284 signal['mparams'] = params 285 286 if ('parameters' in eMatch['signal']) and \ 287 (eMatch['signal']['parameters'] is not None): 288 eSignal = eMatch['signal'] 289 else: 290 eSignal = next(s for s in events['signals'] 291 if s['name'] == eMatch['signal']) 292 signal['signal'] = eSignal['name'] 293 sparams = {} 294 if ('parameters' in eSignal) and \ 295 (eSignal['parameters'] is not None): 296 splist = [] 297 for p in eSignal['parameters']: 298 sp = str(p) 299 if (sp != 'type'): 300 splist.append(sp) 301 if (sp != 'group'): 302 sparams[sp] = "\"" + member[sp] + "\"" 303 else: 304 sparams[sp] = "Group{\n" 305 for m in group['members']: 306 sparams[sp] += ( 307 "{\n" + 308 "\"" + str(m['object']) + "\",\n" + 309 "{\"" + str(m['interface']) + "\"," + 310 "\"" + str(m['property']) + "\"}\n" + 311 "},\n") 312 sparams[sp] += "}" 313 else: 314 sparams[sp] = member[sp] 315 sparams['params'] = splist 316 signal['sparams'] = sparams 317 # Add signal handler 318 eHandler = next(h for h in events['handlers'] 319 if h['name'] == eSignal['handler']) 320 signal['handler'] = eHandler['name'] 321 hparams = {} 322 if ('parameters' in eHandler) and \ 323 (eHandler['parameters'] is not None): 324 hplist = [] 325 for p in eHandler['parameters']: 326 hp = str(p) 327 if (hp != 'type'): 328 hplist.append(hp) 329 if (hp != 'group'): 330 hparams[hp] = "\"" + member[hp] + "\"" 331 else: 332 hparams[hp] = "Group{\n" 333 for m in group['members']: 334 hparams[hp] += ( 335 "{\n" + 336 "\"" + str(m['object']) + "\",\n" + 337 "{\"" + str(m['interface']) + "\"," + 338 "\"" + str(m['property']) + "\"}\n" + 339 "},\n") 340 hparams[hp] += "}" 341 else: 342 hparams[hp] = member[hp] 343 hparams['params'] = hplist 344 signal['hparams'] = hparams 345 signals.append(signal) 346 return signals 347 348 349def getTimer(eTrig): 350 """ 351 Extracts and constructs the required parameters for an 352 event timer. 353 """ 354 timer = {} 355 timer['interval'] = ( 356 "static_cast<std::chrono::microseconds>" + 357 "(" + str(eTrig['interval']) + ")") 358 timer['type'] = "TimerType::" + str(eTrig['type']) 359 return timer 360 361 362def getActions(zNum, zCond, edata, actions, events): 363 """ 364 Extracts and constructs the make_action function call for 365 all the actions within the given event. 366 """ 367 action = [] 368 for eActions in actions['actions']: 369 actions = {} 370 eAction = next(a for a in events['actions'] 371 if a['name'] == eActions['name']) 372 actions['name'] = eAction['name'] 373 params = [] 374 if ('parameters' in eAction) and \ 375 (eAction['parameters'] is not None): 376 for p in eAction['parameters']: 377 param = "static_cast<" 378 if type(eActions[p]) is not dict: 379 if p == 'actions': 380 param = "std::vector<Action>{" 381 pActs = getActions(zNum, 382 zCond, 383 edata, 384 eActions, 385 events) 386 for a in pActs: 387 if (len(a['parameters']) != 0): 388 param += ( 389 "make_action(action::" + 390 a['name'] + 391 "(\n") 392 for i, ap in enumerate(a['parameters']): 393 if (i+1) != len(a['parameters']): 394 param += (ap + ",") 395 else: 396 param += (ap + ")") 397 else: 398 param += ("make_action(action::" + a['name']) 399 param += ")," 400 param += "}" 401 elif p == 'defevents' or p == 'altevents': 402 param = "std::vector<SetSpeedEvent>{\n" 403 for i, e in enumerate(eActions[p]): 404 aEvent = getEvent(zNum, zCond, e, events) 405 if not aEvent: 406 continue 407 if (i+1) != len(eActions[p]): 408 param += genEvent(aEvent) + ",\n" 409 else: 410 param += genEvent(aEvent) + "\n" 411 param += "\t}" 412 elif p == 'property': 413 if isinstance(eActions[p], str) or \ 414 "string" in str(eActions[p]['type']).lower(): 415 param += ( 416 str(eActions[p]['type']).lower() + 417 ">(\"" + str(eActions[p]) + "\")") 418 else: 419 param += ( 420 str(eActions[p]['type']).lower() + 421 ">(" + str(eActions[p]['value']).lower() + ")") 422 else: 423 # Default type to 'size_t' when not given 424 param += ("size_t>(" + str(eActions[p]).lower() + ")") 425 else: 426 if p == 'timer': 427 t = getTimer(eActions[p]) 428 param = ( 429 "TimerConf{" + t['interval'] + "," + 430 t['type'] + "}") 431 else: 432 param += (str(eActions[p]['type']).lower() + ">(") 433 if p != 'map': 434 if isinstance(eActions[p]['value'], str) or \ 435 "string" in str(eActions[p]['type']).lower(): 436 param += \ 437 "\"" + str(eActions[p]['value']) + "\")" 438 else: 439 param += \ 440 str(eActions[p]['value']).lower() + ")" 441 else: 442 param += ( 443 str(eActions[p]['type']).lower() + 444 convertToMap(str(eActions[p]['value'])) + ")") 445 params.append(param) 446 actions['parameters'] = params 447 action.append(actions) 448 return action 449 450 451def getEvent(zone_num, zone_conditions, e, events_data): 452 """ 453 Parses the sections of an event and populates the properties 454 that construct an event within the generated source. 455 """ 456 event = {} 457 458 # Add set speed event groups 459 grps = getGroups(zone_num, zone_conditions, e, events_data) 460 if not grps: 461 return 462 event['groups'] = grps 463 464 # Add optional set speed actions and function parameters 465 event['action'] = [] 466 if ('actions' in e) and \ 467 (e['actions'] is not None): 468 event['action'] = getActions(zone_num, 469 zone_conditions, 470 e, 471 e, 472 events_data) 473 474 # Add event triggers 475 event['triggers'] = {} 476 for trig in e['triggers']: 477 triggers = [] 478 if (trig['name'] == "timer"): 479 event['triggers']['timer'] = getTimer(trig) 480 elif (trig['name'] == "signal"): 481 if ('signals' not in event['triggers']): 482 event['triggers']['signals'] = [] 483 triggers = getSignal(event['groups'], trig, events_data) 484 event['triggers']['signals'].extend(triggers) 485 486 return event 487 488 489def addPrecondition(zNum, zCond, event, events_data): 490 """ 491 Parses the precondition section of an event and populates the necessary 492 structures to generate a precondition for a set speed event. 493 """ 494 precond = {} 495 # Add set speed event precondition group 496 grps = getGroups(zNum, zCond, event['precondition'], events_data) 497 if not grps: 498 return 499 precond['pcgrps'] = grps 500 501 # Add set speed event precondition actions 502 pc = [] 503 pcs = {} 504 pcs['name'] = event['precondition']['name'] 505 epc = next(p for p in events_data['preconditions'] 506 if p['name'] == event['precondition']['name']) 507 params = [] 508 for p in epc['parameters']: 509 param = {} 510 if p == 'groups': 511 param['type'] = "std::vector<PrecondGroup>" 512 param['open'] = "{" 513 param['close'] = "}" 514 values = [] 515 for group in precond['pcgrps']: 516 for pcgrp in group['members']: 517 value = {} 518 value['value'] = ( 519 "PrecondGroup{\"" + 520 str(pcgrp['object']) + "\",\"" + 521 str(pcgrp['interface']) + "\",\"" + 522 str(pcgrp['property']) + "\"," + 523 "static_cast<" + 524 str(pcgrp['type']).lower() + ">") 525 if isinstance(pcgrp['value'], str) or \ 526 "string" in str(pcgrp['type']).lower(): 527 value['value'] += ("(" + str(pcgrp['value']) + ")}") 528 else: 529 value['value'] += \ 530 ("(" + str(pcgrp['value']).lower() + ")}") 531 values.append(value) 532 param['values'] = values 533 params.append(param) 534 pcs['params'] = params 535 pc.append(pcs) 536 precond['pcact'] = pc 537 538 pcevents = [] 539 for pce in event['precondition']['events']: 540 pcevent = getEvent(zNum, zCond, pce, events_data) 541 if not pcevent: 542 continue 543 pcevents.append(pcevent) 544 precond['pcevts'] = pcevents 545 546 # Add precondition event triggers 547 precond['triggers'] = {} 548 for trig in event['precondition']['triggers']: 549 triggers = [] 550 if (trig['name'] == "timer"): 551 precond['triggers']['pctime'] = getTimer(trig) 552 elif (trig['name'] == "signal"): 553 if ('pcsigs' not in precond['triggers']): 554 precond['triggers']['pcsigs'] = [] 555 triggers = getSignal(precond['pcgrps'], trig, events_data) 556 precond['triggers']['pcsigs'].extend(triggers) 557 558 return precond 559 560 561def getEventsInZone(zone_num, zone_conditions, events_data): 562 """ 563 Constructs the event entries defined for each zone using the events yaml 564 provided. 565 """ 566 events = [] 567 568 if 'events' in events_data: 569 for e in events_data['events']: 570 event = {} 571 # Add precondition if given 572 if ('precondition' in e) and \ 573 (e['precondition'] is not None): 574 event['pc'] = addPrecondition(zone_num, 575 zone_conditions, 576 e, 577 events_data) 578 else: 579 event = getEvent(zone_num, zone_conditions, e, events_data) 580 if not event: 581 continue 582 events.append(event) 583 584 return events 585 586 587def getFansInZone(zone_num, profiles, fan_data): 588 """ 589 Parses the fan definition YAML files to find the fans 590 that match both the zone passed in and one of the 591 cooling profiles. 592 """ 593 594 fans = [] 595 596 for f in fan_data['fans']: 597 598 if zone_num != f['cooling_zone']: 599 continue 600 601 # 'cooling_profile' is optional (use 'all' instead) 602 if f.get('cooling_profile') is None: 603 profile = "all" 604 else: 605 profile = f['cooling_profile'] 606 607 if profile not in profiles: 608 continue 609 610 fan = {} 611 fan['name'] = f['inventory'] 612 fan['sensors'] = f['sensors'] 613 fan['target_interface'] = f.get( 614 'target_interface', 615 'xyz.openbmc_project.Control.FanSpeed') 616 fans.append(fan) 617 618 return fans 619 620 621def getIfacesInZone(zone_ifaces): 622 """ 623 Parse given interfaces for a zone for associating a zone with an interface 624 and set any properties listed to defined values upon fan control starting 625 on the zone. 626 """ 627 628 ifaces = [] 629 for i in zone_ifaces: 630 iface = {} 631 # Interface name not needed yet for fan zones but 632 # may be necessary as more interfaces are extended by the zones 633 iface['name'] = i['name'] 634 635 if ('properties' in i) and \ 636 (i['properties'] is not None): 637 props = [] 638 for p in i['properties']: 639 prop = {} 640 prop['name'] = p['name'] 641 prop['func'] = str(p['name']).lower() 642 prop['type'] = parse_cpp_type(p['type']) 643 if ('persist' in p): 644 persist = p['persist'] 645 if (persist is not None): 646 if (isinstance(persist, bool)): 647 prop['persist'] = 'true' if persist else 'false' 648 else: 649 prop['persist'] = 'false' 650 vals = [] 651 for v in p['values']: 652 val = v['value'] 653 if (val is not None): 654 if (isinstance(val, bool)): 655 # Convert True/False to 'true'/'false' 656 val = 'true' if val else 'false' 657 elif (isinstance(val, str)): 658 # Wrap strings with double-quotes 659 val = "\"" + val + "\"" 660 vals.append(val) 661 prop['values'] = vals 662 props.append(prop) 663 iface['props'] = props 664 ifaces.append(iface) 665 666 return ifaces 667 668 669def getConditionInZoneConditions(zone_condition, zone_conditions_data): 670 """ 671 Parses the zone conditions definition YAML files to find the condition 672 that match both the zone condition passed in. 673 """ 674 675 condition = {} 676 677 for c in zone_conditions_data['conditions']: 678 679 if zone_condition != c['name']: 680 continue 681 condition['type'] = c['type'] 682 properties = [] 683 for p in c['properties']: 684 property = {} 685 property['property'] = p['property'] 686 property['interface'] = p['interface'] 687 property['path'] = p['path'] 688 property['type'] = p['type'].lower() 689 property['value'] = str(p['value']).lower() 690 properties.append(property) 691 condition['properties'] = properties 692 693 return condition 694 695 696def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data): 697 """ 698 Combines the zone definition YAML and fan 699 definition YAML to create a data structure defining 700 the fan cooling zones. 701 """ 702 703 zone_groups = [] 704 705 for group in zone_data: 706 conditions = [] 707 # zone conditions are optional 708 if 'zone_conditions' in group and group['zone_conditions'] is not None: 709 for c in group['zone_conditions']: 710 711 if not zone_conditions_data: 712 sys.exit("No zone_conditions YAML file but " + 713 "zone_conditions used in zone YAML") 714 715 condition = getConditionInZoneConditions(c['name'], 716 zone_conditions_data) 717 718 if not condition: 719 sys.exit("Missing zone condition " + c['name']) 720 721 conditions.append(condition) 722 723 zone_group = {} 724 zone_group['conditions'] = conditions 725 726 zones = [] 727 for z in group['zones']: 728 zone = {} 729 730 # 'zone' is required 731 if ('zone' not in z) or (z['zone'] is None): 732 sys.exit("Missing fan zone number in " + zone_yaml) 733 734 zone['num'] = z['zone'] 735 736 zone['full_speed'] = z['full_speed'] 737 738 zone['default_floor'] = z['default_floor'] 739 740 # 'increase_delay' is optional (use 0 by default) 741 key = 'increase_delay' 742 zone[key] = z.setdefault(key, 0) 743 744 # 'decrease_interval' is optional (use 0 by default) 745 key = 'decrease_interval' 746 zone[key] = z.setdefault(key, 0) 747 748 # 'cooling_profiles' is optional (use 'all' instead) 749 if ('cooling_profiles' not in z) or \ 750 (z['cooling_profiles'] is None): 751 profiles = ["all"] 752 else: 753 profiles = z['cooling_profiles'] 754 755 # 'interfaces' is optional (no default) 756 ifaces = [] 757 if ('interfaces' in z) and \ 758 (z['interfaces'] is not None): 759 ifaces = getIfacesInZone(z['interfaces']) 760 761 fans = getFansInZone(z['zone'], profiles, fan_data) 762 events = getEventsInZone(z['zone'], group['zone_conditions'], 763 events_data) 764 765 if len(fans) == 0: 766 sys.exit("Didn't find any fans in zone " + str(zone['num'])) 767 768 if (ifaces): 769 zone['ifaces'] = ifaces 770 zone['fans'] = fans 771 zone['events'] = events 772 zones.append(zone) 773 774 zone_group['zones'] = zones 775 zone_groups.append(zone_group) 776 777 return zone_groups 778 779 780if __name__ == '__main__': 781 parser = ArgumentParser( 782 description="Phosphor fan zone definition parser") 783 784 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml', 785 default="example/zones.yaml", 786 help='fan zone definitional yaml') 787 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml', 788 default="example/fans.yaml", 789 help='fan definitional yaml') 790 parser.add_argument('-e', '--events_yaml', dest='events_yaml', 791 help='events to set speeds yaml') 792 parser.add_argument('-c', '--zone_conditions_yaml', 793 dest='zone_conditions_yaml', 794 help='conditions to determine zone yaml') 795 parser.add_argument('-o', '--output_dir', dest='output_dir', 796 default=".", 797 help='output directory') 798 args = parser.parse_args() 799 800 if not args.zone_yaml or not args.fan_yaml: 801 parser.print_usage() 802 sys.exit(1) 803 804 with open(args.zone_yaml, 'r') as zone_input: 805 zone_data = yaml.safe_load(zone_input) or {} 806 807 with open(args.fan_yaml, 'r') as fan_input: 808 fan_data = yaml.safe_load(fan_input) or {} 809 810 events_data = {} 811 if args.events_yaml: 812 with open(args.events_yaml, 'r') as events_input: 813 events_data = yaml.safe_load(events_input) or {} 814 815 zone_conditions_data = {} 816 if args.zone_conditions_yaml: 817 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input: 818 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {} 819 820 zone_config = buildZoneData(zone_data.get('zone_configuration', {}), 821 fan_data, events_data, zone_conditions_data) 822 823 manager_config = zone_data.get('manager_configuration', {}) 824 825 if manager_config.get('power_on_delay') is None: 826 manager_config['power_on_delay'] = 0 827 828 tmpls_dir = os.path.join( 829 os.path.dirname(os.path.realpath(__file__)), 830 "templates") 831 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp") 832 if sys.version_info < (3, 0): 833 lkup = TemplateLookup( 834 directories=tmpls_dir.split(), 835 disable_unicode=True) 836 else: 837 lkup = TemplateLookup( 838 directories=tmpls_dir.split()) 839 tmpl = lkup.get_template('fan_zone_defs.mako.cpp') 840 with open(output_file, 'w') as output: 841 output.write(tmpl.render(zones=zone_config, 842 mgr_data=manager_config)) 843