#!/usr/bin/env python
#
# Copyright 2011, Pall Sigurdsson <palli@opensource.is>
#
# This script is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


# No real need to change anything below here
version = "1.0"

import sys
import okconfig
import os
import getpass
import pynag.Parsers

import okconfig.migration_tool

# Parse some Arguments
from optparse import OptionParser, OptionGroup, SUPPRESS_HELP

usage = """usage: %prog <command> [arguments]

Common okconfig commands:
  addhost       Add a new host
  addtemplate   Add a new template to existing host
  addgroup      Add a new host/contact/service group
  addcontact    Add a new contact
  addservice    Add a service check to host
  removehost    Delete a host
  listtemplates List all available templates
  listhosts     List all hosts
  listhost      List a single host
  init          Initilize nagios to use okconfig
  install       Install an agent on a remote server
  upgrade       Runs migration routines in case of okconfig upgrades
  verify        Verifies current okconfig installation

For help on specific command type:
 %prog <command> --help

Examples:
 %prog addgroup linuxservers --alias "Example Linux Servers"
 %prog addhost <host_name> --template linux --address 127.0.0.1 --group linuxservers
 %prog addtemplate <host_name> --template oracle
"""
parser = OptionParser(usage=usage)
parser.add_option(
    "--debug",
    dest="debug",
    action="store_true",
    default=False,
    help="Print extra debugging information"
)


def parse_arguments():
    """ Parse command line arguments and run command specified commands """
    if len(sys.argv) < 2:
        parser.error("No command specified")
    elif sys.argv[1] == 'verify':
        verify()
    elif sys.argv[1] == 'addhost':
        addhost()
    elif sys.argv[1] == 'addgroup':
        addgroup()
    elif sys.argv[1] == 'addtemplate':
        addtemplate()
    elif sys.argv[1] == 'addcontact':
        addcontact()
    elif sys.argv[1] == "upgrade":
        upgrade()
    elif sys.argv[1] == "addservice":
        addservice()
    elif sys.argv[1] == "removehost":
        removehost()
    elif sys.argv[1] == "listtemplates":
        listtemplates()
    elif sys.argv[1] == "listhosts":
        listhosts()
    elif sys.argv[1] == "listhost":
        listhost()
    elif sys.argv[1] == "init":
        init()
    elif sys.argv[1] == "install":
        remote_install()
    elif sys.argv[1] == "help":
        parser.print_help()
    elif sys.argv[1] == "--help":
        parser.print_help()
    else:
        parser.error("Unknown command '%s'" % sys.argv[1])


def remote_install():
    """ Install an ok agent on a remote server """
    parser.usage = ''' %prog install <remote_server> [options]

Examples:
  # %prog install linuxserver --ssh --user root --password lol
  # %prog install winserver --winexe --user admin --password lol --domain lol

Run %prog install --help for full list of command line arguments.
'''
    parser.add_option("--user", dest="user",
                      help="Username with admin privileges on remote box")
    parser.add_option("--password", dest="password",
                      help="Password to use to connect", default=None)
    parser.add_option("--domain", dest="domain",
                      help="Use this windows domain (works with --winexe)", default=None)
    parser.add_option("--ssh", dest="ssh", action="store_true",
                      default=False, help="Use ssh to connect")
    parser.add_option("--verbose", dest="verbose", action="store_true",
                      default=False, help="Show output of install script")
    parser.add_option("--winexe", dest="winexe", action="store_true",
                      default=False, help="use winexe to connect")
    parser.add_option("--interactive", dest="interactive",
                      help="Prompt for missing values like password", action="store_true", default=False)
    parser.add_option("--host", dest="host", help="remote host to connect to")
    parser.add_option("--username", dest="user", help=SUPPRESS_HELP)

    (opts, args) = parser.parse_args(sys.argv[2:])
    if opts.host:
        args.append(opts.host)

    if opts.ssh:
        method = "ssh"
    elif opts.winexe:
        method = "winexe"
    else:
        method = None

    if method == "winexe" and not opts.user:
        opts.user = "administrator"
    if method == "ssh" and not opts.user:
        opts.user = "root"
    if opts.interactive:
        if not opts.host:
            args.append(raw_input("Remote Host: "))
        if not opts.user:
            opts.user = raw_input("Username: ")
        if not opts.password or opts.password.lower() == 'ask':
            opts.password = getpass.getpass("Password: ")
        if method == "winexe" and not opts.domain:
            opts.domain = raw_input("Domain: ")

    if method == "winexe" and not opts.user:
        opts.user = "administrator"
    if method == "ssh" and not opts.user:
        opts.user = "root"

    if len(args) == 0:
        parser.error(
            "Please specify at least one host on the command line or use --interactive")

    for host in args:
        try:
            exit_status, stdout, stderr = okconfig.install_okagent(
                remote_host=host, domain=opts.domain, username=opts.user, password=opts.password, install_method=method)
        except okconfig.OKConfigError, e:
            exit_status = 128
            stdout = "Exception occured while running install_okagent"
            stderr = str(e)
        if exit_status == 0:
            print "Install script on %s successful" % (host, )
        else:
            print "Install script on %s failed with exit_status %s" % (host, exit_status)
            print "Output:"
            print stdout
            print "Errors:"
            print stderr
        if opts.verbose:
            print "Script output:"
            print stdout


