#!/usr/bin/env python
#
# Copyright (C) 2004 Norwegian University of Science and Technology
# Copyright (C) 2006, 2011 UNINETT AS
#
# This file is part of Network Administration Visualized (NAV).
#
# NAV is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by
# the Free Software Foundation.
#
# This program 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 NAV. If not, see <http://www.gnu.org/licenses/>.
#
"""This command is your interface to start, stop and query NAV services.

Usage: nav [command] [service ...]

The selected command will be applied to all known services, unless you
specify a list of services after the command.
Available commands are:

  start   -- Start services.
  stop    -- Stop services.
  status  -- Query the status of services.
  info    -- Display information about/description of services.
  list    -- Display list of known services.

  version -- Displays which version of NAV you are running.
"""

import sys
import os
import os.path
import getopt
import textwrap

try:
    from nav.startstop import ServiceRegistry, CommandFailedError
except ImportError:
    print >> sys.stderr, (
        "Fatal error: Could not find the nav.startstop module.\n"
        "Is your PYTHONPATH environment correctly set up?")
    sys.exit(1)

try:
    services = ServiceRegistry()
except OSError, e:
    print >> sys.stderr, ("A problem occurred, which prevented this command "
                          "from running.")
    print >> sys.stderr, str(e)
    sys.exit(1)

allow_non_root = False

def main(args):
    try:
        opts, args = getopt.getopt(args, '', ['nonroot'])
    except getopt.GetoptError, error:
        print >> sys.stderr, error
        usage()
        sys.exit(1)

    for opt, val in opts:
        if opt == '--nonroot':
            global allow_non_root
            allow_non_root = True

    if len(args) == 0:
        usage()
        sys.exit(1)

    command = args.pop(0)
    if len(args) == 0:
        query_list = services.keys()
        query_list.sort()
    else:
        query_list = args

    # Use reflection to decide whether the command is known, and to
    # call it if it is.
    self = sys.modules[__name__]
    command_func_name = 'c_' + command
    if hasattr(self, command_func_name):
        command_func = self.__dict__[command_func_name]
        command_func(query_list)
    else:
        print >> sys.stderr, "Unknown command: " + command

def checkroot():
    if not allow_non_root and os.geteuid() != 0:
        print >> sys.stderr, "You should be root to run this command."
        sys.exit(10)

def service_iterator(query_list, func):
    """Iterate through a list of service names, look up each service instance
    and call func using this instance as its argument.
    """
    unknowns = []
    for name in query_list:
        if name in services:
            func(services[name])
        else:
            unknowns.append(name)
    if len(unknowns):
        sys.stderr.write("Unknown services: %s\n" % " ".join(unknowns))

def action_iterator(query_list, action, ok_string, fail_string):
    """Iterate through a list of service names, performing an action on each
    of them.
    """
    failed = []
    unknowns = []
    errors = []

    any_ok = False
    for name in query_list:
        if name in services:
            method = eval('services[name].%s' % action)
            try:
                if method(silent=True):
                    if not any_ok:
                        any_ok = True
                        print ok_string + ":",
                    print name,
                    sys.stdout.flush()
                else:
                    failed.append(name)
            except CommandFailedError, error:
                errors.append((name, error))
        else:
            unknowns.append(name)
    if any_ok:
        print

    if len(failed):
        print "%s: %s" % (fail_string, " ".join(failed))
    if len(unknowns):
        print "Unknown: %s" % " ".join(unknowns)
    if len(errors):
        print "Errors:",
        print " ".join(["%s (%s)" % error for error in errors])

def c_info(query_list):
    def helper(service):
        kind = service.__class__.__name__
        if kind.endswith("Service"):
            kind = kind[:-7].lower()
        output = "%-18s %8s: %s" % (service.name, "(%s)" % kind, service.info)
        print '\n'.join(textwrap.wrap(output, width=79,
                                      subsequent_indent=" "*29))
    service_iterator(query_list, helper)

def c_list(query_list):
    def helper(service):
        print service.name
    service_iterator(query_list, helper)

def c_start(query_list):
    checkroot()
    action_iterator(query_list, "start", "Starting", "Failed")

def c_stop(query_list):
    checkroot()
    action_iterator(query_list, "stop", "Stopping", "Failed")

def c_restart(query_list):
    checkroot()
    c_stop(query_list)
    c_start(query_list)

def c_status(query_list):
    checkroot()
    action_iterator(query_list, "isUp", "Up", "Down")

def c_version(query_list):
    from nav import buildconf
    print "NAV %s" % buildconf.VERSION

def usage():
    """ Print a usage screen to stderr."""
    print >> sys.stderr, __doc__

##############
# begin here #
##############
if __name__ == '__main__':
    main(sys.argv[1:])
