#! /usr/bin/env python
'''Generate various configuration files.'''

from __future__ import print_function

import os
import optparse
import logging
import bacula_tools


def reload_director(target_director):
    '''use bconsole to reload the config'''
    handle = bacula_tools.BDirector(target_director)
    handle.auth()
    handle.send('reload')
    handle.recv_all()
    handle.send('status dir days=')
    handle.recv_all()


def reload_storage():
    '''Restart bacula-sd with appropriate precautions.  Relies on at(1).'''
    atjob = "/tmp/restart_storage.sh"
    config_file = bacula_tools.ConfigFile(atjob)
    resubmit = config_file.close('''
exec > %(atjob)s.log 2>&1
set -x
status=`echo status dir days=0 | bconsole | grep -i "No Jobs running"`
if [ -z "$status" ]
then
  at -f %(atjob)s now + 1 hour
  exit
fi
service bacula-sd restart
/bin/rm %(atjob)s
''' % locals())
    if resubmit:
        os.system("at -f %(atjob)s now + 1 minute" % locals())
    return

# pylint: disable=too-many-arguments


def query_id_thing(self, sql, obj, output, fun=None, args=(), **kwargs):
    '''Query the database for information to use in constructing various
    objects, returning either the string representation or else the result
    of a passed-in method name.

    In theory we *could* just use the built-in enumeration/search methods
    for each function, but in practice there are some scoping issues to
    deal with.  For example, we don't want *all* of the clients, only the
    ones associated with a particular director.

    This should be monkeypatched onto the Bacula_Config class
    '''
    logging.debug('%s:%s:%s:%s', sql, obj, args, kwargs)
    for object_id in self.do_sql(sql, args):
        created_object = obj(**kwargs)
        created_object.search(object_id)
        if fun:
            output.write(getattr(created_object, fun)(), '\n')
        else:
            output.write(created_object, '\n')
    return
# This line is here because I think it'll be more obvious that way.
bacula_tools.Bacula_Config.query_id_thing = query_id_thing


def parse_command_line_arguments():
    '''Parser configuration and sanity checking.'''

    parser = optparse.OptionParser(
        description='Print Bacula configuration.',
        usage='usage: %prog --(director|fd|sd) hostname [subsidiary hostnames]')
    parser.add_option('--bconsole', action='store_true',
                      default=False,
                      help='Produce a configuration for using bconsole')
    parser.add_option('--director', action='store_true',
                      default=False,
                      help='Produce the configuration for the director')
    parser.add_option('--fd', action='store_true',
                      default=False,
                      help='Produce the configuration for the file daemon')
    parser.add_option('--sd', action='store_true',
                      default=False,
                      help='Produce the configuration for the storage daemon')
    parser.add_option('-d', '--debug', action='store_true',
                      default=False,
                      help='Enable debugging output')

    keylist = [bacula_tools.DIRECTOR,
               bacula_tools.FD,
               bacula_tools.SD,
               bacula_tools.BCONSOLE]
    (args, given_arg) = parser.parse_args()
    option_count = [x for x in keylist if getattr(args, x)]

    if len(option_count) != 1:
        print('You must use one, and only one, option\n')
        parser.print_help()
        exit()

    if (not given_arg) or not len(given_arg) > 0:
        print('You must supply the name of the configuration host\n')
        parser.print_help()
        exit()

    if len(given_arg) > 1 and (not args.bconsole):
        print('Only the bconsole option accepts multiple hostames\n')
        parser.print_help()
        exit()

    if args.debug:
        bacula_tools.set_debug()
    else:
        logging.basicConfig(level=logging.WARNING)
    return args, given_arg


def director_config(bc_object, myname):
    '''Write the director configuration out to a file.
    '''
    # First, the basics
    config_file = bacula_tools.ConfigFile(bacula_tools.BACULA_DIR_CONF)
    director_object = bacula_tools.Director().search(myname)
    config_file.write(director_object, '\n')
    catalog_object = bacula_tools.Catalog(
        {bacula_tools.DIRECTOR_ID: director_object[bacula_tools.ID]}).search()
    config_file.write(catalog_object, '\n')
    bc_object.query_id_thing('SELECT messages_id'
                             '  FROM messages_link'
                             '  WHERE ref_id = %s AND link_type = %s',
                             bacula_tools.Messages, config_file,
                             args=(director_object[bacula_tools.ID],
                                   director_object.IDTAG))
    # Now, for a little more interesting stuff (and we're cheating hard)
    bc_object.query_id_thing('SELECT id FROM filesets ORDER BY name',
                             bacula_tools.Fileset, config_file)
    bc_object.query_id_thing('SELECT id FROM schedules ORDER BY name',
                             bacula_tools.Schedule, config_file)
    # clients
    bc_object.query_id_thing('SELECT id FROM clients ORDER BY name',
                             bacula_tools.Client, config_file,
                             director_id=director_object[bacula_tools.ID])
    # jobs
    bc_object.query_id_thing('SELECT id FROM jobs'
                             '  WHERE jobdef=1 ORDER BY name',
                             bacula_tools.JobDef, config_file)
    bc_object.query_id_thing('SELECT id FROM jobs'
                             '  WHERE NOT jobdef=1 OR jobdef IS NULL'
                             '  ORDER BY name',
                             bacula_tools.Job, config_file)
    # storage
    bc_object.query_id_thing('SELECT id FROM storage ORDER BY name',
                             bacula_tools.Storage, config_file,
                             director_id=director_object[bacula_tools.ID])
    # Pools
    bc_object.query_id_thing('SELECT id FROM pools ORDER BY name',
                             bacula_tools.Pool, config_file)
    # Consoles
    bc_object.query_id_thing('SELECT id FROM consoles ORDER BY name',
                             bacula_tools.Console, config_file,
                             director_id=director_object[bacula_tools.ID])
    # Counters
    bc_object.query_id_thing('SELECT id FROM counters ORDER BY name',
                             bacula_tools.Counter, config_file)
    if config_file.close():
        reload_director(director_object)
    return


