Skip to content

Commit

Permalink
Add sonic-ledd (Front-panel LED control daemon) (sonic-net#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
jleveque authored Jun 9, 2017
1 parent 8cb0740 commit bd7c310
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 0 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SONiC: Software for Open Networking in the Cloud

## sonic-platform-daemons

Daemons for controlling platform-specific functionality in SONiC


# Contribution guide

All contributors must sign a contribution license agreement before contributions can be accepted. Contact kasubra@microsoft.com or daloher@microsoft.com. Later this will be automated.

### GitHub Workflow

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.

Guide for performing commits:

* Isolate each commit to one component/bugfix/issue/feature
* Use a standard commit message format:

> [component/folder touched]: Description intent of your changes
>
> [List of changes]
>
> Signed-off-by: Your Name your@email.com
For example:

> swss-common: Stabilize the ConsumerTable
>
> * Fixing autoreconf
> * Fixing unit-tests by adding checkers and initialize the DB before start
> * Adding the ability to select from multiple channels
> * Health-Monitor - The idea of the patch is that if something went wrong with the notification channel,
> we will have the option to know about it (Query the LLEN table length).
>
> Signed-off-by: user@dev.null

* Each developer should fork this repository and [add the team as a Contributor](https://help.github.com/articles/adding-collaborators-to-a-personal-repository)
* Push your changes to your private fork and do "pull-request" to this repository
* Use a pull request to do code review
* Use issues to keep track of what is going on
228 changes: 228 additions & 0 deletions sonic-ledd/scripts/ledd
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/env python
#
# ledd
#
# Front-panel LED control daemon for SONiC
#

try:
import ast
import getopt
import os
import imp
import signal
import subprocess
import swsssdk
import sys
import syslog
import time
except ImportError, e:
raise ImportError (str(e) + " - required module not found")

#============================= Constants =============================

VERSION = '1.0'

USAGE_HELP="""
Usage: ledd [options]
Options:
-h,--help Print this usage statement and exit
-v,--version Print version information and exit
"""

SYSLOG_IDENTIFIER = "ledd"

LED_MODULE_NAME = "led_control"
LED_CLASS_NAME = "LedControl"

SONIC_CFGGEN = "/usr/local/bin/sonic-cfggen"
MINIGRAPH_FILE = "/etc/sonic/minigraph.xml"
HWSKU_KEY = "minigraph_hwsku"
PLATFORM_KEY = "platform"

PLATFORM_ROOT = "/usr/share/sonic/device"

PORT_TABLE_PREFIX = "PORT_TABLE:"

led_control = None

#========================== Syslog wrappers ==========================

def log_info(msg):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_INFO, msg)
syslog.closelog()

def log_warning(msg):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_WARNING, msg)
syslog.closelog()

def log_error(msg):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_ERR, msg)
syslog.closelog()

#========================== Signal Handling ==========================

def signal_handler(sig, frame):
if sig == signal.SIGHUP:
log_info("Caught SIGHUP - ignoring...\n")
return
elif sig == signal.SIGINT:
log_info("Caught SIGINT - exiting...\n")
sys.exit(0)
elif sig == signal.SIGTERM:
log_info("Caught SIGTERM - exiting...\n")
sys.exit(0)
else:
log_warning("Caught unhandled signal '" + sig + "'")

#============ Functions to load platform-specific classes ============

