Skip to content

Commit bd7c310

Browse files
authored
Add sonic-ledd (Front-panel LED control daemon) (sonic-net#5)
1 parent 8cb0740 commit bd7c310

File tree

5 files changed

+327
-0
lines changed

5 files changed

+327
-0
lines changed

README.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# SONiC: Software for Open Networking in the Cloud
2+
3+
## sonic-platform-daemons
4+
5+
Daemons for controlling platform-specific functionality in SONiC
6+
7+
8+
# Contribution guide
9+
10+
All contributors must sign a contribution license agreement before contributions can be accepted. Contact [email protected] or [email protected]. Later this will be automated.
11+
12+
### GitHub Workflow
13+
14+
We're following basic GitHub Flow. If you have no idea what we're talking about, check out [GitHub's official guide](https://guides.github.com/introduction/flow/). Note that merge is only performed by the repository maintainer.
15+
16+
Guide for performing commits:
17+
18+
* Isolate each commit to one component/bugfix/issue/feature
19+
* Use a standard commit message format:
20+
21+
> [component/folder touched]: Description intent of your changes
22+
>
23+
> [List of changes]
24+
>
25+
> Signed-off-by: Your Name [email protected]
26+
27+
For example:
28+
29+
> swss-common: Stabilize the ConsumerTable
30+
>
31+
> * Fixing autoreconf
32+
> * Fixing unit-tests by adding checkers and initialize the DB before start
33+
> * Adding the ability to select from multiple channels
34+
> * Health-Monitor - The idea of the patch is that if something went wrong with the notification channel,
35+
> we will have the option to know about it (Query the LLEN table length).
36+
>
37+
> Signed-off-by: [email protected]
38+
39+
40+
* Each developer should fork this repository and [add the team as a Contributor](https://help.github.com/articles/adding-collaborators-to-a-personal-repository)
41+
* Push your changes to your private fork and do "pull-request" to this repository
42+
* Use a pull request to do code review
43+
* Use issues to keep track of what is going on

sonic-ledd/scripts/ledd

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env python
2+
#
3+
# ledd
4+
#
5+
# Front-panel LED control daemon for SONiC
6+
#
7+
8+
try:
9+
import ast
10+
import getopt
11+
import os
12+
import imp
13+
import signal
14+
import subprocess
15+
import swsssdk
16+
import sys
17+
import syslog
18+
import time
19+
except ImportError, e:
20+
raise ImportError (str(e) + " - required module not found")
21+
22+
#============================= Constants =============================
23+
24+
VERSION = '1.0'
25+
26+
USAGE_HELP="""
27+
Usage: ledd [options]
28+
29+
Options:
30+
-h,--help Print this usage statement and exit
31+
-v,--version Print version information and exit
32+
"""
33+
34+
SYSLOG_IDENTIFIER = "ledd"
35+
36+
LED_MODULE_NAME = "led_control"
37+
LED_CLASS_NAME = "LedControl"
38+
39+
SONIC_CFGGEN = "/usr/local/bin/sonic-cfggen"
40+
MINIGRAPH_FILE = "/etc/sonic/minigraph.xml"
41+
HWSKU_KEY = "minigraph_hwsku"
42+
PLATFORM_KEY = "platform"
43+
44+
PLATFORM_ROOT = "/usr/share/sonic/device"
45+
46+
PORT_TABLE_PREFIX = "PORT_TABLE:"
47+
48+
led_control = None
49+
50+
#========================== Syslog wrappers ==========================
51+
52+
def log_info(msg):
53+
syslog.openlog(SYSLOG_IDENTIFIER)
54+
syslog.syslog(syslog.LOG_INFO, msg)
55+
syslog.closelog()
56+
57+
def log_warning(msg):
58+
syslog.openlog(SYSLOG_IDENTIFIER)
59+
syslog.syslog(syslog.LOG_WARNING, msg)
60+
syslog.closelog()
61+
62+
def log_error(msg):
63+
syslog.openlog(SYSLOG_IDENTIFIER)
64+
syslog.syslog(syslog.LOG_ERR, msg)
65+
syslog.closelog()
66+
67+
#========================== Signal Handling ==========================
68+
69+
def signal_handler(sig, frame):
70+
if sig == signal.SIGHUP:
71+
log_info("Caught SIGHUP - ignoring...\n")
72+
return
73+
elif sig == signal.SIGINT:
74+
log_info("Caught SIGINT - exiting...\n")
75+
sys.exit(0)
76+
elif sig == signal.SIGTERM:
77+
log_info("Caught SIGTERM - exiting...\n")
78+
sys.exit(0)
79+
else:
80+
log_warning("Caught unhandled signal '" + sig + "'")
81+
82+
#============ Functions to load platform-specific classes ============
83+
84+
# Returns platform and HW SKU
85+
def get_platform_and_hwsku():
86+
try:
87+
proc = subprocess.Popen([SONIC_CFGGEN, '-v', PLATFORM_KEY],
88+
stdout=subprocess.PIPE,
89+
shell=False,
90+
stderr=subprocess.STDOUT)
91+
stdout = proc.communicate()[0]
92+
proc.wait()
93+
platform = stdout.rstrip('\n')
94+
95+
proc = subprocess.Popen([SONIC_CFGGEN, '-m', MINIGRAPH_FILE, '-v', HWSKU_KEY],
96+
stdout=subprocess.PIPE,
97+
shell=False,
98+
stderr=subprocess.STDOUT)
99+
stdout = proc.communicate()[0]
100+
proc.wait()
101+
hwsku = stdout.rstrip('\n')
102+
except OSError, e:
103+
log_error("Cannot detect platform")
104+
raise OSError("Cannot detect platform")
105+
106+
return (platform, hwsku)
107+
108+
109+
# Loads platform-specific LED control module from source
110+
def load_platform_led_control_module():
111+
global led_control
112+
113+
# Get platform and hwsku
114+
(platform, hwsku) = get_platform_and_hwsku()
115+
116+
# Load platform module from source
117+
platform_path = '/'.join([PLATFORM_ROOT, platform])
118+
hwsku_path = '/'.join([platform_path, hwsku])
119+
120+
module_file = '/'.join([platform_path, 'plugins', LED_MODULE_NAME + '.py'])
121+
122+
# If we can't locate a platform-specific module, exit gracefully, assuming this
123+
# platform utilizes a hardware-based LED control solution
124+
if not os.path.isfile(module_file):
125+
log_info("Failed to locate platform-specific %s module. Exiting..." % LED_MODULE_NAME)
126+
sys.exit(0)
127+
128+
try:
129+
module = imp.load_source(LED_MODULE_NAME, module_file)
130+
except IOError, e:
131+
log_error("Failed to load platform module '%s': %s" % (LED_MODULE_NAME, str(e)))
132+
return -1
133+
134+
log_info("Loaded module '%s'." % LED_MODULE_NAME)
135+
136+
try:
137+
led_control_class = getattr(module, LED_CLASS_NAME)
138+
led_control = led_control_class()
139+
except AttributeError, e:
140+
log_error("Failed to instantiate '%s' class: %s" % (LED_CLASS_NAME, str(e)))
141+
return -2
142+
143+
log_info("Instantiated class '%s.%s'." % (LED_MODULE_NAME, LED_CLASS_NAME))
144+
145+
return 0
146+
147+
#=============================== Main ================================
148+
149+
def main():
150+
port_status_dict = {}
151+
152+
log_info("Starting up...")
153+
154+
if not os.geteuid() == 0:
155+
log_error("Must be root to run this daemon")
156+
print "Error: Must be root to run this daemon"
157+
sys.exit(1)
158+
159+
# Parse options if provided
160+
if (len(sys.argv) > 1):
161+
try:
162+
options, remainder = getopt.getopt(sys.argv[1:],
163+
'hv',
164+
['help',
165+
'version'])
166+
except getopt.GetoptError, e:
167+
print e
168+
print USAGE_HELP
169+
sys.exit(2)
170+
171+
for opt, arg in options:
172+
if opt == '--help' or opt == '-h':
173+
print USAGE_HELP
174+
sys.exit(0)
175+
elif opt == '--version' or opt == '-v':
176+
print 'ledd version ' + VERSION
177+
sys.exit(0)
178+
179+
# Register our signal handlers
180+
signal.signal(signal.SIGHUP, signal_handler)
181+
signal.signal(signal.SIGINT, signal_handler)
182+
signal.signal(signal.SIGTERM, signal_handler)
183+
184+
# Load platform-specific LedControl class
185+
err = load_platform_led_control_module()
186+
if err != 0:
187+
sys.exit(1)
188+
189+
# Connect to APPL_DB using SwSS SDK
190+
swss = swsssdk.SonicV2Connector()
191+
swss.connect(swss.APPL_DB)
192+
193+
# Loop forever
194+
while True:
195+
# TODO: Move dictionary creation and population out of while loop. Do it only once
196+
# and subscribe for notifications from DB when ports come or go. Add/remove
197+
# dictionary entries as necessary.
198+
199+
# Get a list of all ports from the database
200+
port_table_keys = swss.keys(swss.APPL_DB, PORT_TABLE_PREFIX + '*');
201+
202+
# Create a dictionary of <sonic_port_name>:<link_status> containing all ports
203+
# Initially set all statuses to 'down'
204+
for key in port_table_keys:
205+
# Remove table name prefix
206+
port_name = key[len(PORT_TABLE_PREFIX):]
207+
208+
# TODO: Once the DB is fixed and this 'ConfigDone' entry is gone, remove this check
209+
if port_name == 'ConfigDone':
210+
continue
211+
212+
port_status_dict[port_name] = 'down'
213+
led_control.port_link_state_change(port_name, port_status_dict[port_name])
214+
215+
for (key, value) in port_status_dict.iteritems():
216+
status = swss.get(swss.APPL_DB, PORT_TABLE_PREFIX + key, 'oper_status')
217+
218+
# If the status has changed, update it in our dictionary and report to our plugin
219+
if value != status:
220+
port_status_dict[key] = status
221+
led_control.port_link_state_change(key, status)
222+
223+
time.sleep(1)
224+
pass
225+
226+
if __name__ == '__main__':
227+
main()
228+

sonic-ledd/setup.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from setuptools import setup
2+
3+
setup(
4+
name='sonic-ledd',
5+
version='1.0',
6+
description='Front-panel LED control daemon for SONiC',
7+
license='Apache 2.0',
8+
author='SONiC Team',
9+
author_email='[email protected]',
10+
url='https://github.com/Azure/sonic-platform-daemons',
11+
maintainer='Joe LeVeque',
12+
maintainer_email='[email protected]',
13+
packages=['sonic_led'],
14+
scripts=[
15+
'scripts/ledd',
16+
],
17+
classifiers=[
18+
'Development Status :: 4 - Beta',
19+
'Environment :: No Input/Output (Daemon)',
20+
'Intended Audience :: Developers',
21+
'Intended Audience :: Information Technology',
22+
'Intended Audience :: System Administrators',
23+
'License :: OSI Approved :: Apache Software License',
24+
'Natural Language :: English',
25+
'Operating System :: POSIX :: Linux',
26+
'Programming Language :: Python :: 2.7',
27+
'Topic :: System :: Hardware',
28+
],
29+
keywords='sonic SONiC LED led daemon LEDD ledd',
30+
)

sonic-ledd/sonic_led/__init__.py

Whitespace-only changes.
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env python
2+
#
3+
# led_control_base.py
4+
#
5+
# Abstract base class for implementing platform-specific
6+
# LED control functionality for SONiC
7+
#
8+
9+
try:
10+
import abc
11+
except ImportError, e:
12+
raise ImportError (str(e) + " - required module not found")
13+
14+
class LedControlBase(object):
15+
__metaclass__ = abc.ABCMeta
16+
17+
@abc.abstractmethod
18+
def port_link_state_change(self, port, state):
19+
"""
20+
Called when port link state changes. Update port link state LED here.
21+
22+
:param port: A string, SONiC port name (e.g., "Ethernet0")
23+
:param state: A string, the port link state (either "up" or "down")
24+
"""
25+
return
26+

0 commit comments

Comments
 (0)