Skip to content

Commit

Permalink
Merge pull request #87 from mbaldessari/fence_ironic
Browse files Browse the repository at this point in the history
Ironic fence agent
  • Loading branch information
oalbrigt committed Dec 13, 2016
2 parents a8dd2f5 + 9832452 commit 0b6d8c6
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
2 changes: 2 additions & 0 deletions configure.ac
Expand Up @@ -167,6 +167,7 @@ AC_PYTHON_MODULE(pywsman, 1)

## path to 3rd-party binaries
AC_PATH_PROG([IPMITOOL_PATH], [ipmitool], [/usr/bin/ipmitool])
AC_PATH_PROG([OPENSTACK_PATH], [openstack], [/usr/bin/openstack])
AC_PATH_PROG([AMTTOOL_PATH], [amttool], [/usr/bin/amttool])
AC_PATH_PROG([GNUTLSCLI_PATH], [gnutlscli], [/usr/bin/gnutls-cli])
AC_PATH_PROG([COROSYNC_CMAPCTL_PATH], [corosync-cmapctl], [/usr/sbin/corosync-cmapctl])
Expand Down Expand Up @@ -210,6 +211,7 @@ AC_SUBST([AGENTS_LIST])
AM_CONDITIONAL(BUILD_XENAPILIB, test $XENAPILIB -eq 1)

AC_SUBST([IPMITOOL_PATH])
AC_SUBST([OPENSTACK_PATH])
AC_SUBST([AMTTOOL_PATH])
AC_SUBST([COROSYNC_CMAPCTL_PATH])
AC_SUBST([SG_PERSIST_PATH])
Expand Down
136 changes: 136 additions & 0 deletions fence/agents/ironic/fence_ironic.py
@@ -0,0 +1,136 @@
#!@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

