diff --git a/SOURCES/bz1497072-fence_compute-fence_evacuate-Instance-HA-OSP12.patch b/SOURCES/bz1497072-fence_compute-fence_evacuate-Instance-HA-OSP12.patch
new file mode 100644
index 0000000..6ca484d
--- /dev/null
+++ b/SOURCES/bz1497072-fence_compute-fence_evacuate-Instance-HA-OSP12.patch
@@ -0,0 +1,1119 @@
+diff -uNr a/fence/agents/compute/fence_compute.py b/fence/agents/compute/fence_compute.py
+--- a/fence/agents/compute/fence_compute.py 2017-09-27 15:01:34.974642469 +0200
++++ b/fence/agents/compute/fence_compute.py 2017-09-27 15:24:57.482819900 +0200
+@@ -18,173 +18,115 @@
+ #END_VERSION_GENERATION
+
+ override_status = ""
+-nova = None
+
+ EVACUABLE_TAG = "evacuable"
+ TRUE_TAGS = ['true']
+
+-def get_power_status(_, options):
+- global override_status
+-
+- status = "unknown"
+- logging.debug("get action: " + options["--action"])
++def get_power_status(connection, options):
+
+ if len(override_status):
+ logging.debug("Pretending we're " + override_status)
+ return override_status
+
+- if nova:
++ status = "unknown"
++ logging.debug("get action: " + options["--action"])
++
++ if connection:
+ try:
+- services = nova.services.list(host=options["--plug"])
++ services = connection.services.list(host=options["--plug"], binary="nova-compute")
+ for service in services:
+- logging.debug("Status of %s is %s" % (service.binary, service.state))
+- if service.binary == "nova-compute":
+- if service.state == "up":
+- status = "on"
+- elif service.state == "down":
+- status = "off"
+- else:
+- logging.debug("Unknown status detected from nova: " + service.state)
+- break
++ logging.debug("Status of %s on %s is %s, %s" % (service.binary, options["--plug"], service.state, service.status))
++ if service.state == "up" and service.status == "enabled":
++ # Up and operational
++ status = "on"
++
++ elif service.state == "down" and service.status == "disabled":
++ # Down and fenced
++ status = "off"
++
++ elif service.state == "down":
++ # Down and requires fencing
++ status = "failed"
++
++ elif service.state == "up":
++ # Up and requires unfencing
++ status = "running"
++ else:
++ logging.warning("Unknown status detected from nova for %s: %s, %s" % (options["--plug"], service.state, service.status))
++ status = "%s %s" % (service.state, service.status)
++ break
+ except requests.exception.ConnectionError as err:
+ logging.warning("Nova connection failed: " + str(err))
++ logging.debug("Final status of %s is %s" % (options["--plug"], status))
+ return status
+
+-# NOTE(sbauza); We mimic the host-evacuate module since it's only a contrib
+-# module which is not stable
+-def _server_evacuate(server, on_shared_storage):
+- success = False
+- error_message = ""
+- try:
+- logging.debug("Resurrecting instance: %s" % server)
+- (response, dictionary) = nova.servers.evacuate(server=server, on_shared_storage=on_shared_storage)
+-
+- if response == None:
+- error_message = "No response while evacuating instance"
+- elif response.status_code == 200:
+- success = True
+- error_message = response.reason
+- else:
+- error_message = response.reason
+-
+- except Exception as e:
+- error_message = "Error while evacuating instance: %s" % e
+-
+- return {
+- "uuid": server,
+- "accepted": success,
+- "reason": error_message,
+- }
+-
+-def _is_server_evacuable(server, evac_flavors, evac_images):
+- if server.flavor.get('id') in evac_flavors:
+- return True
+- if server.image.get('id') in evac_images:
+- return True
+- logging.debug("Instance %s is not evacuable" % server.image.get('id'))
+- return False
+-
+-def _get_evacuable_flavors():
+- result = []
+- flavors = nova.flavors.list()
+- # Since the detailed view for all flavors doesn't provide the extra specs,
+- # we need to call each of the flavor to get them.
+- for flavor in flavors:
+- tag = flavor.get_keys().get(EVACUABLE_TAG)
+- if tag and tag.strip().lower() in TRUE_TAGS:
+- result.append(flavor.id)
+- return result
+-
+-def _get_evacuable_images():
+- result = []
+- images = nova.images.list(detailed=True)
+- for image in images:
+- if hasattr(image, 'metadata'):
+- tag = image.metadata.get(EVACUABLE_TAG)
+- if tag and tag.strip().lower() in TRUE_TAGS:
+- result.append(image.id)
+- return result
+-
+-def _host_evacuate(options):
+- result = True
+- images = _get_evacuable_images()
+- flavors = _get_evacuable_flavors()
+- servers = nova.servers.list(search_opts={'host': options["--plug"], 'all_tenants': 1 })
+-
+- if options["--instance-filtering"] == "False":
+- logging.debug("Not evacuating anything")
+- evacuables = []
+- elif len(flavors) or len(images):
+- logging.debug("Filtering images and flavors: %s %s" % (repr(flavors), repr(images)))
+- # Identify all evacuable servers
+- logging.debug("Checking %s" % repr(servers))
+- evacuables = [server for server in servers
+- if _is_server_evacuable(server, flavors, images)]
+- logging.debug("Evacuating %s" % repr(evacuables))
+- else:
+- logging.debug("Evacuating all images and flavors")
+- evacuables = servers
+-
+- if options["--no-shared-storage"] != "False":
+- on_shared_storage = False
+- else:
+- on_shared_storage = True
+-
+- for server in evacuables:
+- logging.debug("Processing %s" % server)
+- if hasattr(server, 'id'):
+- response = _server_evacuate(server.id, on_shared_storage)
+- if response["accepted"]:
+- logging.debug("Evacuated %s from %s: %s" %
+- (response["uuid"], options["--plug"], response["reason"]))
+- else:
+- logging.error("Evacuation of %s on %s failed: %s" %
+- (response["uuid"], options["--plug"], response["reason"]))
+- result = False
+- else:
+- logging.error("Could not evacuate instance: %s" % server.to_dict())
+- # Should a malformed instance result in a failed evacuation?
+- # result = False
+- return result
++def get_power_status_simple(connection, options):
++ status = get_power_status(connection, options)
++ if status in [ "off" ]:
++ return status
++ return "on"
+
+ def set_attrd_status(host, status, options):
+ logging.debug("Setting fencing status for %s to %s" % (host, status))
+ run_command(options, "attrd_updater -p -n evacuate -Q -N %s -U %s" % (host, status))
+
+-def set_power_status(_, options):
+- global override_status
+-
+- override_status = ""
+- logging.debug("set action: " + options["--action"])
++def get_attrd_status(host, options):
++ (status, pipe_stdout, pipe_stderr) = run_command(options, "attrd_updater -p -n evacuate -Q -N %s" % (host))
++ fields = pipe_stdout.split('"')
++ if len(fields) > 6:
++ return fields[5]
++ logging.debug("Got %s: o:%s e:%s n:%d" % (status, pipe_stdout, pipe_stderr, len(fields)))
++ return ""
++
++def set_power_status_on(connection, options):
++ # Wait for any evacuations to complete
++ while True:
++ current = get_attrd_status(options["--plug"], options)
++ if current in ["no", ""]:
++ logging.info("Evacuation complete for: %s '%s'" % (options["--plug"], current))
++ break
++ else:
++ logging.info("Waiting for %s to complete evacuations: %s" % (options["--plug"], current))
++ time.sleep(2)
+
+- if not nova:
+- return
++ status = get_power_status(connection, options)
++ # Should we do it for 'failed' too?
++ if status in [ "off", "running", "failed" ]:
++ try:
++ # Forcing the host back up
++ logging.info("Forcing nova-compute back up on "+options["--plug"])
++ connection.services.force_down(options["--plug"], "nova-compute", force_down=False)
++ logging.info("Forced nova-compute back up on "+options["--plug"])
++ except Exception as e:
++ # In theory, if force_down=False fails, that's for the exact
++ # same possible reasons that below with force_down=True
++ # eg. either an incompatible version or an old client.
++ # Since it's about forcing back to a default value, there is
++ # no real worries to just consider it's still okay even if the
++ # command failed
++ logging.warn("Exception from attempt to force "
++ "host back up via nova API: "
++ "%s: %s" % (e.__class__.__name__, e))
++
++ # Forcing the service back up in case it was disabled
++ logging.info("Enabling nova-compute on "+options["--plug"])
++ connection.services.enable(options["--plug"], 'nova-compute')
+
+- if options["--action"] == "on":
+- if get_power_status(_, options) != "on":
+- # Forcing the service back up in case it was disabled
+- nova.services.enable(options["--plug"], 'nova-compute')
+- try:
+- # Forcing the host back up
+- nova.services.force_down(
+- options["--plug"], "nova-compute", force_down=False)
+- except Exception as e:
+- # In theory, if force_down=False fails, that's for the exact
+- # same possible reasons that below with force_down=True
+- # eg. either an incompatible version or an old client.
+- # Since it's about forcing back to a default value, there is
+- # no real worries to just consider it's still okay even if the
+- # command failed
+- logging.info("Exception from attempt to force "
+- "host back up via nova API: "
+- "%s: %s" % (e.__class__.__name__, e))
+- else:
+- # Pretend we're 'on' so that the fencing library doesn't loop forever waiting for the node to boot
+- override_status = "on"
++ # Pretend we're 'on' so that the fencing library doesn't loop forever waiting for the node to boot
++ override_status = "on"
++ elif status not in ["on"]:
++ # Not safe to unfence, don't waste time looping to see if the status changes to "on"
++ options["--power-timeout"] = "0"
++
++def set_power_status_off(connection, options):
++ status = get_power_status(connection, options)
++ if status in [ "off" ]:
+ return
+
++ connection.services.disable(options["--plug"], 'nova-compute')
+ try:
+- nova.services.force_down(
++ # Until 2.53
++ connection.services.force_down(
+ options["--plug"], "nova-compute", force_down=True)
+ except Exception as e:
+ # Something went wrong when we tried to force the host down.
+@@ -198,7 +140,7 @@
+ "%s: %s" % (e.__class__.__name__, e))
+ # need to wait for nova to update its internal status or we
+ # cannot call host-evacuate
+- while get_power_status(_, options) != "off":
++ while get_power_status(connection, options) not in ["off"]:
+ # Loop forever if need be.
+ #
+ # Some callers (such as Pacemaker) will have a timer
+@@ -206,47 +148,55 @@
+ logging.debug("Waiting for nova to update its internal state for %s" % options["--plug"])
+ time.sleep(1)
+
+- if not _host_evacuate(options):
+- sys.exit(1)
++ set_attrd_status(options["--plug"], "yes", options)
++
++def set_power_status(connection, options):
++ global override_status
+
+- return
++ override_status = ""
++ logging.debug("set action: " + options["--action"])
++
++ if not connection:
++ return
+
++ if options["--action"] in ["off", "reboot"]:
++ set_power_status_off(connection, options)
++ else:
++ set_power_status_on(connection, options)
++ logging.debug("set action passed: " + options["--action"])
++ sys.exit(0)
+
+-def fix_domain(options):
++def fix_domain(connection, options):
+ domains = {}
+ last_domain = None
+
+- if nova:
++ if connection:
+ # Find it in nova
+
+- hypervisors = nova.hypervisors.list()
+- for hypervisor in hypervisors:
+- shorthost = hypervisor.hypervisor_hostname.split('.')[0]
++ services = connection.services.list(binary="nova-compute")
++ for service in services:
++ shorthost = service.host.split('.')[0]
+
+- if shorthost == hypervisor.hypervisor_hostname:
++ if shorthost == service.host:
+ # Nova is not using FQDN
+ calculated = ""
+ else:
+ # Compute nodes are named as FQDN, strip off the hostname
+- calculated = hypervisor.hypervisor_hostname.replace(shorthost+".", "")
+-
+- domains[calculated] = shorthost
++ calculated = service.host.replace(shorthost+".", "")
+
+ if calculated == last_domain:
+ # Avoid complaining for each compute node with the same name
+ # One hopes they don't appear interleaved as A.com B.com A.com B.com
+- logging.debug("Calculated the same domain from: %s" % hypervisor.hypervisor_hostname)
++ logging.debug("Calculated the same domain from: %s" % service.host)
++ continue
+
+- elif "--domain" in options and options["--domain"] == calculated:
+- # Supplied domain name is valid
+- return
++ domains[calculated] = service.host
++ last_domain = calculated
+
+- elif "--domain" in options:
++ if "--domain" in options and options["--domain"] != calculated:
+ # Warn in case nova isn't available at some point
+ logging.warning("Supplied domain '%s' does not match the one calculated from: %s"
+- % (options["--domain"], hypervisor.hypervisor_hostname))
+-
+- last_domain = calculated
++ % (options["--domain"], service.host))
+
+ if len(domains) == 0 and "--domain" not in options:
+ logging.error("Could not calculate the domain names used by compute nodes in nova")
+@@ -254,9 +204,9 @@
+ elif len(domains) == 1 and "--domain" not in options:
+ options["--domain"] = last_domain
+
+- elif len(domains) == 1:
+- logging.error("Overriding supplied domain '%s' does not match the one calculated from: %s"
+- % (options["--domain"], hypervisor.hypervisor_hostname))
++ elif len(domains) == 1 and options["--domain"] != last_domain:
++ logging.error("Overriding supplied domain '%s' as it does not match the one calculated from: %s"
++ % (options["--domain"], domains[last_domain]))
+ options["--domain"] = last_domain
+
+ elif len(domains) > 1:
+@@ -264,47 +214,49 @@
+ % (options["--domain"], repr(domains)))
+ sys.exit(1)
+
+-def fix_plug_name(options):
++ return last_domain
++
++def fix_plug_name(connection, options):
+ if options["--action"] == "list":
+ return
+
+ if "--plug" not in options:
+ return
+
+- fix_domain(options)
+- short_plug = options["--plug"].split('.')[0]
+- logging.debug("Checking target '%s' against calculated domain '%s'"% (options["--plug"], options["--domain"]))
+-
+- if "--domain" not in options:
++ calculated = fix_domain(connection, options)
++ if calculated is None or "--domain" not in options:
+ # Nothing supplied and nova not available... what to do... nothing
+ return
+
+- elif options["--domain"] == "":
++ short_plug = options["--plug"].split('.')[0]
++ logging.debug("Checking target '%s' against calculated domain '%s'"% (options["--plug"], calculated))
++
++ if options["--domain"] == "":
+ # Ensure any domain is stripped off since nova isn't using FQDN
+ options["--plug"] = short_plug
+
+- elif options["--domain"] in options["--plug"]:
+- # Plug already contains the domain, don't re-add
++ elif options["--plug"].endswith(options["--domain"]):
++ # Plug already uses the domain, don't re-add
+ return
+
+ else:
+ # Add the domain to the plug
+ options["--plug"] = short_plug + "." + options["--domain"]
+
+-def get_plugs_list(_, options):
++def get_plugs_list(connection, options):
+ result = {}
+
+- if nova:
+- hypervisors = nova.hypervisors.list()
+- for hypervisor in hypervisors:
+- longhost = hypervisor.hypervisor_hostname
++ if connection:
++ services = connection.services.list(binary="nova-compute")
++ for service in services:
++ longhost = service.host
+ shorthost = longhost.split('.')[0]
+ result[longhost] = ("", None)
+ result[shorthost] = ("", None)
+ return result
+
+ def create_nova_connection(options):
+- global nova
++ nova = None
+
+ try:
+ from novaclient import client
+@@ -330,41 +282,42 @@
+ if clientargs:
+ # OSP < 11
+ # ArgSpec(args=['version', 'username', 'password', 'project_id', 'auth_url'],
+- # varargs=None,
+- # keywords='kwargs', defaults=(None, None, None, None))
++ # varargs=None,
++ # keywords='kwargs', defaults=(None, None, None, None))
+ nova = client.Client(version,
+- options["--username"],
+- options["--password"],
+- options["--tenant-name"],
+- options["--auth-url"],
+- insecure=options["--insecure"],
+- region_name=options["--region-name"],
+- endpoint_type=options["--endpoint-type"],
+- http_log_debug=options.has_key("--verbose"))
++ options["--username"],
++ options["--password"],
++ options["--tenant-name"],
++ options["--auth-url"],
++ insecure=options["--insecure"],
++ region_name=options["--region-name"],
++ endpoint_type=options["--endpoint-type"],
++ http_log_debug=options.has_key("--verbose"))
+ else:
+ # OSP >= 11
+ # ArgSpec(args=['version'], varargs='args', keywords='kwargs', defaults=None)
+ nova = client.Client(version,
+- username=options["--username"],
+- password=options["--password"],
+- tenant_name=options["--tenant-name"],
+- auth_url=options["--auth-url"],
+- insecure=options["--insecure"],
+- region_name=options["--region-name"],
+- endpoint_type=options["--endpoint-type"],
+- http_log_debug=options.has_key("--verbose"))
++ username=options["--username"],
++ password=options["--password"],
++ tenant_name=options["--tenant-name"],
++ auth_url=options["--auth-url"],
++ insecure=options["--insecure"],
++ region_name=options["--region-name"],
++ endpoint_type=options["--endpoint-type"],
++ http_log_debug=options.has_key("--verbose"))
+
+ try:
+ nova.hypervisors.list()
+- return
++ return nova
+
+ except NotAcceptable as e:
+ logging.warning(e)
+
+ except Exception as e:
+ logging.warning("Nova connection failed. %s: %s" % (e.__class__.__name__, e))
+-
++
+ logging.warning("Couldn't obtain a supported connection to nova, tried: %s\n" % repr(versions))
++ return None
+
+ def define_new_opts():
+ all_opt["endpoint_type"] = {
+@@ -448,11 +401,23 @@
+ "order": 5,
+ }
+
++def set_multi_power_fn(connection, options, set_power_fn, get_power_fn, retry_attempts=1):
++ for _ in range(retry_attempts):
++ set_power_fn(connection, options)
++ time.sleep(int(options["--power-wait"]))
++
++ for _ in range(int(options["--power-timeout"])):
++ if get_power_fn(connection, options) != options["--action"]:
++ time.sleep(1)
++ else:
++ return True
++ return False
++
+ def main():
+ global override_status
+ atexit.register(atexit_handler)
+
+- device_opt = ["login", "passwd", "tenant_name", "auth_url", "fabric_fencing",
++ device_opt = ["login", "passwd", "tenant_name", "auth_url", "fabric_fencing",
+ "no_login", "no_password", "port", "domain", "no_shared_storage", "endpoint_type",
+ "record_only", "instance_filtering", "insecure", "region_name"]
+ define_new_opts()
+@@ -472,30 +437,28 @@
+
+ run_delay(options)
+
+- create_nova_connection(options)
++ logging.debug("Running "+options["--action"])
++ connection = create_nova_connection(options)
+
+- fix_plug_name(options)
+- if options["--record-only"] in [ "1", "True", "true", "Yes", "yes"]:
+- if options["--action"] == "on":
+- set_attrd_status(options["--plug"], "no", options)
+- sys.exit(0)
+-
+- elif options["--action"] in ["off", "reboot"]:
+- set_attrd_status(options["--plug"], "yes", options)
+- sys.exit(0)
++ if options["--action"] in ["off", "on", "reboot", "status"]:
++ fix_plug_name(connection, options)
+
+- elif options["--action"] in ["monitor", "status"]:
+- sys.exit(0)
+
+- if options["--action"] in ["off", "reboot"]:
+- # Pretend we're 'on' so that the fencing library will always call set_power_status(off)
+- override_status = "on"
+-
+- if options["--action"] == "on":
+- # Pretend we're 'off' so that the fencing library will always call set_power_status(on)
+- override_status = "off"
++ if options["--action"] in ["reboot"]:
++ options["--action"]="off"
++
++ if options["--action"] in ["off", "on"]:
++ # No status first, call our own version
++ result = not set_multi_power_fn(connection, options, set_power_status, get_power_status_simple,
++ 1 + int(options["--retry-on"]))
++ elif options["--action"] in ["monitor"]:
++ result = 0
++ else:
++ result = fence_action(connection, options, set_power_status, get_power_status_simple, get_plugs_list, None)
+
+- result = fence_action(None, options, set_power_status, get_power_status, get_plugs_list, None)
++ logging.debug("Result for "+options["--action"]+": "+repr(result))
++ if result == None:
++ result = 0
+ sys.exit(result)
+
+ if __name__ == "__main__":
+diff -uNr a/fence/agents/compute/fence_evacuate.py b/fence/agents/compute/fence_evacuate.py
+--- a/fence/agents/compute/fence_evacuate.py 1970-01-01 01:00:00.000000000 +0100
++++ b/fence/agents/compute/fence_evacuate.py 2017-09-27 15:25:54.234304769 +0200
+@@ -0,0 +1,366 @@
++#!/usr/bin/python -tt
++
++import sys
++import time
++import atexit
++import logging
++import inspect
++import requests.exceptions
++
++sys.path.append("@FENCEAGENTSLIBDIR@")
++from fencing import *
++from fencing import fail_usage, is_executable, run_command, run_delay
++
++EVACUABLE_TAG = "evacuable"
++TRUE_TAGS = ['true']
++
++def get_power_status(connection, options):
++
++ status = "unknown"
++ logging.debug("get action: " + options["--action"])
++
++ if connection:
++ try:
++ services = connection.services.list(host=options["--plug"], binary="nova-compute")
++ for service in services:
++ logging.debug("Status of %s is %s, %s" % (service.binary, service.state, service.status))
++ if service.state == "up" and service.status == "enabled":
++ # Up and operational
++ status = "on"
++
++ elif service.state == "down" and service.status == "disabled":
++ # Down and fenced
++ status = "off"
++
++ elif service.state == "down":
++ # Down and requires fencing
++ status = "failed"
++
++ elif service.state == "up":
++ # Up and requires unfencing
++ status = "running"
++ else:
++ logging.warning("Unknown status detected from nova for %s: %s, %s" % (options["--plug"], service.state, service.status))
++ status = "%s %s" % (service.state, service.status)
++ break
++ except requests.exception.ConnectionError as err:
++ logging.warning("Nova connection failed: " + str(err))
++ return status
++
++# NOTE(sbauza); We mimic the host-evacuate module since it's only a contrib
++# module which is not stable
++def _server_evacuate(connection, server, on_shared_storage):
++ success = False
++ error_message = ""
++ try:
++ logging.debug("Resurrecting instance: %s" % server)
++ (response, dictionary) = connection.servers.evacuate(server=server, on_shared_storage=on_shared_storage)
++
++ if response == None:
++ error_message = "No response while evacuating instance"
++ elif response.status_code == 200:
++ success = True
++ error_message = response.reason
++ else:
++ error_message = response.reason
++
++ except Exception as e:
++ error_message = "Error while evacuating instance: %s" % e
++
++ return {
++ "uuid": server,
++ "accepted": success,
++ "reason": error_message,
++ }
++
++def _is_server_evacuable(server, evac_flavors, evac_images):
++ if server.flavor.get('id') in evac_flavors:
++ return True
++ if hasattr(server.image, 'get'):
++ if server.image.get('id') in evac_images:
++ return True
++ logging.debug("Instance %s is not evacuable" % server.image.get('id'))
++ return False
++
++def _get_evacuable_flavors(connection):
++ result = []
++ flavors = connection.flavors.list()
++ # Since the detailed view for all flavors doesn't provide the extra specs,
++ # we need to call each of the flavor to get them.
++ for flavor in flavors:
++ tag = flavor.get_keys().get(EVACUABLE_TAG)
++ if tag and tag.strip().lower() in TRUE_TAGS:
++ result.append(flavor.id)
++ return result
++
++def _get_evacuable_images(connection):
++ result = []
++ images = []
++ if hasattr(connection, "images"):
++ images = connection.images.list(detailed=True)
++ elif hasattr(connection, "glance"):
++ # OSP12+
++ images = connection.glance.list()
++
++ for image in images:
++ if hasattr(image, 'metadata'):
++ tag = image.metadata.get(EVACUABLE_TAG)
++ if tag and tag.strip().lower() in TRUE_TAGS:
++ result.append(image.id)
++ elif hasattr(image, 'tags'):
++ # OSP12+
++ if EVACUABLE_TAG in image.tags:
++ result.append(image.id)
++ return result
++
++def _host_evacuate(connection, options):
++ result = True
++ images = _get_evacuable_images(connection)
++ flavors = _get_evacuable_flavors(connection)
++ servers = connection.servers.list(search_opts={'host': options["--plug"], 'all_tenants': 1 })
++
++ if options["--instance-filtering"] == "False":
++ logging.debug("Not evacuating anything")
++ evacuables = []
++ elif len(flavors) or len(images):
++ logging.debug("Filtering images and flavors: %s %s" % (repr(flavors), repr(images)))
++ # Identify all evacuable servers
++ logging.debug("Checking %s" % repr(servers))
++ evacuables = [server for server in servers
++ if _is_server_evacuable(server, flavors, images)]
++ logging.debug("Evacuating %s" % repr(evacuables))
++ else:
++ logging.debug("Evacuating all images and flavors")
++ evacuables = servers
++
++ if options["--no-shared-storage"] != "False":
++ on_shared_storage = False
++ else:
++ on_shared_storage = True
++
++ for server in evacuables:
++ logging.debug("Processing %s" % server)
++ if hasattr(server, 'id'):
++ response = _server_evacuate(connection, server.id, on_shared_storage)
++ if response["accepted"]:
++ logging.debug("Evacuated %s from %s: %s" %
++ (response["uuid"], options["--plug"], response["reason"]))
++ else:
++ logging.error("Evacuation of %s on %s failed: %s" %
++ (response["uuid"], options["--plug"], response["reason"]))
++ result = False
++ else:
++ logging.error("Could not evacuate instance: %s" % server.to_dict())
++ # Should a malformed instance result in a failed evacuation?
++ # result = False
++ return result
++
++def set_attrd_status(host, status, options):
++ logging.debug("Setting fencing status for %s to %s" % (host, status))
++ run_command(options, "attrd_updater -p -n evacuate -Q -N %s -U %s" % (host, status))
++
++def set_power_status(connection, options):
++ logging.debug("set action: " + options["--action"])
++
++ if not connection:
++ return
++
++ if options["--action"] == "off" and not _host_evacuate(options):
++ sys.exit(1)
++
++ sys.exit(0)
++
++def get_plugs_list(connection, options):
++ result = {}
++
++ if connection:
++ services = connection.services.list(binary="nova-compute")
++ for service in services:
++ longhost = service.host
++ shorthost = longhost.split('.')[0]
++ result[longhost] = ("", None)
++ result[shorthost] = ("", None)
++ return result
++
++def create_nova_connection(options):
++ nova = None
++
++ try:
++ from novaclient import client
++ from novaclient.exceptions import NotAcceptable
++ except ImportError:
++ fail_usage("Nova not found or not accessible")
++
++ versions = [ "2.11", "2" ]
++ for version in versions:
++ clientargs = inspect.getargspec(client.Client).varargs
++
++ # Some versions of Openstack prior to Ocata only
++ # supported positional arguments for username,
++ # password and tenant.
++ #
++ # Versions since Ocata only support named arguments.
++ #
++ # So we need to use introspection to figure out how to
++ # create a Nova client.
++ #
++ # Happy days
++ #
++ if clientargs:
++ # OSP < 11
++ # ArgSpec(args=['version', 'username', 'password', 'project_id', 'auth_url'],
++ # varargs=None,
++ # keywords='kwargs', defaults=(None, None, None, None))
++ nova = client.Client(version,
++ options["--username"],
++ options["--password"],
++ options["--tenant-name"],
++ options["--auth-url"],
++ insecure=options["--insecure"],
++ region_name=options["--region-name"],
++ endpoint_type=options["--endpoint-type"],
++ http_log_debug=options.has_key("--verbose"))
++ else:
++ # OSP >= 11
++ # ArgSpec(args=['version'], varargs='args', keywords='kwargs', defaults=None)
++ nova = client.Client(version,
++ username=options["--username"],
++ password=options["--password"],
++ tenant_name=options["--tenant-name"],
++ auth_url=options["--auth-url"],
++ insecure=options["--insecure"],
++ region_name=options["--region-name"],
++ endpoint_type=options["--endpoint-type"],
++ http_log_debug=options.has_key("--verbose"))
++
++ try:
++ nova.hypervisors.list()
++ return nova
++
++ except NotAcceptable as e:
++ logging.warning(e)
++
++ except Exception as e:
++ logging.warning("Nova connection failed. %s: %s" % (e.__class__.__name__, e))
++
++ logging.warning("Couldn't obtain a supported connection to nova, tried: %s\n" % repr(versions))
++ return None
++
++def define_new_opts():
++ all_opt["endpoint_type"] = {
++ "getopt" : "e:",
++ "longopt" : "endpoint-type",
++ "help" : "-e, --endpoint-type=[endpoint] Nova Endpoint type (publicURL, internalURL, adminURL)",
++ "required" : "0",
++ "shortdesc" : "Nova Endpoint type",
++ "default" : "internalURL",
++ "order": 1,
++ }
++ all_opt["tenant_name"] = {
++ "getopt" : "t:",
++ "longopt" : "tenant-name",
++ "help" : "-t, --tenant-name=[tenant] Keystone Admin Tenant",
++ "required" : "0",
++ "shortdesc" : "Keystone Admin Tenant",
++ "default" : "",
++ "order": 1,
++ }
++ all_opt["auth_url"] = {
++ "getopt" : "k:",
++ "longopt" : "auth-url",
++ "help" : "-k, --auth-url=[url] Keystone Admin Auth URL",
++ "required" : "0",
++ "shortdesc" : "Keystone Admin Auth URL",
++ "default" : "",
++ "order": 1,
++ }
++ all_opt["region_name"] = {
++ "getopt" : "",
++ "longopt" : "region-name",
++ "help" : "--region-name=[region] Region Name",
++ "required" : "0",
++ "shortdesc" : "Region Name",
++ "default" : "",
++ "order": 1,
++ }
++ all_opt["insecure"] = {
++ "getopt" : "",
++ "longopt" : "insecure",
++ "help" : "--insecure Explicitly allow agent to perform \"insecure\" TLS (https) requests",
++ "required" : "0",
++ "shortdesc" : "Allow Insecure TLS Requests",
++ "default" : "False",
++ "order": 2,
++ }
++ all_opt["domain"] = {
++ "getopt" : "d:",
++ "longopt" : "domain",
++ "help" : "-d, --domain=[string] DNS domain in which hosts live, useful when the cluster uses short names and nova uses FQDN",
++ "required" : "0",
++ "shortdesc" : "DNS domain in which hosts live",
++ "order": 5,
++ }
++ all_opt["instance_filtering"] = {
++ "getopt" : "",
++ "longopt" : "instance-filtering",
++ "help" : "--instance-filtering Allow instances created from images and flavors with evacuable=true to be evacuated (or all if no images/flavors have been tagged)",
++ "required" : "0",
++ "shortdesc" : "Allow instances to be evacuated",
++ "default" : "True",
++ "order": 5,
++ }
++ all_opt["no_shared_storage"] = {
++ "getopt" : "",
++ "longopt" : "no-shared-storage",
++ "help" : "--no-shared-storage Disable functionality for shared storage",
++ "required" : "0",
++ "shortdesc" : "Disable functionality for dealing with shared storage",
++ "default" : "False",
++ "order": 5,
++ }
++
++def main():
++ atexit.register(atexit_handler)
++
++ device_opt = ["login", "passwd", "tenant_name", "auth_url",
++ "no_login", "no_password", "port", "domain", "no_shared_storage", "endpoint_type",
++ "instance_filtering", "insecure", "region_name"]
++ define_new_opts()
++ all_opt["shell_timeout"]["default"] = "180"
++
++ options = check_input(device_opt, process_input(device_opt))
++
++ docs = {}
++ docs["shortdesc"] = "Fence agent for the automatic resurrection of OpenStack compute instances"
++ docs["longdesc"] = "Used to reschedule flagged instances"
++ docs["vendorurl"] = ""
++
++ show_docs(options, docs)
++
++ run_delay(options)
++
++ connection = create_nova_connection(options)
++
++ # Un-evacuating a server doesn't make sense
++ if options["--action"] in ["on"]:
++ logging.error("Action %s is not supported by this agent" % (options["--action"]))
++ sys.exit(1)
++
++ if options["--action"] in ["off", "reboot"]:
++ status = get_power_status(connection, options)
++ if status != "off":
++ logging.error("Cannot resurrect instances from %s in state '%s'" % (options["--plug"], status))
++ sys.exit(1)
++
++ elif not _host_evacuate(connection, options):
++ logging.error("Resurrection of instances from %s failed" % (options["--plug"]))
++ sys.exit(1)
++
++ logging.info("Resurrection of instances from %s complete" % (options["--plug"]))
++ sys.exit(0)
++
++ result = fence_action(connection, options, set_power_status, get_power_status, get_plugs_list, None)
++ sys.exit(result)
++
++if __name__ == "__main__":
++ main()
+diff -uNr a/fence/agents/compute/Makefile.am b/fence/agents/compute/Makefile.am
+--- a/fence/agents/compute/Makefile.am 2017-09-27 15:01:34.844643650 +0200
++++ b/fence/agents/compute/Makefile.am 2017-09-27 15:57:50.963839738 +0200
+@@ -1,14 +1,14 @@
+ MAINTAINERCLEANFILES = Makefile.in
+
+-TARGET = fence_compute
++TARGET = fence_compute fence_evacuate
+
+-SRC = $(TARGET).py
++SRC = $(TARGET:=.py)
+
+ EXTRA_DIST = $(SRC)
+
+ sbin_SCRIPTS = $(TARGET)
+
+-man_MANS = $(TARGET).8
++man_MANS = $(TARGET:=.8)
+
+ FENCE_TEST_ARGS = -l test -p test -n 1
+
+diff -uNr a/tests/data/metadata/fence_evacuate.xml b/tests/data/metadata/fence_evacuate.xml
+--- a/tests/data/metadata/fence_evacuate.xml 1970-01-01 01:00:00.000000000 +0100
++++ b/tests/data/metadata/fence_evacuate.xml 2017-09-27 15:28:10.978063549 +0200
+@@ -0,0 +1,163 @@
++
++
++Used to reschedule flagged instances
++
++
++
++
++
++ Keystone Admin Tenant
++
++
++
++
++ Keystone Admin Auth URL
++
++
++
++
++ Physical plug number, name of virtual machine or UUID
++
++
++
++
++ Script to retrieve password
++
++
++
++
++ Region Name
++
++
++
++
++ Login password or passphrase
++
++
++
++
++ Nova Endpoint type
++
++
++
++
++ Fencing Action
++
++
++
++
++ Login Name
++
++
++
++
++ Physical plug number, name of virtual machine or UUID
++
++
++
++
++ Login Name
++
++
++
++
++ Login password or passphrase
++
++
++
++
++ Script to retrieve password
++
++
++
++
++ Allow Insecure TLS Requests
++
++
++
++
++ DNS domain in which hosts live
++
++
++
++
++ Allow instances to be evacuated
++
++
++
++
++ Disable functionality for dealing with shared storage
++
++
++
++
++ Verbose mode
++
++
++
++
++ Write debug information to given file
++
++
++
++
++ Write debug information to given file
++
++
++
++
++ Display version information and exit
++
++
++
++
++ Display help and exit
++
++
++
++
++ Separator for CSV created by operation list
++
++
++
++
++ Wait X seconds after issuing ON/OFF
++
++
++
++
++ Wait X seconds for cmd prompt after login
++
++
++
++
++ Wait X seconds before fencing is started
++
++
++
++
++ Test X seconds for status change after ON/OFF
++
++
++
++
++ Wait X seconds for cmd prompt after issuing command
++
++
++
++
++ Count of attempts to retry power on
++
++
++
++
++
++
++
++
++
++
++
++
++
++
diff --git a/SOURCES/bz1497241-fence_compute-fence_scsi-fix-parameters.patch b/SOURCES/bz1497241-fence_compute-fence_scsi-fix-parameters.patch
new file mode 100644
index 0000000..2d24cf4
--- /dev/null
+++ b/SOURCES/bz1497241-fence_compute-fence_scsi-fix-parameters.patch
@@ -0,0 +1,18 @@
+--- a/fence/agents/lib/fencing.py.py 2017-09-19 12:29:04.158438532 +0200
++++ b/fence/agents/lib/fencing.py.py 2017-09-19 12:48:22.252509114 +0200
+@@ -705,11 +705,12 @@
+ continue
+
+ (name, value) = (line + "=").split("=", 1)
+- name = name.replace("-", "_");
+ value = value[:-1]
+
+- if name in mapping_longopt_names:
+- name = mapping_longopt_names[name]
++ if name.replace("-", "_") in mapping_longopt_names:
++ name = mapping_longopt_names[name.replace("-", "_")]
++ elif name.replace("_", "-") in mapping_longopt_names:
++ name = mapping_longopt_names[name.replace("_", "-")]
+
+ if avail_opt.count(name) == 0 and name in ["nodename"]:
+ continue
diff --git a/SPECS/fence-agents.spec b/SPECS/fence-agents.spec
index be476e8..b2b851b 100644
--- a/SPECS/fence-agents.spec
+++ b/SPECS/fence-agents.spec
@@ -16,7 +16,7 @@
Name: fence-agents
Summary: Fence Agents for Red Hat Cluster
Version: 4.0.11
-Release: 66%{?alphatag:.%{alphatag}}%{?dist}.1
+Release: 66%{?alphatag:.%{alphatag}}%{?dist}.3
License: GPLv2+ and LGPLv2+
Group: System Environment/Base
URL: http://sourceware.org/cluster/wiki/
@@ -137,6 +137,8 @@ Patch112: bz1426693-1-fence_compute-project_id-to-project_name.patch
Patch113: bz1426693-2-fence_compute-project_id-to-project_name.patch
Patch114: bz1459199-fence_vmware_soap-fix-for-selfsigned-certificate.patch
Patch115: bz1479851-fence_compute-fence_scsi-fix-parameters.patch
+Patch116: bz1497072-fence_compute-fence_evacuate-Instance-HA-OSP12.patch
+Patch117: bz1497241-fence_compute-fence_scsi-fix-parameters.patch
%if 0%{?rhel}
%global supportedagents apc apc_snmp bladecenter brocade cisco_mds cisco_ucs compute drac5 eaton_snmp emerson eps hpblade ibmblade ifmib ilo ilo_moonshot ilo_mp ilo_ssh intelmodular ipdu ipmilan mpath kdump rhevm rsa rsb sbd scsi vmware_soap wti
@@ -282,6 +284,8 @@ BuildRequires: autoconf automake libtool
%patch113 -p1 -b .bz1426693-2
%patch114 -p1 -b .bz1459199
%patch115 -p1 -b .bz1479851
+%patch116 -p1 -b .bz1497072
+%patch117 -p1 -b .bz1497241
%build
./autogen.sh
@@ -455,7 +459,9 @@ The fence-agents-compute package contains a fence agent for Nova compute nodes.
%files compute
%defattr(-,root,root,-)
%{_sbindir}/fence_compute
+%{_sbindir}/fence_evacuate
%{_mandir}/man8/fence_compute.8*
+%{_mandir}/man8/fence_evacuate.8*
%package drac5
License: GPLv2+ and LGPLv2+
@@ -871,6 +877,15 @@ The fence-agents-zvm package contains a fence agent for z/VM hypervisors
%endif
%changelog
+* Fri Sep 29 2017 Oyvind Albrigtsen - 4.0.11-66.3
+- fence_compute/fence_scsi: fix issue with some parameters
+ (for ABI compatibility)
+ Resolves: rhbz#1497241
+
+* Fri Sep 29 2017 Oyvind Albrigtsen - 4.0.11-66.2
+- fence_compute/fence_evacuate: changes to support Instance HA on OSP12
+ Resolves: rhbz#1497072
+
* Thu Aug 10 2017 Oyvind Albrigtsen - 4.0.11-66.1
- fence_compute/fence_scsi: fix issue with some parameters
Resolves: rhbz#1479851