summaryrefslogtreecommitdiffstats
path: root/agents/ironic/fence_ironic.py
blob: 66d84fca9ea799a739587f5a81f74734adb21f06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!@PYTHON@ -tt

import atexit
import logging
import os
import re
import sys
from pipes import quote
sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import *
from fencing import fail_usage, is_executable, run_command, run_delay

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" : "@OPENSTACK_PATH@",
        "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 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()