#BEGIN_VERSION_GENERATION
RELEASE_VERSION=""
REDHAT_COPYRIGHT=""
BUILD_DATE=""
#END_VERSION_GENERATION

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()
1 change: 1 addition & 0 deletions make/fencebuild.mk
Expand Up @@ -11,6 +11,7 @@ define gen_agent_from_py
-e 's#@''SBINDIR@#${sbindir}#g' \
-e 's#@''LIBEXECDIR@#${libexecdir}#g' \
-e 's#@''IPMITOOL_PATH@#${IPMITOOL_PATH}#g' \
-e 's#@''OPENSTACK_PATH@#${OPENSTACK_PATH}#g' \
-e 's#@''AMTTOOL_PATH@#${AMTTOOL_PATH}#g' \
-e 's#@''GNUTLSCLI_PATH@#${GNUTLSCLI_PATH}#g' \
-e 's#@''COROSYNC_CMAPCTL_PATH@#${COROSYNC_CMAPCTL_PATH}#g' \
Expand Down
113 changes: 113 additions & 0 deletions tests/data/metadata/fence_ironic.xml
@@ -0,0 +1,113 @@
<?xml version="1.0" ?>
<resource-agent name="fence_ironic" shortdesc="Fence agent for OpenStack's Ironic (Bare Metal as a service) service" >
<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.</longdesc>
<vendor-url>https://wiki.openstack.org/wiki/Ironic</vendor-url>
<parameters>
<parameter name="action" unique="0" required="1">
<getopt mixed="-o, --action=[action]" />
<content type="string" default="reboot" />
<shortdesc lang="en">Fencing action</shortdesc>
</parameter>
<parameter name="auth-url" unique="0" required="1">
<getopt mixed="--auth-url=[authurl]" />
<content type="string" />
<shortdesc lang="en">Keystone Admin Auth URL</shortdesc>
</parameter>
<parameter name="login" unique="0" required="1">
<getopt mixed="-l, --username=[name]" />
<content type="string" />
<shortdesc lang="en">Login name</shortdesc>
</parameter>
<parameter name="passwd" unique="0" required="0">
<getopt mixed="-p, --password=[password]" />
<content type="string" />
<shortdesc lang="en">Login password or passphrase</shortdesc>
</parameter>
<parameter name="passwd_script" unique="0" required="0">
<getopt mixed="-S, --password-script=[script]" />
<content type="string" />
<shortdesc lang="en">Script to run to retrieve password</shortdesc>
</parameter>
<parameter name="port" unique="0" required="1">
<getopt mixed="-n, --plug=[id]" />
<content type="string" />
<shortdesc lang="en">Physical plug number on device, UUID or identification of machine</shortdesc>
</parameter>
<parameter name="tenant-name" unique="0" required="0">
<getopt mixed="-t, --tenant-name=[tenant]" />
<content type="string" default="admin" />
<shortdesc lang="en">Keystone Admin Tenant</shortdesc>
</parameter>
<parameter name="verbose" unique="0" required="0">
<getopt mixed="-v, --verbose" />
<content type="boolean" />
<shortdesc lang="en">Verbose mode</shortdesc>
</parameter>
<parameter name="debug" unique="0" required="0">
<getopt mixed="-D, --debug-file=[debugfile]" />
<content type="string" />
<shortdesc lang="en">Write debug information to given file</shortdesc>
</parameter>
<parameter name="version" unique="0" required="0">
<getopt mixed="-V, --version" />
<content type="boolean" />
<shortdesc lang="en">Display version information and exit</shortdesc>
</parameter>
<parameter name="help" unique="0" required="0">
<getopt mixed="-h, --help" />
<content type="boolean" />
<shortdesc lang="en">Display help and exit</shortdesc>
</parameter>
<parameter name="separator" unique="0" required="0">
<getopt mixed="-C, --separator=[char]" />
<content type="string" default="," />
<shortdesc lang="en">Separator for CSV created by 'list' operation</shortdesc>
</parameter>
<parameter name="delay" unique="0" required="0">
<getopt mixed="--delay=[seconds]" />
<content type="second" default="0" />
<shortdesc lang="en">Wait X seconds before fencing is started</shortdesc>
</parameter>
<parameter name="login_timeout" unique="0" required="0">
<getopt mixed="--login-timeout=[seconds]" />
<content type="second" default="5" />
<shortdesc lang="en">Wait X seconds for cmd prompt after login</shortdesc>
</parameter>
<parameter name="openstack-path" unique="0" required="0">
<getopt mixed="--openstack-path=[path]" />
<content type="string" default="/usr/bin/openstack" />
<shortdesc lang="en">Path to the OpenStack binary</shortdesc>
</parameter>
<parameter name="power_timeout" unique="0" required="0">
<getopt mixed="--power-timeout=[seconds]" />
<content type="second" default="20" />
<shortdesc lang="en">Test X seconds for status change after ON/OFF</shortdesc>
</parameter>
<parameter name="power_wait" unique="0" required="0">
<getopt mixed="--power-wait=[seconds]" />
<content type="second" default="0" />
<shortdesc lang="en">Wait X seconds after issuing ON/OFF</shortdesc>
</parameter>
<parameter name="shell_timeout" unique="0" required="0">
<getopt mixed="--shell-timeout=[seconds]" />
<content type="second" default="3" />
<shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</shortdesc>
</parameter>
<parameter name="retry_on" unique="0" required="0">
<getopt mixed="--retry-on=[attempts]" />
<content type="integer" default="1" />
<shortdesc lang="en">Count of attempts to retry power on</shortdesc>
</parameter>
</parameters>
<actions>
<action name="on" automatic="0"/>
<action name="off" />
<action name="reboot" />
<action name="status" />
<action name="list" />
<action name="list-status" />
<action name="monitor" />
<action name="metadata" />
<action name="validate-all" />
</actions>
</resource-agent>

0 comments on commit 0b6d8c6

Please sign in to comment.