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