def verify():
    """ Verify that current install of okconfig is functional. """
    result = okconfig.verify()
    print "Okconfig verification"
    print "---------------------"
    for k, v in result.items():
        if v is True:
            v = "success"
        else:
            v = "fail"
        print "* %-70s...%s" % (k, v)


def init():
    """ Initilize nagios configuration to use okconfig """
    print "Initilizating nagios to use okconfig..."
    maincfg = pynag.Parsers.Config().guess_cfg_file()
    template_dir = okconfig.config.template_directory
    dest_dir = okconfig.config.destination_directory
    print "Using configuration file %s" % (maincfg, )
    if not os.path.exists(maincfg):
        raise okconfig.OKConfigError(
            'Error: Nagios configuration file not found under %s' % (maincfg, ))
    print "Using Nagios configuration file %s" % (maincfg, )

    c = pynag.Parsers.config(cfg_file=maincfg)
    c._edit_static_file(
        attribute="cfg_dir", new_value=dest_dir, filename=maincfg)
    c._edit_static_file(
        attribute="cfg_dir", new_value=template_dir, filename=maincfg)
    if not os.path.exists(template_dir):
        print "Creating %s" % (template_dir, )
        os.makedirs(template_dir)
    if not os.path.exists(dest_dir):
        print "Creating %s" % (dest_dir,)
        os.makedirs(dest_dir)
    print 'All Done. Run "%s verify" to verify everything is working' % sys.argv[0]


def listhosts():
    """ List all available and active hosts in the system  """
    import pynag.Model
    hosts = pynag.Model.Host.objects.filter(register='1')
    header = "%-40s%s" % ('host name', 'groups')
    print header
    print "-" * len(header)
    for i in hosts:
        print "%-40s%s" % (i.host_name, i.hostgroups)
    footer = "--- %s hosts found" % (len(hosts))
    print footer,
    print "-" * (len(header) - len(footer))

def listhost():
    """ List a single host, useful for verifying if host is present in config """
    parser.usage = ''' %prog listhost <host_name>

Examples:
  # %prog listhost myhost.example.com

Run %prog listhost --help for full list of command line arguments.
'''
    parser.add_option("--host", dest="host",
                      help="Name of host to add")
    (opts, args) = parser.parse_args(sys.argv[2:])

    if not opts.host:
        if len(args) > 0:
            opts.host = args[0]
        else:
            parser.error("Please specify both host. See --help")

    import pynag.Model
    try:
        host = pynag.Model.Host.objects.get_by_shortname(opts.host)
        header = "%-40s%s" % ('attribute', 'value')
        print header
        print "-" * len(header)
        for key in host.keys():
            if key in ['id', 'meta']:
                continue
            print "%-40s%s" % (key, host.get_attribute(key))
        print "-" * len(header)
    except KeyError:
        print "Host not found."


def listtemplates():
    """ List all available oktemplates """
    for i in okconfig.get_templates():
        print i


def addgroup():
    """ Add new group to current nagios configuration files """
    parser.usage = ''' %prog addgroup <group_name> [options]

Examples:
  # %prog addgroup linuxservers --alias "My Linux Servers"

Run %prog addgroup --help for full list of command line arguments.
'''
    parser.add_option("--group", dest="group",
                      help="Name of group")
    parser.add_option("--force", dest="force", action="store_true",
                      default=False, help="Overwrite files if needed")
    parser.add_option("--alias", dest="alias", default=None,
                      help="Friendly name for this group")
    (opts, args) = parser.parse_args(sys.argv[2:])

    if opts.group:
        args.append(opts.group)

    if len(args) == 0:
        parser.error("Please specify at least one group on the command line.")

    for group in args:
        f = okconfig.addgroup(group_name=group, alias=opts.alias,
                              force=opts.force)
        for i in f:
            print "Saved", i


def addcontact():
    """ Add new contact to current nagios configuration files """
    parser.usage = ''' %prog addcontact <contact_name> [options]

Examples:
  # %prog addcontact "webmasters" --email "webmaster@example.com" 
  # %prog addcontact "admin" --email "admin@local" --alias "sysadmin group" 

Run %prog addcontact --help for full list of command line arguments.
'''
    parser.add_option("--contact", dest="contact", default=None,
                      help="contact_name")
    parser.add_option("--email", dest="email", default=None,
                      help="email for this contact")
    parser.add_option("--force", dest="force", action="store_true",
                      default=False, help="Overwrite files if needed")
    parser.add_option("--alias", dest="alias", default=None,
                      help="Friendly name for this contact")
    parser.add_option("--group", dest="group",
                      help="Put contact in the this contactgroup")

    (opts, args) = parser.parse_args(sys.argv[2:])

    if opts.contact:
        args.append(opts.contact)

    if len(args) == 0:
        parser.error(
            "Please specify a contact on the command line. See --help.")
    for contact in args:
        f = okconfig.addcontact(contact_name=contact, alias=opts.alias,
                                force=opts.force, group_name=opts.group, email=opts.email)
        for i in f:
            print "Saved", i


