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 e += "\"" + event['name'] + "\",\n" 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 name 498 event['name'] = e['name'] 499 500 # Add set speed event groups 501 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data) 502 503 # Add optional set speed actions and function parameters 504 event['action'] = [] 505 if ('actions' in e) and \ 506 (e['actions'] is not None): 507 event['action'] = getActions(zone_num, 508 zone_conditions, 509 e, 510 e, 511 events_data) 512 513 # Add event triggers 514 event['triggers'] = {} 515 for trig in e['triggers']: 516 triggers = [] 517 if (trig['name'] == "timer"): 518 event['triggers']['timer'] = getTimer(trig) 519 elif (trig['name'] == "signal"): 520 if ('signals' not in event['triggers']): 521 event['triggers']['signals'] = [] 522 triggers = getSignal(event['groups'], trig, events_data) 523 event['triggers']['signals'].extend(triggers) 524 elif (trig['name'] == "init"): 525 triggers = getInit(event['groups'], trig, events_data) 526 event['triggers']['init'] = triggers 527 528 return event 529 530 531def addPrecondition(zNum, zCond, event, events_data): 532 """ 533 Parses the precondition section of an event and populates the necessary 534 structures to generate a precondition for a set speed event. 535 """ 536 precond = {} 537 538 # Add set speed event precondition name 539 precond['pcname'] = event['name'] 540 541 # Add set speed event precondition group 542 precond['pcgrps'] = getGroups(zNum, 543 zCond, 544 event['precondition'], 545 events_data) 546 547 # Add set speed event precondition actions 548 pc = [] 549 pcs = {} 550 pcs['name'] = event['precondition']['name'] 551 epc = next(p for p in events_data['preconditions'] 552 if p['name'] == event['precondition']['name']) 553 params = [] 554 for p in epc['parameters']: 555 param = {} 556 if p == 'groups': 557 param['type'] = "std::vector<PrecondGroup>" 558 param['open'] = "{" 559 param['close'] = "}" 560 values = [] 561 for group in precond['pcgrps']: 562 for pcgrp in group['members']: 563 value = {} 564 value['value'] = ( 565 "PrecondGroup{\"" + 566 str(pcgrp['object']) + "\",\"" + 567 str(pcgrp['interface']) + "\",\"" + 568 str(pcgrp['property']) + "\"," + 569 "static_cast<" + 570 str(pcgrp['type']).lower() + ">") 571 if isinstance(pcgrp['value'], str) or \ 572 "string" in str(pcgrp['type']).lower(): 573 value['value'] += ("(" + str(pcgrp['value']) + ")}") 574 else: 575 value['value'] += \ 576 ("(" + str(pcgrp['value']).lower() + ")}") 577 values.append(value) 578 param['values'] = values 579 params.append(param) 580 pcs['params'] = params 581 pc.append(pcs) 582 precond['pcact'] = pc 583 584 pcevents = [] 585 for pce in event['precondition']['events']: 586 pcevent = getEvent(zNum, zCond, pce, events_data) 587 if not pcevent: 588 continue 589 pcevents.append(pcevent) 590 precond['pcevts'] = pcevents 591 592 # Add precondition event triggers 593 precond['triggers'] = {} 594 for trig in event['precondition']['triggers']: 595 triggers = [] 596 if (trig['name'] == "timer"): 597 precond['triggers']['pctime'] = getTimer(trig) 598 elif (trig['name'] == "signal"): 599 if ('pcsigs' not in precond['triggers']): 600 precond['triggers']['pcsigs'] = [] 601 triggers = getSignal(precond['pcgrps'], trig, events_data) 602 precond['triggers']['pcsigs'].extend(triggers) 603 elif (trig['name'] == "init"): 604 triggers = getInit(precond['pcgrps'], trig, events_data) 605 precond['triggers']['init'] = triggers 606 607 return precond 608 609 610def getEventsInZone(zone_num, zone_conditions, events_data): 611 """ 612 Constructs the event entries defined for each zone using the events yaml 613 provided. 614 """ 615 events = [] 616 617 if 'events' in events_data: 618 for e in events_data['events']: 619 event = {} 620 621 # Add precondition if given 622 if ('precondition' in e) and \ 623 (e['precondition'] is not None): 624 event['pc'] = addPrecondition(zone_num, 625 zone_conditions, 626 e, 627 events_data) 628 else: 629 event = getEvent(zone_num, zone_conditions, e, events_data) 630 if not event: 631 continue 632 events.append(event) 633 634 return events 635 636 637def getFansInZone(zone_num, profiles, fan_data): 638 """ 639 Parses the fan definition YAML files to find the fans 640 that match both the zone passed in and one of the 641 cooling profiles. 642 """ 643 644 fans = [] 645 646 for f in fan_data['fans']: 647 648 if zone_num != f['cooling_zone']: 649 continue 650 651 # 'cooling_profile' is optional (use 'all' instead) 652 if f.get('cooling_profile') is None: 653 profile = "all" 654 else: 655 profile = f['cooling_profile'] 656 657 if profile not in profiles: 658 continue 659 660 fan = {} 661 fan['name'] = f['inventory'] 662 fan['sensors'] = f['sensors'] 663 fan['target_interface'] = f.get( 664 'target_interface', 665 'xyz.openbmc_project.Control.FanSpeed') 666 fans.append(fan) 667 668 return fans 669 670 671def getIfacesInZone(zone_ifaces): 672 """ 673 Parse given interfaces for a zone for associating a zone with an interface 674 and set any properties listed to defined values upon fan control starting 675 on the zone. 676 """ 677 678 ifaces = [] 679 for i in zone_ifaces: 680 iface = {} 681 # Interface name not needed yet for fan zones but 682 # may be necessary as more interfaces are extended by the zones 683 iface['name'] = i['name'] 684 685 if ('properties' in i) and \ 686 (i['properties'] is not None): 687 props = [] 688 for p in i['properties']: 689 prop = {} 690 prop['name'] = p['name'] 691 prop['func'] = str(p['name']).lower() 692 prop['type'] = parse_cpp_type(p['type']) 693 if ('persist' in p): 694 persist = p['persist'] 695 if (persist is not None): 696 if (isinstance(persist, bool)): 697 prop['persist'] = 'true' if persist else 'false' 698 else: 699 prop['persist'] = 'false' 700 vals = [] 701 for v in p['values']: 702 val = v['value'] 703 if (val is not None): 704 if (isinstance(val, bool)): 705 # Convert True/False to 'true'/'false' 706 val = 'true' if val else 'false' 707 elif (isinstance(val, str)): 708 # Wrap strings with double-quotes 709 val = "\"" + val + "\"" 710 vals.append(val) 711 prop['values'] = vals 712 props.append(prop) 713 iface['props'] = props 714 ifaces.append(iface) 715 716 return ifaces 717 718 719def getConditionInZoneConditions(zone_condition, zone_conditions_data): 720 """ 721 Parses the zone conditions definition YAML files to find the condition 722 that match both the zone condition passed in. 723 """ 724 725 condition = {} 726 727 for c in zone_conditions_data['conditions']: 728 729 if zone_condition != c['name']: 730 continue 731 condition['type'] = c['type'] 732 properties = [] 733 for p in c['properties']: 734 property = {} 735 property['property'] = p['property'] 736 property['interface'] = p['interface'] 737 property['path'] = p['path'] 738 property['type'] = p['type'].lower() 739 property['value'] = str(p['value']).lower() 740 properties.append(property) 741 condition['properties'] = properties 742 743 return condition 744 745 746def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data): 747 """ 748 Combines the zone definition YAML and fan 749 definition YAML to create a data structure defining 750 the fan cooling zones. 751 """ 752 753 zone_groups = [] 754 755 for group in zone_data: 756 conditions = [] 757 # zone conditions are optional 758 if 'zone_conditions' in group and group['zone_conditions'] is not None: 759 for c in group['zone_conditions']: 760 761 if not zone_conditions_data: 762 sys.exit("No zone_conditions YAML file but " + 763 "zone_conditions used in zone YAML") 764 765 condition = getConditionInZoneConditions(c['name'], 766 zone_conditions_data) 767 768 if not condition: 769 sys.exit("Missing zone condition " + c['name']) 770 771 conditions.append(condition) 772 773 zone_group = {} 774 zone_group['conditions'] = conditions 775 776 zones = [] 777 for z in group['zones']: 778 zone = {} 779 780 # 'zone' is required 781 if ('zone' not in z) or (z['zone'] is None): 782 sys.exit("Missing fan zone number in " + zone_yaml) 783 784 zone['num'] = z['zone'] 785 786 zone['full_speed'] = z['full_speed'] 787 788 zone['default_floor'] = z['default_floor'] 789 790 # 'increase_delay' is optional (use 0 by default) 791 key = 'increase_delay' 792 zone[key] = z.setdefault(key, 0) 793 794 # 'decrease_interval' is optional (use 0 by default) 795 key = 'decrease_interval' 796 zone[key] = z.setdefault(key, 0) 797 798 # 'cooling_profiles' is optional (use 'all' instead) 799 if ('cooling_profiles' not in z) or \ 800 (z['cooling_profiles'] is None): 801 profiles = ["all"] 802 else: 803 profiles = z['cooling_profiles'] 804 805 # 'interfaces' is optional (no default) 806 ifaces = [] 807 if ('interfaces' in z) and \ 808 (z['interfaces'] is not None): 809 ifaces = getIfacesInZone(z['interfaces']) 810 811 fans = getFansInZone(z['zone'], profiles, fan_data) 812 events = getEventsInZone(z['zone'], group['zone_conditions'], 813 events_data) 814 815 if len(fans) == 0: 816 sys.exit("Didn't find any fans in zone " + str(zone['num'])) 817 818 if (ifaces): 819 zone['ifaces'] = ifaces 820 zone['fans'] = fans 821 zone['events'] = events 822 zones.append(zone) 823 824 zone_group['zones'] = zones 825 zone_groups.append(zone_group) 826 827 return zone_groups 828 829 830if __name__ == '__main__': 831 parser = ArgumentParser( 832 description="Phosphor fan zone definition parser") 833 834 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml', 835 default="example/zones.yaml", 836 help='fan zone definitional yaml') 837 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml', 838 default="example/fans.yaml", 839 help='fan definitional yaml') 840 parser.add_argument('-e', '--events_yaml', dest='events_yaml', 841 help='events to set speeds yaml') 842 parser.add_argument('-c', '--zone_conditions_yaml', 843 dest='zone_conditions_yaml', 844 help='conditions to determine zone yaml') 845 parser.add_argument('-o', '--output_dir', dest='output_dir', 846 default=".", 847 help='output directory') 848 args = parser.parse_args() 849 850 if not args.zone_yaml or not args.fan_yaml: 851 parser.print_usage() 852 sys.exit(1) 853 854 with open(args.zone_yaml, 'r') as zone_input: 855 zone_data = yaml.safe_load(zone_input) or {} 856 857 with open(args.fan_yaml, 'r') as fan_input: 858 fan_data = yaml.safe_load(fan_input) or {} 859 860 events_data = {} 861 if args.events_yaml: 862 with open(args.events_yaml, 'r') as events_input: 863 events_data = yaml.safe_load(events_input) or {} 864 865 zone_conditions_data = {} 866 if args.zone_conditions_yaml: 867 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input: 868 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {} 869 870 zone_config = buildZoneData(zone_data.get('zone_configuration', {}), 871 fan_data, events_data, zone_conditions_data) 872 873 manager_config = zone_data.get('manager_configuration', {}) 874 875 if manager_config.get('power_on_delay') is None: 876 manager_config['power_on_delay'] = 0 877 878 tmpls_dir = os.path.join( 879 os.path.dirname(os.path.realpath(__file__)), 880 "templates") 881 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp") 882 if sys.version_info < (3, 0): 883 lkup = TemplateLookup( 884 directories=tmpls_dir.split(), 885 disable_unicode=True) 886 else: 887 lkup = TemplateLookup( 888 directories=tmpls_dir.split()) 889 tmpl = lkup.get_template('fan_zone_defs.mako.cpp') 890 with open(output_file, 'w') as output: 891 output.write(tmpl.render(zones=zone_config, 892 mgr_data=manager_config)) 893