2020-10-03 15:46:21 -05:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
try:
|
|
|
|
import os
|
|
|
|
import sys
|
2022-01-16 23:46:20 -06:00
|
|
|
import time
|
2022-09-29 17:18:43 -05:00
|
|
|
import signal
|
|
|
|
import syslog
|
2022-12-14 08:21:36 -06:00
|
|
|
import logging
|
|
|
|
import threading
|
2020-10-03 15:46:21 -05:00
|
|
|
|
|
|
|
sys.path.append(os.path.dirname(__file__))
|
|
|
|
|
2020-10-05 12:50:03 -05:00
|
|
|
from .platform_thrift_client import thrift_try
|
2020-10-03 15:46:21 -05:00
|
|
|
|
|
|
|
from sonic_platform_base.psu_base import PsuBase
|
2022-10-10 20:12:28 -05:00
|
|
|
from sonic_platform.thermal import psu_thermals_list_get
|
2022-09-29 17:18:43 -05:00
|
|
|
from platform_utils import cancel_on_sigterm
|
2022-12-14 08:21:36 -06:00
|
|
|
from sonic_platform.bfn_extensions.psu_sensors import get_psu_metrics
|
|
|
|
from sonic_platform.bfn_extensions.psu_sensors import get_metric_value
|
2020-10-03 15:46:21 -05:00
|
|
|
except ImportError as e:
|
|
|
|
raise ImportError (str(e) + "- required module not found")
|
|
|
|
|
|
|
|
class Psu(PsuBase):
|
|
|
|
"""Platform-specific PSU class"""
|
|
|
|
|
2022-12-14 08:21:36 -06:00
|
|
|
__lock = threading.Lock()
|
|
|
|
__sensors_info = None
|
|
|
|
__timestamp = 0
|
|
|
|
|
2022-09-29 17:18:43 -05:00
|
|
|
sigterm = False
|
|
|
|
sigterm_default_handler = None
|
|
|
|
cls_inited = False
|
|
|
|
|
2020-10-03 15:46:21 -05:00
|
|
|
def __init__(self, index):
|
|
|
|
PsuBase.__init__(self)
|
2021-03-12 13:50:36 -06:00
|
|
|
self.__index = index
|
2022-10-10 20:12:28 -05:00
|
|
|
self.__thermals = None
|
2022-01-16 23:46:20 -06:00
|
|
|
self.__info = None
|
|
|
|
self.__ts = 0
|
|
|
|
# STUB IMPLEMENTATION
|
|
|
|
self.color = ""
|
2021-03-12 13:50:36 -06:00
|
|
|
|
2022-09-29 17:18:43 -05:00
|
|
|
syslog.syslog(syslog.LOG_INFO, "Created PSU #{} instance".format(self.__index))
|
|
|
|
if not Psu.cls_inited:
|
|
|
|
Psu.sigterm_default_handler = signal.getsignal(signal.SIGTERM)
|
|
|
|
signal.signal(signal.SIGTERM, Psu.signal_handler)
|
|
|
|
if Psu.sigterm_default_handler:
|
|
|
|
syslog.syslog(syslog.LOG_INFO, "Default SIGTERM handler overridden!!")
|
|
|
|
Psu.cls_inited = True
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def signal_handler(cls, sig, frame):
|
|
|
|
if cls.sigterm_default_handler:
|
|
|
|
cls.sigterm_default_handler(sig, frame)
|
|
|
|
syslog.syslog(syslog.LOG_INFO, "Canceling PSU platform API calls...")
|
|
|
|
cls.sigterm = True
|
|
|
|
|
2022-12-14 08:21:36 -06:00
|
|
|
@classmethod
|
|
|
|
def __sensors_get(cls, cached=True):
|
|
|
|
cls.__lock.acquire()
|
|
|
|
if time.time() > cls.__timestamp + 15:
|
|
|
|
# Update cache once per 15 seconds
|
|
|
|
try:
|
|
|
|
cls.__sensors_info = get_psu_metrics()
|
|
|
|
cls.__timestamp = time.time()
|
|
|
|
except Exception as e:
|
|
|
|
logging.warning("Failed to update sensors cache: " + str(e))
|
|
|
|
info = cls.__sensors_info
|
|
|
|
cls.__lock.release()
|
|
|
|
return info
|
|
|
|
|
2021-03-12 13:50:36 -06:00
|
|
|
'''
|
|
|
|
Units of returned info object values:
|
|
|
|
vin - V
|
|
|
|
iout - mA
|
|
|
|
vout - V
|
|
|
|
pwr_out - mW
|
|
|
|
fspeed - RPM
|
|
|
|
'''
|
|
|
|
def __info_get(self):
|
2022-09-29 17:18:43 -05:00
|
|
|
@cancel_on_sigterm
|
2021-03-12 13:50:36 -06:00
|
|
|
def psu_info_get(client):
|
|
|
|
return client.pltfm_mgr.pltfm_mgr_pwr_supply_info_get(self.__index)
|
|
|
|
|
2022-01-16 23:46:20 -06:00
|
|
|
# Update cache once per 2 seconds
|
2022-09-29 17:18:43 -05:00
|
|
|
if self.__ts + 2 < time.time() and not Psu.sigterm:
|
2022-01-16 23:46:20 -06:00
|
|
|
self.__info = None
|
|
|
|
try:
|
|
|
|
self.__info = thrift_try(psu_info_get, attempts=1)
|
2022-09-29 17:18:43 -05:00
|
|
|
except Exception as e:
|
|
|
|
if "Canceling" in str(e):
|
|
|
|
syslog.syslog(syslog.LOG_INFO, "{}".format(e))
|
2022-01-16 23:46:20 -06:00
|
|
|
finally:
|
|
|
|
self.__ts = time.time()
|
|
|
|
return self.__info
|
|
|
|
return self.__info
|
|
|
|
|
2020-10-03 15:46:21 -05:00
|
|
|
@staticmethod
|
|
|
|
def get_num_psus():
|
|
|
|
"""
|
|
|
|
Retrieves the number of PSUs available on the device
|
|
|
|
:return: An integer, the number of PSUs available on the device
|
|
|
|
"""
|
|
|
|
return 2
|
|
|
|
|
[platform][barefoot] Add Psu.get_name (#7717)
#### Why I did it
To fix the following:
```
# psuutil status
Traceback (most recent call last):
File "/usr/local/bin/psuutil", line 8, in <module>
sys.exit(cli())
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/psuutil/main.py", line 93, in status
psu_name = psu.get_name()
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/device_base.py", line 28, in get_name
raise NotImplementedError
NotImplementedError
```
#### How I did it
Implemented get_name
Signed-off-by: Volodymyr Boyko <volodymyrx.boiko@intel.com>
2021-05-26 14:02:46 -05:00
|
|
|
def get_name(self):
|
|
|
|
return f"psu-{self.__index}"
|
|
|
|
|
2020-10-03 15:46:21 -05:00
|
|
|
def get_powergood_status(self):
|
|
|
|
"""
|
|
|
|
Retrieves the oprational status of power supply unit (PSU) defined
|
|
|
|
by 1-based self.index <self.index>
|
|
|
|
:param self.index: An integer, 1-based self.index of the PSU of which to query status
|
|
|
|
:return: Boolean, True if PSU is operating properly, False if PSU is faulty
|
|
|
|
"""
|
2021-03-12 13:50:36 -06:00
|
|
|
info = self.__info_get()
|
2022-01-16 23:46:20 -06:00
|
|
|
if info is None:
|
|
|
|
return False
|
2021-03-12 13:50:36 -06:00
|
|
|
return info.ffault == False and info.vout != 0
|
|
|
|
|
|
|
|
def get_voltage(self):
|
|
|
|
"""
|
|
|
|
Retrieves current PSU voltage output
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A float number, the output voltage in volts,
|
|
|
|
e.g. 12.1
|
|
|
|
"""
|
2022-12-14 08:21:36 -06:00
|
|
|
return get_metric_value(Psu.__sensors_get(), "PSU%d 12V Output Voltage_in1_input" % self.__index)
|
2021-03-12 13:50:36 -06:00
|
|
|
|
|
|
|
def get_current(self):
|
|
|
|
"""
|
|
|
|
Retrieves present electric current supplied by PSU
|
2020-10-05 12:50:03 -05:00
|
|
|
|
2021-03-12 13:50:36 -06:00
|
|
|
Returns:
|
|
|
|
A float number, the electric current in amperes, e.g 15.4
|
|
|
|
"""
|
2022-12-14 08:21:36 -06:00
|
|
|
return get_metric_value(Psu.__sensors_get(), "PSU%d 12V Output Current_curr2_input" % self.__index)
|
|
|
|
|
|
|
|
def get_input_voltage(self):
|
|
|
|
"""
|
|
|
|
Retrieves current PSU voltage input
|
|
|
|
Returns:
|
|
|
|
A float number, the input voltage in volts,
|
|
|
|
e.g. 220
|
|
|
|
"""
|
|
|
|
return get_metric_value(Psu.__sensors_get(), "PSU%d Input Voltage_in0_input" % self.__index)
|
|
|
|
|
|
|
|
def get_input_current(self):
|
|
|
|
"""
|
|
|
|
Retrieves the input current draw of the power supply
|
|
|
|
Returns:
|
|
|
|
A float number, the electric current in amperes, e.g 0.8
|
|
|
|
"""
|
|
|
|
return get_metric_value(Psu.__sensors_get(), "PSU%d Input Current_curr1_input" % self.__index)
|
2020-10-03 15:46:21 -05:00
|
|
|
|
2021-03-12 13:50:36 -06:00
|
|
|
def get_power(self):
|
|
|
|
"""
|
|
|
|
Retrieves current energy supplied by PSU
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A float number, the power in watts, e.g. 302.6
|
|
|
|
"""
|
2022-01-16 23:46:20 -06:00
|
|
|
info = self.__info_get()
|
|
|
|
return info.pwr_out / 1000 if info else 0
|
2020-10-03 15:46:21 -05:00
|
|
|
|
|
|
|
def get_presence(self):
|
|
|
|
"""
|
|
|
|
Retrieves the presence status of power supply unit (PSU) defined
|
|
|
|
by 1-based self.index <self.index>
|
|
|
|
:param self.index: An integer, 1-based self.index of the PSU of which to query status
|
|
|
|
:return: Boolean, True if PSU is plugged, False if not
|
|
|
|
"""
|
2022-12-07 01:38:23 -06:00
|
|
|
@cancel_on_sigterm
|
2020-10-05 12:50:03 -05:00
|
|
|
def psu_present_get(client):
|
2021-03-12 13:50:36 -06:00
|
|
|
return client.pltfm_mgr.pltfm_mgr_pwr_supply_present_get(self.__index)
|
2020-10-03 15:46:21 -05:00
|
|
|
|
2022-01-16 23:46:20 -06:00
|
|
|
status = False
|
|
|
|
try:
|
2022-12-07 01:38:23 -06:00
|
|
|
status = thrift_try(psu_present_get, attempts=1)
|
|
|
|
except Exception as e:
|
|
|
|
if "Canceling" in str(e):
|
|
|
|
syslog.syslog(syslog.LOG_INFO, "{}".format(e))
|
2022-01-16 23:46:20 -06:00
|
|
|
finally:
|
|
|
|
return status
|
|
|
|
|
|
|
|
def set_status_led(self, color):
|
|
|
|
"""
|
|
|
|
Sets the state of the PSU status LED
|
|
|
|
|
|
|
|
Args:
|
|
|
|
color: A string representing the color with which to set the
|
|
|
|
PSU status LED
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if status LED state is set successfully, False if not
|
|
|
|
"""
|
|
|
|
# STUB IMPLEMENTATION
|
|
|
|
self.color = color
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_status_led(self):
|
|
|
|
"""
|
|
|
|
Gets the state of the PSU status LED
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A string, one of the predefined STATUS_LED_COLOR_* strings above
|
|
|
|
"""
|
|
|
|
# STUB IMPLEMENTATION
|
|
|
|
return self.color
|
2021-03-12 13:50:36 -06:00
|
|
|
|
|
|
|
# DeviceBase iface:
|
|
|
|
def get_serial(self):
|
2022-01-16 23:46:20 -06:00
|
|
|
"""
|
|
|
|
Retrieves the serial number of the device
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
string: Serial number of device
|
|
|
|
"""
|
|
|
|
info = self.__info_get()
|
|
|
|
return info.serial if info else "N/A"
|
2021-03-12 13:50:36 -06:00
|
|
|
|
|
|
|
def get_model(self):
|
2022-01-16 23:46:20 -06:00
|
|
|
"""
|
|
|
|
Retrieves the model number (or part number) of the device
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
string: Model/part number of device
|
|
|
|
"""
|
|
|
|
info = self.__info_get()
|
|
|
|
return info.model if info else "N/A"
|
2021-03-12 13:50:36 -06:00
|
|
|
|
|
|
|
def is_replaceable(self):
|
2022-01-16 23:46:20 -06:00
|
|
|
"""
|
|
|
|
Indicate whether this device is replaceable.
|
|
|
|
Returns:
|
|
|
|
bool: True if it is replaceable.
|
|
|
|
"""
|
2021-03-12 13:50:36 -06:00
|
|
|
return True
|
2021-06-04 11:48:57 -05:00
|
|
|
|
2022-01-16 23:46:20 -06:00
|
|
|
def get_revision(self):
|
|
|
|
"""
|
|
|
|
Retrieves the hardware revision of the device
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
string: Revision value of device
|
|
|
|
"""
|
|
|
|
info = self.__info_get()
|
|
|
|
return info.rev if info else "N/A"
|
|
|
|
|
|
|
|
def get_status(self):
|
|
|
|
"""
|
|
|
|
Retrieves the operational status of the device
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A boolean value, True if device is operating properly, False if not
|
|
|
|
"""
|
|
|
|
return self.get_powergood_status()
|
|
|
|
|
|
|
|
def get_position_in_parent(self):
|
|
|
|
"""
|
|
|
|
Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position
|
|
|
|
for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned
|
|
|
|
Returns:
|
|
|
|
integer: The 1-based relative physical position in parent device or -1 if cannot determine the position
|
|
|
|
"""
|
|
|
|
return self.__index
|
|
|
|
|
2022-10-10 20:12:28 -05:00
|
|
|
def get_temperature(self):
|
|
|
|
"""
|
|
|
|
Retrieves current temperature reading from PSU
|
|
|
|
Returns:
|
|
|
|
A float number of current temperature in Celsius up to nearest thousandth
|
|
|
|
of one degree Celsius, e.g. 30.125
|
|
|
|
"""
|
|
|
|
return self.get_thermal(0).get_temperature()
|
|
|
|
|
|
|
|
def get_temperature_high_threshold(self):
|
|
|
|
"""
|
|
|
|
Retrieves the high threshold temperature of PSU
|
|
|
|
Returns:
|
|
|
|
A float number, the high threshold temperature of PSU in Celsius
|
|
|
|
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
|
|
|
"""
|
|
|
|
return self.get_thermal(0).get_high_threshold()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _thermal_list(self):
|
|
|
|
if self.__thermals is None:
|
|
|
|
self.__thermals = psu_thermals_list_get(self.get_name())
|
|
|
|
return self.__thermals
|
|
|
|
|
|
|
|
@_thermal_list.setter
|
|
|
|
def _thermal_list(self, value):
|
|
|
|
pass
|
|
|
|
|
2021-06-04 11:48:57 -05:00
|
|
|
def psu_list_get():
|
|
|
|
psu_list = []
|
|
|
|
for i in range(1, Psu.get_num_psus() + 1):
|
|
|
|
psu = Psu(i)
|
|
|
|
psu_list.append(psu)
|
|
|
|
return psu_list
|