diff --git a/SOURCES/bz2000954-3-fence_kubevirt-get-namespace-from-context.patch b/SOURCES/bz2000954-3-fence_kubevirt-get-namespace-from-context.patch new file mode 100644 index 0000000..799bf31 --- /dev/null +++ b/SOURCES/bz2000954-3-fence_kubevirt-get-namespace-from-context.patch @@ -0,0 +1,102 @@ +From 647841dea9d93922779a4aa7d0b5f52f5bc2b4e9 Mon Sep 17 00:00:00 2001 +From: Dan Kenigsberg +Date: Thu, 13 Jan 2022 14:57:26 +0200 +Subject: [PATCH] fence_kubevirt: take default namespace from context + +If --namespace is not provided to kubectl, a default one is taken from +kubeconfig context. Let fence_kubevirt behave similarly. + +Signed-off-by: Dan Kenigsberg +--- + agents/kubevirt/fence_kubevirt.py | 24 +++++++++++++----------- + tests/data/metadata/fence_kubevirt.xml | 2 +- + 2 files changed, 14 insertions(+), 12 deletions(-) + +diff --git a/agents/kubevirt/fence_kubevirt.py b/agents/kubevirt/fence_kubevirt.py +index 8392b75a0..8c27a0334 100755 +--- a/agents/kubevirt/fence_kubevirt.py ++++ b/agents/kubevirt/fence_kubevirt.py +@@ -12,12 +12,21 @@ + except ImportError: + logging.error("Couldn\'t import kubernetes.client.exceptions.ApiException - not found or not accessible") + ++def _get_namespace(options): ++ from kubernetes import config ++ ++ ns = options.get("--namespace") ++ if ns is None: ++ ns = config.kube_config.list_kube_config_contexts()[1]['context']['namespace'] ++ ++ return ns ++ + def get_nodes_list(conn, options): + logging.debug("Starting list/monitor operation") + result = {} + try: + apiversion = options.get("--apiversion") +- namespace = options.get("--namespace") ++ namespace = _get_namespace(options) + include_uninitialized = True + vm_api = conn.resources.get(api_version=apiversion, kind='VirtualMachine') + vm_list = vm_api.get(namespace=namespace) +@@ -31,7 +40,7 @@ def get_power_status(conn, options): + logging.debug("Starting get status operation") + try: + apiversion = options.get("--apiversion") +- namespace = options.get("--namespace") ++ namespace = _get_namespace(options) + name = options.get("--plug") + vmi_api = conn.resources.get(api_version=apiversion, + kind='VirtualMachineInstance') +@@ -61,7 +70,7 @@ def set_power_status(conn, options): + logging.debug("Starting set status operation") + try: + apiversion= options.get("--apiversion") +- namespace = options.get("--namespace") ++ namespace = _get_namespace(options) + name = options.get("--plug") + action = 'start' if options["--action"] == "on" else 'stop' + virtctl_vm_action(conn, action, namespace, name, apiversion) +@@ -75,7 +84,7 @@ def define_new_opts(): + "longopt" : "namespace", + "help" : "--namespace=[namespace] Namespace of the KubeVirt machine", + "shortdesc" : "Namespace of the KubeVirt machine.", +- "required" : "1", ++ "required" : "0", + "order" : 2 + } + all_opt["kubeconfig"] = { +@@ -101,11 +110,6 @@ def virtctl_vm_action(conn, action, namespace, name, apiversion): + path = path.format(api_version=apiversion, namespace=namespace, name=name, action=action) + return conn.request('put', path, header_params={'accept': '*/*'}) + +-def validate_options(required_options_list, options): +- for required_option in required_options_list: +- if required_option not in options: +- fail_usage("Failed: %s option must be provided" % required_option) +- + # Main agent method + def main(): + conn = None +@@ -127,8 +131,6 @@ def main(): + + run_delay(options) + +- validate_options(['--namespace'], options) +- + # Disable insecure-certificate-warning message + if "--ssl-insecure" in options: + import urllib3 +diff --git a/tests/data/metadata/fence_kubevirt.xml b/tests/data/metadata/fence_kubevirt.xml +index 24e975587..ccb20c224 100644 +--- a/tests/data/metadata/fence_kubevirt.xml ++++ b/tests/data/metadata/fence_kubevirt.xml +@@ -23,7 +23,7 @@ + + Use SSL connection without verifying certificate + +- ++ + + + Namespace of the KubeVirt machine. diff --git a/SOURCES/bz2022334-fence_zvmip-add-disable-ssl.patch b/SOURCES/bz2022334-fence_zvmip-add-disable-ssl.patch new file mode 100644 index 0000000..916391d --- /dev/null +++ b/SOURCES/bz2022334-fence_zvmip-add-disable-ssl.patch @@ -0,0 +1,98 @@ +From 90c4f78e005ca4141039d1dce032b5f2c2ff4783 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Thu, 10 Feb 2022 12:22:58 +0100 +Subject: [PATCH 1/2] fencing: add ability to set bool parameters to 0 or false + +--- + lib/fencing.py.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/fencing.py.py b/lib/fencing.py.py +index 55e38c407..696388d55 100644 +--- a/lib/fencing.py.py ++++ b/lib/fencing.py.py +@@ -1502,6 +1502,8 @@ def _parse_input_stdin(avail_opt): + opt["--"+all_opt[name]["longopt"].rstrip(":")] = value + elif value.lower() in ["1", "yes", "on", "true"]: + opt["--"+all_opt[name]["longopt"]] = "1" ++ elif value.lower() in ["0", "no", "off", "false"]: ++ opt["--"+all_opt[name]["longopt"]] = "0" + else: + logging.warning("Parse error: Ignoring option '%s' because it does not have value\n", name) + + +From 249abc8e5620fb1a3d97a0af6db34b1f2cbf3ae5 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Thu, 10 Feb 2022 12:27:02 +0100 +Subject: [PATCH 2/2] fence_zvmip: add --disable-ssl + +--- + agents/zvm/fence_zvmip.py | 18 +++++++++++++++++- + tests/data/metadata/fence_zvmip.xml | 7 ++++++- + 2 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/agents/zvm/fence_zvmip.py b/agents/zvm/fence_zvmip.py +index 96021b13e..e8f849eda 100644 +--- a/agents/zvm/fence_zvmip.py ++++ b/agents/zvm/fence_zvmip.py +@@ -156,11 +156,24 @@ def get_list_of_images(options, command, data_as_plug): + conn.close() + return (return_code, reason_code, images) + ++def define_new_opts(): ++ all_opt["disable_ssl"] = { ++ "getopt" : "", ++ "longopt" : "disable-ssl", ++ "help" : "--disable-ssl Don't use SSL connection", ++ "required" : "0", ++ "shortdesc" : "Don't use SSL", ++ "order": 2 ++ } ++ + def main(): + device_opt = ["ipaddr", "login", "passwd", "port", "method", "missing_as_off", +- "inet4_only", "inet6_only", "ssl"] ++ "inet4_only", "inet6_only", "ssl", "disable_ssl"] + + atexit.register(atexit_handler) ++ define_new_opts() ++ ++ all_opt["ssl"]["help"] = "-z, --ssl Use SSL connection with verifying certificate (Default)" + + all_opt["ipport"]["default"] = "44444" + all_opt["shell_timeout"]["default"] = "5" +@@ -168,6 +181,9 @@ def main(): + all_opt["ssl"]["default"] = "1" + options = check_input(device_opt, process_input(device_opt), other_conditions=True) + ++ if "--disable-ssl" in options or options["--ssl"] == "0": ++ del options["--ssl"] ++ + if len(options.get("--plug", "")) > 8: + fail_usage("Failed: Name of image can not be longer than 8 characters") + +diff --git a/tests/data/metadata/fence_zvmip.xml b/tests/data/metadata/fence_zvmip.xml +index f32fc159d..0b7ba4785 100644 +--- a/tests/data/metadata/fence_zvmip.xml ++++ b/tests/data/metadata/fence_zvmip.xml +@@ -94,7 +94,7 @@ to access the system's directory manager. + + + +- Use SSL connection with verifying certificate ++ Use SSL connection with verifying certificate (Default) + + + +@@ -111,6 +111,11 @@ to access the system's directory manager. + + Login name + ++ ++ ++ ++ Don't use SSL ++ + + + diff --git a/SOURCES/bz2041933-bz2041935-1-fence_openstack-clouds-openrc.patch b/SOURCES/bz2041933-bz2041935-1-fence_openstack-clouds-openrc.patch new file mode 100644 index 0000000..06363d8 --- /dev/null +++ b/SOURCES/bz2041933-bz2041935-1-fence_openstack-clouds-openrc.patch @@ -0,0 +1,313 @@ +From 2f741df2ce73da85bbd205d861b527aa141d9776 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Fri, 14 Jan 2022 14:47:41 +0100 +Subject: [PATCH 1/2] fencing: add source_env() + +--- + lib/fencing.py.py | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/lib/fencing.py.py b/lib/fencing.py.py +index d85b23568..55e38c407 100644 +--- a/lib/fencing.py.py ++++ b/lib/fencing.py.py +@@ -1143,6 +1143,14 @@ def fence_logout(conn, logout_string, sleep=0): + 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): + +From fe2183a97e0a5734702e9cba8da21f01afd8f577 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Fri, 14 Jan 2022 14:54:10 +0100 +Subject: [PATCH 2/2] fence_openstack: add support for reading config from + clouds.yaml and openrc + +--- + agents/openstack/fence_openstack.py | 116 ++++++++++++++++++++---- + tests/data/metadata/fence_openstack.xml | 32 +++++-- + 2 files changed, 126 insertions(+), 22 deletions(-) + mode change 100755 => 100644 agents/openstack/fence_openstack.py + +diff --git a/agents/openstack/fence_openstack.py b/agents/openstack/fence_openstack.py +old mode 100755 +new mode 100644 +index 36b353b52..d3a4be3aa +--- a/agents/openstack/fence_openstack.py ++++ b/agents/openstack/fence_openstack.py +@@ -8,7 +8,7 @@ + + sys.path.append("@FENCEAGENTSLIBDIR@") + from fencing import * +-from fencing import fail_usage, run_delay ++from fencing import fail_usage, run_delay, source_env + + try: + from novaclient import client +@@ -26,6 +26,23 @@ def translate_status(instance_status): + return "off" + return "unknown" + ++def get_cloud(options): ++ import os, yaml ++ ++ clouds_yaml = os.path.expanduser("~/.config/openstack/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"]) +@@ -153,7 +170,7 @@ def define_new_opts(): + "getopt": ":", + "longopt": "auth-url", + "help": "--auth-url=[authurl] Keystone Auth URL", +- "required": "1", ++ "required": "0", + "shortdesc": "Keystone Auth URL", + "order": 2, + } +@@ -161,7 +178,7 @@ def define_new_opts(): + "getopt": ":", + "longopt": "project-name", + "help": "--project-name=[project] Tenant Or Project Name", +- "required": "1", ++ "required": "0", + "shortdesc": "Keystone Project", + "default": "admin", + "order": 3, +@@ -184,13 +201,38 @@ def define_new_opts(): + "default": "Default", + "order": 5, + } ++ all_opt["clouds-yaml"] = { ++ "getopt": ":", ++ "longopt": "clouds-yaml", ++ "help": "--clouds-yaml=[clouds-yaml] Path to the clouds.yaml config file", ++ "required": "0", ++ "shortdesc": "clouds.yaml config file", ++ "default": "~/.config/openstack/clouds.yaml", ++ "order": 6, ++ } ++ all_opt["cloud"] = { ++ "getopt": ":", ++ "longopt": "cloud", ++ "help": "--cloud=[cloud] Openstack cloud (from clouds.yaml).", ++ "required": "0", ++ "shortdesc": "Cloud from clouds.yaml", ++ "order": 7, ++ } ++ all_opt["openrc"] = { ++ "getopt": ":", ++ "longopt": "openrc", ++ "help": "--openrc=[openrc] Path to the openrc config file", ++ "required": "0", ++ "shortdesc": "openrc config file", ++ "order": 8, ++ } + all_opt["uuid"] = { + "getopt": ":", + "longopt": "uuid", + "help": "--uuid=[uuid] Replaced by -n, --plug", + "required": "0", + "shortdesc": "Replaced by port/-n/--plug", +- "order": 6, ++ "order": 9, + } + all_opt["cacert"] = { + "getopt": ":", +@@ -199,7 +241,7 @@ def define_new_opts(): + "required": "0", + "shortdesc": "SSL X.509 certificates file", + "default": "", +- "order": 7, ++ "order": 10, + } + all_opt["apitimeout"] = { + "getopt": ":", +@@ -209,7 +251,7 @@ def define_new_opts(): + "shortdesc": "Timeout in seconds to use for API calls, default is 60.", + "required": "0", + "default": 60, +- "order": 8, ++ "order": 11, + } + + +@@ -218,11 +260,16 @@ def main(): + + device_opt = [ + "login", ++ "no_login", + "passwd", ++ "no_password", + "auth-url", + "project-name", + "user-domain-name", + "project-domain-name", ++ "clouds-yaml", ++ "cloud", ++ "openrc", + "port", + "no_port", + "uuid", +@@ -265,19 +312,56 @@ def main(): + + 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"] ++ if options.get("--clouds-yaml"): ++ if not os.path.exists(os.path.expanduser(options["--clouds-yaml"])): ++ fail_usage("Failed: {} does not exist".format(options.get("--clouds-yaml"))) ++ if not options.get("--cloud"): ++ fail_usage("Failed: \"cloud\" not specified") ++ cloud = get_cloud(options) ++ username = cloud.get("username") ++ password = cloud.get("password") ++ projectname = cloud.get("project_name") ++ auth_url = None ++ try: ++ auth_url = cloud.get("auth_url") ++ except KeyError: ++ fail_usage("Failed: You have to set the Keystone service endpoint for authorization") ++ user_domain_name = cloud.get("user_domain_name") ++ project_domain_name = cloud.get("project_domain_name") ++ caverify = cloud.get("verify") ++ if caverify in [True, False]: ++ options["--ssl-insecure"] = caverify ++ else: ++ options["--cacert"] = caverify ++ if options.get("--openrc") and os.path.exists(os.path.expanduser(options["--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, +diff --git a/tests/data/metadata/fence_openstack.xml b/tests/data/metadata/fence_openstack.xml +index c8dc2e60f..55a57b4d7 100644 +--- a/tests/data/metadata/fence_openstack.xml ++++ b/tests/data/metadata/fence_openstack.xml +@@ -8,7 +8,7 @@ + + Fencing action + +- ++ + + + Login name +@@ -48,27 +48,27 @@ + + Use SSL connection without verifying certificate + +- ++ + + + Login name + +- ++ + + + Keystone Auth URL + +- ++ + + + Keystone Auth URL + +- ++ + + + Keystone Project + +- ++ + + + Keystone Project +@@ -93,6 +93,26 @@ + + Keystone Project Domain Name + ++ ++ ++ ++ clouds.yaml config file ++ ++ ++ ++ ++ clouds.yaml config file ++ ++ ++ ++ ++ Cloud from clouds.yaml ++ ++ ++ ++ ++ openrc config file ++ + + + diff --git a/SOURCES/bz2041933-bz2041935-2-fence_openstack-clouds-openrc.patch b/SOURCES/bz2041933-bz2041935-2-fence_openstack-clouds-openrc.patch new file mode 100644 index 0000000..6daa2bb --- /dev/null +++ b/SOURCES/bz2041933-bz2041935-2-fence_openstack-clouds-openrc.patch @@ -0,0 +1,171 @@ +From 7d9572ec947d23fa18ac530f07fe33ba148c9634 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Mon, 17 Jan 2022 14:32:53 +0100 +Subject: [PATCH] fence_openstack: fix issues with new clouds.yaml/openrc + parameters - hardcoded clouds.yaml paths to work like the openstack cli + client (used by the resource agents) + +--- + agents/openstack/fence_openstack.py | 55 +++++++++++-------------- + tests/data/metadata/fence_openstack.xml | 10 ----- + 2 files changed, 25 insertions(+), 40 deletions(-) + +diff --git a/agents/openstack/fence_openstack.py b/agents/openstack/fence_openstack.py +index d3a4be3aa..666016d78 100644 +--- a/agents/openstack/fence_openstack.py ++++ b/agents/openstack/fence_openstack.py +@@ -3,6 +3,7 @@ + import atexit + import logging + import sys ++import os + + import urllib3 + +@@ -27,9 +28,15 @@ def translate_status(instance_status): + return "unknown" + + def get_cloud(options): +- import os, yaml ++ import yaml + +- clouds_yaml = os.path.expanduser("~/.config/openstack/clouds.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: +@@ -201,22 +208,13 @@ def define_new_opts(): + "default": "Default", + "order": 5, + } +- all_opt["clouds-yaml"] = { +- "getopt": ":", +- "longopt": "clouds-yaml", +- "help": "--clouds-yaml=[clouds-yaml] Path to the clouds.yaml config file", +- "required": "0", +- "shortdesc": "clouds.yaml config file", +- "default": "~/.config/openstack/clouds.yaml", +- "order": 6, +- } + all_opt["cloud"] = { + "getopt": ":", + "longopt": "cloud", +- "help": "--cloud=[cloud] Openstack cloud (from clouds.yaml).", ++ "help": "--cloud=[cloud] Openstack cloud (from ~/.config/openstack/clouds.yaml or /etc/openstack/clouds.yaml).", + "required": "0", + "shortdesc": "Cloud from clouds.yaml", +- "order": 7, ++ "order": 6, + } + all_opt["openrc"] = { + "getopt": ":", +@@ -224,7 +222,7 @@ def define_new_opts(): + "help": "--openrc=[openrc] Path to the openrc config file", + "required": "0", + "shortdesc": "openrc config file", +- "order": 8, ++ "order": 7, + } + all_opt["uuid"] = { + "getopt": ":", +@@ -232,7 +230,7 @@ def define_new_opts(): + "help": "--uuid=[uuid] Replaced by -n, --plug", + "required": "0", + "shortdesc": "Replaced by port/-n/--plug", +- "order": 9, ++ "order": 8, + } + all_opt["cacert"] = { + "getopt": ":", +@@ -241,7 +239,7 @@ def define_new_opts(): + "required": "0", + "shortdesc": "SSL X.509 certificates file", + "default": "", +- "order": 10, ++ "order": 9, + } + all_opt["apitimeout"] = { + "getopt": ":", +@@ -251,7 +249,7 @@ def define_new_opts(): + "shortdesc": "Timeout in seconds to use for API calls, default is 60.", + "required": "0", + "default": 60, +- "order": 11, ++ "order": 10, + } + + +@@ -267,7 +265,6 @@ def main(): + "project-name", + "user-domain-name", + "project-domain-name", +- "clouds-yaml", + "cloud", + "openrc", + "port", +@@ -312,28 +309,26 @@ def main(): + + run_delay(options) + +- if options.get("--clouds-yaml"): +- if not os.path.exists(os.path.expanduser(options["--clouds-yaml"])): +- fail_usage("Failed: {} does not exist".format(options.get("--clouds-yaml"))) +- if not options.get("--cloud"): +- fail_usage("Failed: \"cloud\" not specified") ++ if options.get("--cloud"): + cloud = get_cloud(options) +- username = cloud.get("username") +- password = cloud.get("password") +- projectname = cloud.get("project_name") ++ 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_url") ++ 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("user_domain_name") +- project_domain_name = cloud.get("project_domain_name") ++ 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 +- if options.get("--openrc") and os.path.exists(os.path.expanduser(options["--openrc"])): ++ 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") +diff --git a/tests/data/metadata/fence_openstack.xml b/tests/data/metadata/fence_openstack.xml +index 55a57b4d7..67b2191b7 100644 +--- a/tests/data/metadata/fence_openstack.xml ++++ b/tests/data/metadata/fence_openstack.xml +@@ -93,16 +93,6 @@ + + Keystone Project Domain Name + +- +- +- +- clouds.yaml config file +- +- +- +- +- clouds.yaml config file +- + + + diff --git a/SOURCES/bz2042496-fence_ibm_vpc-fence_ibm_powervs.patch b/SOURCES/bz2042496-fence_ibm_vpc-fence_ibm_powervs.patch new file mode 100644 index 0000000..d68612c --- /dev/null +++ b/SOURCES/bz2042496-fence_ibm_vpc-fence_ibm_powervs.patch @@ -0,0 +1,740 @@ +From 3078e4d55d3bad2bbf9309785fdb2b53afac8d65 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Tue, 13 Jul 2021 13:39:33 +0200 +Subject: [PATCH] fence_ibm_vpc/fence_ibm_powervs: new fence agents + +--- + agents/ibm_powervs/fence_ibm_powervs.py | 202 +++++++++++++++++++ + agents/ibm_vpc/fence_ibm_vpc.py | 230 ++++++++++++++++++++++ + tests/data/metadata/fence_ibm_powervs.xml | 134 +++++++++++++ + tests/data/metadata/fence_ibm_vpc.xml | 134 +++++++++++++ + 5 files changed, 724 insertions(+) + create mode 100755 agents/ibm_powervs/fence_ibm_powervs.py + create mode 100755 agents/ibm_vpc/fence_ibm_vpc.py + create mode 100644 tests/data/metadata/fence_ibm_powervs.xml + create mode 100644 tests/data/metadata/fence_ibm_vpc.xml + +diff --git a/agents/ibm_powervs/fence_ibm_powervs.py b/agents/ibm_powervs/fence_ibm_powervs.py +new file mode 100755 +index 000000000..6649771ea +--- /dev/null ++++ b/agents/ibm_powervs/fence_ibm_powervs.py +@@ -0,0 +1,202 @@ ++#!@PYTHON@ -tt ++ ++import sys ++import pycurl, io, json ++import logging ++import atexit ++sys.path.append("@FENCEAGENTSLIBDIR@") ++from fencing import * ++from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS ++ ++state = { ++ "ACTIVE": "on", ++ "SHUTOFF": "off", ++ "ERROR": "unknown" ++} ++ ++def get_list(conn, options): ++ outlets = {} ++ ++ try: ++ command = "cloud-instances/{}/pvm-instances".format(options["--instance"]) ++ res = send_command(conn, command) ++ except Exception as e: ++ logging.debug("Failed: {}".format(e)) ++ return outlets ++ ++ for r in res["pvmInstances"]: ++ if "--verbose" in options: ++ logging.debug(json.dumps(r, indent=2)) ++ outlets[r["pvmInstanceID"]] = (r["serverName"], state[r["status"]]) ++ ++ return outlets ++ ++def get_power_status(conn, options): ++ try: ++ command = "cloud-instances/{}/pvm-instances/{}".format( ++ options["--instance"], options["--plug"]) ++ res = send_command(conn, command) ++ result = get_list(conn, options)[options["--plug"]][1] ++ except KeyError as e: ++ logging.debug("Failed: Unable to get status for {}".format(e)) ++ fail(EC_STATUS) ++ ++ return result ++ ++def set_power_status(conn, options): ++ action = { ++ "on" : '{"action" : "start"}', ++ "off" : '{"action" : "immediate-shutdown"}', ++ }[options["--action"]] ++ ++ try: ++ send_command(conn, "cloud-instances/{}/pvm-instances/{}/action".format( ++ options["--instance"], options["--plug"]), "POST", action) ++ except Exception as e: ++ logging.debug("Failed: Unable to set power to {} for {}".format(options["--action"], e)) ++ fail(EC_STATUS) ++ ++def connect(opt): ++ conn = pycurl.Curl() ++ ++ ## setup correct URL ++ conn.base_url = "https://" + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" ++ ++ if opt["--verbose-level"] > 1: ++ conn.setopt(pycurl.VERBOSE, 1) ++ ++ conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"])) ++ conn.setopt(pycurl.SSL_VERIFYPEER, 1) ++ conn.setopt(pycurl.SSL_VERIFYHOST, 2) ++ ++ # set auth token for later requests ++ conn.setopt(pycurl.HTTPHEADER, [ ++ "Content-Type: application/json", ++ "Authorization: Bearer {}".format(opt["--token"]), ++ "CRN: {}".format(opt["--crn"]), ++ "User-Agent: curl", ++ ]) ++ ++ return conn ++ ++def disconnect(conn): ++ conn.close() ++ ++def send_command(conn, command, method="GET", action=None): ++ url = conn.base_url + command ++ ++ conn.setopt(pycurl.URL, url.encode("ascii")) ++ ++ web_buffer = io.BytesIO() ++ ++ if method == "GET": ++ conn.setopt(pycurl.POST, 0) ++ if method == "POST": ++ conn.setopt(pycurl.POSTFIELDS, action) ++ if method == "DELETE": ++ conn.setopt(pycurl.CUSTOMREQUEST, "DELETE") ++ ++ conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) ++ ++ try: ++ conn.perform() ++ except Exception as e: ++ raise(e) ++ ++ rc = conn.getinfo(pycurl.HTTP_CODE) ++ result = web_buffer.getvalue().decode("UTF-8") ++ ++ web_buffer.close() ++ ++ if rc != 200: ++ if len(result) > 0: ++ raise Exception("{}: {}".format(rc, ++ result["value"]["messages"][0]["default_message"])) ++ else: ++ raise Exception("Remote returned {} for request to {}".format(rc, url)) ++ ++ if len(result) > 0: ++ result = json.loads(result) ++ ++ logging.debug("url: {}".format(url)) ++ logging.debug("method: {}".format(method)) ++ logging.debug("response code: {}".format(rc)) ++ logging.debug("result: {}\n".format(result)) ++ ++ return result ++ ++def define_new_opts(): ++ all_opt["token"] = { ++ "getopt" : ":", ++ "longopt" : "token", ++ "help" : "--token=[token] Bearer Token", ++ "required" : "1", ++ "shortdesc" : "Bearer Token", ++ "order" : 0 ++ } ++ all_opt["crn"] = { ++ "getopt" : ":", ++ "longopt" : "crn", ++ "help" : "--crn=[crn] CRN", ++ "required" : "1", ++ "shortdesc" : "CRN", ++ "order" : 0 ++ } ++ all_opt["instance"] = { ++ "getopt" : ":", ++ "longopt" : "instance", ++ "help" : "--instance=[instance] PowerVS Instance", ++ "required" : "1", ++ "shortdesc" : "PowerVS Instance", ++ "order" : 0 ++ } ++ all_opt["region"] = { ++ "getopt" : ":", ++ "longopt" : "region", ++ "help" : "--region=[region] Region", ++ "required" : "1", ++ "shortdesc" : "Region", ++ "order" : 0 ++ } ++ ++ ++def main(): ++ device_opt = [ ++ "token", ++ "crn", ++ "instance", ++ "region", ++ "port", ++ "no_password", ++ ] ++ ++ atexit.register(atexit_handler) ++ define_new_opts() ++ ++ all_opt["shell_timeout"]["default"] = "15" ++ all_opt["power_timeout"]["default"] = "30" ++ all_opt["power_wait"]["default"] = "1" ++ ++ options = check_input(device_opt, process_input(device_opt)) ++ ++ docs = {} ++ docs["shortdesc"] = "Fence agent for IBM PowerVS" ++ docs["longdesc"] = """fence_ibm_powervs is an I/O Fencing agent which can be \ ++used with IBM PowerVS to fence virtual machines.""" ++ docs["vendorurl"] = "https://www.ibm.com" ++ show_docs(options, docs) ++ ++ #### ++ ## Fence operations ++ #### ++ run_delay(options) ++ ++ conn = connect(options) ++ atexit.register(disconnect, conn) ++ ++ result = fence_action(conn, options, set_power_status, get_power_status, get_list) ++ ++ sys.exit(result) ++ ++if __name__ == "__main__": ++ main() +diff --git a/agents/ibm_vpc/fence_ibm_vpc.py b/agents/ibm_vpc/fence_ibm_vpc.py +new file mode 100755 +index 000000000..9f84f7b2d +--- /dev/null ++++ b/agents/ibm_vpc/fence_ibm_vpc.py +@@ -0,0 +1,230 @@ ++#!@PYTHON@ -tt ++ ++import sys ++import pycurl, io, json ++import logging ++import atexit ++sys.path.append("@FENCEAGENTSLIBDIR@") ++from fencing import * ++from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS ++ ++state = { ++ "running": "on", ++ "stopped": "off", ++ "starting": "unknown", ++ "stopping": "unknown", ++ "restarting": "unknown", ++ "pending": "unknown", ++} ++ ++def get_list(conn, options): ++ outlets = {} ++ ++ try: ++ command = "instances?version=2021-05-25&generation=2&limit={}".format(options["--limit"]) ++ res = send_command(conn, command) ++ except Exception as e: ++ logging.debug("Failed: Unable to get list: {}".format(e)) ++ return outlets ++ ++ for r in res["instances"]: ++ if options["--verbose-level"] > 1: ++ logging.debug("Node:\n{}".format(json.dumps(r, indent=2))) ++ logging.debug("Status: " + state[r["status"]]) ++ outlets[r["id"]] = (r["name"], state[r["status"]]) ++ ++ return outlets ++ ++def get_power_status(conn, options): ++ try: ++ command = "instances/{}?version=2021-05-25&generation=2".format(options["--plug"]) ++ res = send_command(conn, command) ++ result = state[res["status"]] ++ if options["--verbose-level"] > 1: ++ logging.debug("Result:\n{}".format(json.dumps(res, indent=2))) ++ logging.debug("Status: " + result) ++ except Exception as e: ++ logging.debug("Failed: Unable to get status for {}: {}".format(options["--plug"], e)) ++ fail(EC_STATUS) ++ ++ return result ++ ++def set_power_status(conn, options): ++ action = { ++ "on" : '{"type" : "start"}', ++ "off" : '{"type" : "stop"}', ++ }[options["--action"]] ++ ++ try: ++ command = "instances/{}/actions?version=2021-05-25&generation=2".format(options["--plug"]) ++ send_command(conn, command, "POST", action, 201) ++ except Exception as e: ++ logging.debug("Failed: Unable to set power to {} for {}".format(options["--action"], e)) ++ fail(EC_STATUS) ++ ++def get_bearer_token(conn, options): ++ token = None ++ try: ++ conn.setopt(pycurl.HTTPHEADER, [ ++ "Content-Type: application/x-www-form-urlencoded", ++ "User-Agent: curl", ++ ]) ++ token = send_command(conn, "https://iam.cloud.ibm.com/identity/token", "POST", "grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}".format(options["--apikey"]))["access_token"] ++ except Exception: ++ logging.error("Failed: Unable to authenticate") ++ fail(EC_LOGIN_DENIED) ++ ++ return token ++ ++def connect(opt): ++ conn = pycurl.Curl() ++ ++ ## setup correct URL ++ conn.base_url = "https://" + opt["--region"] + ".iaas.cloud.ibm.com/v1/" ++ ++ if opt["--verbose-level"] > 1: ++ conn.setopt(pycurl.VERBOSE, 1) ++ ++ conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"])) ++ conn.setopt(pycurl.SSL_VERIFYPEER, 1) ++ conn.setopt(pycurl.SSL_VERIFYHOST, 2) ++ ++ # get bearer token ++ bearer_token = get_bearer_token(conn, opt) ++ ++ # set auth token for later requests ++ conn.setopt(pycurl.HTTPHEADER, [ ++ "Content-Type: application/json", ++ "Authorization: Bearer {}".format(bearer_token), ++ "User-Agent: curl", ++ ]) ++ ++ return conn ++ ++def disconnect(conn): ++ conn.close() ++ ++def send_command(conn, command, method="GET", action=None, expected_rc=200): ++ if not command.startswith("https"): ++ url = conn.base_url + command ++ else: ++ url = command ++ ++ conn.setopt(pycurl.URL, url.encode("ascii")) ++ ++ web_buffer = io.BytesIO() ++ ++ if method == "GET": ++ conn.setopt(pycurl.POST, 0) ++ if method == "POST": ++ conn.setopt(pycurl.POSTFIELDS, action) ++ if method == "DELETE": ++ conn.setopt(pycurl.CUSTOMREQUEST, "DELETE") ++ ++ conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) ++ ++ try: ++ conn.perform() ++ except Exception as e: ++ raise(e) ++ ++ rc = conn.getinfo(pycurl.HTTP_CODE) ++ result = web_buffer.getvalue().decode("UTF-8") ++ ++ web_buffer.close() ++ ++ # actions (start/stop/reboot) report 201 when they've been created ++ if rc != expected_rc: ++ logging.debug("rc: {}, result: {}".format(rc, result)) ++ if len(result) > 0: ++ raise Exception("{}: {}".format(rc, ++ result["value"]["messages"][0]["default_message"])) ++ else: ++ raise Exception("Remote returned {} for request to {}".format(rc, url)) ++ ++ if len(result) > 0: ++ result = json.loads(result) ++ ++ logging.debug("url: {}".format(url)) ++ logging.debug("method: {}".format(method)) ++ logging.debug("response code: {}".format(rc)) ++ logging.debug("result: {}\n".format(result)) ++ ++ return result ++ ++def define_new_opts(): ++ all_opt["apikey"] = { ++ "getopt" : ":", ++ "longopt" : "apikey", ++ "help" : "--apikey=[key] API Key", ++ "required" : "1", ++ "shortdesc" : "API Key", ++ "order" : 0 ++ } ++ all_opt["instance"] = { ++ "getopt" : ":", ++ "longopt" : "instance", ++ "help" : "--instance=[instance] Cloud Instance", ++ "required" : "1", ++ "shortdesc" : "Cloud Instance", ++ "order" : 0 ++ } ++ all_opt["region"] = { ++ "getopt" : ":", ++ "longopt" : "region", ++ "help" : "--region=[region] Region", ++ "required" : "1", ++ "shortdesc" : "Region", ++ "order" : 0 ++ } ++ all_opt["limit"] = { ++ "getopt" : ":", ++ "longopt" : "limit", ++ "help" : "--limit=[number] Limit number of nodes returned by API", ++ "required" : "1", ++ "default": 50, ++ "shortdesc" : "Number of nodes returned by API", ++ "order" : 0 ++ } ++ ++ ++def main(): ++ device_opt = [ ++ "apikey", ++ "instance", ++ "region", ++ "limit", ++ "port", ++ "no_password", ++ ] ++ ++ atexit.register(atexit_handler) ++ define_new_opts() ++ ++ all_opt["shell_timeout"]["default"] = "15" ++ all_opt["power_timeout"]["default"] = "30" ++ all_opt["power_wait"]["default"] = "1" ++ ++ options = check_input(device_opt, process_input(device_opt)) ++ ++ docs = {} ++ docs["shortdesc"] = "Fence agent for IBM Cloud VPC" ++ docs["longdesc"] = """fence_ibm_vpc is an I/O Fencing agent which can be \ ++used with IBM Cloud VPC to fence virtual machines.""" ++ docs["vendorurl"] = "https://www.ibm.com" ++ show_docs(options, docs) ++ ++ #### ++ ## Fence operations ++ #### ++ run_delay(options) ++ ++ conn = connect(options) ++ atexit.register(disconnect, conn) ++ ++ result = fence_action(conn, options, set_power_status, get_power_status, get_list) ++ ++ sys.exit(result) ++ ++if __name__ == "__main__": ++ main() +diff --git a/tests/data/metadata/fence_ibm_powervs.xml b/tests/data/metadata/fence_ibm_powervs.xml +new file mode 100644 +index 000000000..fe86331bd +--- /dev/null ++++ b/tests/data/metadata/fence_ibm_powervs.xml +@@ -0,0 +1,134 @@ ++ ++ ++fence_ibm_powervs is an I/O Fencing agent which can be used with IBM PowerVS to fence virtual machines. ++https://www.ibm.com ++ ++ ++ ++ ++ CRN ++ ++ ++ ++ ++ PowerVS Instance ++ ++ ++ ++ ++ Region ++ ++ ++ ++ ++ Bearer Token ++ ++ ++ ++ ++ Fencing action ++ ++ ++ ++ ++ Physical plug number on device, UUID or identification of machine ++ ++ ++ ++ ++ Physical plug number on device, UUID or identification of machine ++ ++ ++ ++ ++ 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 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/tests/data/metadata/fence_ibm_vpc.xml b/tests/data/metadata/fence_ibm_vpc.xml +new file mode 100644 +index 000000000..926efcaa0 +--- /dev/null ++++ b/tests/data/metadata/fence_ibm_vpc.xml +@@ -0,0 +1,134 @@ ++ ++ ++fence_ibm_vpc is an I/O Fencing agent which can be used with IBM Cloud VPC to fence virtual machines. ++https://www.ibm.com ++ ++ ++ ++ ++ API Key ++ ++ ++ ++ ++ Cloud Instance ++ ++ ++ ++ ++ Number of nodes returned by API ++ ++ ++ ++ ++ Region ++ ++ ++ ++ ++ Fencing action ++ ++ ++ ++ ++ Physical plug number on device, UUID or identification of machine ++ ++ ++ ++ ++ Physical plug number on device, UUID or identification of machine ++ ++ ++ ++ ++ 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 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff --git a/SPECS/fence-agents.spec b/SPECS/fence-agents.spec index a353967..3c514ae 100644 --- a/SPECS/fence-agents.spec +++ b/SPECS/fence-agents.spec @@ -59,7 +59,7 @@ Name: fence-agents Summary: Set of unified programs capable of host isolation ("fencing") Version: 4.10.0 -Release: 14%{?alphatag:.%{alphatag}}%{?dist} +Release: 18%{?alphatag:.%{alphatag}}%{?dist} License: GPLv2+ and LGPLv2+ URL: https://github.com/ClusterLabs/fence-agents Source0: https://fedorahosted.org/releases/f/e/fence-agents/%{name}-%{version}.tar.gz @@ -228,8 +228,13 @@ Patch11: bz2000954-2-fence_kubevirt.patch Patch12: bz2022334-fence_zvmip-add-ssl-tls-support.patch Patch13: bz2029791-1-fence_openstack-add-ssl-insecure.patch Patch14: bz2029791-2-fence_openstack-cacert-default.patch +Patch15: bz2000954-3-fence_kubevirt-get-namespace-from-context.patch +Patch16: bz2041933-bz2041935-1-fence_openstack-clouds-openrc.patch +Patch17: bz2041933-bz2041935-2-fence_openstack-clouds-openrc.patch +Patch18: bz2042496-fence_ibm_vpc-fence_ibm_powervs.patch +Patch19: bz2022334-fence_zvmip-add-disable-ssl.patch -%global supportedagents amt_ws apc apc_snmp bladecenter brocade cisco_mds cisco_ucs compute drac5 eaton_snmp emerson eps evacuate hpblade ibmblade 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 hpblade ibmblade ibm_powervs 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 %global testagents virsh heuristics_ping aliyun aws azure_arm gce openstack virt %endif @@ -338,7 +343,27 @@ BuildRequires: %{systemd_units} %prep %setup -q -n %{name}-%{version}%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} -%autopatch -p1 +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 -F2 +%patch16 -p1 -F1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 + # prevent compilation of something that won't get used anyway sed -i.orig 's|FENCE_ZVM=1|FENCE_ZVM=0|' configure.ac @@ -926,6 +951,17 @@ via the SNMP protocol. %{_sbindir}/fence_ibmblade %{_mandir}/man8/fence_ibmblade.8* +%package ibm-powervs +License: GPLv2+ and LGPLv2+ +Summary: Fence agent for IBM PowerVS +Requires: fence-agents-common = %{version}-%{release} +BuildArch: noarch +%description ibm-powervs +Fence agent for IBM PowerVS that are accessed via REST API. +%files ibm-powervs +%{_sbindir}/fence_ibm_powervs +%{_mandir}/man8/fence_ibm_powervs.8* + %package ifmib License: GPLv2+ and LGPLv2+ Summary: Fence agent for devices with IF-MIB interfaces @@ -1406,6 +1442,23 @@ are located on corosync cluster nodes. %endif %changelog +* Fri Feb 11 2022 Oyvind Albrigtsen - 4.10.0-18 +- fence_zvmip: add SSL/TLS support + Resolves: rhbz#2022334 + +* Mon Feb 7 2022 Oyvind Albrigtsen - 4.10.0-17 +- fence_ibm_powervs: new fence agent + Resolves: rhbz#2042496 + +* Wed Jan 19 2022 Oyvind Albrigtsen - 4.10.0-16 +- fence_openstack: add support for reading config from clouds.yaml + and openrc + Resolves: rhbz#2041933, rhbz#2041935 + +* Mon Jan 17 2022 Oyvind Albrigtsen - 4.10.0-15 +- fence_kubevirt: new fence agent + Resolves: rhbz#2000954 + * Tue Jan 11 2022 Oyvind Albrigtsen - 4.10.0-14 - fence_openstack: add --ssl-insecure Resolves: rhbz#2029791 @@ -1414,14 +1467,6 @@ are located on corosync cluster nodes. - fence_amt_ws: fix "or" causing dead code Resolves: rhbz#2010709 -* Thu Nov 11 2021 Oyvind Albrigtsen - 4.10.0-12 -- fence_zvmip: add SSL/TLS support - Resolves: rhbz#2022334 - -* Mon Nov 8 2021 Oyvind Albrigtsen - 4.10.0-11 -- fence_kubevirt: new fence agent - Resolves: rhbz#2000954 - * Tue Oct 5 2021 Oyvind Albrigtsen - 4.10.0-10 - fence_azure_arm: fix sovereign cloud and MSI support Resolves: rhbz#2010652