Skip to content

Commit aac555e

Browse files
committed
Refactor ledd daemon and fix high CPU usage due to unexpected socket close
1 parent 604e454 commit aac555e

File tree

1 file changed

+54
-35
lines changed
  • sonic-ledd/scripts

1 file changed

+54
-35
lines changed

sonic-ledd/scripts/ledd

+54-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python
1+
#!/usr/bin/env python3
22

33
"""
44
ledd
@@ -15,7 +15,7 @@ from swsscommon import swsscommon
1515

1616
#============================= Constants =============================
1717

18-
VERSION = '1.0'
18+
VERSION = '2.0'
1919

2020
SYSLOG_IDENTIFIER = "ledd"
2121

@@ -33,6 +33,7 @@ LED_CLASS_NAME = "LedControl"
3333
SELECT_TIMEOUT = 1000
3434

3535
LEDUTIL_LOAD_ERROR = 1
36+
LEDUTIL_RUNTIME_ERROR = 2
3637

3738

3839
class DaemonLedd(daemon_base.DaemonBase):
@@ -56,50 +57,67 @@ class DaemonLedd(daemon_base.DaemonBase):
5657
namespaces = multi_asic.get_front_end_namespaces()
5758

5859
# Subscribe to PORT table notifications in the Application DB
59-
appl_db = {}
60-
self.sst = {}
60+
self.tables = {}
6161
self.sel = swsscommon.Select()
6262

6363
for namespace in namespaces:
64-
# Open a handle to the Application database, in all namespaces
65-
appl_db[namespace] = daemon_base.db_connect("APPL_DB", namespace=namespace)
66-
self.sst[namespace] = swsscommon.SubscriberStateTable(appl_db[namespace], swsscommon.APP_PORT_TABLE_NAME)
67-
self.sel.addSelectable(self.sst[namespace])
68-
69-
# Run daemon
70-
def run(self):
71-
# Use timeout to prevent ignoring the signals we want to handle
72-
# in signal_handler() (e.g. SIGTERM for graceful shutdown)
73-
(state, selectableObj) = self.sel.select(SELECT_TIMEOUT)
74-
75-
if state == swsscommon.Select.TIMEOUT:
76-
# Do not flood log when select times out
77-
return 1
78-
79-
if state != swsscommon.Select.OBJECT:
80-
self.log_warning("sel.select() did not return swsscommon.Select.OBJECT")
81-
return 2
64+
self.subscribeDbTable("APPL_DB", swsscommon.APP_PORT_TABLE_NAME, namespace)
65+
66+
def connectDB(self, dbname, namespace):
67+
db = daemon_base.db_connect(dbname, namespace=namespace)
68+
return db
69+
70+
def subscribeDbTable(self, dbname, tblname, namespace):
71+
db = self.connectDB(dbname, namespace)
72+
self.tables[namespace] = swsscommon.SubscriberStateTable(db, tblname)
73+
self.sel.addSelectable(self.tables[namespace])
74+
75+
def isFrontPanelPort(self, port_name):
76+
return not port_name.startswith((backplane_prefix(), inband_prefix(), recirc_prefix()))
77+
78+
def updatePortLedColor(self, port_name, port_status):
79+
self.led_control.port_link_state_change(port_name, port_status)
80+
81+
def getEventNamespace(self, selectObj):
82+
# Get the corresponding namespace from redisselect db connector object
83+
return selectObj.getDbConnector().getNamespace()
84+
85+
def processPortTableEvent(self, selectableObj):
86+
''' Process (if any) event from the PORT table in the Application DB '''
8287

8388
# Get the redisselect object from selectable object
8489
redisSelectObj = swsscommon.CastSelectableToRedisSelectObj(selectableObj)
90+
namespace = self.getEventNamespace(redisSelectObj)
8591

86-
# Get the corresponding namespace from redisselect db connector object
87-
namespace = redisSelectObj.getDbConnector().getNamespace()
88-
89-
(key, op, fvp) = self.sst[namespace].pop()
92+
(key, op, fvp) = self.tables[namespace].pop()
9093
if fvp:
9194
# TODO: Once these flag entries have been removed from the DB,
9295
# we can remove this check
9396
if key in ["PortConfigDone", "PortInitDone"]:
94-
return 3
97+
return
9598

9699
fvp_dict = dict(fvp)
97100

98-
if op == "SET" and "oper_status" in fvp_dict:
99-
if not key.startswith((backplane_prefix(), inband_prefix(), recirc_prefix())):
100-
self.led_control.port_link_state_change(key, fvp_dict["oper_status"])
101-
else:
102-
return 4
101+
if op == "SET" and "oper_status" in fvp_dict and self.isFrontPanelPort(key):
102+
self.updatePortLedColor(key, fvp_dict["oper_status"])
103+
104+
105+
106+
# Run daemon
107+
def run(self):
108+
# Use timeout to prevent ignoring the signals we want to handle
109+
# in signal_handler() (e.g. SIGTERM for graceful shutdown)
110+
(state, selectableObj) = self.sel.select(SELECT_TIMEOUT)
111+
112+
if state == swsscommon.Select.TIMEOUT:
113+
# NOOP - Nothing to process here
114+
return 0
115+
116+
if state != swsscommon.Select.OBJECT:
117+
self.log_warning("sel.select() did not return swsscommon.Select.OBJECT - May be socket closed???")
118+
return -1 ## Fail here so that the daemon can be restarted
119+
120+
self.processPortTableEvent(selectableObj)
103121

104122
return 0
105123

@@ -126,8 +144,9 @@ def main():
126144

127145
# Listen indefinitely for changes to the PORT table in the Application DB's
128146
while True:
129-
ledd.run()
130-
147+
if 0 != ledd.run():
148+
print("ledd.run() failed... Exiting")
149+
sys.exit(LEDUTIL_RUNTIME_ERROR)
131150

132151
if __name__ == '__main__':
133-
main()
152+
main()

0 commit comments

Comments
 (0)