#!/usr/bin/env python

# Copyright C 2008 by Isilon Systems, Inc. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
# 
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# Inc.
# 
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems. 
#  
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# Inc.  Legion is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.

import sys
import os
import string
from isi.app.lib import safelockfile

SYSLOG_FILE    = '/etc/mcp/override/syslog.conf'
DEFAULT_FILTER = '*.warn;*.notice;kern.*;ifs.info;istat.none'

progname = os.path.basename(sys.argv[0])

USAGE_STR = r'''
Usage: ''' + progname + r''' COMMAND [ARGUMENTS ...]

Commands:

    help
      Print this help and exit.
    
    list
      List all configured remote servers.

    clear
      Clear all configured remote servers.

    add HOST [FILTER]
      Add remote logging to hostname HOST.  If logging is already
      configured for HOST, the configuration will be replaced.
      If FILTER is not specified, the default 
          "''' + DEFAULT_FILTER + '''"
      will be used. For more on FILTER, see man syslog.conf
   
    delete HOST
      Remove configuration for HOST if it exists.
'''

class Usage(Exception):
    pass

def usage():
    print >> sys.stderr, USAGE_STR
    raise Usage

def read_config(will_write):
    lockfile = None
    rules    = []

    if will_write:
        lockfile = safelockfile(SYSLOG_FILE + '.isi', 'w')

    try:
        file = open(SYSLOG_FILE)

        for line in file.readlines():
            filter, host = line.split(None, 2)
            host = host.lstrip('@').rstrip()
            rules.append([filter, host])

        file.close()

    except IOError:
        pass

    return rules, lockfile

def write_config(rules, lockfile, msg):
    for rule in rules:
        lockfile.write('%s\t\t@%s\n' % (rule[0], rule[1]))

    # XXX this pattern, which is used in many files of this sort, seems racey
    os.rename(SYSLOG_FILE + '.isi', SYSLOG_FILE)
    lockfile.close()

    print 'Logging change made: ', msg
    print 'It make take a short time for the change to propagate to all nodes'

    os.system('logger ' + msg)

def has_whitespace(s):
    for char in string.whitespace:
        if s.find(char) != -1:
            return True
    return False

def validate_host(name):
    if not name:
        raise RuntimeError, 'Hostname must be given'
    if has_whitespace(name):
        raise RuntimeError, 'Hostname cannot include whitespace'
    return name.lstrip('@')


def validate_filter(filter):
    if has_whitespace(filter):
        raise RuntimeError, 'Filter cannot include whitespace'
    return filter

def list_servers():
    rules, nil = read_config(False)

    print '%-30s %s' % ( 'LOGSERVER', 'FILTER' )
    for rule in rules:
        print '%-30s %s' % ( rule[1], rule[0] )

def clear_servers():
    rules, lockfile = read_config(True)
    write_config([], lockfile, 'all remote logging servers cleared')

def add_server(args):
    if len(args) < 1 or len(args) > 2:
        raise RuntimeError, 'Add command takes one or two arguments'

    host = validate_host(args[0])

    if len(args) > 1:
        filter = validate_filter(args[1])
    else:
        filter = DEFAULT_FILTER

    rules, lockfile = read_config(True)
    
    rules = [ rule for rule in rules if rule[1] != host ]
    rules.append([filter, host])
    write_config(rules, lockfile, 'remote logging for host %s added' % host)
    

def delete_server(args):
    if len(args) != 1:
        raise RuntimeError, 'Delete command requires one argument'

    host = args[0]

    rules, lockfile = read_config(True)

    rules = [ rule for rule in rules if rule[1] != host ]
    write_config(rules, lockfile, 'remote logging for host %s removed' % host)


def main():
    if len(sys.argv) < 2:
        usage()

    verb = sys.argv[1]
    args = sys.argv[2:]

    if verb == 'help':
        usage()
    elif verb == 'list':
        list_servers()
    elif verb == 'clear':
        clear_servers()
    elif verb == 'add':
        add_server(args)
    elif verb == 'delete':
        delete_server(args)
    else:
        raise RuntimeError, "Unknown command '%s'" % verb

def error(str):
    print >> sys.stderr, progname + ':', str
    print >> sys.stderr, "Use '%s help' for more information" % progname
    sys.exit(1)

if __name__ == '__main__':
    try:
        main()
    except Usage:
        pass
    except Exception, e:
        error('%s' % e)
