Skip to content

Commit

Permalink
Merge branch 'master' into master-thermalctld-interval
Browse files Browse the repository at this point in the history
  • Loading branch information
andywongarista committed Apr 16, 2021
2 parents 7618176 + 911601d commit 91ba5fc
Show file tree
Hide file tree
Showing 29 changed files with 1,283 additions and 651 deletions.
4 changes: 2 additions & 2 deletions sonic-ledd/scripts/ledd
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import sys

from sonic_py_common import daemon_base
from sonic_py_common import multi_asic
from sonic_py_common.interface import backplane_prefix
from sonic_py_common.interface import backplane_prefix, inband_prefix
from swsscommon import swsscommon

#============================= Constants =============================
Expand Down Expand Up @@ -96,7 +96,7 @@ class DaemonLedd(daemon_base.DaemonBase):
fvp_dict = dict(fvp)

if op == "SET" and "oper_status" in fvp_dict:
if not key.startswith(backplane_prefix()):
if not key.startswith(backplane_prefix()) and not key.startswith(inband_prefix()):
self.led_control.port_link_state_change(key, fvp_dict["oper_status"])
else:
return 4
Expand Down
2 changes: 1 addition & 1 deletion sonic-psud/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[pytest]
addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml --junitxml=test-results.xml -v
addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml --junitxml=test-results.xml -vv
182 changes: 96 additions & 86 deletions sonic-psud/scripts/psud
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,14 @@
The loop interval is PSU_INFO_UPDATE_PERIOD_SECS in seconds.
"""

import os
import signal
import sys
import threading
from datetime import datetime

from sonic_platform.psu import Psu
from sonic_py_common import daemon_base, logger

# If unit testing is occurring, mock swsscommon and module_base
if os.getenv("PSUD_UNIT_TESTING") == "1":
from tests.mock_platform import MockPsu as Psu
from tests import mock_swsscommon as swsscommon
else:
from sonic_platform.psu import Psu
from swsscommon import swsscommon
from swsscommon import swsscommon


#
Expand All @@ -32,8 +25,8 @@ else:

# TODO: Once we no longer support Python 2, we can eliminate this and get the
# name using the 'name' field (e.g., `signal.SIGINT.name`) starting with Python 3.5
SIGNALS_TO_NAMES_DICT = dict((getattr(signal, n), n) \
for n in dir(signal) if n.startswith('SIG') and '_' not in n )
SIGNALS_TO_NAMES_DICT = dict((getattr(signal, n), n)
for n in dir(signal) if n.startswith('SIG') and '_' not in n)

SYSLOG_IDENTIFIER = "psud"

Expand Down Expand Up @@ -85,8 +78,11 @@ PSUUTIL_LOAD_ERROR = 1
platform_psuutil = None
platform_chassis = None

exit_code = 0

# temporary wrappers that are compliable with both new platform api and old-style plugin mode


def _wrapper_get_num_psus():
if platform_chassis is not None:
try:
Expand Down Expand Up @@ -131,7 +127,7 @@ def psu_db_update(psu_tbl, psu_num):
psu_tbl.set(get_psu_key(psu_index), fvs)


# try get information from platform API and return a default value if caught NotImplementedError
# try get information from platform API and return a default value if we catch NotImplementedError
def try_get(callback, default=None):
"""
Handy function to invoke the callback and catch NotImplementedError
Expand Down Expand Up @@ -257,11 +253,10 @@ class PsuChassisInfo(logger.Logger):
self.master_status_good = master_status_good

# Update the PSU master status LED
try:
color = Psu.STATUS_LED_COLOR_GREEN if master_status_good else Psu.STATUS_LED_COLOR_RED
Psu.set_status_master_led(color)
except NotImplementedError:
self.log_warning("set_status_master_led() not implemented")
# set_status_master_led() is a class method implemented in PsuBase
# so we do not need to catch NotImplementedError here
color = Psu.STATUS_LED_COLOR_GREEN if master_status_good else Psu.STATUS_LED_COLOR_RED
Psu.set_status_master_led(color)

