#!/usr/bin/python3 -tt
# -*- coding: utf-8 -*-

import sys
import atexit
sys.path.append("/usr/share/fence")
from fencing import *
from fencing import fail, EC_STATUS, EC_LOGIN_DENIED, EC_INVALID_PRIVILEGES, run_delay
import requests
import ast
import urllib3
import json
import logging

from requests.exceptions import ConnectionError


###################################################################################
# Inner functions


def authorize_and_get_cookie(skala_ip, login, password, options):
    URL0 = proto + str(skala_ip) + '/api/0/auth'
    cred = {
        "login" : str(login),
        "password" : str(password)
    }
    
    try:
        with requests.Session() as session:
            session.post(url=URL0, data=cred, verify=ssl_verify)
            cookie = session.cookies.get_dict()
    except:
        logging.exception('Exception occured.')
        fail(EC_LOGIN_DENIED)
    if 'api_token' in cookie:
        return cookie
    else:
        fail(EC_LOGIN_DENIED)


def logout(skala_ip):
    URL1 = proto + str(skala_ip) + '/api/0/logout'
    
    try:
        with requests.Session() as session:
            session.post(url=URL1, verify=ssl_verify, cookies=cookie)
    except:
        ## Logout; we do not care about result as we will end in any case
        pass


def get_vm_id(skala_ip, uuid, options, cookie):
    URL2 = proto + str(skala_ip) + '/api/0/vm'
    parameters = {
        "uuid": str(uuid)
    }
    
    vm_info = requests.get(url=URL2, verify=ssl_verify, params=parameters, cookies=cookie)
    jvm_info = vm_info.json()
    if jvm_info["vm_list"]["items"] == []:
        raise NameError('Can not find VM by uuid.')
    logging.debug("VM_INFO:\n{}".format(json.dumps(vm_info.json(), indent=4, sort_keys=True)))
    return jvm_info["vm_list"]["items"][0]["vm_id"]


def vm_task(skala_ip, vm_id, command, options, cookie):

    # command(str) – Command for vm: ‘vm_start’, ‘vm_stop’, ‘vm_restart’,
    # ‘vm_suspend’, ‘vm_resume’, ‘vm_pause’, ‘vm_reset’
    # main_request_id(TaskId) – Parent task id
    # graceful(bool) – vm_stop command parameter, graceful or not, default
    # false - *args[0]
    # force(bool) – vm_stop command parameter, force stop or not, default
    # false - *args[1]
    
    if "--graceful" in options:
        graceful = True
    else:
        graceful = False
    if "--force" in options:
        force = True
    else:
        force = False

    URL3 = proto + str(skala_ip) + '/api/0/vm/' + str(vm_id) + '/task'

    logging.debug("vm_task skala_ip: " + str(skala_ip))
    logging.debug("vm_task vm_id: " + str(vm_id))
    logging.debug("vm_task command: " + str(command))
    logging.debug("vm_task cookie: " + str(cookie))


    def checking(vm_id, command, graceful, force):
        firstcondition = type(vm_id) is int
        secondcondition = command in ['vm_start', 'vm_stop', 'vm_restart', 'vm_suspend', 'vm_resume', 'vm_pause', 'vm_reset']
        thirdcondition = type(graceful) is bool
        fourthcondition = type(force) is bool
        return firstcondition * secondcondition * thirdcondition * fourthcondition

    if not checking(vm_id, command, graceful, force):
        print('Wrong parameters! \n'
              'command(str) – Command for vm: ‘vm_start’, ‘vm_stop’, \n'
              '‘vm_restart’,‘vm_suspend’, ‘vm_resume’, ‘vm_pause’, ‘vm_reset’ \n'
              'graceful(bool) – vm_stop command parameter, graceful or not, default false \n'
              'force(bool) – vm_stop command parameter, force stop or not, default false \n'
              )
    else:
        parameters = {
            "command": command,
            "graceful": graceful,
            "force": force
        }

    with requests.Session() as session:
        response = session.post(url=URL3, params=parameters, verify=ssl_verify, cookies=cookie)
    if response.status_code != 200:
        raise Exception('Invalid response code from server: {}.'.format(response.status_code))
    return

