|
| 1 | +import json |
| 2 | +from .thermal_policy import ThermalPolicy |
| 3 | +from .thermal_json_object import ThermalJsonObject |
| 4 | + |
| 5 | + |
| 6 | +class ThermalManagerBase(object): |
| 7 | + """ |
| 8 | + Base class of ThermalManager representing a manager to control all thermal policies. |
| 9 | + """ |
| 10 | + # JSON field definition. |
| 11 | + JSON_FIELD_POLICIES = 'policies' |
| 12 | + JSON_FIELD_INFO_TYPES = 'info_types' |
| 13 | + JSON_FIELD_POLICY_NAME = 'name' |
| 14 | + JSON_FIELD_THERMAL_ALGORITHM = "thermal_control_algorithm" |
| 15 | + JSON_FIELD_FAN_SPEED_WHEN_SUSPEND = "fan_speed_when_suspend" |
| 16 | + JSON_FIELD_RUN_AT_BOOT_UP = "run_at_boot_up" |
| 17 | + |
| 18 | + # Dictionary of ThermalPolicy objects. |
| 19 | + _policy_dict = {} |
| 20 | + |
| 21 | + # Dictionary of thermal information objects. A thermal information object is used by Thermal Policy |
| 22 | + _thermal_info_dict = {} |
| 23 | + |
| 24 | + _fan_speed_when_suspend = None |
| 25 | + |
| 26 | + _run_thermal_algorithm_at_boot_up = None |
| 27 | + |
| 28 | + @classmethod |
| 29 | + def initialize(cls): |
| 30 | + """ |
| 31 | + Initialize thermal manager, including register thermal condition types and thermal action types |
| 32 | + and any other vendor specific initialization. |
| 33 | + :return: |
| 34 | + """ |
| 35 | + pass |
| 36 | + |
| 37 | + @classmethod |
| 38 | + def deinitialize(cls): |
| 39 | + """ |
| 40 | + Destroy thermal manager, including any vendor specific cleanup. The default behavior of this function |
| 41 | + is a no-op. |
| 42 | + :return: |
| 43 | + """ |
| 44 | + pass |
| 45 | + |
| 46 | + @classmethod |
| 47 | + def start_thermal_control_algorithm(cls): |
| 48 | + """ |
| 49 | + Start vendor specific thermal control algorithm. The default behavior of this function is a no-op. |
| 50 | + :return: |
| 51 | + """ |
| 52 | + pass |
| 53 | + |
| 54 | + @classmethod |
| 55 | + def stop_thermal_control_algorithm(cls): |
| 56 | + """ |
| 57 | + Stop vendor specific thermal control algorithm. The default behavior of this function is a no-op. |
| 58 | + :return: |
| 59 | + """ |
| 60 | + pass |
| 61 | + |
| 62 | + @classmethod |
| 63 | + def load(cls, policy_file_name): |
| 64 | + """ |
| 65 | + Load all thermal policies from JSON policy file. An example looks like: |
| 66 | + { |
| 67 | + "thermal_control_algorithm": { |
| 68 | + "run_at_boot_up": "false", |
| 69 | + "fan_speed_when_suspend": "60" |
| 70 | + }, |
| 71 | + "info_types": [ |
| 72 | + { |
| 73 | + "type": "fan_info" # collect fan information for each iteration |
| 74 | + }, |
| 75 | + { |
| 76 | + "type": "psu_info" # collect psu information for each iteration |
| 77 | + } |
| 78 | + ], |
| 79 | + "policies": [ |
| 80 | + { |
| 81 | + "name": "any fan absence", # if any fan absence, set all fan speed to 100% and disable thermal control algorithm |
| 82 | + "conditions": [ |
| 83 | + { |
| 84 | + "type": "fan.any.absence" # see sonic-platform-daemons.sonic-thermalctld.thermal_policy.thermal_conditions |
| 85 | + } |
| 86 | + ], |
| 87 | + "actions": [ |
| 88 | + { |
| 89 | + "type": "fan.all.set_speed", # see sonic-platform-daemons.sonic-thermalctld.thermal_policy.thermal_actions |
| 90 | + "speed": "100" |
| 91 | + }, |
| 92 | + { |
| 93 | + "type": "thermal_control.control", |
| 94 | + "status": "false" |
| 95 | + } |
| 96 | + ] |
| 97 | + }, |
| 98 | + { |
| 99 | + "name": "all fan absence", # if all fan absence, shutdown the switch |
| 100 | + "conditions": [ |
| 101 | + { |
| 102 | + "type": "fan.all.absence" |
| 103 | + } |
| 104 | + ], |
| 105 | + "actions": [ |
| 106 | + { |
| 107 | + "type": "switch.shutdown" |
| 108 | + } |
| 109 | + ] |
| 110 | + } |
| 111 | + ] |
| 112 | + } |
| 113 | + :param policy_file_name: Path of JSON policy file. |
| 114 | + :return: |
| 115 | + """ |
| 116 | + with open(policy_file_name, 'r') as policy_file: |
| 117 | + json_obj = json.load(policy_file) |
| 118 | + if cls.JSON_FIELD_POLICIES in json_obj: |
| 119 | + json_policies = json_obj[cls.JSON_FIELD_POLICIES] |
| 120 | + for json_policy in json_policies: |
| 121 | + cls._load_policy(json_policy) |
| 122 | + |
| 123 | + if cls.JSON_FIELD_INFO_TYPES in json_obj: |
| 124 | + for json_info in json_obj[cls.JSON_FIELD_INFO_TYPES]: |
| 125 | + info_type = ThermalJsonObject.get_type(json_info) |
| 126 | + info_obj = info_type() |
| 127 | + cls._thermal_info_dict[json_info[ThermalJsonObject.JSON_FIELD_TYPE]] = info_obj |
| 128 | + |
| 129 | + if cls.JSON_FIELD_THERMAL_ALGORITHM in json_obj: |
| 130 | + json_thermal_algorithm_config = json_obj[cls.JSON_FIELD_THERMAL_ALGORITHM] |
| 131 | + if cls.JSON_FIELD_RUN_AT_BOOT_UP in json_thermal_algorithm_config: |
| 132 | + cls._run_thermal_algorithm_at_boot_up = \ |
| 133 | + True if json_thermal_algorithm_config[cls.JSON_FIELD_RUN_AT_BOOT_UP].lower() == 'true' else False |
| 134 | + |
| 135 | + if cls.JSON_FIELD_FAN_SPEED_WHEN_SUSPEND in json_thermal_algorithm_config: |
| 136 | + # if the string is not a valid int, let it raise |
| 137 | + cls._fan_speed_when_suspend = \ |
| 138 | + int(json_thermal_algorithm_config[cls.JSON_FIELD_FAN_SPEED_WHEN_SUSPEND]) |
| 139 | + |
| 140 | + @classmethod |
| 141 | + def _load_policy(cls, json_policy): |
| 142 | + """ |
| 143 | + Load a policy object from a JSON object. |
| 144 | + :param json_policy: A JSON object representing a thermal policy. |
| 145 | + :return: |
| 146 | + """ |
| 147 | + if cls.JSON_FIELD_POLICY_NAME in json_policy: |
| 148 | + name = json_policy[cls.JSON_FIELD_POLICY_NAME] |
| 149 | + if name in cls._policy_dict: |
| 150 | + raise Exception('Policy {} already exists'.format(name)) |
| 151 | + |
| 152 | + policy = ThermalPolicy() |
| 153 | + policy.load_from_json(json_policy) |
| 154 | + policy.validate_duplicate_policy(cls._policy_dict.values()) |
| 155 | + cls._policy_dict[name] = policy |
| 156 | + else: |
| 157 | + raise Exception('{} not found in policy'.format(cls.JSON_FIELD_POLICY_NAME)) |
| 158 | + |
| 159 | + @classmethod |
| 160 | + def run_policy(cls, chassis): |
| 161 | + """ |
| 162 | + Collect thermal information, run each policy, if one policy matches, execute the policy's action. |
| 163 | + :param chassis: The chassis object. |
| 164 | + :return: |
| 165 | + """ |
| 166 | + if not cls._policy_dict: |
| 167 | + return |
| 168 | + |
| 169 | + cls._collect_thermal_information(chassis) |
| 170 | + |
| 171 | + for policy in cls._policy_dict.values(): |
| 172 | + if policy.is_match(cls._thermal_info_dict): |
| 173 | + policy.do_action(cls._thermal_info_dict) |
| 174 | + |
| 175 | + @classmethod |
| 176 | + def _collect_thermal_information(cls, chassis): |
| 177 | + """ |
| 178 | + Collect thermal information. This function will be called before run_policy. |
| 179 | + :param chassis: The chassis object. |
| 180 | + :return: |
| 181 | + """ |
| 182 | + for thermal_info in cls._thermal_info_dict.values(): |
| 183 | + thermal_info.collect(chassis) |
| 184 | + |
| 185 | + @classmethod |
| 186 | + def init_thermal_algorithm(cls, chassis): |
| 187 | + """ |
| 188 | + Initialize thermal algorithm according to policy file. |
| 189 | + :param chassis: The chassis object. |
| 190 | + :return: |
| 191 | + """ |
| 192 | + if cls._run_thermal_algorithm_at_boot_up is not None: |
| 193 | + if cls._run_thermal_algorithm_at_boot_up: |
| 194 | + cls.start_thermal_control_algorithm() |
| 195 | + else: |
| 196 | + cls.stop_thermal_control_algorithm() |
| 197 | + if cls._fan_speed_when_suspend is not None: |
| 198 | + for fan in chassis.get_all_fans(): |
| 199 | + fan.set_speed(cls._fan_speed_when_suspend) |
| 200 | + |
| 201 | + for psu in chassis.get_all_psus(): |
| 202 | + for fan in psu.get_all_fans(): |
| 203 | + fan.set_speed(cls._fan_speed_when_suspend) |
0 commit comments