# Returns platform and HW SKU
def get_platform_and_hwsku():
try:
proc = subprocess.Popen([SONIC_CFGGEN, '-v', PLATFORM_KEY],
stdout=subprocess.PIPE,
shell=False,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
platform = stdout.rstrip('\n')

proc = subprocess.Popen([SONIC_CFGGEN, '-m', MINIGRAPH_FILE, '-v', HWSKU_KEY],
stdout=subprocess.PIPE,
shell=False,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
hwsku = stdout.rstrip('\n')
except OSError, e:
log_error("Cannot detect platform")
raise OSError("Cannot detect platform")

return (platform, hwsku)


# Loads platform-specific LED control module from source
def load_platform_led_control_module():
global led_control

# Get platform and hwsku
(platform, hwsku) = get_platform_and_hwsku()

# Load platform module from source
platform_path = '/'.join([PLATFORM_ROOT, platform])
hwsku_path = '/'.join([platform_path, hwsku])

module_file = '/'.join([platform_path, 'plugins', LED_MODULE_NAME + '.py'])

# If we can't locate a platform-specific module, exit gracefully, assuming this
# platform utilizes a hardware-based LED control solution
if not os.path.isfile(module_file):
log_info("Failed to locate platform-specific %s module. Exiting..." % LED_MODULE_NAME)
sys.exit(0)

try:
module = imp.load_source(LED_MODULE_NAME, module_file)
except IOError, e:
log_error("Failed to load platform module '%s': %s" % (LED_MODULE_NAME, str(e)))
return -1

log_info("Loaded module '%s'." % LED_MODULE_NAME)

try:
led_control_class = getattr(module, LED_CLASS_NAME)
led_control = led_control_class()
except AttributeError, e:
log_error("Failed to instantiate '%s' class: %s" % (LED_CLASS_NAME, str(e)))
return -2

log_info("Instantiated class '%s.%s'." % (LED_MODULE_NAME, LED_CLASS_NAME))

return 0

#=============================== Main ================================

def main():
port_status_dict = {}

log_info("Starting up...")

if not os.geteuid() == 0:
log_error("Must be root to run this daemon")
print "Error: Must be root to run this daemon"
sys.exit(1)

# Parse options if provided
if (len(sys.argv) > 1):
try:
options, remainder = getopt.getopt(sys.argv[1:],
'hv',
['help',
'version'])
except getopt.GetoptError, e:
print e
print USAGE_HELP
sys.exit(2)

for opt, arg in options:
if opt == '--help' or opt == '-h':
print USAGE_HELP
sys.exit(0)
elif opt == '--version' or opt == '-v':
print 'ledd version ' + VERSION
sys.exit(0)

# Register our signal handlers
signal.signal(signal.SIGHUP, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# Load platform-specific LedControl class
err = load_platform_led_control_module()
if err != 0:
sys.exit(1)

# Connect to APPL_DB using SwSS SDK
swss = swsssdk.SonicV2Connector()
swss.connect(swss.APPL_DB)

# Loop forever
while True:
# TODO: Move dictionary creation and population out of while loop. Do it only once
# and subscribe for notifications from DB when ports come or go. Add/remove
# dictionary entries as necessary.

# Get a list of all ports from the database
port_table_keys = swss.keys(swss.APPL_DB, PORT_TABLE_PREFIX + '*');

# Create a dictionary of <sonic_port_name>:<link_status> containing all ports
# Initially set all statuses to 'down'
for key in port_table_keys:
# Remove table name prefix
port_name = key[len(PORT_TABLE_PREFIX):]

# TODO: Once the DB is fixed and this 'ConfigDone' entry is gone, remove this check
if port_name == 'ConfigDone':
continue

port_status_dict[port_name] = 'down'
led_control.port_link_state_change(port_name, port_status_dict[port_name])

for (key, value) in port_status_dict.iteritems():
status = swss.get(swss.APPL_DB, PORT_TABLE_PREFIX + key, 'oper_status')

# If the status has changed, update it in our dictionary and report to our plugin
if value != status:
port_status_dict[key] = status
led_control.port_link_state_change(key, status)

time.sleep(1)
pass

if __name__ == '__main__':
main()

30 changes: 30 additions & 0 deletions sonic-ledd/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from setuptools import setup

setup(
name='sonic-ledd',
version='1.0',
description='Front-panel LED control daemon for SONiC',
license='Apache 2.0',
author='SONiC Team',
author_email='linuxnetdev@microsoft.com',
url='https://github.com/Azure/sonic-platform-daemons',
maintainer='Joe LeVeque',
maintainer_email='jolevequ@microsoft.com',
packages=['sonic_led'],
scripts=[
'scripts/ledd',
],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: No Input/Output (Daemon)',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: Apache Software License',
'Natural Language :: English',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 2.7',
'Topic :: System :: Hardware',
],
keywords='sonic SONiC LED led daemon LEDD ledd',
)
Empty file.
26 changes: 26 additions & 0 deletions sonic-ledd/sonic_led/led_control_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python
#
# led_control_base.py
#
# Abstract base class for implementing platform-specific
# LED control functionality for SONiC
#

try:
import abc
except ImportError, e:
raise ImportError (str(e) + " - required module not found")

class LedControlBase(object):
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def port_link_state_change(self, port, state):
"""
Called when port link state changes. Update port link state LED here.
:param port: A string, SONiC port name (e.g., "Ethernet0")
:param state: A string, the port link state (either "up" or "down")
"""
return

0 comments on commit bd7c310

Please sign in to comment.