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