log_on_status_changed(self, self.master_status_good,
'PSU supplied power warning cleared: supplied power is back to normal.',
Expand Down Expand Up @@ -310,7 +305,7 @@ class PsuStatus(object):
return True

def set_voltage(self, voltage, high_threshold, low_threshold):
if not voltage or not high_threshold or not low_threshold:
if voltage == NOT_AVAILABLE or high_threshold == NOT_AVAILABLE or low_threshold == NOT_AVAILABLE:
if self.voltage_good is not True:
self.logger.log_warning('PSU voltage or high_threshold or low_threshold become unavailable, '
'voltage={}, high_threshold={}, low_threshold={}'.format(voltage, high_threshold, low_threshold))
Expand All @@ -325,7 +320,7 @@ class PsuStatus(object):
return True

def set_temperature(self, temperature, high_threshold):
if not temperature or not high_threshold:
if temperature == NOT_AVAILABLE or high_threshold == NOT_AVAILABLE:
if self.temperature_good is not True:
self.logger.log_warning('PSU temperature or high_threshold become unavailable, '
'temperature={}, high_threshold={}'.format(temperature, high_threshold))
Expand All @@ -351,32 +346,21 @@ class DaemonPsud(daemon_base.DaemonBase):
def __init__(self, log_identifier):
super(DaemonPsud, self).__init__(log_identifier)

self.stop = threading.Event()
# Set minimum logging level to INFO
self.set_min_log_priority_info()

self.stop_event = threading.Event()
self.num_psus = 0
self.psu_status_dict = {}
self.chassis_tbl = None
self.fan_tbl = None
self.psu_tbl = None
self.psu_chassis_info = None
self.first_run = True

# Signal handler
def signal_handler(self, sig, frame):
if sig == signal.SIGHUP:
self.log_info("Caught SIGHUP - ignoring...")
elif sig == signal.SIGINT:
self.log_info("Caught SIGINT - exiting...")
self.stop.set()
elif sig == signal.SIGTERM:
self.log_info("Caught SIGTERM - exiting...")
self.stop.set()
else:
self.log_warning("Caught unhandled signal '{}'".format(SIGNALS_TO_NAMES_DICT[sig]))

# Run daemon
def run(self):
global platform_psuutil
global platform_chassis

self.log_info("Starting up...")

# Load new platform api class
try:
import sonic_platform.platform
Expand All @@ -394,72 +378,90 @@ class DaemonPsud(daemon_base.DaemonBase):

# Connect to STATE_DB and create psu/chassis info tables
state_db = daemon_base.db_connect("STATE_DB")
chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE)
psu_tbl = swsscommon.Table(state_db, PSU_INFO_TABLE)
self.chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE)
self.psu_tbl = swsscommon.Table(state_db, PSU_INFO_TABLE)
self.fan_tbl = swsscommon.Table(state_db, FAN_INFO_TABLE)
self.phy_entity_tbl = swsscommon.Table(state_db, PHYSICAL_ENTITY_INFO_TABLE)

# Post psu number info to STATE_DB
psu_num = _wrapper_get_num_psus()
fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_PSU_NUM_FIELD, str(psu_num))])
chassis_tbl.set(CHASSIS_INFO_KEY, fvs)
self.num_psus = _wrapper_get_num_psus()
fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_PSU_NUM_FIELD, str(self.num_psus))])
self.chassis_tbl.set(CHASSIS_INFO_KEY, fvs)

# Start main loop
self.log_info("Start daemon main loop")
def __del__(self):
# Delete all the information from DB and then exit
for psu_index in range(1, self.num_psus + 1):
self.psu_tbl._del(get_psu_key(psu_index))

