-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
fan.py
245 lines (213 loc) · 7.66 KB
/
fan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/env python
########################################################################
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform.
#
########################################################################
import os.path
try:
from sonic_platform_base.fan_base import FanBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_S6100_PSU_FAN_SPEED = 18000
MAX_S6100_FAN_SPEED = 16000
class Fan(FanBase):
"""DellEMC Platform-specific Fan class"""
HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/"
HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE
def __init__(self, fantray_index=1, psu_index=1, psu_fan=False, dependency=None):
FanBase.__init__(self)
self.is_psu_fan = psu_fan
if not self.is_psu_fan:
self.fantrayindex = fantray_index
self.dependency = dependency
self.fan_status_reg = "fan{}_alarm".format(
2 * self.fantrayindex - 1)
self.get_fan_speed_reg = "fan{}_input".format(
2 * self.fantrayindex - 1)
self.get_fan_dir_reg = "fan{}_airflow".format(
2 * self.fantrayindex - 1)
self.max_fan_speed = MAX_S6100_FAN_SPEED
else:
self.psuindex = psu_index
self.fan_presence_reg = "fan{}_fault".format(self.psuindex + 10)
self.get_fan_speed_reg = "fan{}_input".format(self.psuindex + 10)
self.get_fan_dir_reg = "fan{}_airflow".format(self.psuindex + 10)
self.max_fan_speed = MAX_S6100_PSU_FAN_SPEED
def _get_pmc_register(self, reg_name):
# On successful read, returns the value read from given
# reg_name and on failure returns 'ERR'
rv = 'ERR'
mb_reg_file = self.MAILBOX_DIR+'/'+reg_name
if (not os.path.isfile(mb_reg_file)):
return rv
try:
with open(mb_reg_file, 'r') as fd:
rv = fd.read()
except Exception as error:
rv = 'ERR'
rv = rv.rstrip('\r\n')
rv = rv.lstrip(" ")
return rv
def get_name(self):
"""
Retrieves the fan name
Returns:
string: The name of the device
"""
if not self.is_psu_fan:
return "FanTray{}-Fan1".format(self.fantrayindex)
else:
return "PSU{} Fan".format(self.psuindex)
def get_model(self):
"""
Retrieves the part number of the FAN
Returns:
string: Part number of FAN
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
string: Serial number of FAN
"""
return 'NA'
def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if fan is present, False if not
"""
if not self.is_psu_fan:
return self.dependency.get_presence()
presence = False
fan_presence = self._get_pmc_register(self.fan_presence_reg)
if (fan_presence != 'ERR'):
fan_presence = int(fan_presence, 10)
if (~fan_presence & 0b1):
presence = True
return presence
def get_status(self):
"""
Retrieves the operational status of the FAN
Returns:
bool: True if FAN is operating properly, False if not
"""
status = False
if self.is_psu_fan:
fantray_status = self._get_pmc_register(self.get_fan_speed_reg)
if (fantray_status != 'ERR'):
fantray_status = int(fantray_status, 10)
if (fantray_status > 1000):
status = True
else:
fantray_status = self._get_pmc_register(self.fan_status_reg)
if (fantray_status != 'ERR'):
fantray_status = int(fantray_status, 10)
if (~fantray_status & 0b1):
status = True
return status
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return 1
def is_replaceable(self):
"""
Indicate whether Fan is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_direction(self):
"""
Retrieves the fan airflow direction
Returns:
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
depending on fan direction
Notes:
In DellEMC platforms,
- Forward/Exhaust : Air flows from Port side to Fan side.
- Reverse/Intake : Air flows from Fan side to Port side.
"""
direction = [self.FAN_DIRECTION_INTAKE, self.FAN_DIRECTION_EXHAUST]
fan_direction = self._get_pmc_register(self.get_fan_dir_reg)
if (fan_direction != 'ERR') and self.get_presence():
fan_direction = int(fan_direction, 10)
else:
return self.FAN_DIRECTION_NOT_APPLICABLE
return direction[fan_direction]
def get_speed(self):
"""
Retrieves the speed of fan
Returns:
int: percentage of the max fan speed
"""
fan_speed = self._get_pmc_register(self.get_fan_speed_reg)
if (fan_speed != 'ERR') and self.get_presence():
speed_in_rpm = int(fan_speed, 10)
speed = (100 * speed_in_rpm)//self.max_fan_speed
else:
speed = 0
return speed
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan
Returns:
An integer, the percentage of variance from target speed which is
considered tolerable
"""
if self.get_presence():
# The tolerance value is fixed as 20% for all the DellEmc platform
tolerance = 20
else:
tolerance = 0
return tolerance
def set_speed(self, speed):
"""
Set fan speed to expected value
Args:
speed: An integer, the percentage of full fan speed to set fan to,
in the range 0 (off) to 100 (full speed)
Returns:
bool: True if set success, False if fail.
"""
# Fan speeds are controlled by Smart-fussion FPGA.
return False
def set_status_led(self, color):
"""
Set led to expected color
Args:
color: A string representing the color with which to set the
fan module status LED
Returns:
bool: True if set success, False if fail.
"""
# No LED available for FanTray and PSU Fan
# Return True to avoid thermalctld alarm.
return True
def get_status_led(self):
"""
Gets the state of the Fan status LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings.
"""
# No LED available for FanTray and PSU Fan
return None
def get_target_speed(self):
"""
Retrieves the target (expected) speed of the fan
Returns:
An integer, the percentage of full fan speed, in the range 0 (off)
to 100 (full speed)
"""
# Fan speeds are controlled by Smart-fussion FPGA.
# Return current speed to avoid false thermalctld alarm.
return self.get_speed()