def file_daemon_config(bc_object, myname):
    '''Write the file daemon configuration out to the file.
    '''
    config_file = bacula_tools.ConfigFile(bacula_tools.BACULA_FD_CONF)
    client_object = bacula_tools.Client().search(myname)
    config_file.write(client_object.fd(), '\n')

    bc_object.query_id_thing('SELECT director_id'
                             '  FROM pwords'
                             '  WHERE obj_id = %s AND obj_type = %s'
                             '                    AND director_type = %s'
                             '  ORDER BY director_id',
                             bacula_tools.Director, config_file, fun='fd',
                             args=(client_object[bacula_tools.ID],
                                   client_object.IDTAG,
                                   bacula_tools.Director.IDTAG),
                             client_id=client_object[bacula_tools.ID])

    bc_object.query_id_thing('SELECT director_id'
                             '  FROM pwords'
                             '  WHERE obj_id = %s AND obj_type = %s'
                             '                    AND director_type = %s'
                             '  ORDER BY director_id',
                             bacula_tools.Console, config_file, fun='fd',
                             args=(client_object[bacula_tools.ID],
                                   client_object.IDTAG,
                                   bacula_tools.Console.IDTAG),
                             client_id=client_object[bacula_tools.ID])

    bc_object.query_id_thing('SELECT messages_id'
                             '  FROM messages_link'
                             '  WHERE ref_id = %s AND link_type = %s',
                             bacula_tools.Messages, config_file,
                             args=(client_object[bacula_tools.ID],
                                   client_object.IDTAG))

    if config_file.close():
        pass
    return


def storage_daemon_config(bc_object, myname):
    '''Write the file daemon configuration out to the file.
    '''
    configuration_file = bacula_tools.ConfigFile(bacula_tools.BACULA_SD_CONF)
    storage_daemon = bacula_tools.Storage().search(myname)
    configuration_file.write(storage_daemon.sd(), '\n')

    logging.debug('Getting Director credentials')
    bc_object.query_id_thing('SELECT director_id'
                             '  FROM pwords'
                             '  WHERE obj_id = %s AND obj_type = %s'
                             '                    AND director_type = %s'
                             '  ORDER BY director_id',
                             bacula_tools.Director, configuration_file,
                             fun='sd',
                             args=(storage_daemon[bacula_tools.ID],
                                   storage_daemon.IDTAG,
                                   bacula_tools.Director.IDTAG),
                             storage_id=storage_daemon[bacula_tools.ID])

    logging.debug('Getting Console credentials')
    bc_object.query_id_thing('SELECT director_id'
                             '  FROM pwords'
                             '  WHERE obj_id = %s AND obj_type = %s'
                             '                    AND director_type = %s'
                             '  ORDER BY director_id',
                             bacula_tools.Console, configuration_file,
                             fun='sd',
                             args=(storage_daemon[bacula_tools.ID],
                                   storage_daemon.IDTAG,
                                   bacula_tools.Console.IDTAG),
                             storage_id=storage_daemon[bacula_tools.ID])

    logging.debug('Getting devices')
    bc_object.query_id_thing('SELECT device_id FROM device_link'
                             '  WHERE storage_id = %s',
                             bacula_tools.Device, configuration_file,
                             args=(storage_daemon[bacula_tools.ID],))

    logging.debug('Getting messages')
    bc_object.query_id_thing('SELECT messages_id'
                             '  FROM messages_link'
                             '  WHERE ref_id = %s AND link_type = %s',
                             bacula_tools.Messages, configuration_file,
                             args=(storage_daemon[bacula_tools.ID],
                                   storage_daemon.IDTAG))

    if configuration_file.close():
        pass
    return


def bconsole_config(name_list):
    '''Write the bconsole configuration out to the file.
    '''
    configuration_file = bacula_tools.ConfigFile(bacula_tools.BCONSOLE_CONF)
    for myname in name_list:
        director_object = bacula_tools.Director().search(myname.strip())
        if not director_object[bacula_tools.ID]:
            director_object = bacula_tools.Console().search(myname.strip())
        configuration_file.write(director_object.bconsole(), '\n')
    configuration_file.close()
    return

bacula = bacula_tools.Bacula_Factory()  # Instantiate our DB connection thingy

arguments, hostname_list = parse_command_line_arguments()
hostname = hostname_list[0].strip()

if arguments.director:               # Director configuration stanzas
    director_config(bacula, hostname)
if arguments.fd:                     # File Daemon configuration stanzas
    file_daemon_config(bacula, hostname)
if arguments.sd:
    storage_daemon_config(bacula, hostname)
if arguments.bconsole:               # bconsole setup
    bconsole_config(hostname_list)
