import fence-agents-4.2.1-103.el8
This commit is contained in:
parent
84dad5f9cc
commit
09e4353cac
@ -22,7 +22,6 @@ e0fa19f8fda46a1fa2253477499b116b33f67175 SOURCES/pyasn1-0.4.8.tar.gz
|
|||||||
43b89feb6864fe359aae89120627165219de313b SOURCES/pyasn1-modules-0.2.8.tar.gz
|
43b89feb6864fe359aae89120627165219de313b SOURCES/pyasn1-modules-0.2.8.tar.gz
|
||||||
326a73f58a62ebee00c11a12cfdd838b196e0e8e SOURCES/pycryptodome-3.6.4.tar.gz
|
326a73f58a62ebee00c11a12cfdd838b196e0e8e SOURCES/pycryptodome-3.6.4.tar.gz
|
||||||
c8307f47e3b75a2d02af72982a2dfefa3f56e407 SOURCES/pyparsing-2.4.7-py2.py3-none-any.whl
|
c8307f47e3b75a2d02af72982a2dfefa3f56e407 SOURCES/pyparsing-2.4.7-py2.py3-none-any.whl
|
||||||
6082312a090f5be5e796e0854294da0738ec0379 SOURCES/pyparsing-3.0.1.tar.gz
|
|
||||||
c2ba10c775b7a52a4b57cac4d4110a0c0f812a82 SOURCES/python-dateutil-2.8.2.tar.gz
|
c2ba10c775b7a52a4b57cac4d4110a0c0f812a82 SOURCES/python-dateutil-2.8.2.tar.gz
|
||||||
1dc2fa004aa6517f1620e55d8a7b8e68a9cf2a47 SOURCES/python-string-utils-1.0.0.tar.gz
|
1dc2fa004aa6517f1620e55d8a7b8e68a9cf2a47 SOURCES/python-string-utils-1.0.0.tar.gz
|
||||||
8c7a89d183d3e9b70bf91ba5b75eccf7111b9d8d SOURCES/requests-2.26.0.tar.gz
|
8c7a89d183d3e9b70bf91ba5b75eccf7111b9d8d SOURCES/requests-2.26.0.tar.gz
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,7 +22,6 @@ SOURCES/pyasn1-0.4.8.tar.gz
|
|||||||
SOURCES/pyasn1-modules-0.2.8.tar.gz
|
SOURCES/pyasn1-modules-0.2.8.tar.gz
|
||||||
SOURCES/pycryptodome-3.6.4.tar.gz
|
SOURCES/pycryptodome-3.6.4.tar.gz
|
||||||
SOURCES/pyparsing-2.4.7-py2.py3-none-any.whl
|
SOURCES/pyparsing-2.4.7-py2.py3-none-any.whl
|
||||||
SOURCES/pyparsing-3.0.1.tar.gz
|
|
||||||
SOURCES/python-dateutil-2.8.2.tar.gz
|
SOURCES/python-dateutil-2.8.2.tar.gz
|
||||||
SOURCES/python-string-utils-1.0.0.tar.gz
|
SOURCES/python-string-utils-1.0.0.tar.gz
|
||||||
SOURCES/requests-2.26.0.tar.gz
|
SOURCES/requests-2.26.0.tar.gz
|
||||||
|
17
SOURCES/bz1886074-1-fencing-source_env.patch
Normal file
17
SOURCES/bz1886074-1-fencing-source_env.patch
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
--- a/lib/fencing.py.py 2022-04-22 10:25:59.347139685 +0200
|
||||||
|
+++ b/lib/fencing.py.py 2022-04-22 10:26:05.127169492 +0200
|
||||||
|
@@ -1115,6 +1115,14 @@
|
||||||
|
except pexpect.ExceptionPexpect:
|
||||||
|
pass
|
||||||
|
|
||||||
|
+def source_env(env_file):
|
||||||
|
+ # POSIX: name shall not contain '=', value doesn't contain '\0'
|
||||||
|
+ output = subprocess.check_output("source {} && env -0".format(env_file), shell=True,
|
||||||
|
+ executable="/bin/sh")
|
||||||
|
+ # replace env
|
||||||
|
+ os.environ.clear()
|
||||||
|
+ os.environ.update(line.partition('=')[::2] for line in output.decode("utf-8").split('\0'))
|
||||||
|
+
|
||||||
|
# Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is
|
||||||
|
# in format a.b.c.d...z and returned dict has key only z
|
||||||
|
def array_to_dict(array):
|
460
SOURCES/bz1886074-2-fence_openstack.patch
Normal file
460
SOURCES/bz1886074-2-fence_openstack.patch
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
--- a/agents/openstack/fence_openstack.py 2018-06-28 14:24:54.000000000 +0200
|
||||||
|
+++ b/agents/openstack/fence_openstack.py 2022-04-22 10:15:28.069884360 +0200
|
||||||
|
@@ -2,110 +2,303 @@
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import logging
|
||||||
|
-import os
|
||||||
|
-import re
|
||||||
|
import sys
|
||||||
|
-from pipes import quote
|
||||||
|
-sys.path.append("/usr/share/fence")
|
||||||
|
+import os
|
||||||
|
+
|
||||||
|
+import urllib3
|
||||||
|
+
|
||||||
|
+sys.path.append("@FENCEAGENTSLIBDIR@")
|
||||||
|
from fencing import *
|
||||||
|
-from fencing import fail_usage, is_executable, run_command, run_delay
|
||||||
|
-from keystoneclient.v3 import client as ksclient
|
||||||
|
-from novaclient import client as novaclient
|
||||||
|
-from keystoneclient import session as ksc_session
|
||||||
|
-from keystoneclient.auth.identity import v3
|
||||||
|
-
|
||||||
|
-def get_name_or_uuid(options):
|
||||||
|
- return options["--uuid"] if "--uuid" in options else options["--plug"]
|
||||||
|
-
|
||||||
|
-def get_power_status(_, options):
|
||||||
|
- output = nova_run_command(options, "status")
|
||||||
|
- if (output == 'ACTIVE'):
|
||||||
|
- return 'on'
|
||||||
|
- else:
|
||||||
|
- return 'off'
|
||||||
|
+from fencing import fail_usage, run_delay, source_env
|
||||||
|
+
|
||||||
|
+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_cloud(options):
|
||||||
|
+ import yaml
|
||||||
|
+
|
||||||
|
+ clouds_yaml = "~/.config/openstack/clouds.yaml"
|
||||||
|
+ if not os.path.exists(os.path.expanduser(clouds_yaml)):
|
||||||
|
+ clouds_yaml = "/etc/openstack/clouds.yaml"
|
||||||
|
+ if not os.path.exists(os.path.expanduser(clouds_yaml)):
|
||||||
|
+ fail_usage("Failed: ~/.config/openstack/clouds.yaml and /etc/openstack/clouds.yaml does not exist")
|
||||||
|
+
|
||||||
|
+ clouds_yaml = os.path.expanduser(clouds_yaml)
|
||||||
|
+ if os.path.exists(clouds_yaml):
|
||||||
|
+ with open(clouds_yaml, "r") as yaml_stream:
|
||||||
|
+ try:
|
||||||
|
+ clouds = yaml.safe_load(yaml_stream)
|
||||||
|
+ except yaml.YAMLError as exc:
|
||||||
|
+ fail_usage("Failed: Unable to read: " + clouds_yaml)
|
||||||
|
+
|
||||||
|
+ cloud = clouds.get("clouds").get(options["--cloud"])
|
||||||
|
+ if not cloud:
|
||||||
|
+ fail_usage("Cloud: {} not found.".format(options["--cloud"]))
|
||||||
|
+
|
||||||
|
+ return cloud
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+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,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ 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 set_power_status(_, options):
|
||||||
|
- nova_run_command(options, options["--action"])
|
||||||
|
- return
|
||||||
|
-
|
||||||
|
-def nova_login(username,password,projectname,auth_url,user_domain_name,project_domain_name):
|
||||||
|
- auth=v3.Password(username=username,password=password,project_name=projectname,user_domain_name=user_domain_name,project_domain_name=project_domain_name,auth_url=auth_url)
|
||||||
|
- session = ksc_session.Session(auth=auth)
|
||||||
|
- keystone = ksclient.Client(session=session)
|
||||||
|
- nova = novaclient.Client(session=session)
|
||||||
|
- return nova
|
||||||
|
-
|
||||||
|
-def nova_run_command(options,action,timeout=None):
|
||||||
|
- username=options["--username"]
|
||||||
|
- password=options["--password"]
|
||||||
|
- projectname=options["--project-name"]
|
||||||
|
- auth_url=options["--auth-url"]
|
||||||
|
- user_domain_name=options["--user-domain-name"]
|
||||||
|
- project_domain_name=options["--project-domain-name"]
|
||||||
|
- novaclient=nova_login(username,password,projectname,auth_url,user_domain_name,project_domain_name)
|
||||||
|
- server = novaclient.servers.get(options["--uuid"])
|
||||||
|
- if action == "status":
|
||||||
|
- return server.status
|
||||||
|
- if action == "on":
|
||||||
|
- server.start()
|
||||||
|
- if action == "off":
|
||||||
|
- server.stop()
|
||||||
|
- if action == "reboot":
|
||||||
|
- server.reboot('REBOOT_HARD')
|
||||||
|
|
||||||
|
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": 1
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "auth-url",
|
||||||
|
+ "help": "--auth-url=[authurl] Keystone Auth URL",
|
||||||
|
+ "required": "0",
|
||||||
|
+ "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",
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "project-name",
|
||||||
|
+ "help": "--project-name=[project] Tenant Or Project Name",
|
||||||
|
+ "required": "0",
|
||||||
|
+ "shortdesc": "Keystone Project",
|
||||||
|
"default": "admin",
|
||||||
|
- "order": 1
|
||||||
|
+ "order": 3,
|
||||||
|
}
|
||||||
|
all_opt["user-domain-name"] = {
|
||||||
|
- "getopt" : ":",
|
||||||
|
- "longopt" : "user-domain-name",
|
||||||
|
- "help" : "--user-domain-name=[user-domain] Keystone User Domain Name",
|
||||||
|
- "required" : "0",
|
||||||
|
- "shortdesc" : "Keystone 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": 1
|
||||||
|
+ "order": 4,
|
||||||
|
}
|
||||||
|
all_opt["project-domain-name"] = {
|
||||||
|
- "getopt" : ":",
|
||||||
|
- "longopt" : "project-domain-name",
|
||||||
|
- "help" : "--project-domain-name=[project-domain] Keystone Project Domain Name",
|
||||||
|
- "required" : "0",
|
||||||
|
- "shortdesc" : "Keystone 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": 1
|
||||||
|
+ "order": 5,
|
||||||
|
+ }
|
||||||
|
+ all_opt["cloud"] = {
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "cloud",
|
||||||
|
+ "help": "--cloud=[cloud] Openstack cloud (from ~/.config/openstack/clouds.yaml or /etc/openstack/clouds.yaml).",
|
||||||
|
+ "required": "0",
|
||||||
|
+ "shortdesc": "Cloud from clouds.yaml",
|
||||||
|
+ "order": 6,
|
||||||
|
+ }
|
||||||
|
+ all_opt["openrc"] = {
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "openrc",
|
||||||
|
+ "help": "--openrc=[openrc] Path to the openrc config file",
|
||||||
|
+ "required": "0",
|
||||||
|
+ "shortdesc": "openrc config file",
|
||||||
|
+ "order": 7,
|
||||||
|
}
|
||||||
|
all_opt["uuid"] = {
|
||||||
|
- "getopt" : ":",
|
||||||
|
- "longopt" : "uuid",
|
||||||
|
- "help" : "--uuid=[uuid] UUID of the nova instance",
|
||||||
|
- "required" : "1",
|
||||||
|
- "shortdesc" : "UUID of the nova instance",
|
||||||
|
- "order": 1
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "uuid",
|
||||||
|
+ "help": "--uuid=[uuid] Replaced by -n, --plug",
|
||||||
|
+ "required": "0",
|
||||||
|
+ "shortdesc": "Replaced by port/-n/--plug",
|
||||||
|
+ "order": 8,
|
||||||
|
+ }
|
||||||
|
+ all_opt["cacert"] = {
|
||||||
|
+ "getopt": ":",
|
||||||
|
+ "longopt": "cacert",
|
||||||
|
+ "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": "",
|
||||||
|
+ "order": 9,
|
||||||
|
+ }
|
||||||
|
+ 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": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
def main():
|
||||||
|
+ conn = None
|
||||||
|
+
|
||||||
|
+ device_opt = [
|
||||||
|
+ "login",
|
||||||
|
+ "no_login",
|
||||||
|
+ "passwd",
|
||||||
|
+ "no_password",
|
||||||
|
+ "auth-url",
|
||||||
|
+ "project-name",
|
||||||
|
+ "user-domain-name",
|
||||||
|
+ "project-domain-name",
|
||||||
|
+ "cloud",
|
||||||
|
+ "openrc",
|
||||||
|
+ "port",
|
||||||
|
+ "no_port",
|
||||||
|
+ "uuid",
|
||||||
|
+ "ssl_insecure",
|
||||||
|
+ "cacert",
|
||||||
|
+ "apitimeout",
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
atexit.register(atexit_handler)
|
||||||
|
|
||||||
|
- device_opt = ["login", "passwd", "auth-url", "project-name", "user-domain-name", "project-domain-name", "uuid"]
|
||||||
|
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 \
|
||||||
|
@@ -116,9 +309,73 @@
|
||||||
|
|
||||||
|
run_delay(options)
|
||||||
|
|
||||||
|
- result = fence_action(None, options, set_power_status, get_power_status,None)
|
||||||
|
+ if options.get("--cloud"):
|
||||||
|
+ cloud = get_cloud(options)
|
||||||
|
+ username = cloud.get("auth").get("username")
|
||||||
|
+ password = cloud.get("auth").get("password")
|
||||||
|
+ projectname = cloud.get("auth").get("project_name")
|
||||||
|
+ auth_url = None
|
||||||
|
+ try:
|
||||||
|
+ auth_url = cloud.get("auth").get("auth_url")
|
||||||
|
+ except KeyError:
|
||||||
|
+ fail_usage("Failed: You have to set the Keystone service endpoint for authorization")
|
||||||
|
+ user_domain_name = cloud.get("auth").get("user_domain_name")
|
||||||
|
+ project_domain_name = cloud.get("auth").get("project_domain_name")
|
||||||
|
+ caverify = cloud.get("verify")
|
||||||
|
+ if caverify in [True, False]:
|
||||||
|
+ options["--ssl-insecure"] = caverify
|
||||||
|
+ else:
|
||||||
|
+ options["--cacert"] = caverify
|
||||||
|
+ elif options.get("--openrc"):
|
||||||
|
+ if not os.path.exists(os.path.expanduser(options["--openrc"])):
|
||||||
|
+ fail_usage("Failed: {} does not exist".format(options.get("--openrc")))
|
||||||
|
+ source_env(options["--openrc"])
|
||||||
|
+ env = os.environ
|
||||||
|
+ username = env.get("OS_USERNAME")
|
||||||
|
+ password = env.get("OS_PASSWORD")
|
||||||
|
+ projectname = env.get("OS_PROJECT_NAME")
|
||||||
|
+ auth_url = None
|
||||||
|
+ try:
|
||||||
|
+ auth_url = env["OS_AUTH_URL"]
|
||||||
|
+ except KeyError:
|
||||||
|
+ fail_usage("Failed: You have to set the Keystone service endpoint for authorization")
|
||||||
|
+ user_domain_name = env.get("OS_USER_DOMAIN_NAME")
|
||||||
|
+ project_domain_name = env.get("OS_PROJECT_DOMAIN_NAME")
|
||||||
|
+ else:
|
||||||
|
+ 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()
|
||||||
|
-
|
@ -0,0 +1,22 @@
|
|||||||
|
From fbca33a536413565108374dd4ed3237b6f7896bd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
||||||
|
Date: Mon, 15 Aug 2022 10:40:19 +0200
|
||||||
|
Subject: [PATCH] fencing: source_env(): dont process empty lines
|
||||||
|
|
||||||
|
---
|
||||||
|
lib/fencing.py.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/lib/fencing.py.py b/lib/fencing.py.py
|
||||||
|
index fc3679e33..940bd01d1 100644
|
||||||
|
--- a/lib/fencing.py.py
|
||||||
|
+++ b/lib/fencing.py.py
|
||||||
|
@@ -1264,7 +1264,7 @@ def source_env(env_file):
|
||||||
|
executable="/bin/sh")
|
||||||
|
# replace env
|
||||||
|
os.environ.clear()
|
||||||
|
- os.environ.update(line.partition('=')[::2] for line in output.decode("utf-8").split('\0'))
|
||||||
|
+ os.environ.update(line.partition('=')[::2] for line in output.decode("utf-8").split('\0') if not re.match("^\s*$", line))
|
||||||
|
|
||||||
|
# Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is
|
||||||
|
# in format a.b.c.d...z and returned dict has key only z
|
243
SOURCES/bz2072421-1-all-agents-unify-ssl-parameters.patch
Normal file
243
SOURCES/bz2072421-1-all-agents-unify-ssl-parameters.patch
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
From 999f2f8b4dc7d258679daf8c3f13d9b317ff4435 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
||||||
|
Date: Thu, 7 Apr 2022 13:11:12 +0200
|
||||||
|
Subject: [PATCH] all agents: unify ssl parameters to avoid having to use --ssl
|
||||||
|
when using --ssl-secure/--ssl-insecure for some agents
|
||||||
|
|
||||||
|
THIS MIGHT BREAK SETUPS USING fence_docker or fence_pve!
|
||||||
|
---
|
||||||
|
agents/cisco_ucs/fence_cisco_ucs.py | 9 +++++----
|
||||||
|
agents/docker/fence_docker.py | 9 ++++++---
|
||||||
|
agents/rhevm/fence_rhevm.py | 8 ++++----
|
||||||
|
agents/vmware_rest/fence_vmware_rest.py | 8 ++++----
|
||||||
|
agents/vmware_soap/fence_vmware_soap.py | 2 +-
|
||||||
|
agents/vmware_vcloud/fence_vmware_vcloud.py | 4 ++--
|
||||||
|
agents/zvm/fence_zvmip.py | 8 +++++---
|
||||||
|
lib/fencing.py.py | 6 ++++++
|
||||||
|
tests/data/metadata/fence_docker.xml | 4 ++--
|
||||||
|
13 files changed, 64 insertions(+), 31 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/agents/cisco_ucs/fence_cisco_ucs.py b/agents/cisco_ucs/fence_cisco_ucs.py
|
||||||
|
index 2280dbbc7..b85379a73 100644
|
||||||
|
--- a/agents/cisco_ucs/fence_cisco_ucs.py
|
||||||
|
+++ b/agents/cisco_ucs/fence_cisco_ucs.py
|
||||||
|
@@ -99,7 +99,7 @@ def get_list(conn, options):
|
||||||
|
|
||||||
|
def send_command(opt, command, timeout):
|
||||||
|
## setup correct URL
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
+ if "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
url = "https:"
|
||||||
|
else:
|
||||||
|
url = "http:"
|
||||||
|
@@ -114,13 +114,14 @@ def send_command(opt, command, timeout):
|
||||||
|
conn.setopt(pycurl.POSTFIELDS, command.encode("ascii"))
|
||||||
|
conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write)
|
||||||
|
conn.setopt(pycurl.TIMEOUT, timeout)
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt:
|
||||||
|
+
|
||||||
|
+ if "--ssl-secure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 2)
|
||||||
|
-
|
||||||
|
- if "--ssl-insecure" in opt:
|
||||||
|
+ elif "--ssl-insecure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 0)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 0)
|
||||||
|
+
|
||||||
|
conn.perform()
|
||||||
|
result = web_buffer.getvalue().decode()
|
||||||
|
|
||||||
|
diff --git a/agents/docker/fence_docker.py b/agents/docker/fence_docker.py
|
||||||
|
index fef87da86..004402518 100644
|
||||||
|
--- a/agents/docker/fence_docker.py
|
||||||
|
+++ b/agents/docker/fence_docker.py
|
||||||
|
@@ -43,7 +43,7 @@ def get_list(conn, options):
|
||||||
|
|
||||||
|
|
||||||
|
def send_cmd(options, cmd, post = False):
|
||||||
|
- url = "http%s://%s:%s/v%s/%s" % ("s" if "--ssl" in options else "", options["--ip"], options["--ipport"], options["--api-version"], cmd)
|
||||||
|
+ url = "http%s://%s:%s/v%s/%s" % ("s" if "--ssl-secure" in options or "--ssl-insecure" in options else "", options["--ip"], options["--ipport"], options["--api-version"], cmd)
|
||||||
|
conn = pycurl.Curl()
|
||||||
|
output_buffer = io.BytesIO()
|
||||||
|
if logging.getLogger().getEffectiveLevel() < logging.WARNING:
|
||||||
|
@@ -55,7 +55,8 @@ def send_cmd(options, cmd, post = False):
|
||||||
|
conn.setopt(pycurl.POSTFIELDSIZE, 0)
|
||||||
|
conn.setopt(pycurl.WRITEFUNCTION, output_buffer.write)
|
||||||
|
conn.setopt(pycurl.TIMEOUT, int(options["--shell-timeout"]))
|
||||||
|
- if "--ssl" in options:
|
||||||
|
+
|
||||||
|
+ if "--ssl-secure" in options:
|
||||||
|
if not (set(("--tlscert", "--tlskey", "--tlscacert")) <= set(options)):
|
||||||
|
fail_usage("Failed. If --ssl option is used, You have to also \
|
||||||
|
specify: --tlscert, --tlskey and --tlscacert")
|
||||||
|
@@ -63,7 +64,7 @@ def send_cmd(options, cmd, post = False):
|
||||||
|
conn.setopt(pycurl.SSLCERT, options["--tlscert"])
|
||||||
|
conn.setopt(pycurl.SSLKEY, options["--tlskey"])
|
||||||
|
conn.setopt(pycurl.CAINFO, options["--tlscacert"])
|
||||||
|
- else:
|
||||||
|
+ elif "--ssl-insecure" in options:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 0)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 0)
|
||||||
|
|
||||||
|
@@ -136,6 +137,8 @@ def main():
|
||||||
|
|
||||||
|
device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", "tlscert", "tlskey", "tlscacert", "ssl", "api_version"]
|
||||||
|
|
||||||
|
+ all_opt["ssl"]["default"] = "1"
|
||||||
|
+
|
||||||
|
options = check_input(device_opt, process_input(device_opt))
|
||||||
|
|
||||||
|
docs = { }
|
||||||
|
diff --git a/agents/rhevm/fence_rhevm.py b/agents/rhevm/fence_rhevm.py
|
||||||
|
index 25aecbe58..5f74d06f6 100644
|
||||||
|
--- a/agents/rhevm/fence_rhevm.py
|
||||||
|
+++ b/agents/rhevm/fence_rhevm.py
|
||||||
|
@@ -85,7 +85,7 @@ def send_command(opt, command, method="GET"):
|
||||||
|
logging.debug("auto-detected API version: " + opt["--api-version"])
|
||||||
|
|
||||||
|
## setup correct URL
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
+ if "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
url = "https:"
|
||||||
|
else:
|
||||||
|
url = "http:"
|
||||||
|
@@ -126,11 +126,11 @@ def send_command(opt, command, method="GET"):
|
||||||
|
conn.setopt(pycurl.COOKIEJAR, cookie_file)
|
||||||
|
|
||||||
|
conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"]))
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt:
|
||||||
|
+
|
||||||
|
+ if "--ssl-secure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 2)
|
||||||
|
-
|
||||||
|
- if "--ssl-insecure" in opt:
|
||||||
|
+ elif "--ssl-insecure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 0)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 0)
|
||||||
|
|
||||||
|
diff --git a/agents/vmware_rest/fence_vmware_rest.py b/agents/vmware_rest/fence_vmware_rest.py
|
||||||
|
index e49fd5663..4b884fc62 100644
|
||||||
|
--- a/agents/vmware_rest/fence_vmware_rest.py
|
||||||
|
+++ b/agents/vmware_rest/fence_vmware_rest.py
|
||||||
|
@@ -69,7 +69,7 @@ def connect(opt):
|
||||||
|
conn = pycurl.Curl()
|
||||||
|
|
||||||
|
## setup correct URL
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
+ if "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
conn.base_url = "https:"
|
||||||
|
else:
|
||||||
|
conn.base_url = "http:"
|
||||||
|
@@ -89,11 +89,11 @@ def connect(opt):
|
||||||
|
conn.setopt(pycurl.USERPWD, opt["--username"] + ":" + opt["--password"])
|
||||||
|
|
||||||
|
conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"]))
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt:
|
||||||
|
+
|
||||||
|
+ if "--ssl-secure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 2)
|
||||||
|
-
|
||||||
|
- if "--ssl-insecure" in opt:
|
||||||
|
+ elif "--ssl-insecure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 0)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 0)
|
||||||
|
|
||||||
|
diff --git a/agents/vmware_soap/fence_vmware_soap.py b/agents/vmware_soap/fence_vmware_soap.py
|
||||||
|
index 2cd45e0b3..51fb0f147 100644
|
||||||
|
--- a/agents/vmware_soap/fence_vmware_soap.py
|
||||||
|
+++ b/agents/vmware_soap/fence_vmware_soap.py
|
||||||
|
@@ -34,7 +34,7 @@ def send(self, request):
|
||||||
|
def soap_login(options):
|
||||||
|
run_delay(options)
|
||||||
|
|
||||||
|
- if "--ssl" in options or "--ssl-secure" in options or "--ssl-insecure" in options:
|
||||||
|
+ if "--ssl-secure" in options or "--ssl-insecure" in options:
|
||||||
|
if "--ssl-insecure" in options:
|
||||||
|
import ssl
|
||||||
|
import urllib3
|
||||||
|
diff --git a/agents/vmware_vcloud/fence_vmware_vcloud.py b/agents/vmware_vcloud/fence_vmware_vcloud.py
|
||||||
|
index 42372a83d..7626b82bb 100644
|
||||||
|
--- a/agents/vmware_vcloud/fence_vmware_vcloud.py
|
||||||
|
+++ b/agents/vmware_vcloud/fence_vmware_vcloud.py
|
||||||
|
@@ -60,7 +60,7 @@ def connect(opt):
|
||||||
|
conn = pycurl.Curl()
|
||||||
|
|
||||||
|
## setup correct URL
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
+ if "--ssl-secure" in opt or "--ssl-insecure" in opt:
|
||||||
|
conn.base_url = "https:"
|
||||||
|
else:
|
||||||
|
conn.base_url = "http:"
|
||||||
|
@@ -76,7 +76,7 @@ def connect(opt):
|
||||||
|
conn.setopt(pycurl.USERPWD, opt["--username"] + ":" + opt["--password"])
|
||||||
|
|
||||||
|
conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"]))
|
||||||
|
- if "--ssl" in opt or "--ssl-secure" in opt:
|
||||||
|
+ if "--ssl-secure" in opt:
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYPEER, 1)
|
||||||
|
conn.setopt(pycurl.SSL_VERIFYHOST, 2)
|
||||||
|
elif "--ssl-insecure" in opt:
|
||||||
|
diff --git a/agents/zvm/fence_zvmip.py b/agents/zvm/fence_zvmip.py
|
||||||
|
index e8f849eda..90ca95d45 100644
|
||||||
|
--- a/agents/zvm/fence_zvmip.py
|
||||||
|
+++ b/agents/zvm/fence_zvmip.py
|
||||||
|
@@ -26,7 +26,7 @@ def open_socket(options):
|
||||||
|
except socket.gaierror:
|
||||||
|
fail(EC_LOGIN_DENIED)
|
||||||
|
|
||||||
|
- if "--ssl" in options:
|
||||||
|
+ if "--ssl-secure" in options or "--ssl-insecure" in options:
|
||||||
|
import ssl
|
||||||
|
sock = socket.socket()
|
||||||
|
sslcx = ssl.create_default_context()
|
||||||
|
@@ -132,7 +132,7 @@ def get_list_of_images(options, command, data_as_plug):
|
||||||
|
images = set()
|
||||||
|
|
||||||
|
if output_len > 3*INT4:
|
||||||
|
- recvflag = socket.MSG_WAITALL if "--ssl" not in options else 0
|
||||||
|
+ recvflag = socket.MSG_WAITALL if "--ssl-secure" not in options and "--ssl-insecure" not in options else 0
|
||||||
|
array_len = struct.unpack("!i", conn.recv(INT4))[0]
|
||||||
|
data = ""
|
||||||
|
|
||||||
|
diff --git a/lib/fencing.py.py b/lib/fencing.py.py
|
||||||
|
index 696388d55..b746ede8b 100644
|
||||||
|
--- a/lib/fencing.py.py
|
||||||
|
+++ b/lib/fencing.py.py
|
||||||
|
@@ -789,6 +789,12 @@ def check_input(device_opt, opt, other_conditions = False):
|
||||||
|
if "--password-script" in options:
|
||||||
|
options["--password"] = os.popen(options["--password-script"]).read().rstrip()
|
||||||
|
|
||||||
|
+ if "--ssl-secure" in options or "--ssl-insecure" in options:
|
||||||
|
+ options["--ssl"] = ""
|
||||||
|
+
|
||||||
|
+ if "--ssl" in options and "--ssl-insecure" not in options:
|
||||||
|
+ options["--ssl-secure"] = ""
|
||||||
|
+
|
||||||
|
if os.environ.get("PCMK_service") == "pacemaker-fenced" and "--disable-timeout" not in options:
|
||||||
|
options["--disable-timeout"] = "1"
|
||||||
|
|
||||||
|
diff --git a/tests/data/metadata/fence_docker.xml b/tests/data/metadata/fence_docker.xml
|
||||||
|
index 723e72280..51c7c470a 100644
|
||||||
|
--- a/tests/data/metadata/fence_docker.xml
|
||||||
|
+++ b/tests/data/metadata/fence_docker.xml
|
||||||
|
@@ -20,7 +20,7 @@
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ipport" unique="0" required="0">
|
||||||
|
<getopt mixed="-u, --ipport=[port]" />
|
||||||
|
- <content type="integer" default="80" />
|
||||||
|
+ <content type="integer" default="443" />
|
||||||
|
<shortdesc lang="en">TCP/UDP port to use for connection with device</shortdesc>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="method" unique="0" required="0">
|
||||||
|
@@ -43,7 +43,7 @@
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ssl" unique="0" required="0">
|
||||||
|
<getopt mixed="-z, --ssl" />
|
||||||
|
- <content type="boolean" />
|
||||||
|
+ <content type="boolean" default="1" />
|
||||||
|
<shortdesc lang="en">Use SSL connection with verifying certificate</shortdesc>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ssl_insecure" unique="0" required="0">
|
30
SOURCES/bz2072421-2-fence_zvmip-connect-error.patch
Normal file
30
SOURCES/bz2072421-2-fence_zvmip-connect-error.patch
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
From 6430104318b4bf349425b08636183bf839812e04 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
||||||
|
Date: Tue, 31 May 2022 08:55:25 +0200
|
||||||
|
Subject: [PATCH] fence_zvmip: show unable to connect error instead of full
|
||||||
|
stacktrace, e.g. when not using --ssl for SSL devices
|
||||||
|
|
||||||
|
---
|
||||||
|
agents/zvm/fence_zvmip.py | 9 +++++++--
|
||||||
|
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/agents/zvm/fence_zvmip.py b/agents/zvm/fence_zvmip.py
|
||||||
|
index 90ca95d45..4f538e10d 100644
|
||||||
|
--- a/agents/zvm/fence_zvmip.py
|
||||||
|
+++ b/agents/zvm/fence_zvmip.py
|
||||||
|
@@ -127,8 +127,13 @@ def get_list_of_images(options, command, data_as_plug):
|
||||||
|
|
||||||
|
conn.send(packet)
|
||||||
|
|
||||||
|
- request_id = struct.unpack("!i", conn.recv(INT4))[0]
|
||||||
|
- (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii", conn.recv(INT4 * 4))
|
||||||
|
+ try:
|
||||||
|
+ request_id = struct.unpack("!i", conn.recv(INT4))[0]
|
||||||
|
+ (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii", conn.recv(INT4 * 4))
|
||||||
|
+ except struct.error:
|
||||||
|
+ logging.debug(sys.exc_info())
|
||||||
|
+ fail_usage("Failed: Unable to connect to {} port: {} SSL: {} \n".format(options["--ip"], options["--ipport"], bool("--ssl" in options)))
|
||||||
|
+
|
||||||
|
images = set()
|
||||||
|
|
||||||
|
if output_len > 3*INT4:
|
563
SOURCES/bz2078244-fence_gce-update.patch
Normal file
563
SOURCES/bz2078244-fence_gce-update.patch
Normal file
@ -0,0 +1,563 @@
|
|||||||
|
--- fence-agents-4.2.1/agents/gce/fence_gce.py 2022-04-28 15:58:42.949723547 +0200
|
||||||
|
+++ fence-agents-4.2.1/agents/gce/fence_gce.py2 2022-04-28 15:59:21.054915265 +0200
|
||||||
|
@@ -1,10 +1,22 @@
|
||||||
|
#!@PYTHON@ -tt
|
||||||
|
|
||||||
|
+#
|
||||||
|
+# Requires the googleapiclient and oauth2client
|
||||||
|
+# RHEL 7.x: google-api-python-client==1.6.7 python-gflags==2.0 pyasn1==0.4.8 rsa==3.4.2 pysocks==1.7.1 httplib2==0.19.0
|
||||||
|
+# RHEL 8.x: pysocks==1.7.1 httplib2==0.19.0
|
||||||
|
+# SLES 12.x: python-google-api-python-client python-oauth2client python-oauth2client-gce pysocks==1.7.1 httplib2==0.19.0
|
||||||
|
+# SLES 15.x: python3-google-api-python-client python3-oauth2client pysocks==1.7.1 httplib2==0.19.0
|
||||||
|
+#
|
||||||
|
+
|
||||||
|
import atexit
|
||||||
|
import logging
|
||||||
|
+import json
|
||||||
|
+import re
|
||||||
|
import os
|
||||||
|
+import socket
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
+
|
||||||
|
if sys.version_info >= (3, 0):
|
||||||
|
# Python 3 imports.
|
||||||
|
import urllib.parse as urlparse
|
||||||
|
@@ -15,7 +27,7 @@
|
||||||
|
import urllib2 as urlrequest
|
||||||
|
sys.path.append("@FENCEAGENTSLIBDIR@")
|
||||||
|
|
||||||
|
-from fencing import fail_usage, run_delay, all_opt, atexit_handler, check_input, process_input, show_docs, fence_action
|
||||||
|
+from fencing import fail_usage, run_delay, all_opt, atexit_handler, check_input, process_input, show_docs, fence_action, run_command
|
||||||
|
try:
|
||||||
|
sys.path.insert(0, '/usr/lib/fence-agents/bundled/google')
|
||||||
|
import httplib2
|
||||||
|
@@ -30,6 +42,85 @@
|
||||||
|
|
||||||
|
METADATA_SERVER = 'http://metadata.google.internal/computeMetadata/v1/'
|
||||||
|
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
|
||||||
|
+INSTANCE_LINK = 'https://www.googleapis.com/compute/v1/projects/{}/zones/{}/instances/{}'
|
||||||
|
+
|
||||||
|
+def run_on_fail(options):
|
||||||
|
+ if "--runonfail" in options:
|
||||||
|
+ run_command(options, options["--runonfail"])
|
||||||
|
+
|
||||||
|
+def fail_fence_agent(options, message):
|
||||||
|
+ run_on_fail(options)
|
||||||
|
+ fail_usage(message)
|
||||||
|
+
|
||||||
|
+def raise_fence_agent(options, message):
|
||||||
|
+ run_on_fail(options)
|
||||||
|
+ raise Exception(message)
|
||||||
|
+
|
||||||
|
+#
|
||||||
|
+# Will use baremetalsolution setting or the environment variable
|
||||||
|
+# FENCE_GCE_URI_REPLACEMENTS to replace the uri for calls to *.googleapis.com.
|
||||||
|
+#
|
||||||
|
+def replace_api_uri(options, http_request):
|
||||||
|
+ uri_replacements = []
|
||||||
|
+ # put any env var replacements first, then baremetalsolution if in options
|
||||||
|
+ if "FENCE_GCE_URI_REPLACEMENTS" in os.environ:
|
||||||
|
+ logging.debug("FENCE_GCE_URI_REPLACEMENTS environment variable exists")
|
||||||
|
+ env_uri_replacements = os.environ["FENCE_GCE_URI_REPLACEMENTS"]
|
||||||
|
+ try:
|
||||||
|
+ uri_replacements_json = json.loads(env_uri_replacements)
|
||||||
|
+ if isinstance(uri_replacements_json, list):
|
||||||
|
+ uri_replacements = uri_replacements_json
|
||||||
|
+ else:
|
||||||
|
+ logging.warning("FENCE_GCE_URI_REPLACEMENTS exists, but is not a JSON List")
|
||||||
|
+ except ValueError as e:
|
||||||
|
+ logging.warning("FENCE_GCE_URI_REPLACEMENTS exists but is not valid JSON")
|
||||||
|
+ if "--baremetalsolution" in options:
|
||||||
|
+ uri_replacements.append(
|
||||||
|
+ {
|
||||||
|
+ "matchlength": 4,
|
||||||
|
+ "match": "https://compute.googleapis.com/compute/v1/projects/(.*)/zones/(.*)/instances/(.*)/reset(.*)",
|
||||||
|
+ "replace": "https://baremetalsolution.googleapis.com/v1/projects/\\1/locations/\\2/instances/\\3:resetInstance\\4"
|
||||||
|
+ })
|
||||||
|
+ for uri_replacement in uri_replacements:
|
||||||
|
+ # each uri_replacement should have matchlength, match, and replace
|
||||||
|
+ if "matchlength" not in uri_replacement or "match" not in uri_replacement or "replace" not in uri_replacement:
|
||||||
|
+ logging.warning("FENCE_GCE_URI_REPLACEMENTS missing matchlength, match, or replace in %s" % uri_replacement)
|
||||||
|
+ continue
|
||||||
|
+ match = re.match(uri_replacement["match"], http_request.uri)
|
||||||
|
+ if match is None or len(match.groups()) != uri_replacement["matchlength"]:
|
||||||
|
+ continue
|
||||||
|
+ replaced_uri = re.sub(uri_replacement["match"], uri_replacement["replace"], http_request.uri)
|
||||||
|
+ match = re.match("https:\/\/.*.googleapis.com", replaced_uri)
|
||||||
|
+ if match is None or match.start() != 0:
|
||||||
|
+ logging.warning("FENCE_GCE_URI_REPLACEMENTS replace is not "
|
||||||
|
+ "targeting googleapis.com, ignoring it: %s" % replaced_uri)
|
||||||
|
+ continue
|
||||||
|
+ logging.debug("Replacing googleapis uri %s with %s" % (http_request.uri, replaced_uri))
|
||||||
|
+ http_request.uri = replaced_uri
|
||||||
|
+ break
|
||||||
|
+ return http_request
|
||||||
|
+
|
||||||
|
+def retry_api_execute(options, http_request):
|
||||||
|
+ replaced_http_request = replace_api_uri(options, http_request)
|
||||||
|
+ retries = 3
|
||||||
|
+ if options.get("--retries"):
|
||||||
|
+ retries = int(options.get("--retries"))
|
||||||
|
+ retry_sleep = 5
|
||||||
|
+ if options.get("--retrysleep"):
|
||||||
|
+ retry_sleep = int(options.get("--retrysleep"))
|
||||||
|
+ retry = 0
|
||||||
|
+ current_err = None
|
||||||
|
+ while retry <= retries:
|
||||||
|
+ if retry > 0:
|
||||||
|
+ time.sleep(retry_sleep)
|
||||||
|
+ try:
|
||||||
|
+ return replaced_http_request.execute()
|
||||||
|
+ except Exception as err:
|
||||||
|
+ current_err = err
|
||||||
|
+ logging.warning("Could not execute api call to: %s, retry: %s, "
|
||||||
|
+ "err: %s" % (replaced_http_request.uri, retry, str(err)))
|
||||||
|
+ retry += 1
|
||||||
|
+ raise current_err
|
||||||
|
|
||||||
|
|
||||||
|
def translate_status(instance_status):
|
||||||
|
@@ -43,86 +134,174 @@
|
||||||
|
|
||||||
|
def get_nodes_list(conn, options):
|
||||||
|
result = {}
|
||||||
|
+ if "--zone" not in options:
|
||||||
|
+ fail_fence_agent(options, "Failed: get_nodes_list: Please specify the --zone in the command")
|
||||||
|
try:
|
||||||
|
- instanceList = conn.instances().list(project=options["--project"], zone=options["--zone"]).execute()
|
||||||
|
- for instance in instanceList["items"]:
|
||||||
|
- result[instance["id"]] = (instance["name"], translate_status(instance["status"]))
|
||||||
|
+ for zone in options["--zone"].split(","):
|
||||||
|
+ instanceList = retry_api_execute(options, conn.instances().list(
|
||||||
|
+ project=options["--project"],
|
||||||
|
+ zone=zone))
|
||||||
|
+ for instance in instanceList["items"]:
|
||||||
|
+ result[instance["id"]] = (instance["name"], translate_status(instance["status"]))
|
||||||
|
except Exception as err:
|
||||||
|
- fail_usage("Failed: get_nodes_list: {}".format(str(err)))
|
||||||
|
+ fail_fence_agent(options, "Failed: get_nodes_list: {}".format(str(err)))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_power_status(conn, options):
|
||||||
|
+ logging.debug("get_power_status")
|
||||||
|
+ # if this is bare metal we need to just send back the opposite of the
|
||||||
|
+ # requested action: if on send off, if off send on
|
||||||
|
+ if "--baremetalsolution" in options:
|
||||||
|
+ if options.get("--action") == "on":
|
||||||
|
+ return "off"
|
||||||
|
+ else:
|
||||||
|
+ return "on"
|
||||||
|
+ # If zone is not listed for an entry we attempt to get it automatically
|
||||||
|
+ instance = options["--plug"]
|
||||||
|
+ zone = get_zone(conn, options, instance) if "--plugzonemap" not in options else options["--plugzonemap"][instance]
|
||||||
|
+ instance_status = get_instance_power_status(conn, options, instance, zone)
|
||||||
|
+ # If any of the instances do not match the intended status we return the
|
||||||
|
+ # the opposite status so that the fence agent can change it.
|
||||||
|
+ if instance_status != options.get("--action"):
|
||||||
|
+ return instance_status
|
||||||
|
+
|
||||||
|
+ return options.get("--action")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def get_instance_power_status(conn, options, instance, zone):
|
||||||
|
try:
|
||||||
|
- instance = conn.instances().get(
|
||||||
|
- project=options["--project"],
|
||||||
|
- zone=options["--zone"],
|
||||||
|
- instance=options["--plug"]).execute()
|
||||||
|
+ instance = retry_api_execute(
|
||||||
|
+ options,
|
||||||
|
+ conn.instances().get(project=options["--project"], zone=zone, instance=instance))
|
||||||
|
return translate_status(instance["status"])
|
||||||
|
except Exception as err:
|
||||||
|
- fail_usage("Failed: get_power_status: {}".format(str(err)))
|
||||||
|
+ fail_fence_agent(options, "Failed: get_instance_power_status: {}".format(str(err)))
|
||||||
|
|
||||||
|
|
||||||
|
-def wait_for_operation(conn, project, zone, operation):
|
||||||
|
+def check_for_existing_operation(conn, options, instance, zone, operation_type):
|
||||||
|
+ logging.debug("check_for_existing_operation")
|
||||||
|
+ if "--baremetalsolution" in options:
|
||||||
|
+ # There is no API for checking in progress operations
|
||||||
|
+ return False
|
||||||
|
+
|
||||||
|
+ project = options["--project"]
|
||||||
|
+ target_link = INSTANCE_LINK.format(project, zone, instance)
|
||||||
|
+ query_filter = '(targetLink = "{}") AND (operationType = "{}") AND (status = "RUNNING")'.format(target_link, operation_type)
|
||||||
|
+ result = retry_api_execute(
|
||||||
|
+ options,
|
||||||
|
+ conn.zoneOperations().list(project=project, zone=zone, filter=query_filter, maxResults=1))
|
||||||
|
+
|
||||||
|
+ if "items" in result and result["items"]:
|
||||||
|
+ logging.info("Existing %s operation found", operation_type)
|
||||||
|
+ return result["items"][0]
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def wait_for_operation(conn, options, zone, operation):
|
||||||
|
+ if 'name' not in operation:
|
||||||
|
+ logging.warning('Cannot wait for operation to complete, the'
|
||||||
|
+ ' requested operation will continue asynchronously')
|
||||||
|
+ return False
|
||||||
|
+
|
||||||
|
+ wait_time = 0
|
||||||
|
+ project = options["--project"]
|
||||||
|
while True:
|
||||||
|
- result = conn.zoneOperations().get(
|
||||||
|
+ result = retry_api_execute(options, conn.zoneOperations().get(
|
||||||
|
project=project,
|
||||||
|
zone=zone,
|
||||||
|
- operation=operation['name']).execute()
|
||||||
|
+ operation=operation['name']))
|
||||||
|
if result['status'] == 'DONE':
|
||||||
|
if 'error' in result:
|
||||||
|
- raise Exception(result['error'])
|
||||||
|
- return
|
||||||
|
+ raise_fence_agent(options, result['error'])
|
||||||
|
+ return True
|
||||||
|
+
|
||||||
|
+ if "--errortimeout" in options and wait_time > int(options["--errortimeout"]):
|
||||||
|
+ raise_fence_agent(options, "Operation did not complete before the timeout.")
|
||||||
|
+
|
||||||
|
+ if "--warntimeout" in options and wait_time > int(options["--warntimeout"]):
|
||||||
|
+ logging.warning("Operation did not complete before the timeout.")
|
||||||
|
+ if "--runonwarn" in options:
|
||||||
|
+ run_command(options, options["--runonwarn"])
|
||||||
|
+ return False
|
||||||
|
+
|
||||||
|
+ wait_time = wait_time + 1
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
def set_power_status(conn, options):
|
||||||
|
+ logging.debug("set_power_status")
|
||||||
|
+ instance = options["--plug"]
|
||||||
|
+ # If zone is not listed for an entry we attempt to get it automatically
|
||||||
|
+ zone = get_zone(conn, options, instance) if "--plugzonemap" not in options else options["--plugzonemap"][instance]
|
||||||
|
+ set_instance_power_status(conn, options, instance, zone, options["--action"])
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def set_instance_power_status(conn, options, instance, zone, action):
|
||||||
|
+ logging.info("Setting power status of %s in zone %s", instance, zone)
|
||||||
|
+ project = options["--project"]
|
||||||
|
+
|
||||||
|
try:
|
||||||
|
- if options["--action"] == "off":
|
||||||
|
- logging.info("Issuing poweroff of %s in zone %s" % (options["--plug"], options["--zone"]))
|
||||||
|
- operation = conn.instances().stop(
|
||||||
|
- project=options["--project"],
|
||||||
|
- zone=options["--zone"],
|
||||||
|
- instance=options["--plug"]).execute()
|
||||||
|
- wait_for_operation(conn, options["--project"], options["--zone"], operation)
|
||||||
|
- logging.info("Poweroff of %s in zone %s complete" % (options["--plug"], options["--zone"]))
|
||||||
|
- elif options["--action"] == "on":
|
||||||
|
- logging.info("Issuing poweron of %s in zone %s" % (options["--plug"], options["--zone"]))
|
||||||
|
- operation = conn.instances().start(
|
||||||
|
- project=options["--project"],
|
||||||
|
- zone=options["--zone"],
|
||||||
|
- instance=options["--plug"]).execute()
|
||||||
|
- wait_for_operation(conn, options["--project"], options["--zone"], operation)
|
||||||
|
- logging.info("Poweron of %s in zone %s complete" % (options["--plug"], options["--zone"]))
|
||||||
|
+ if action == "off":
|
||||||
|
+ logging.info("Issuing poweroff of %s in zone %s", instance, zone)
|
||||||
|
+ operation = check_for_existing_operation(conn, options, instance, zone, "stop")
|
||||||
|
+ if operation and "--earlyexit" in options:
|
||||||
|
+ return
|
||||||
|
+ if not operation:
|
||||||
|
+ operation = retry_api_execute(
|
||||||
|
+ options,
|
||||||
|
+ conn.instances().stop(project=project, zone=zone, instance=instance))
|
||||||
|
+ logging.info("Poweroff command completed, waiting for the operation to complete")
|
||||||
|
+ if wait_for_operation(conn, options, zone, operation):
|
||||||
|
+ logging.info("Poweroff of %s in zone %s complete", instance, zone)
|
||||||
|
+ elif action == "on":
|
||||||
|
+ logging.info("Issuing poweron of %s in zone %s", instance, zone)
|
||||||
|
+ operation = check_for_existing_operation(conn, options, instance, zone, "start")
|
||||||
|
+ if operation and "--earlyexit" in options:
|
||||||
|
+ return
|
||||||
|
+ if not operation:
|
||||||
|
+ operation = retry_api_execute(
|
||||||
|
+ options,
|
||||||
|
+ conn.instances().start(project=project, zone=zone, instance=instance))
|
||||||
|
+ if wait_for_operation(conn, options, zone, operation):
|
||||||
|
+ logging.info("Poweron of %s in zone %s complete", instance, zone)
|
||||||
|
except Exception as err:
|
||||||
|
- fail_usage("Failed: set_power_status: {}".format(str(err)))
|
||||||
|
-
|
||||||
|
+ fail_fence_agent(options, "Failed: set_instance_power_status: {}".format(str(err)))
|
||||||
|
|
||||||
|
def power_cycle(conn, options):
|
||||||
|
+ logging.debug("power_cycle")
|
||||||
|
+ instance = options["--plug"]
|
||||||
|
+ # If zone is not listed for an entry we attempt to get it automatically
|
||||||
|
+ zone = get_zone(conn, options, instance) if "--plugzonemap" not in options else options["--plugzonemap"][instance]
|
||||||
|
+ return power_cycle_instance(conn, options, instance, zone)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def power_cycle_instance(conn, options, instance, zone):
|
||||||
|
+ logging.info("Issuing reset of %s in zone %s", instance, zone)
|
||||||
|
+ project = options["--project"]
|
||||||
|
+
|
||||||
|
try:
|
||||||
|
- logging.info('Issuing reset of %s in zone %s' % (options["--plug"], options["--zone"]))
|
||||||
|
- operation = conn.instances().reset(
|
||||||
|
- project=options["--project"],
|
||||||
|
- zone=options["--zone"],
|
||||||
|
- instance=options["--plug"]).execute()
|
||||||
|
- wait_for_operation(conn, options["--project"], options["--zone"], operation)
|
||||||
|
- logging.info('Reset of %s in zone %s complete' % (options["--plug"], options["--zone"]))
|
||||||
|
+ operation = check_for_existing_operation(conn, options, instance, zone, "reset")
|
||||||
|
+ if operation and "--earlyexit" in options:
|
||||||
|
+ return True
|
||||||
|
+ if not operation:
|
||||||
|
+ operation = retry_api_execute(
|
||||||
|
+ options,
|
||||||
|
+ conn.instances().reset(project=project, zone=zone, instance=instance))
|
||||||
|
+ logging.info("Reset command sent, waiting for the operation to complete")
|
||||||
|
+ if wait_for_operation(conn, options, zone, operation):
|
||||||
|
+ logging.info("Reset of %s in zone %s complete", instance, zone)
|
||||||
|
return True
|
||||||
|
except Exception as err:
|
||||||
|
- logging.error("Failed: power_cycle: {}".format(str(err)))
|
||||||
|
- return False
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-def get_instance(conn, project, zone, instance):
|
||||||
|
- request = conn.instances().get(
|
||||||
|
- project=project, zone=zone, instance=instance)
|
||||||
|
- return request.execute()
|
||||||
|
+ logging.exception("Failed: power_cycle")
|
||||||
|
+ raise err
|
||||||
|
|
||||||
|
|
||||||
|
-def get_zone(conn, project, instance):
|
||||||
|
+def get_zone(conn, options, instance):
|
||||||
|
+ logging.debug("get_zone");
|
||||||
|
+ project = options['--project']
|
||||||
|
fl = 'name="%s"' % instance
|
||||||
|
- request = conn.instances().aggregatedList(project=project, filter=fl)
|
||||||
|
+ request = replace_api_uri(options, conn.instances().aggregatedList(project=project, filter=fl))
|
||||||
|
while request is not None:
|
||||||
|
response = request.execute()
|
||||||
|
zones = response.get('items', {})
|
||||||
|
@@ -130,9 +309,9 @@
|
||||||
|
for inst in zone.get('instances', []):
|
||||||
|
if inst['name'] == instance:
|
||||||
|
return inst['zone'].split("/")[-1]
|
||||||
|
- request = conn.instances().aggregatedList_next(
|
||||||
|
- previous_request=request, previous_response=response)
|
||||||
|
- raise Exception("Unable to find instance %s" % (instance))
|
||||||
|
+ request = replace_api_uri(options, conn.instances().aggregatedList_next(
|
||||||
|
+ previous_request=request, previous_response=response))
|
||||||
|
+ raise_fence_agent(options, "Unable to find instance %s" % (instance))
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadata(metadata_key, params=None, timeout=None):
|
||||||
|
@@ -149,6 +328,7 @@
|
||||||
|
Raises:
|
||||||
|
urlerror.HTTPError: raises when the GET request fails.
|
||||||
|
"""
|
||||||
|
+ logging.debug("get_metadata");
|
||||||
|
timeout = timeout or 60
|
||||||
|
metadata_url = os.path.join(METADATA_SERVER, metadata_key)
|
||||||
|
params = urlparse.urlencode(params or {})
|
||||||
|
@@ -178,12 +358,50 @@
|
||||||
|
all_opt["stackdriver-logging"] = {
|
||||||
|
"getopt" : "",
|
||||||
|
"longopt" : "stackdriver-logging",
|
||||||
|
- "help" : "--stackdriver-logging Enable Logging to Stackdriver. Using stackdriver logging requires additional libraries (google-cloud-logging).",
|
||||||
|
- "shortdesc" : "Stackdriver-logging support. Requires additional libraries (google-cloud-logging).",
|
||||||
|
- "longdesc" : "If enabled IP failover logs will be posted to stackdriver logging. Using stackdriver logging requires additional libraries (google-cloud-logging).",
|
||||||
|
+ "help" : "--stackdriver-logging Enable Logging to Stackdriver",
|
||||||
|
+ "shortdesc" : "Stackdriver-logging support.",
|
||||||
|
+ "longdesc" : "If enabled IP failover logs will be posted to stackdriver logging.",
|
||||||
|
"required" : "0",
|
||||||
|
"order" : 4
|
||||||
|
}
|
||||||
|
+ all_opt["baremetalsolution"] = {
|
||||||
|
+ "getopt" : "",
|
||||||
|
+ "longopt" : "baremetalsolution",
|
||||||
|
+ "help" : "--baremetalsolution Enable on bare metal",
|
||||||
|
+ "shortdesc" : "If enabled this is a bare metal offering from google.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 5
|
||||||
|
+ }
|
||||||
|
+ 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" : 6
|
||||||
|
+ }
|
||||||
|
+ all_opt["retries"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "type" : "integer",
|
||||||
|
+ "longopt" : "retries",
|
||||||
|
+ "help" : "--retries=[retries] Number of retries on failure for API calls",
|
||||||
|
+ "shortdesc" : "Number of retries on failure for API calls, default is 3.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "default" : 3,
|
||||||
|
+ "order" : 7
|
||||||
|
+ }
|
||||||
|
+ all_opt["retrysleep"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "type" : "second",
|
||||||
|
+ "longopt" : "retrysleep",
|
||||||
|
+ "help" : "--retrysleep=[seconds] Time to sleep between API retries",
|
||||||
|
+ "shortdesc" : "Time to sleep in seconds between API retries, default is 5.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "default" : 5,
|
||||||
|
+ "order" : 8
|
||||||
|
+ }
|
||||||
|
all_opt["serviceaccount"] = {
|
||||||
|
"getopt" : ":",
|
||||||
|
"longopt" : "serviceaccount",
|
||||||
|
@@ -192,13 +410,21 @@
|
||||||
|
"required" : "0",
|
||||||
|
"order" : 9
|
||||||
|
}
|
||||||
|
+ all_opt["plugzonemap"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "longopt" : "plugzonemap",
|
||||||
|
+ "help" : "--plugzonemap=[plugzonemap] Comma separated zone map when fencing multiple plugs",
|
||||||
|
+ "shortdesc" : "Comma separated zone map when fencing multiple plugs.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 10
|
||||||
|
+ }
|
||||||
|
all_opt["proxyhost"] = {
|
||||||
|
"getopt" : ":",
|
||||||
|
"longopt" : "proxyhost",
|
||||||
|
"help" : "--proxyhost=[proxy_host] The proxy host to use, if one is needed to access the internet (Example: 10.122.0.33)",
|
||||||
|
"shortdesc" : "If a proxy is used for internet access, the proxy host should be specified.",
|
||||||
|
"required" : "0",
|
||||||
|
- "order" : 10
|
||||||
|
+ "order" : 11
|
||||||
|
}
|
||||||
|
all_opt["proxyport"] = {
|
||||||
|
"getopt" : ":",
|
||||||
|
@@ -207,7 +433,49 @@
|
||||||
|
"help" : "--proxyport=[proxy_port] The proxy port to use, if one is needed to access the internet (Example: 3127)",
|
||||||
|
"shortdesc" : "If a proxy is used for internet access, the proxy port should be specified.",
|
||||||
|
"required" : "0",
|
||||||
|
- "order" : 11
|
||||||
|
+ "order" : 12
|
||||||
|
+ }
|
||||||
|
+ all_opt["earlyexit"] = {
|
||||||
|
+ "getopt" : "",
|
||||||
|
+ "longopt" : "earlyexit",
|
||||||
|
+ "help" : "--earlyexit Return early if reset is already in progress",
|
||||||
|
+ "shortdesc" : "If an existing reset operation is detected, the fence agent will return before the operation completes with a 0 return code.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 13
|
||||||
|
+ }
|
||||||
|
+ all_opt["warntimeout"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "type" : "second",
|
||||||
|
+ "longopt" : "warntimeout",
|
||||||
|
+ "help" : "--warntimeout=[warn_timeout] Timeout seconds before logging a warning and returning a 0 status code",
|
||||||
|
+ "shortdesc" : "If the operation is not completed within the timeout, the cluster operations are allowed to continue.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 14
|
||||||
|
+ }
|
||||||
|
+ all_opt["errortimeout"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "type" : "second",
|
||||||
|
+ "longopt" : "errortimeout",
|
||||||
|
+ "help" : "--errortimeout=[error_timeout] Timeout seconds before failing and returning a non-zero status code",
|
||||||
|
+ "shortdesc" : "If the operation is not completed within the timeout, cluster is notified of the operation failure.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 15
|
||||||
|
+ }
|
||||||
|
+ all_opt["runonwarn"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "longopt" : "runonwarn",
|
||||||
|
+ "help" : "--runonwarn=[run_on_warn] If a timeout occurs and warning is generated, run the supplied command",
|
||||||
|
+ "shortdesc" : "If a timeout would occur while running the agent, then the supplied command is run.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 16
|
||||||
|
+ }
|
||||||
|
+ all_opt["runonfail"] = {
|
||||||
|
+ "getopt" : ":",
|
||||||
|
+ "longopt" : "runonfail",
|
||||||
|
+ "help" : "--runonfail=[run_on_fail] If a failure occurs, run the supplied command",
|
||||||
|
+ "shortdesc" : "If a failure would occur while running the agent, then the supplied command is run.",
|
||||||
|
+ "required" : "0",
|
||||||
|
+ "order" : 17
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -215,7 +483,9 @@
|
||||||
|
conn = None
|
||||||
|
|
||||||
|
device_opt = ["port", "no_password", "zone", "project", "stackdriver-logging",
|
||||||
|
- "method", "serviceaccount", "proxyhost", "proxyport"]
|
||||||
|
+ "method", "baremetalsolution", "apitimeout", "retries", "retrysleep",
|
||||||
|
+ "serviceaccount", "plugzonemap", "proxyhost", "proxyport", "earlyexit",
|
||||||
|
+ "warntimeout", "errortimeout", "runonwarn", "runonfail"]
|
||||||
|
|
||||||
|
atexit.register(atexit_handler)
|
||||||
|
|
||||||
|
@@ -259,6 +529,11 @@
|
||||||
|
logging.error('Couldn\'t import google.cloud.logging, '
|
||||||
|
'disabling Stackdriver-logging support')
|
||||||
|
|
||||||
|
+ # if apitimeout is defined we set the socket timeout, if not we keep the
|
||||||
|
+ # socket default which is 60s
|
||||||
|
+ if options.get("--apitimeout"):
|
||||||
|
+ socket.setdefaulttimeout(options["--apitimeout"])
|
||||||
|
+
|
||||||
|
# Prepare cli
|
||||||
|
try:
|
||||||
|
serviceaccount = options.get("--serviceaccount")
|
||||||
|
@@ -291,20 +566,39 @@
|
||||||
|
conn = googleapiclient.discovery.build(
|
||||||
|
'compute', 'v1', credentials=credentials, cache_discovery=False)
|
||||||
|
except Exception as err:
|
||||||
|
- fail_usage("Failed: Create GCE compute v1 connection: {}".format(str(err)))
|
||||||
|
+ fail_fence_agent(options, "Failed: Create GCE compute v1 connection: {}".format(str(err)))
|
||||||
|
|
||||||
|
# Get project and zone
|
||||||
|
if not options.get("--project"):
|
||||||
|
try:
|
||||||
|
options["--project"] = get_metadata('project/project-id')
|
||||||
|
except Exception as err:
|
||||||
|
- fail_usage("Failed retrieving GCE project. Please provide --project option: {}".format(str(err)))
|
||||||
|
+ fail_fence_agent(options, "Failed retrieving GCE project. Please provide --project option: {}".format(str(err)))
|
||||||
|
|
||||||
|
- if not options.get("--zone"):
|
||||||
|
- try:
|
||||||
|
- options["--zone"] = get_zone(conn, options['--project'], options['--plug'])
|
||||||
|
- except Exception as err:
|
||||||
|
- fail_usage("Failed retrieving GCE zone. Please provide --zone option: {}".format(str(err)))
|
||||||
|
+ if "--baremetalsolution" in options:
|
||||||
|
+ options["--zone"] = "none"
|
||||||
|
+
|
||||||
|
+ # Populates zone automatically if missing from the command
|
||||||
|
+ zones = [] if not "--zone" in options else options["--zone"].split(",")
|
||||||
|
+ options["--plugzonemap"] = {}
|
||||||
|
+ if "--plug" in options:
|
||||||
|
+ for i, instance in enumerate(options["--plug"].split(",")):
|
||||||
|
+ if len(zones) == 1:
|
||||||
|
+ # If only one zone is specified, use it across all plugs
|
||||||
|
+ options["--plugzonemap"][instance] = zones[0]
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ if len(zones) - 1 >= i:
|
||||||
|
+ # If we have enough zones specified with the --zone flag use the zone at
|
||||||
|
+ # the same index as the plug
|
||||||
|
+ options["--plugzonemap"][instance] = zones[i]
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ # In this case we do not have a zone specified so we attempt to detect it
|
||||||
|
+ options["--plugzonemap"][instance] = get_zone(conn, options, instance)
|
||||||
|
+ except Exception as err:
|
||||||
|
+ fail_fence_agent(options, "Failed retrieving GCE zone. Please provide --zone option: {}".format(str(err)))
|
||||||
|
|
||||||
|
# Operate the fencing device
|
||||||
|
result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list, power_cycle)
|
@ -87,7 +87,7 @@
|
|||||||
Name: fence-agents
|
Name: fence-agents
|
||||||
Summary: Set of unified programs capable of host isolation ("fencing")
|
Summary: Set of unified programs capable of host isolation ("fencing")
|
||||||
Version: 4.2.1
|
Version: 4.2.1
|
||||||
Release: 89%{?alphatag:.%{alphatag}}%{?dist}.3
|
Release: 103%{?alphatag:.%{alphatag}}%{?dist}
|
||||||
License: GPLv2+ and LGPLv2+
|
License: GPLv2+ and LGPLv2+
|
||||||
Group: System Environment/Base
|
Group: System Environment/Base
|
||||||
URL: https://github.com/ClusterLabs/fence-agents
|
URL: https://github.com/ClusterLabs/fence-agents
|
||||||
@ -136,8 +136,7 @@ Source33: %{setuptools}-%{setuptools_version}.tar.gz
|
|||||||
## required for installation
|
## required for installation
|
||||||
Source34: setuptools_scm-6.3.2.tar.gz
|
Source34: setuptools_scm-6.3.2.tar.gz
|
||||||
Source35: packaging-21.2-py3-none-any.whl
|
Source35: packaging-21.2-py3-none-any.whl
|
||||||
Source36: pyparsing-3.0.1.tar.gz
|
Source36: tomli-1.0.1.tar.gz
|
||||||
Source37: tomli-1.0.1.tar.gz
|
|
||||||
### END
|
### END
|
||||||
|
|
||||||
Patch0: fence_impilan-fence_ilo_ssh-add-ilo5-support.patch
|
Patch0: fence_impilan-fence_ilo_ssh-add-ilo5-support.patch
|
||||||
@ -249,19 +248,25 @@ Patch105: bz1977588-4-fence_kubevirt-power-timeout-40s.patch
|
|||||||
Patch106: bz1963163-fence_zvmip-add-ssl-tls-support.patch
|
Patch106: bz1963163-fence_zvmip-add-ssl-tls-support.patch
|
||||||
Patch107: bz1977588-5-fence_kubevirt-get-namespace-from-context.patch
|
Patch107: bz1977588-5-fence_kubevirt-get-namespace-from-context.patch
|
||||||
Patch108: bz2048857-fence_aws-botocore-bundled.patch
|
Patch108: bz2048857-fence_aws-botocore-bundled.patch
|
||||||
Patch109: bz2083254-fence_ibm_vpc-fix-parameters.patch
|
Patch109: bz1886074-1-fencing-source_env.patch
|
||||||
Patch110: bz2086838-1-fence_apc-fence_ilo_moonshot-import-logging.patch
|
Patch110: bz1886074-2-fence_openstack.patch
|
||||||
Patch111: bz2086838-2-fence_lpar-fix-import-fail_usage.patch
|
Patch111: bz2072421-1-all-agents-unify-ssl-parameters.patch
|
||||||
Patch112: bz2092460-fence_ibm_vpc-add-proxy-support.patch
|
Patch112: bz2078244-fence_gce-update.patch
|
||||||
Patch113: bz2100152-fence_ibm_powervs-proxy-private-api-servers.patch
|
Patch113: bz2080994-fence_ibm_vpc-fix-parameters.patch
|
||||||
|
Patch114: bz2080729-1-fence_apc-fence_ilo_moonshot-import-logging.patch
|
||||||
|
Patch115: bz2080729-2-fence_lpar-fix-import-fail_usage.patch
|
||||||
|
Patch116: bz2072421-2-fence_zvmip-connect-error.patch
|
||||||
|
Patch117: bz2091826-fence_ibm_vpc-add-proxy-support.patch
|
||||||
|
Patch118: bz2092921-fence_ibm_powervs-proxy-private-api-servers.patch
|
||||||
|
Patch119: bz1886074-4-fencing-source_env-dont-process-empty-lines.patch
|
||||||
|
|
||||||
%if 0%{?fedora} || 0%{?rhel} > 7
|
%if 0%{?fedora} || 0%{?rhel} > 7
|
||||||
%global supportedagents amt_ws apc apc_snmp bladecenter brocade cisco_mds cisco_ucs compute drac5 eaton_snmp emerson eps evacuate hds_cb hpblade ibmblade ibm_powervs ibm_vpc ifmib ilo ilo_moonshot ilo_mp ilo_ssh intelmodular ipdu ipmilan kdump kubevirt lpar mpath redfish rhevm rsa rsb sbd scsi vmware_rest vmware_soap wti
|
%global supportedagents amt_ws apc apc_snmp bladecenter brocade cisco_mds cisco_ucs compute drac5 eaton_snmp emerson eps evacuate hds_cb hpblade ibmblade ibm_powervs ibm_vpc ifmib ilo ilo_moonshot ilo_mp ilo_ssh intelmodular ipdu ipmilan kdump kubevirt lpar mpath redfish rhevm rsa rsb sbd scsi vmware_rest vmware_soap wti
|
||||||
%ifarch x86_64
|
%ifarch x86_64
|
||||||
%global testagents virsh heuristics_ping aliyun aws azure_arm gce
|
%global testagents virsh heuristics_ping aliyun aws azure_arm gce openstack
|
||||||
%endif
|
%endif
|
||||||
%ifarch ppc64le
|
%ifarch ppc64le
|
||||||
%global testagents virsh heuristics_ping
|
%global testagents virsh heuristics_ping openstack
|
||||||
%endif
|
%endif
|
||||||
%ifarch s390x
|
%ifarch s390x
|
||||||
%global testagents virsh zvm heuristics_ping
|
%global testagents virsh zvm heuristics_ping
|
||||||
@ -445,6 +450,12 @@ BuildRequires: python3-google-api-client python3-pip python3-wheel python3-jinja
|
|||||||
%patch111 -p1
|
%patch111 -p1
|
||||||
%patch112 -p1
|
%patch112 -p1
|
||||||
%patch113 -p1
|
%patch113 -p1
|
||||||
|
%patch114 -p1
|
||||||
|
%patch115 -p1
|
||||||
|
%patch116 -p1
|
||||||
|
%patch117 -p1
|
||||||
|
%patch118 -p1
|
||||||
|
%patch119 -p1
|
||||||
|
|
||||||
# prevent compilation of something that won't get used anyway
|
# prevent compilation of something that won't get used anyway
|
||||||
sed -i.orig 's|FENCE_ZVM=1|FENCE_ZVM=0|' configure.ac
|
sed -i.orig 's|FENCE_ZVM=1|FENCE_ZVM=0|' configure.ac
|
||||||
@ -1227,6 +1238,23 @@ Device Mapper Multipath.
|
|||||||
%{_datadir}/cluster/fence_mpath_check*
|
%{_datadir}/cluster/fence_mpath_check*
|
||||||
%{_mandir}/man8/fence_mpath.8*
|
%{_mandir}/man8/fence_mpath.8*
|
||||||
|
|
||||||
|
%ifarch x86_64 ppc64le
|
||||||
|
%package openstack
|
||||||
|
License: GPLv2+ and LGPLv2+ and ASL 2.0 and MIT and Python
|
||||||
|
Summary: Fence agent for OpenStack's Nova service
|
||||||
|
%if 0%{?fedora} || 0%{?centos} > 7 || 0%{?rhel} > 7 || 0%{?suse_version}
|
||||||
|
Requires: python3-requests
|
||||||
|
%else
|
||||||
|
Requires: python-requests
|
||||||
|
%endif
|
||||||
|
Requires: fence-agents-common = %{version}-%{release}
|
||||||
|
%description openstack
|
||||||
|
Fence agent for OpenStack's Nova service.
|
||||||
|
%files openstack
|
||||||
|
%{_sbindir}/fence_openstack
|
||||||
|
%{_mandir}/man8/fence_openstack.8*
|
||||||
|
%endif
|
||||||
|
|
||||||
%package redfish
|
%package redfish
|
||||||
License: GPLv2+ and LGPLv2+
|
License: GPLv2+ and LGPLv2+
|
||||||
Group: System Environment/Base
|
Group: System Environment/Base
|
||||||
@ -1411,23 +1439,38 @@ Fence agent for IBM z/VM over IP.
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Thu Jun 23 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-89.3
|
* Tue Aug 16 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-103
|
||||||
- fence_ibm_vpc: add proxy support
|
- fence_openstack: new fence agent
|
||||||
Resolves: rhbz#2092460
|
Resolves: rhbz#1886074
|
||||||
|
|
||||||
|
* Wed Jun 22 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-99
|
||||||
- fence_ibm_powervs: add support for proxy, private API servers and
|
- fence_ibm_powervs: add support for proxy, private API servers and
|
||||||
get token via API key
|
get token via API key
|
||||||
Resolves: rhbz#2100152
|
Resolves: rhbz#2092921
|
||||||
|
|
||||||
* Tue May 17 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-89.2
|
* Tue Jun 7 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-98
|
||||||
|
- fence_ibm_vpc: add proxy support
|
||||||
|
Resolves: rhbz#2091826
|
||||||
|
|
||||||
|
* Tue May 31 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-96
|
||||||
|
- all agents: unify ssl parameters to avoid having to use --ssl when
|
||||||
|
using --ssl-secure/--ssl-insecure for some agents
|
||||||
|
Resolves: rhbz#2072421
|
||||||
|
|
||||||
|
* Tue May 17 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-95
|
||||||
- fence_apc/fence_ilo_moonshot/fence_lpar: add missing "import logging"
|
- fence_apc/fence_ilo_moonshot/fence_lpar: add missing "import logging"
|
||||||
Resolves: rhbz#2086838
|
Resolves: rhbz#2080729
|
||||||
|
|
||||||
* Tue May 10 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-89.1
|
* Thu May 5 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-94
|
||||||
- fence_ibm_vpc: remove unused instance parameter and make limit
|
- fence_ibm_vpc: remove unused instance parameter and make limit
|
||||||
optional
|
optional
|
||||||
Resolves: rhbz#2083254
|
Resolves: rhbz#2080994
|
||||||
|
|
||||||
* Fri Mar 4 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-89
|
* Thu Apr 28 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-93
|
||||||
|
- fence_gce: update fence agent
|
||||||
|
Resolves: rhbz#2078244
|
||||||
|
|
||||||
|
* Wed Apr 27 2022 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-92
|
||||||
- fence_ibm_vpc: new fence agent
|
- fence_ibm_vpc: new fence agent
|
||||||
Resolves: rhbz#1728203
|
Resolves: rhbz#1728203
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user