#!/usr/bin/python

import sys
import os
import re
import argparse
from collections import OrderedDict

lockfile="/run/network/.lock"
modules_configfile='/var/lib/ifupdownaddons/addons.conf'
modules_dir='/usr/share/ifupdownaddons'

addon_config = OrderedDict([('pre-up', []),
                            ('up', []),
                            ('post-up', []),
                            ('pre-down', []),
                            ('down', []),
                            ('post-down', [])])

def read_modules_config():
    with open(modules_configfile, 'r') as f:
        lines = f.readlines()
        for l in lines:
            litems = l.rstrip(' \n').split(',')
            operation = litems[0]
            mname = litems[1]
            addon_config[operation].append(mname)

def man_rst_header():
    print '=========================='
    print 'ifupdown-addons-interfaces'
    print '=========================='

    print '---------------------------------------------------------'
    print 'ifupdown2 addon modules interface configuration'
    print '---------------------------------------------------------'

    print ':Author: roopa@cumulusnetworks.com'
    print ':Date:   2013-09-25'
    print ':Copyright: Copyright 2013 Cumulus Networks, Inc.  All rights reserved.'
    print ':Version: 0.1'
    print ':Manual section: 5'
    print '\n'

def man_rst_body():

    print 'DESCRIPTION'
    print '==========='

    print ('''    ifupdown2 addon modules add incremental functionality to
    core ifupdown2 tool.
           
    All installed addon modules are executed on every interface
    listed in the interfaces file. Addon modules are installed under
    /usr/share/ifupdownaddons. To see the list of active addon
    modules, see ifaddon(8).

    Addon modules add new attributes to the interfaces(5) file.
    Below is a list of attribute options provided by each module.
    These can be listed under each iface section in the interfaces(5)
    file.  ''')

    print '\n'

def get_addon_modinfo(modules_dir):
    """ load python modules from modules_dir

    Default modules_dir is /usr/share/ifupdownmodules

    """
    if not modules_dir in sys.path:
        sys.path.append(modules_dir)
    read_modules_config()
    modinfo = {}
    try:
        for op, mlist in addon_config.items():
            for mname in mlist:
                if mname in modinfo.keys(): continue
                mpath = modules_dir + '/' + mname + '.py'
                if os.path.exists(mpath):
                    try:
                        m = __import__(mname)
                        mclass = getattr(m, mname)
                    except:
                        pass
                        continue
                    minstance = mclass()
                    if hasattr(minstance, 'get_modinfo'):
                       modinfo[mname] = minstance.get_modinfo()
    except: 
        raise

    return modinfo

def print_long_string(indent, strarg):
    slen = 70 - len(indent)
    tmphelpstr = strarg
    l = len(strarg)
    while l > 0:
        rem = slen if l >= slen else l
        print('%s%s' %(indent, tmphelpstr[:rem]))
        tmphelpstr = tmphelpstr[rem:].strip()
        l -= rem

def man_rst_examples():
    print 'EXAMPLES'
    print '========'
    print '''    Listed below are addon modules and their supported attributes.
    The attributes if applicable go under the iface section in the
    interfaces(5) file.\n'''

    indent = '    '
    modinfo = get_addon_modinfo(modules_dir)
    for m, mdict in modinfo.items():
        aindent = indent + '  '
        aindentplus = aindent + '  '
        if not mdict:
            continue
        print_long_string(indent, '**%s**: %s' %(m, mdict.get('mhelp', '')))
        attrdict = mdict.get('attrs')
        if not attrdict:
            continue
        print '\n'
        try:
            for attrname, attrvaldict in attrdict.items():
                if attrvaldict.get('compat', False):
                    continue
                print('%s**%s**\n' %(aindent, attrname))
                print_long_string(aindentplus, '**help**: %s'
                        %(attrvaldict.get('help', '')))
                print '\n'
                print('%s**required**: %s\n' %(aindentplus,
                            attrvaldict.get('required', False)))
                default = attrvaldict.get('default')
                if default:
                    print('%s**default**: %s\n' %(aindentplus, default))
                validrange = attrvaldict.get('validrange')
                if validrange:
                    print('%svalidrange: %s\n'
                          %(aindentplus, '-'.join(validrange)))
                validvals = attrvaldict.get('validvals')
                if validvals:
                    print('%s**validvals**: %s\n'
                              %(aindentplus, ','.join(validvals)))
                examples = attrvaldict.get('example')
                if not examples:
                    continue
                print '%s**example**:' %(aindentplus)
                for e in examples:
                    print '%s%s\n' %(aindentplus + indent, e)
                print ''
        except Exception, e:
            print "Roopa: m = %s, str(e) = %s\n"  %(m, str(e))
            pass
        print ''

def man_rst_see_also():
    print 'SEE ALSO'
    print '========'
    print '''    interfaces(5),
    ifup(8),
    ip(8),
    mstpctl(8),
    brctl(8),
    ethtool(8),
    clagctl(8)'''

def show_man_rst():
    man_rst_header()
    man_rst_body()
    man_rst_examples()
    man_rst_see_also()

