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