#!/usr/bin/env python

""" cloud_admin - tool to control settings and VMs in cloud scheduler."""
import requests
import sys
from optparse import OptionParser
import logging
import logging.handlers
import platform
import urllib

import cloudscheduler.utilities as utilities 
import cloudscheduler.cloud_management as cloud_management
import cloudscheduler.config as config

def main(argv=None):

    # Parse command line options
    parser = OptionParser()
    parser.add_option("-f", "--config-file", dest="config_file", metavar="FILE",
                      help="Designate a Cloud Sceduler config file")
    parser.add_option("-s", "--server-hostname", dest="server_hostname",
                      metavar="HOSTNAME",
                      help="Pick a specific machine's Cloud Scheduler"
                           "information server")
    parser.add_option("-P", "--port", dest="port", metavar="PORT",
                      help="Pick a custom port to connect to Cloud Scheduler"
                           "information server")
    parser.add_option("-a", "--all", action="store_true", 
                      dest="all", default=False, 
                      help="Apply Operation to All [VMs, Jobs, etc]")
    parser.add_option("-k", "--kill-vm", dest="killvm", action="store_true", 
                      default=False, help="Kill a VM, must specify cloud and VM ID.")
    parser.add_option("-c", "--cloud", dest="cloud_name", metavar="NAME", 
                      help="Cloud name for operation.")
    parser.add_option("-n", "--vm-id", dest="vm_id", metavar="ID", 
                      help="The VM ID of VM for operation.")
    parser.add_option("-b", "--number", dest="number", metavar="NUM", 
                      help="Specify number of virtual machine to perform operation on.")
    parser.add_option("-p", "--refresh-proxy", metavar="PROXYTYPE", action="store",
                      dest="proxy_refresh", type="choice", choices=['vm','job'], help="Refresh Proxies for user")
    parser.add_option("-u", "--user", dest="user", action="store", metavar="USER", help="User to perform operation on")
    parser.add_option("-r", "--reconfig", dest="reconfig", action="store_true", default=False, help="Reload cloud_resources.conf")
    parser.add_option("-l", "--log-level", dest="log_level", action="store", type="choice", choices=['VERBOSE', 'DEBUG', 'WARNING', 'ERROR', 'INFO', 'CRITICAL'], help="Change the logging level")
    parser.add_option("-e", "--enable", dest="enable", action="store", metavar="NAME", help="Enable cloud NAME")
    parser.add_option("-d", "--disable", dest="disable", action="store", metavar="NAME", help="Disable cloud NAME")
    parser.add_option("-m", "--remove", dest="remove", action="store_true", help="Remove a VM from Cloudscheduler management, leave VM running. Requires -c name, and -n id or -a all.")
    parser.add_option("-q", "--quick-exit", dest="quick", action="store_true", default=False, help="Toggle the quick shutdown flag of CS.")
    parser.add_option("-i", "--reload-limits", dest="limits", action="store_true", default=False, help="Reload the user limits file.")
    parser.add_option("-j", "--list-limits", dest="list_limits", action="store_true", default=False, help="List the current user limits.")
    parser.add_option("-o", "--force-retire", dest="force_retire", action="store_true", default=False, help="Force Retire a VM. Requires -c name, and -n id or -a all.")
    parser.add_option("-x", "--reset-vm-state", dest="reset_state", action="store_true", default=False, help="Reset the override state of VM. Requires -c name and -n id.")
    parser.add_option("-t", "--reload-target-alias", dest="alias", action="store_true", default=False, help="Reload the Target Cloud Alias file.")
    parser.add_option("-y", "--list-alias", dest="list_alias", action="store_true", default=False, help="List the current cloud aliases.")
    parser.add_option("-v", "--vm-allocation", dest="vm_allocation", action="store", metavar="NUM", help="Specify a new vm slot value for cloud")
    parser.add_option("-C", "--comment", dest="comment", action="store", default=None, metavar="COM", help="Comment of why command being executed. Required for all commands")

    try:
        (cli_options, _) = parser.parse_args()
    except Exception as e:
        print "Failed to parse options does some value need to be wrapped in quotes?"
        print e
        sys.exit(-1)

    # Initialize config
    if cli_options.config_file:
        config.setup(cli_options.config_file)
    else:
        config.setup()

    # Set up logging
    log = logging.getLogger("cloudadmin")
    logging._srcfile = None
    logging.logProcesses = 0
    log.setLevel(utilities.LEVELS[config.log_level])
    log_formatter = logging.Formatter(config.log_format)
    if config.log_stdout:
        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(log_formatter)
        log.addHandler(stream_handler)

    if config.log_location_cloud_admin:
        file_handler = None
        if config.log_max_size:
            file_handler = logging.handlers.RotatingFileHandler(
                                            config.log_location_cloud_admin,
                                            maxBytes=config.log_max_size)
        else:
            try:
                file_handler = logging.handlers.WatchedFileHandler(
                                            config.log_location_cloud_admin,)
            except AttributeError:
                # Python 2.5 doesn't support WatchedFileHandler
                file_handler = logging.handlers.RotatingFileHandler(
                                            config.log_location_cloud_admin,)

        file_handler.setFormatter(log_formatter)
        log.addHandler(file_handler)

    if not config.log_location_cloud_admin and not config.log_stdout:
        null_handler = utilities.NullHandler()
        log.addHandler(null_handler)

    def _check_for_comment():
            if config.admin_log_comments:
                if not cli_options.comment:
                    print "Admin comments being enforced. Please use -C 'reason for command' when performing this option."
                    log.debug("Attempted to perform action without required comment.")
                    return False
                else:
                    log.info("Comment: %s" % cli_options.comment)
                    return True
            else:
                return True
    # Get port to connect to info server.
    #   Precedence: -p argument, then from config module
    if cli_options.port:
        server_port = cli_options.port
    else:
        server_port = config.admin_server_port

    if cli_options.server_hostname:
        server_hostname = cli_options.server_hostname
    else:
        server_hostname = platform.node()

    base_url = "http://{}:{}/".format(server_hostname, server_port)

    # Connect to admin server
    try:
        if cli_options.cloud_name:
            cloud_name = urllib.quote(cli_options.cloud_name, safe='')
        if cli_options.vm_id:
            vm_id = urllib.quote(cli_options.vm_id, safe='')

        if cli_options.killvm and _check_for_comment():
            if cli_options.cloud_name:
                if cli_options.vm_id:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms/' + vm_id, data={'action': 'shutdown'}).text
                    log.info("Shutting down VM: %s on: %s" % (cli_options.vm_id, cli_options.cloud_name))
                elif cli_options.all:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms', data={'action': 'shutdown', 'count': 'all'}).text
                    log.info("Shutting down all VMs on cloud: %s" % cli_options.cloud_name)
                elif cli_options.number:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms', data={'action': 'shutdown', 'count': cli_options.number}).text
                    log.info("Shutting down %s VMs on cloud: %s" % (cli_options.number, cli_options.cloud_name))
                else:
                    print "Must provide a -n [VM ID], -a to kill all VMs, or -b [count] to kill a set number on cloud."
                    log.debug("Kill VMs called without valid vm id or all flag.")
            else:
                print "Must Provide the cloud name to kill VMs on and the VM ID, count, or all flag."
                log.debug("Kill VMs called without valid cloud name.")
        elif cli_options.remove and _check_for_comment():
            if cli_options.cloud_name:
                if cli_options.vm_id:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms/' + vm_id, data={'action': 'remove'}).text
                    log.info("Removing VM entry: %s on: %s" % (cli_options.vm_id, cli_options.cloud_name))
                elif cli_options.all:
                    print base_url + 'clouds/' + cloud_name + '/vms'
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms', data={'action': 'remove', 'count': 'all'}).text
                    log.info("Removing all VM entries from Cloud Scheduler on cloud: %s" % cli_options.cloud_name)
                else:
                    print "Must provide a -n [VM ID] or -a to remove all VMs on this cloud."
                    log.debug("Remove VMs called without vm id or all flag.")
            else:
                print "Must Provide the cloud name to remove VMs on and the VM ID or all flag."
                log.debug("Remove VMs called without cloud name.")
        elif cli_options.force_retire and _check_for_comment():
            if cli_options.cloud_name:
                if cli_options.vm_id:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms/' + vm_id, data={'action': 'force_retire'}).text
                    log.info("Force retiring VM: %s on cloud: %s" % (cli_options.vm_id, cli_options.cloud_name))
                elif cli_options.all:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms', data={'action': 'force_retire', 'count': 'all'}).text
                    log.info("Force retiring all VMs on: %s" % cli_options.cloud_name)
                elif cli_options.number:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms', data={'action': 'force_retire', 'count': cli_options.number}).text
                    log.info("Force retiring %s VMs, on cloud: %s" %(cli_options.number, cli_options.cloud_name))
                else:
                    print "Must provide a -n [VM ID], -a (all), or -b [count] to force retire all or some VMs on this cloud."
                    log.debug("Force retire called on cloud: %s, missing valid vm or option flag." % cli_options.cloud_name)
            else:
                print "Must Provide the cloud name to force retire VMs on and the VM ID, all or count flag."
                log.debug("Force retire called without valid cloud name.")
        elif cli_options.reset_state and _check_for_comment():
            if cli_options.cloud_name:
                if cli_options.vm_id:
                    print requests.put(base_url + 'clouds/' + cloud_name + '/vms/' + vm_id, data={'action': 'reset_override_state'}).text
                    log.info("Reset state of vm: %s on cloud: %s" % (cli_options.vm_id, cli_options.cloud_name))
                else:
                    print "Must provide -n [VM ID] to reset state of VM."
                    log.debug("Reset state attempted without valid vm id.")
            else:
                print "Must provide the cloud name and VM ID to reset override state on."
                log.debug("reset state attempted without valid cloud name.")
        elif cli_options.enable:
            cloud_name = urllib.quote(cli_options.enable, safe='')
            print requests.put(base_url + 'clouds/' + cloud_name, data={'action': 'enable'}).text
            log.info("Cloud: %s enabled." % cli_options.enable)
        elif cli_options.disable:
            cloud_name = urllib.quote(cli_options.disable, safe='')
            print requests.put(base_url + 'clouds/' + cloud_name, data={'action': 'disable'}).text
            log.info("Cloud: %s disabled." % cli_options.disable)
        elif cli_options.reconfig:
            print "Reconfig Disabled - use 'quickrestart'"
            log.debug("Call to disabled reconfig option. service quickrestart suggested.")
            #print s.cloud_resources_reconfig()
        elif cli_options.limits and _check_for_comment():
            if requests.post(base_url + 'user-limits').text == 'True':
                print "User-Limit file reloaded."
                log.info("Reloaded the user limlt file.")
            else:
                print "No Limits set - list empty or problem parsing file."
                log.debug("Problem reloading user limits, check format, path, permissions.")
        elif cli_options.list_limits:
            print requests.get(base_url + 'user-limits').text
            log.debug("Listing the user limit file")
        elif cli_options.alias and _check_for_comment():
            if requests.post(base_url + 'cloud-aliases').text == 'True':
                print "Target Cloud Alias file reloaded."
                log.info("Reloaded the cloud_alias file")
            else:
                print "Failed to load alias file - check json format, path, or permissions."
                log.debug("Failed to reload cloud alias file, check format, path, permissions.")
        elif cli_options.list_alias:
            print requests.get(base_url + 'cloud-aliases').text
            log.debug("Listing cloud_alias contents")
        elif cli_options.log_level and _check_for_comment():
            print requests.put(base_url, data={'log_level': cli_options.log_level}).text
            log.info("Changed logging level to: %s" % cli_options.log_level)
        elif cli_options.proxy_refresh and _check_for_comment():
            if cli_options.user:
                user = urllib.quote(cli_options.user, safe='')
                if cli_options.proxy_refresh.startswith('v'):
                    print "Refreshing VM proxies for user %s." % cli_options.user
                    requests.post(base_url + 'users/' + user, data={'refresh': 'vm_proxy'})
                    log.info("Refreshing VM proxies for user %s." % cli_options.user)
                elif cli_options.proxy_refresh.startswith('j'):
                        print "Refreshing Job proxies for user %s." % cli_options.user
                        requests.post(base_url + 'users/' + user, data={'refresh': 'job_proxy'})
                        log.info("Refresh job proxies for user %s" % cli_options.user)
                else:
                    print "Unsupported Proxy type, enter 'job' or 'vm'."
                    log.debug("Incorrect proxy type entered: valid options are job or vm")
            else:
                print "Must Specify a user with -u or --user."
                log.debug("Attempting to refresh proxy, missing user flag.")
        elif cli_options.quick and _check_for_comment():
            print requests.post(base_url, data={'action': 'quick_shutdown'}).text
            print "Toggled Quick shutdown flag."
            log.info("Toggled quick shutdown flag.")
        elif cli_options.vm_allocation and _check_for_comment():
            if cli_options.cloud_name:
                print 'Adjusting vm_slots on %s to %s' % (cli_options.cloud_name, cli_options.vm_allocation)
                print requests.put(base_url + 'clouds/' + cloud_name, data={'allocations': cli_options.vm_allocation}).text
                log.info("Attempting to adjust vm allocation on %s. Target: %s" % (cli_options.cloud_name, cli_options.vm_allocation))
            else:
                print "Change vm slot of cloud need '-c cloudname' for cloud to change allocation of"
                log.debug("Attempting to adjust vm allocation but missing cloud name.")
        else:
            print "Run cloud_admin -h for help."
            log.debug("Invoked with no arguments or other error.")


    except requests.exceptions.ConnectionError:
        print "%s: couldn't connect to cloud scheduler at %s on port %s."\
               % (sys.argv[0], server_hostname, server_port)
        print "Is the cloud scheduler running on port %s?" % server_port
    except:
        print "Unexpected error: ", sys.exc_info()[0], sys.exc_info()[1]
        print "Is the cloud scheduler running on port %s?" % server_port



if __name__ == "__main__":
    sys.exit(main())
