diff --git a/agents/openstack/fence_openstack.py b/agents/openstack/fence_openstack.py index c2d9df16..36b353b5 100755 --- a/agents/openstack/fence_openstack.py +++ b/agents/openstack/fence_openstack.py @@ -1,296 +1,302 @@ #!@PYTHON@ -tt import atexit import logging import sys import urllib3 sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, run_delay try: from novaclient import client from novaclient.exceptions import Conflict, NotFound except ImportError: pass urllib3.disable_warnings(urllib3.exceptions.SecurityWarning) def translate_status(instance_status): if instance_status == "ACTIVE": return "on" elif instance_status == "SHUTOFF": return "off" return "unknown" def get_nodes_list(conn, options): logging.info("Running %s action", options["--action"]) result = {} response = conn.servers.list(detailed=True) if response is not None: for item in response: instance_id = item.id instance_name = item.name instance_status = item.status result[instance_id] = (instance_name, translate_status(instance_status)) return result def get_power_status(conn, options): logging.info("Running %s action on %s", options["--action"], options["--plug"]) server = None try: server = conn.servers.get(options["--plug"]) except NotFound as e: fail_usage("Failed: Not Found: " + str(e)) if server is None: fail_usage("Server %s not found", options["--plug"]) state = server.status status = translate_status(state) logging.info("get_power_status: %s (state: %s)" % (status, state)) return status def set_power_status(conn, options): logging.info("Running %s action on %s", options["--action"], options["--plug"]) action = options["--action"] server = None try: server = conn.servers.get(options["--plug"]) except NotFound as e: fail_usage("Failed: Not Found: " + str(e)) if server is None: fail_usage("Server %s not found", options["--plug"]) if action == "on": logging.info("Starting instance " + server.name) try: server.start() except Conflict as e: fail_usage(e) logging.info("Called start API call for " + server.id) if action == "off": logging.info("Stopping instance " + server.name) try: server.stop() except Conflict as e: fail_usage(e) logging.info("Called stop API call for " + server.id) if action == "reboot": logging.info("Rebooting instance " + server.name) try: server.reboot("HARD") except Conflict as e: fail_usage(e) logging.info("Called reboot hard API call for " + server.id) def nova_login(username, password, projectname, auth_url, user_domain_name, project_domain_name, ssl_insecure, cacert, apitimeout): legacy_import = False try: from keystoneauth1 import loading from keystoneauth1 import session as ksc_session from keystoneauth1.exceptions.discovery import DiscoveryFailure from keystoneauth1.exceptions.http import Unauthorized except ImportError: try: from keystoneclient import session as ksc_session from keystoneclient.auth.identity import v3 legacy_import = True except ImportError: fail_usage("Failed: Keystone client not found or not accessible") if not legacy_import: loader = loading.get_plugin_loader("password") auth = loader.load_from_options( auth_url=auth_url, username=username, password=password, project_name=projectname, user_domain_name=user_domain_name, project_domain_name=project_domain_name, ) else: auth = v3.Password( auth_url=auth_url, username=username, password=password, project_name=projectname, user_domain_name=user_domain_name, project_domain_name=project_domain_name, cacert=cacert, ) - session = ksc_session.Session(auth=auth, verify=False if ssl_insecure else cacert, timeout=apitimeout) + caverify=True + if ssl_insecure: + caverify=False + elif cacert: + caverify=cacert + + session = ksc_session.Session(auth=auth, verify=caverify, timeout=apitimeout) nova = client.Client("2", session=session, timeout=apitimeout) apiversion = None try: apiversion = nova.versions.get_current() except DiscoveryFailure as e: fail_usage("Failed: Discovery Failure: " + str(e)) except Unauthorized as e: fail_usage("Failed: Unauthorized: " + str(e)) except Exception as e: logging.error(e) logging.debug("Nova version: %s", apiversion) return nova def define_new_opts(): all_opt["auth-url"] = { "getopt": ":", "longopt": "auth-url", "help": "--auth-url=[authurl] Keystone Auth URL", "required": "1", "shortdesc": "Keystone Auth URL", "order": 2, } all_opt["project-name"] = { "getopt": ":", "longopt": "project-name", "help": "--project-name=[project] Tenant Or Project Name", "required": "1", "shortdesc": "Keystone Project", "default": "admin", "order": 3, } all_opt["user-domain-name"] = { "getopt": ":", "longopt": "user-domain-name", "help": "--user-domain-name=[domain] Keystone User Domain Name", "required": "0", "shortdesc": "Keystone User Domain Name", "default": "Default", "order": 4, } all_opt["project-domain-name"] = { "getopt": ":", "longopt": "project-domain-name", "help": "--project-domain-name=[domain] Keystone Project Domain Name", "required": "0", "shortdesc": "Keystone Project Domain Name", "default": "Default", "order": 5, } all_opt["uuid"] = { "getopt": ":", "longopt": "uuid", "help": "--uuid=[uuid] Replaced by -n, --plug", "required": "0", "shortdesc": "Replaced by port/-n/--plug", "order": 6, } all_opt["cacert"] = { "getopt": ":", "longopt": "cacert", - "help": "--cacert=[cacert] Path to the PEM file with trusted authority certificates", + "help": "--cacert=[cacert] Path to the PEM file with trusted authority certificates (override global CA trust)", "required": "0", "shortdesc": "SSL X.509 certificates file", - "default": "/etc/pki/tls/certs/ca-bundle.crt", + "default": "", "order": 7, } all_opt["apitimeout"] = { "getopt": ":", "type": "second", "longopt": "apitimeout", "help": "--apitimeout=[seconds] Timeout to use for API calls", "shortdesc": "Timeout in seconds to use for API calls, default is 60.", "required": "0", "default": 60, "order": 8, } def main(): conn = None device_opt = [ "login", "passwd", "auth-url", "project-name", "user-domain-name", "project-domain-name", "port", "no_port", "uuid", "ssl_insecure", "cacert", "apitimeout", ] atexit.register(atexit_handler) define_new_opts() all_opt["port"]["required"] = "0" all_opt["port"]["help"] = "-n, --plug=[UUID] UUID of the node to be fenced" all_opt["port"]["shortdesc"] = "UUID of the node to be fenced." all_opt["power_timeout"]["default"] = "60" options = check_input(device_opt, process_input(device_opt)) # workaround to avoid regressions if "--uuid" in options: options["--plug"] = options["--uuid"] del options["--uuid"] elif ("--help" not in options and options["--action"] in ["off", "on", "reboot", "status", "validate-all"] and "--plug" not in options): stop_after_error = False if options["--action"] == "validate-all" else True fail_usage( "Failed: You have to enter plug number or machine identification", stop_after_error, ) docs = {} docs["shortdesc"] = "Fence agent for OpenStack's Nova service" docs["longdesc"] = "fence_openstack is a Fencing agent \ which can be used with machines controlled by the Openstack's Nova service. \ This agent calls the python-novaclient and it is mandatory to be installed " docs["vendorurl"] = "https://wiki.openstack.org/wiki/Nova" show_docs(options, docs) run_delay(options) username = options["--username"] password = options["--password"] projectname = options["--project-name"] auth_url = None try: auth_url = options["--auth-url"] except KeyError: fail_usage("Failed: You have to set the Keystone service endpoint for authorization") user_domain_name = options["--user-domain-name"] project_domain_name = options["--project-domain-name"] ssl_insecure = "--ssl-insecure" in options cacert = options["--cacert"] apitimeout = options["--apitimeout"] try: conn = nova_login( username, password, projectname, auth_url, user_domain_name, project_domain_name, ssl_insecure, cacert, apitimeout, ) except Exception as e: fail_usage("Failed: Unable to connect to Nova: " + str(e)) # Operate the fencing device result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list) sys.exit(result) if __name__ == "__main__": main() diff --git a/tests/data/metadata/fence_openstack.xml b/tests/data/metadata/fence_openstack.xml index 926d18c3..c8dc2e60 100644 --- a/tests/data/metadata/fence_openstack.xml +++ b/tests/data/metadata/fence_openstack.xml @@ -1,204 +1,204 @@ fence_openstack is a Fencing agent which can be used with machines controlled by the Openstack's Nova service. This agent calls the python-novaclient and it is mandatory to be installed https://wiki.openstack.org/wiki/Nova Fencing action Login name Login password or passphrase Script to run to retrieve password Login password or passphrase Script to run to retrieve password UUID of the node to be fenced. UUID of the node to be fenced. Use SSL connection without verifying certificate Login name Keystone Auth URL Keystone Auth URL Keystone Project Keystone Project Keystone User Domain Name Keystone User Domain Name Keystone Project Domain Name Keystone Project Domain Name Replaced by port/-n/--plug - + SSL X.509 certificates file Timeout in seconds to use for API calls, default is 60. Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog. Verbose mode. Multiple -v flags can be stacked on the command line (e.g., -vvv) to increase verbosity. Level of debugging detail in output. Defaults to the number of --verbose flags specified on the command line, or to 1 if verbose=1 in a stonith device configuration (i.e., on stdin). 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 'list' operation Wait X seconds before fencing is started Disable timeout (true/false) (default: true when run from Pacemaker 2.0+) Wait X seconds for cmd prompt after login Test X seconds for status change after ON/OFF Wait X seconds after issuing ON/OFF Wait X seconds for cmd prompt after issuing command Sleep X seconds between status calls during a STONITH action Count of attempts to retry power on