def addtemplate():
    """ Add new template to a current nagios host """
    parser.usage = ''' %prog addtemplate <host_name> <template_name>

Examples:
  # %prog addtemplate myhost.example.com --template linux
  # %prog addtemplate myhost.example.com --template brocade
  # %prog addtemplate myhost.example.com --template ciscoswitch

Run %prog addtemplate --help for full list of command line arguments.
'''
    parser.add_option("--host", dest="host",
                      help="Name of host to add")
    parser.add_option("--template", dest="template",
                      help="Name of template to use")
    parser.add_option("--force", dest="force", action="store_true",
                      default=False, help="Overwrite files if needed")
    (opts, args) = parser.parse_args(sys.argv[2:])

    if not opts.host:
        if len(args) > 0:
            opts.host = args[0]
    if not opts.template:
        if len(args) > 1:
            opts.template = args[1]
    if not opts.template or not opts.host:
        parser.error("Please specify both host and template. See --help")
    templates = opts.template.split(',')
    for template_name in templates:
        f = okconfig.addtemplate(
            host_name=opts.host, template_name=template_name,
            force=opts.force)
        for i in f:
            print "Saved", i


def addhost():
    """ Add new host to nagios configuration files """
    parser.usage = ''' %prog addhost <host_name> [options]

Examples:
  # %prog addhost switch1.example.com --template ciscoswitch --alias "My main switch"
  # %prog addhost www.example.com --template linux --group webservers
  # %prog addhost localhost2 --address 127.0.0.2 --alias "my other localhost"

Run %prog addhost --help for full list of command line arguments.
'''

    parser.add_option("--host", dest="host",
                      help="Name of host to add")
    parser.add_option("--group", dest="group", default="default",
                      help="Group this host belongs to",)
    parser.add_option("--template", dest="template",
                      help="Add specified templates to this host")
    parser.add_option("--force", dest="force", action="store_true",
                      default=False, help="Overwrite files if needed")
    parser.add_option("--use", dest="use", default=None,
                      help="Inherit settings from specified template")
    parser.add_option("--address", dest="address", default=None,
                      help="IP Address of host")
    parser.add_option("--alias", dest="alias", default=None,
                      help="Alias to use")
    parser.add_option("--host_template", dest="ht", default="host",
                      help="Name of host template to use")

    (opts, args) = parser.parse_args(sys.argv[2:])

    if opts.host:
        args.append(opts.host)

    if len(args) == 0:
        parser.error("Please specify at least one host on the command line.")

    try:
        templates = opts.template.split(',')
    except AttributeError:
        templates = ()
    for host in args:
        f = okconfig.addhost(
            host_name=host, address=opts.address, group_name=opts.group,
            use=opts.use, force=opts.force, templates=templates, host_template=opts.ht, alias=opts.alias)
        for i in f:
            print "Saved", i


def upgrade():
    """ Upgrade current nagios configuration in case okconfig tools have been updated """
    return okconfig.migration_tool.upgrade_okconfig()


def addservice():
    """ Add a single service check to host """
    parser.usage = ''' %prog addservice [attribute1=value1] [attribute2=value2] ...

Examples:
  # %prog addservice host_name=myhost use=okc-windows-updates service_description="My service check" 

Run %prog addservice --help for full list of command line arguments.
'''
    parser.add_option("--filename", dest="file", default=None,
                      help="Save the new service to specified filename")

    (opts, args) = parser.parse_args(sys.argv[2:])
    service_attributes = {}
    for arg in args:
        if not arg.find('=') > 0:
            parser.error(
                'Attribute not in the form of key=value: \n %s \n See --help')
        key, value = arg.split('=', 1)
        service_attributes[key] = value
    from pynag import Model
    new_service = Model.Service(filename=opts.file)
    for k, v in service_attributes.items():
        new_service[k] = v
    new_service.save()
    print "# Saved: \n%s \n# To %s" % (new_service, new_service.get_filename())


def removehost():
    """ Delete a host from current nagios configuration files """
    parser.usage = ''' %prog removehost <host_name> [options]

Examples:
  # %prog removehost "host.example.com" --remove-services

Run %prog removehost --help for full list of command line arguments.
'''
    parser.add_option("--host", dest="host", default=None,
                      help="Host to be removed")
    parser.add_option(
        "--remove-services", dest="recursive", action="store_true",
        default=False, help="Also remove all services that belong to this host")

    (opts, args) = parser.parse_args(sys.argv[2:])

    if opts.host:
        args.append(opts.host)

    if len(args) == 0:
        parser.error(
            "Please specify a host_name on the command line. See --help.")
    for i in args:
        okconfig.removehost(host_name=i, recursive=opts.recursive)
        print "Host %s removed." % (i,)


if __name__ == '__main__':
    try:
        parse_arguments()
        sys.exit(0)
    except Exception, e:
            print "Error: ", e
            sys.exit(1)
