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