#! /usr/bin/python
#-------------------------------------------------------------------------------
#
# Copyright 2014, Cumulus Networks Inc  all rights reserved
#
#-------------------------------------------------------------------------------
#

#-------------------------------------------------------------------------------
#
# Imports
#
import syslog
import warnings
import exceptions
import re
import os
import sys
import time
import signal
import traceback
import argparse
import json
import cumulus.platforms

#Classes

class ArgParseError(RuntimeError):
    pass

class STATUSLEDRuntimeError(RuntimeError):
    pass
#-------------------------------------------------------------------------------
#
# Functions
#

#--------------------
#
# warning formats
#

def statusledwarn(message, category, filename, lineno, line=None):
    return '%s:%s : %s : %s\n' % (filename, lineno, category.__name__, message)

#--------------------
#
# check to see if an instance is already running
#
def already_running(pidfile, prog):
    if prog is None:
        myname=os.path.basename(sys.argv[0])
    else:
        myname=os.path.basename(prog)
    try:
        if not os.path.isfile(pidfile):
            return False
        oldpid = re.findall('\D*(\d+).*', (file(pidfile, 'r').readline()))[0]
        if not os.path.exists('/proc/%s' % oldpid):
            return False
        if myname not in file('/proc/%s/cmdline' % oldpid, 'r').readline():
            return False
        if prog is None:
            sys.stderr.write("%s already running as process %s\n" % (myname, oldpid))
        return True
    except Exception as inst:
        raise STATUSLEDRuntimeError("unable to validate pidfile %s: %s" %
                                (pidfile, str(inst)))

#--------------------
#
# normal exit
#
def exit_normally(signum=0, frame=None):
    syslog.syslog(syslog.LOG_INFO, "exiting normally")
    sys.stderr.write("%s : exiting normally\n" % sys.argv[0])
    exit(0)

def led_update_units(platform, args):
    units = []
    for unit in platform.statusleds.units:
        cp = unit
        if platform.cpld is not None:
            cp.update_cpld_path(platform.cpld.path)
        if args.display:
            loc, color = cp.led_read()
            print '%15s: %s' %(loc, color)
        else:
            units.append(cp)

    if args.display:
        exit(0)
    if args.json:
        json_output = []
        for unit in units:
            unit.led_update_status()
            loc, color = unit.led_read()
            unit.attrs['color'] = color
            json_output.append(unit.attrs)
        print json.dumps(json_output, indent=4)
        exit(0)
    return units
            

def main():
    parser = argparse.ArgumentParser(description="Update Status LEDs on the platform in a loop")
    parser.add_argument('-d', '--display', help ="Display led information and exit",
                        action="store_true")
    parser.add_argument('-i', '--interval', type = int,
                        help ="Update Interval in secs (default is 5 secs)")
    parser.add_argument('-j', '--json', help ="Generate JSON output",
                        action="store_true")

    args = parser.parse_args()

    units = []
    platform = cumulus.platforms.probe()
    interval = 5
    if hasattr(platform.statusleds, 'interval'):
        interval = platform.statusleds.interval
    if args.interval:
        interval = args.interval

    if platform.statusleds is None:
        units = None
        if args.display:
            print 'LEDs are not managed by ledmgrd on this platform'
            exit(0)
        else:
            syslog.syslog(syslog.LOG_INFO, "LED Manager Daemon doesnt manage leds on this platform")
    else:         
        units = led_update_units(platform, args)
        syslog.syslog(syslog.LOG_INFO, "Starting LED Manager Daemon")

    pidfile = "/var/run/ledmgrd.pid"
    if already_running(pidfile, None):
        sys.exit(1)
    else:
        file(pidfile, 'w').write(str(os.getpid()))
        
    while (True):
        # First check if smond is running
        pidfile = "/var/run/smond.pid"
        if not already_running(pidfile, 'smond'):
            syslog.syslog(syslog.LOG_ERR, 'smond is not running. Start smond and then run ledmgrd')
            sys.exit(1)

        # When smond starts, it deletes "/var/cache/cumulus/unit_state".
        # Since ledmgrd needs this path to read sensor status, we should wait
        # till this path is created.
        s_path = "/var/cache/cumulus/unit_state"
        if os.path.exists(s_path):
            if units:
                for unit in units:
                    unit.run_state()
        else:
            syslog.syslog(syslog.LOG_WARNING, 'smond is initializing. Skipping setting status LEDs for %d secs' %interval)
        time.sleep(interval)
    return 0

#--------------------
#
# execution check
#
if __name__ == "__main__":
    try:
        signal.signal(signal.SIGTERM, exit_normally)
        # Cause all warnings to always be triggered.
        warnings.simplefilter("always")
        warnings.formatwarning = statusledwarn
        syslog.openlog(": %s : " % sys.argv[0])
        exit(main())
    except STATUSLEDRuntimeError, errstr:
        syslog.syslog(syslog.LOG_ERR, "ERROR : %s" % str(errstr))
        sys.stderr.write("%s : ERROR : %s\n" % (sys.argv[0], str(errstr)))
        exit(1)
    except KeyboardInterrupt:
        exit_normally()
    except Exception:
        (exc_type, exc_value, exc_traceback) = sys.exc_info()
        err = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
        log = 'Unhandled Exception : %s' % err
        syslog.syslog(syslog.LOG_ERR, log)
        sys.stderr.write(log)
