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