#!/usr/bin/python3 -tt

import atexit
import logging
import os
import re
import sys
sys.path.append("/usr/share/fence")
from fencing import *
from fencing import fail_usage, is_executable, run_command, run_delay

try:
	from shlex import quote
except ImportError:
	from pipes import quote

def get_name_or_uuid(options):
	return options["--uuid"] if "--uuid" in options else options["--plug"]

def get_power_status(_, options):
    output = ironic_run_command(options, "status")
    stdout = output[1]
    match = re.search('power[\\s]*([a-zA-Z]{2,3})', str(stdout))
    status = match.group(1) if match else None
    return status

def set_power_status(_, options):
    ironic_run_command(options, options["--action"])
    return

def get_devices_list(_, options):
    nodes = {}
    output = ironic_run_command(options, "list")
    stdout = output[1]
    for line in stdout.splitlines():
        uuid = "UUID"
        try:
            (uuid, name, state) = line.split(',')
        except ValueError:
            pass
        if "UUID" in uuid:
            continue # skip line header
        match = re.search('power[\\s]*([a-zA-Z]{2,3})', state)
        status = match.group(1) if match else None
        nodes[uuid] = (name, status)

    return nodes

def ironic_run_command(options, action, timeout=None):
    cmd = options["--openstack-path"] + " baremetal"
    env = os.environ.copy()
    # --username / -l
    if "--username" in options and len(options["--username"]) != 0:
        env["OS_USERNAME"] = options["--username"]

    # --password / -p
    if "--password" in options:
        env["OS_PASSWORD"] = options["--password"]

    # --tenant-name -t
    if "--tenant-name" in options:
        env["OS_TENANT_NAME"] = options["--tenant-name"]

    #  --auth-url
    if "--auth-url" in options:
        env["OS_AUTH_URL"] = options["--auth-url"]

    # --action / -o
    if action == "status":
        cmd += " show %s -c power_state --format value" % (get_name_or_uuid(options))
    elif action in ["on", "off"]:
        cmd += " power %s %s" % (action, get_name_or_uuid(options))
    elif action == "list":
        cmd += " list -c 'Instance UUID' -c Name -c 'Power State' --format csv --quote minimal"


    logging.debug("cmd -> %s" % cmd)
    return run_command(options, cmd, timeout, env)

def define_new_opts():
    all_opt["auth-url"] = {
        "getopt" : ":",
        "longopt" : "auth-url",
        "help" : "--auth-url=[authurl]            Auth URL",
        "required" : "1",
        "shortdesc" : "Keystone Admin Auth URL",
        "order": 1
    }
    all_opt["tenant-name"] = {
        "getopt" : "t:",
        "longopt" : "tenant-name",
        "help" : "-t, --tenant-name=[tenant]      Tenantname",
        "required" : "0",
        "shortdesc" : "Keystone Admin Tenant",
        "default": "admin",
        "order": 1
    }
    all_opt["openstack-path"] = {
        "getopt" : ":",
        "longopt" : "openstack-path",
        "help" : "--openstack-path=[path]       Path to openstack binary",
        "required" : "0",
        "shortdesc" : "Path to the OpenStack binary",
        "default" : "/usr/bin/openstack",
        "order": 200
    }

def main():
    atexit.register(atexit_handler)

    device_opt = ["login", "passwd", "port", "auth-url", "tenant-name", "openstack-path"]
    define_new_opts()

    options = check_input(device_opt, process_input(device_opt))

    docs = {}
    docs["shortdesc"] = "Fence agent for OpenStack's Ironic (Bare Metal as a service) service"
    docs["longdesc"] = "fence_ironic is a Power Fencing agent \
which can be used with machines controlled by the Ironic service. \
This agent calls the openstack CLI. \
WARNING! This fence agent is not intended for production use. Relying on a functional ironic service for fencing is not a good design choice."
    docs["vendorurl"] = "https://wiki.openstack.org/wiki/Ironic"
    show_docs(options, docs)

    run_delay(options)

    if not is_executable(options["--openstack-path"]):
        fail_usage("openstack tool not found or not accessible")

    result = fence_action(None, options, set_power_status, get_power_status, get_devices_list)
    sys.exit(result)

if __name__ == "__main__":
    main()
