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