while not self.stop.wait(PSU_INFO_UPDATE_PERIOD_SECS):
self._update_psu_entity_info()
psu_db_update(psu_tbl, psu_num)
self.update_psu_data(psu_tbl)
self._update_led_color(psu_tbl)
self.chassis_tbl._del(CHASSIS_INFO_KEY)
self.chassis_tbl._del(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1))

if platform_chassis and platform_chassis.is_modular_chassis():
self.update_psu_chassis_info(chassis_tbl)
# Override signal handler from DaemonBase
def signal_handler(self, sig, frame):
FATAL_SIGNALS = [signal.SIGINT, signal.SIGTERM]
NONFATAL_SIGNALS = [signal.SIGHUP]

self.first_run = False
global exit_code

self.log_info("Stop daemon main loop")
if sig in FATAL_SIGNALS:
self.log_info("Caught signal '{}' - exiting...".format(SIGNALS_TO_NAMES_DICT[sig]))
exit_code = 128 + sig # Make sure we exit with a non-zero code so that supervisor will try to restart us
self.stop_event.set()
elif sig in NONFATAL_SIGNALS:
self.log_info("Caught signal '{}' - ignoring...".format(SIGNALS_TO_NAMES_DICT[sig]))
else:
self.log_warning("Caught unhandled signal '{}' - ignoring...".format(SIGNALS_TO_NAMES_DICT[sig]))

# Delete all the information from DB and then exit
for psu_index in range(1, psu_num + 1):
psu_tbl._del(get_psu_key(psu_index))
# Main daemon logic
def run(self):
if self.stop_event.wait(PSU_INFO_UPDATE_PERIOD_SECS):
# We received a fatal signal
return False

chassis_tbl._del(CHASSIS_INFO_KEY)
chassis_tbl._del(CHASSIS_INFO_POWER_KEY_TEMPLATE.format(1))
self._update_psu_entity_info()
psu_db_update(self.psu_tbl, self.num_psus)
self.update_psu_data()
self._update_led_color()

self.log_info("Shutting down...")
if platform_chassis and platform_chassis.is_modular_chassis():
self.update_psu_chassis_info()

def update_psu_data(self, psu_tbl):
if self.first_run:
self.first_run = False

return True

def update_psu_data(self):
if not platform_chassis:
return

for index, psu in enumerate(platform_chassis.get_all_psus()):
try:
self._update_single_psu_data(index + 1, psu, psu_tbl)
self._update_single_psu_data(index + 1, psu)
except Exception as e:
self.log_warning("Failed to update PSU data - {}".format(e))

def _update_single_psu_data(self, index, psu, psu_tbl):
def _update_single_psu_data(self, index, psu):
name = get_psu_key(index)
presence = _wrapper_get_psu_presence(index)
power_good = False
voltage = None
voltage_high_threshold = None
voltage_low_threshold = None
temperature = None
temperature_threshold = None
current = None
power = None
voltage = NOT_AVAILABLE
voltage_high_threshold = NOT_AVAILABLE
voltage_low_threshold = NOT_AVAILABLE
temperature = NOT_AVAILABLE
temperature_threshold = NOT_AVAILABLE
current = NOT_AVAILABLE
power = NOT_AVAILABLE
is_replaceable = try_get(psu.is_replaceable, False)
if presence:
power_good = _wrapper_get_psu_status(index)
voltage = try_get(psu.get_voltage)
voltage_high_threshold = try_get(psu.get_voltage_high_threshold)
voltage_low_threshold = try_get(psu.get_voltage_low_threshold)
temperature = try_get(psu.get_temperature)
temperature_threshold = try_get(psu.get_temperature_high_threshold)
current = try_get(psu.get_current)
power = try_get(psu.get_power)
voltage = try_get(psu.get_voltage, NOT_AVAILABLE)
voltage_high_threshold = try_get(psu.get_voltage_high_threshold, NOT_AVAILABLE)
voltage_low_threshold = try_get(psu.get_voltage_low_threshold, NOT_AVAILABLE)
temperature = try_get(psu.get_temperature, NOT_AVAILABLE)
temperature_threshold = try_get(psu.get_temperature_high_threshold, NOT_AVAILABLE)
current = try_get(psu.get_current, NOT_AVAILABLE)
power = try_get(psu.get_power, NOT_AVAILABLE)

