#!/usr/bin/python

# Copyright (C) 2013:
#    Gabes Jean, j.gabes@shinken-solutions.com
#
# This file is part of Shinken Enterprise, all rights reserved.

import os
import sys
import optparse

try:
    import shinken
except ImportError:
    print 'Cannot import shinken lib, please install it before launching this tool'
    sys.exit(2)

from shinkensolutions.localinstall import get_local_daemons, VERSION, ERROR_SHELL_TAG, RESET_SHELL_TAG, OK_COLOR, check_root, POSSIBLE_DAEMONS, set_local_daemon_instance, get_next_local_instance_number_available

DEFAULT_UMASK = 0022
# Set umask to avoid problems when creating files
os.umask(DEFAULT_UMASK)

# This command is not allowed for specific types
_UN_ALLOWED_DAEMONS = set(['arbiter', 'synchronizer'])
ALLOWED_DAEMONS = list(set(POSSIBLE_DAEMONS) - _UN_ALLOWED_DAEMONS)
ALLOWED_DAEMONS.sort()

# After the help, we want to give some examples
_MAGENTA = '\033[95m'
EXAMPLE_STRING = '''
Examples:
# %sStep1 [on the scheduler server]%s: Create the scheduler ini file so the server can start it:
  shinken-daemons-create --ini --type=scheduler --port=8768 --name=scheduler-customer-A
  
# %sStep2 [on the arbiter server]%s: Create the scheduler in the arbiter configuration:
  shinken-daemons-create --cfg --type=scheduler --port=8768 --name=scheduler-customer-A --address=192.168.0.100
  
''' % (_MAGENTA, RESET_SHELL_TAG, _MAGENTA, RESET_SHELL_TAG)

# WARNING/HERESY: By default the formatter strip new lines, do not allow this
formatter = optparse.TitledHelpFormatter(width=200)
formatter._format_text = lambda txt: txt

NOT_ALLOWED_NAME_CHARACTERS = (' ', '/', r'\\', '\t')


# If there is a critical error: print it in RED and exit in bad state
def exit_error(txt):
    print "%sERROR: %s%s" % (ERROR_SHELL_TAG, txt, RESET_SHELL_TAG)
    parser.print_help()
    sys.exit(2)


if __name__ == '__main__':
    parser = optparse.OptionParser("%prog", version="%prog: " + VERSION, formatter=formatter, description='This tool is used to create daemons on servers (local ini file) or declare the daemon in your arbiter configuration (cfg file)',
                                   epilog=EXAMPLE_STRING)
    parser.add_option('-t', '--type', type='choice', dest='daemon_type', choices=ALLOWED_DAEMONS, help="Daemon type to create or declare. Allowed daemon types are: %s" % ', '.join(ALLOWED_DAEMONS))
    parser.add_option('-p', '--port', dest='port', help="Daemon port")
    parser.add_option('--ini', dest='is_ini', action='store_true', help="Creates the daemon locally on the server (will create the .ini configuration file)")
    parser.add_option('--cfg', dest='is_cfg', action='store_true', help="Declares the daemon in your arbiter configuration (will create the .cfg configuration file). Must be launched only from your arbiter server.")
    parser.add_option('--address', dest='address', help="For cfg, set the address for this daemon (Mandatory). For ini, address the daemon will listen to on that local server (will listen to all interfaces by default)")
    parser.add_option('--name', dest='name', help="Name of this daemon")
    
    opts, args = parser.parse_args()
    
    # If not root, exit
    check_root()
    
    is_ini = opts.is_ini
    is_cfg = opts.is_cfg
    if not is_ini and not is_cfg:
        exit_error('Please select at least either --ini or --cfg option')
    
    daemon_type = opts.daemon_type
    if not daemon_type:
        exit_error('Please select a valid daemon type option with the --type parameter')
    
    already_enabled = get_local_daemons()
    
    is_arbiter = ('arbiter' in already_enabled or 'central' in already_enabled)
    if is_cfg and not is_arbiter:
        exit_error('The cfg creation is only possible on the server where the arbiter daemon is running. Exiting')
    
    daemon_port = opts.port
    if not daemon_port:
        exit_error('Missing --port parameter.')
    
    # What about name and address
    name = opts.name
    address = opts.address
    # In the cfg, it must be present
    if is_cfg:
        if not name:
            exit_error('Missing --name parameter.')
        if not address:
            exit_error('Missing --address parameter.')
    # and in the ini file if missing, we have default values
    if is_ini:
        if not name:
            name = 'unnamed-%s' % daemon_type
        if not address:
            address = '0.0.0.0'  # all interfaces
    
    # Check if the name match name rules
    for c in name:
        if c in NOT_ALLOWED_NAME_CHARACTERS:
            exit_error('The caracter "%s" is not allowed in the name.' % c)
    
    # If local create, we will :
    # * create the ini file
    # * update the context file with the new daemon
    if is_ini:
        skel = '/etc/shinken/_default/daemons/skeleton.ini'
        next_id = get_next_local_instance_number_available(daemon_type)
        dest = '/etc/shinken/daemons/%sd-%s.ini' % (daemon_type, next_id)
        
        # If the destination is already exiting, bail out, we only create, not replace
        if os.path.exists(dest):
            exit_error('This daemon already exists. (file %s exists)' % dest)
        
        skel_buf = open(skel, 'r').read()
        
        final_buf = skel_buf.replace('__TYPE__', daemon_type).replace('__NAME__', name).replace('__ADDRESS__', address).replace('__PORT__', str(daemon_port)).replace('__ID__', next_id)
        
        f = open(dest, 'w')
        f.write(final_buf)
        f.close()
        
        set_local_daemon_instance(daemon_type, next_id, True)
        
        print ' INI file for %s:\033[%dm CREATED\033[0m  in the file %s' % (('%s [id:\033[%dm%s\033[0m]' % (daemon_type, OK_COLOR, next_id)).ljust(25), OK_COLOR, dest)
    
    # If distant create, we will :
    # * create the cfg file with the valid address
    if is_cfg:
        skel = '/etc/shinken/_default/daemons/%s.cfg.skeleton' % daemon_type
        
        dest = '/etc/shinken/%ss/%s.cfg' % (daemon_type, name)
        
        # If the destination is already exiting, bail out, we only create, not replace
        if os.path.exists(dest):
            exit_error('This daemon already exists. (file %s exists)' % dest)
        
        skel_buf = open(skel, 'r').read()
        
        final_buf = skel_buf.replace('__NAME__', name).replace('__PORT__', str(daemon_port)).replace('__ADDRESS__', address)
        
        f = open(dest, 'w')
        f.write(final_buf)
        f.close()
        
        print ' CFG file for %s:\033[%dm CREATED\033[0m  in the file %s' % (('%s [name:%s] [address:%s] [port:%s]' % (daemon_type, name, address, daemon_port)).ljust(25), OK_COLOR, dest)
    
    sys.exit(0)
