Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved xcvrd shutdown flow #1

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 71 additions & 186 deletions sonic-ledd/scripts/ledd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

"""
ledd

Front-panel LED control daemon for SONiC
"""

Expand All @@ -15,6 +14,9 @@ try:
import sys
import syslog
from swsscommon import swsscommon
from sonic_daemon_base import daemon_base
from sonic_daemon_base.daemon_base import Logger
from sonic_daemon_base.daemon_base import DaemonBase
except ImportError, e:
raise ImportError (str(e) + " - required module not found")

Expand All @@ -35,199 +37,82 @@ Options:
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"
SELECT_TIMEOUT = 1000

# platform directory in base image
PLATFORM_ROOT = "/usr/share/sonic/device"
LEDUTIL_LOAD_ERROR = 1

# platform root directory inside docker
PLATFORM_ROOT_DOCKER = "/usr/share/sonic/platform"
logger = Logger(SYSLOG_IDENTIFIER)

REDIS_HOSTNAME = "localhost"
REDIS_PORT = 6379
REDIS_TIMEOUT_USECS = 0
class DaemonLedd(DaemonBase):
def __init__(self):
DaemonBase.__init__(self)

SELECT_TIMEOUT = 1000
# Run daemon
def run(self):
# 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)

#========================== 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...")
return
elif sig == signal.SIGINT:
log_info("Caught SIGINT - exiting...")
sys.exit(128 + sig)
elif sig == signal.SIGTERM:
log_info("Caught SIGTERM - exiting...")
sys.exit(128 + sig)
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():
# 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_base = '/'.join([platform_path, 'plugins', LED_MODULE_NAME + '.py'])

module_file_docker = '/'.join([PLATFORM_ROOT_DOCKER, '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 os.path.isfile(module_file_base):
module_file = module_file_base
elif os.path.isfile(module_file_docker):
module_file = module_file_docker
else:
log_info("Failed to locate platform-specific %s module." % LED_MODULE_NAME)
return None

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 None

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 None

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

return led_control

#=============================== Main ================================
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)

def main():
log_info("Starting up...")
# Load platform-specific LedControl module
try:
led_control = self.load_platform_util(LED_MODULE_NAME, LED_CLASS_NAME)
except Exception as e:
logger.log_error("Failed to load ledutil: %s" % (str(e)), True)
sys.exit(LEDUTIL_LOAD_ERROR)

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)
# Open a handle to the Application database
appl_db = daemon_base.db_connect(swsscommon.APPL_DB)

# 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 module
led_control = load_platform_led_control_module()
if led_control is None:
sys.exit(0)

# Open a handle to the Application database
appl_db = swsscommon.DBConnector(swsscommon.APPL_DB,
REDIS_HOSTNAME,
REDIS_PORT,
REDIS_TIMEOUT_USECS)

# Subscribe to PORT table notifications in the Application DB
sel = swsscommon.Select()
sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME)
sel.addSelectable(sst)

# Listen indefinitely for changes to the PORT table in the Application DB
while True:
# Use timeout to prevent ignoring the signals we want to handle
# in signal_handler() (e.g. SIGTERM for graceful shutdown)
(state, c) = sel.select(SELECT_TIMEOUT)

if state == swsscommon.Select.TIMEOUT:
# Do not flood log when select times out
continue
if state != swsscommon.Select.OBJECT:
log_warning("sel.select() did not return swsscommon.Select.OBJECT")
continue

(key, op, fvp) = sst.pop()

# TODO: Once these flag entries have been removed from the DB,
# we can remove this check
if key in ["PortConfigDone", "PortInitDone"]:
continue

fvp_dict = dict(fvp)

if op == "SET" and "oper_status" in fvp_dict:
led_control.port_link_state_change(key, fvp_dict["oper_status"])
# Subscribe to PORT table notifications in the Application DB
sel = swsscommon.Select()
sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME)
sel.addSelectable(sst)

# Listen indefinitely for changes to the PORT table in the Application DB
while True:
# Use timeout to prevent ignoring the signals we want to handle
# in signal_handler() (e.g. SIGTERM for graceful shutdown)
(state, c) = sel.select(SELECT_TIMEOUT)

if state == swsscommon.Select.TIMEOUT:
# Do not flood log when select times out
continue
if state != swsscommon.Select.OBJECT:
logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT")
continue

(key, op, fvp) = sst.pop()

# TODO: Once these flag entries have been removed from the DB,
# we can remove this check
if key in ["PortConfigDone", "PortInitDone"]:
continue

fvp_dict = dict(fvp)

if op == "SET" and "oper_status" in fvp_dict:
led_control.port_link_state_change(key, fvp_dict["oper_status"])

return 1

def main():
ledd = DaemonLedd()
ledd.run()

if __name__ == '__main__':
main()
Loading