######################################################################################

def get_power_status(conn, options):
    state = {"RUNNING": "on", "PAUSED": "on", "STOPPED": "off", "SUSPENDED": "off", "ERROR": "off", "DELETED": "off",
             "CREATING": "off", "FAILED_TO_CREATE": "off", "NODE_OFFLINE": "off", "STARTING": "off", "STOPPING": "on"}

    URL4 = proto + options["--ip"] + '/api/0/vm/'
    parameters = {
        "uuid": str(options["--plug"])
    }

    vm_info = requests.get(url=URL4, params=parameters, verify=ssl_verify, cookies=cookie)
    jvm_info = vm_info.json()
    if jvm_info["vm_list"]["items"] == []:
        raise NameError('Can not find VM by uuid.')
    logging.debug("VM_INFO:\n{}".format(json.dumps(vm_info.json(), indent=4, sort_keys=True)))
    status_v = jvm_info["vm_list"]["items"][0]["status"]
    if status_v not in state:
        raise Exception('Unknown VM state: {}.'.format(status_v))
    return state[status_v]


def set_power_status(conn, options):
    action = {
        "on" : "vm_start",
        "reboot": "vm_restart",
        "off" : "vm_stop"
        }

    vm_id_v = get_vm_id(options["--ip"], options["--plug"], options, cookie)
    vm_task(options["--ip"], vm_id_v, action[options["--action"]], options, cookie)
    return


def get_list(conn, options):
    outlets = {}
    URL5 = proto + options["--ip"] + '/api/0/vm'
    
    vm_info = requests.get(url=URL5, verify=ssl_verify, cookies=cookie)
    jvm_info = vm_info.json()
    list_jvm = jvm_info["vm_list"]["items"]
    for elem in list_jvm:
        outlets[elem["name"]] = (elem["uuid"], None)
    return outlets


def define_new_opts():
    all_opt["graceful"] = {
            "getopt" : "",
            "longopt" : "graceful",
            "help" : "--graceful                     vm_stop command parameter, graceful stop or not, default false", 
            "required" : "0",
            "shortdesc" : "vm_stop command parameter, graceful stop or not, default false",
            "order" : 1}

    all_opt["force"] = {
            "getopt" : "",
            "longopt" : "force",
            "help" : "--force                        vm_stop command parameter, force stop or not, default false",
            "required" : "0",
            "shortdesc" : "vm_stop command parameter, force stop or not, default false", 
            "order" : 1}


def main():
    global cookie, proto, ssl_verify
    define_new_opts()
    device_opt = ["ipaddr", "login", "passwd", "port", "web", "ssl", "verbose", "graceful", "force"]
    
    atexit.register(atexit_handler)
    options = check_input(device_opt, process_input(device_opt))

    docs = {}
    docs["shortdesc"] = "Skala-R Fence agent"
    docs["longdesc"] = "A fence agent for Skala-R."
    docs["vendorurl"] = "https://www.skala-r.ru/"
    show_docs(options, docs)
    options["eol"] = "\r"
    
    run_delay(options)
    
    proto = "https://"
    if "--ssl" in options or "--ssl-secure" in options:
        ssl_verify = True
    elif "--ssl-insecure" in options:
        ssl_verify = False
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    else:
        proto = "http://"
        ssl_verify = False
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    cookie = authorize_and_get_cookie(options["--ip"], options["--username"], options["--password"], options)
    atexit.register(logout, options["--ip"])
    
    logging.debug("OPTIONS: " + str(options) + "\n")
    
    try:
        result = fence_action(None, options, set_power_status, get_power_status, get_list)
        sys.exit(result)
    except Exception:
        logging.exception('Exception occured.')
        fail(EC_STATUS)

if __name__ == "__main__":
    main()
