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