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