if index not in self.psu_status_dict:
self.psu_status_dict[index] = PsuStatus(self, psu)
Expand Down Expand Up @@ -506,8 +508,8 @@ class DaemonPsud(daemon_base.DaemonBase):
self._set_psu_led(psu, psu_status)

fvs = swsscommon.FieldValuePairs(
[(PSU_INFO_MODEL_FIELD, str(try_get(psu.get_model))),
(PSU_INFO_SERIAL_FIELD, str(try_get(psu.get_serial))),
[(PSU_INFO_MODEL_FIELD, str(try_get(psu.get_model, NOT_AVAILABLE))),
(PSU_INFO_SERIAL_FIELD, str(try_get(psu.get_serial, NOT_AVAILABLE))),
(PSU_INFO_TEMP_FIELD, str(temperature)),
(PSU_INFO_TEMP_TH_FIELD, str(temperature_threshold)),
(PSU_INFO_VOLTAGE_FIELD, str(voltage)),
Expand All @@ -517,7 +519,7 @@ class DaemonPsud(daemon_base.DaemonBase):
(PSU_INFO_POWER_FIELD, str(power)),
(PSU_INFO_FRU_FIELD, str(is_replaceable)),
])
psu_tbl.set(name, fvs)
self.psu_tbl.set(name, fvs)

def _update_psu_entity_info(self):
if not platform_chassis:
Expand Down Expand Up @@ -567,15 +569,15 @@ class DaemonPsud(daemon_base.DaemonBase):
except NotImplementedError:
self.log_warning("set_status_led() not implemented")

def _update_led_color(self, psu_tbl):
def _update_led_color(self):
if not platform_chassis:
return

for index, psu_status in self.psu_status_dict.items():
fvs = swsscommon.FieldValuePairs([
('led_status', str(try_get(psu_status.psu.get_status_led, NOT_AVAILABLE)))
])
psu_tbl.set(get_psu_key(index), fvs)
])
self.psu_tbl.set(get_psu_key(index), fvs)
self._update_psu_fan_led_status(psu_status.psu, index)

def _update_psu_fan_led_status(self, psu, psu_index):
Expand All @@ -588,14 +590,14 @@ class DaemonPsud(daemon_base.DaemonBase):
])
self.fan_tbl.set(fan_name, fvs)

def update_psu_chassis_info(self, chassis_tbl):
def update_psu_chassis_info(self):
if not platform_chassis:
return

if not self.psu_chassis_info:
self.psu_chassis_info = PsuChassisInfo(SYSLOG_IDENTIFIER, platform_chassis)

self.psu_chassis_info.run_power_budget(chassis_tbl)
self.psu_chassis_info.run_power_budget(self.chassis_tbl)
self.psu_chassis_info.update_master_status()


Expand All @@ -606,8 +608,16 @@ class DaemonPsud(daemon_base.DaemonBase):

def main():
psud = DaemonPsud(SYSLOG_IDENTIFIER)
psud.run()

psud.log_info("Starting up...")

while psud.run():
pass

psud.log_info("Shutting down...")

return exit_code


if __name__ == '__main__':
main()
sys.exit(main())
3 changes: 2 additions & 1 deletion sonic-psud/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
tests_require=[
'mock>=2.0.0; python_version < "3.3"',
'pytest',
'pytest-cov'
'pytest-cov',
'sonic_platform_common'
],
classifiers=[
'Development Status :: 4 - Beta',
Expand Down
11 changes: 0 additions & 11 deletions sonic-psud/tests/mock_device_base.py

This file was deleted.

Loading

0 comments on commit 91ba5fc

Please sign in to comment.