def show():
    for operation, mlist in addon_config.items():
        postion = 1
        for m in mlist:
            print '%d. %s' %(postion, m)
            postion += 1

def write_modules_config():
    with open(modules_configfile, 'w') as f:
        for op, mlist in addon_config.items():
            [f.write('%s,%s\n' %(op, m)) for m in mlist]

def process_add_cmd(args):
    op = args.operation
    module = args.module
    position = args.position
    if not op:
        for k, vlist in addon_config.items():
            if module not in vlist:
                addon_config[k].append(module)
            else:
                print '%s: module %s already present' %(k, module)
        return
    if module in addon_config.get(op):
        print 'module already present'
        return
    if position:
       try:
            addon_config[op].insert(position, module)
       except Exception, e:
           print ('error inserting module %s at postion %s (%s)'
                    %(module, position, str(e)))
           raise
    else:
       addon_config[op].append(module)


def process_del_cmd(args):
    op = args.operation
    module = args.module

    if op:
        del addon_config[op]
    else:
       try:
            [addon_config[op].remove(module) for op in addon_config.keys()]
       except ValueError:
           pass

def process_move_cmd(args):
    op = args.operation
    module = args.module
    pos = 0

    try:
        pos = int(args.position)
        if pos < 0 or pos > len(addon_config.get(op)):
            raise Exception('invalid value for position')
    except:
        raise

    if addon_config[op].index(module) == pos:
        print '%s module %s already at location %d' %(op, module, pos)
        return

    addon_config[op].remove(module)
    addon_config[op].insert(pos, module)

def print_mlist(mlist, indent):
    for idx, val in enumerate(mlist):
        print '%s%d. %s' %(indent, idx, val)

def process_show_cmd(args):
    indent = '   '
    op = args.operation

    if args.man:
        show_man_rst()
        return

    if op:
        mlist = addon_config[op]
        print '%s:' %op
        print_mlist(mlist, indent)
    else:
        for op, mlist in addon_config.items():
            print '%s:' %op
            print_mlist(mlist, indent)
            print ''

cmdhandlers = {'add' : process_add_cmd,
               'del' : process_del_cmd,
               'move' : process_move_cmd,
               'show' : process_show_cmd}

def update_subparser_add(subparser):
    subparser.add_argument('module', metavar='MODULE', help='module name')
    subparser.add_argument('operation', metavar='OPERATION',
                           choices=['pre-up', 'up', 'post-up',
                                    'pre-down', 'down', 'post-down'],
                                    help='operations', nargs='?')
    subparser.add_argument('position', metavar='POSITION', nargs='?',
                           help='position')
    subparser.set_defaults(func=process_add_cmd)

def update_subparser_del(subparser):
    subparser.add_argument('module', metavar='MODULE', help='module name')
    subparser.add_argument('operation', metavar='OPERATION',
                           choices=['pre-up', 'up', 'post-up',
                                    'pre-down', 'down', 'post-down'],
                                    help='operations', nargs='?')
    subparser.add_argument('position', metavar='POSITION', nargs='?',
                           help='position')
    subparser.set_defaults(func=process_del_cmd)

def update_subparser_move(subparser):
    subparser.add_argument('module', metavar='MODULE', help='module name')
    subparser.add_argument('operation', metavar='OPERATION',
                           choices=['pre-up', 'up', 'post-up',
                                    'pre-down', 'down', 'post-down'],
                                    help='operations')
    subparser.add_argument('position', metavar='POSITION',
                           help='position')
    subparser.set_defaults(func=process_move_cmd)


def update_subparser_show(subparser):
    subparser.add_argument('--man', action='store_true',
                           help=argparse.SUPPRESS)
    subparser.add_argument('operation', metavar='OPERATION',
                           choices=addon_config.keys(),
                           help='operations %s' %str(addon_config.keys()),
                           nargs='?')
    subparser.set_defaults(func=process_show_cmd)

def update_argparser(argparser):
    subparsers = argparser.add_subparsers(help='sub-command help')

    parser_add = subparsers.add_parser('add')
    update_subparser_add(parser_add)

    parser_del = subparsers.add_parser('del', help='del help')
    update_subparser_del(parser_del)

    parser_move = subparsers.add_parser('move', help='move help')
    update_subparser_move(parser_move)

    parser_show = subparsers.add_parser('show', help='show help')
    update_subparser_show(parser_show)

def parse_args(argsv):
    descr = 'ifupdown addon modules management command.\n \
            This command helps add/del/display/reorder modules \n \
            in all ifupdown module categories'

    argparser = argparse.ArgumentParser(description=descr)
    update_argparser(argparser)

    args = argparser.parse_args(argsv)
    return args

def main(argv):
    """ main function """
    try:
        # Command line arg parser
        args = parse_args(argv[1:])
        read_modules_config()
        args.func(args)
        write_modules_config()
    except Exception, e:
        print 'error processing command (%s)' %str(e)

if __name__ == "__main__":
    if not os.geteuid() == 0:
        print 'Error: Must be root to run this command'
        exit(1)

    main(sys.argv)
