- fence_aliyun: add credentials file support

Resolves: RHEL-99338
This commit is contained in:
Oyvind Albrigtsen 2025-06-23 12:14:51 +02:00
parent bb9ebd89b8
commit d72be1e434
2 changed files with 216 additions and 1 deletions

View File

@ -0,0 +1,209 @@
--- a/agents/aliyun/fence_aliyun.py 2025-06-23 12:03:30.176181831 +0200
+++ b/agents/aliyun/fence_aliyun.py 2025-06-23 12:04:38.243037392 +0200
@@ -1,53 +1,67 @@
#!@PYTHON@ -tt
-import sys, re
+import sys
import logging
import atexit
import json
+
sys.path.append("@FENCEAGENTSLIBDIR@")
from fencing import *
-from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay
+from fencing import fail_usage, run_delay
+
try:
sys.path.insert(0, '/usr/lib/fence-agents/bundled/aliyun')
from aliyunsdkcore import client
from aliyunsdkcore.auth.credentials import EcsRamRoleCredential
+ from aliyunsdkcore.profile import region_provider
+except ImportError as e:
+ logging.warning("The 'aliyunsdkcore' module has been not installed or is unavailable, try to execute the command 'pip install aliyun-python-sdk-core --upgrade' to solve. error: %s" % e)
+
+
+try:
from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
from aliyunsdkecs.request.v20140526.StartInstanceRequest import StartInstanceRequest
from aliyunsdkecs.request.v20140526.StopInstanceRequest import StopInstanceRequest
from aliyunsdkecs.request.v20140526.RebootInstanceRequest import RebootInstanceRequest
- from aliyunsdkcore.profile import region_provider
-except ImportError:
- pass
+except ImportError as e:
+ logging.warning("The 'aliyunsdkecs' module has been not installed or is unavailable, try to execute the command 'pip install aliyun-python-sdk-ecs --upgrade' to solve. error: %s" % e)
+
def _send_request(conn, request):
+ logging.debug("send request action: %s" % request.get_action_name())
request.set_accept_format('json')
try:
response_str = conn.do_action_with_exception(request)
- response_detail = json.loads(response_str)
- logging.debug("_send_request reponse: %s" % response_detail)
- return response_detail
except Exception as e:
- fail_usage("Failed: _send_request failed: %s" % e)
+ fail_usage("Failed: send request failed: Error: %s" % e)
+
+ response_detail = json.loads(response_str)
+ logging.debug("reponse: %s" % response_detail)
+ return response_detail
def start_instance(conn, instance_id):
+ logging.debug("start instance %s" % instance_id)
request = StartInstanceRequest()
request.set_InstanceId(instance_id)
_send_request(conn, request)
def stop_instance(conn, instance_id):
+ logging.debug("stop instance %s" % instance_id)
request = StopInstanceRequest()
request.set_InstanceId(instance_id)
request.set_ForceStop('true')
_send_request(conn, request)
def reboot_instance(conn, instance_id):
+ logging.debug("reboot instance %s" % instance_id)
request = RebootInstanceRequest()
request.set_InstanceId(instance_id)
request.set_ForceStop('true')
_send_request(conn, request)
def get_status(conn, instance_id):
+ logging.debug("get instance %s status" % instance_id)
request = DescribeInstancesRequest()
request.set_InstanceIds(json.dumps([instance_id]))
response = _send_request(conn, request)
@@ -59,20 +73,30 @@
return instance_status
def get_nodes_list(conn, options):
+ logging.debug("start to get nodes list")
result = {}
request = DescribeInstancesRequest()
request.set_PageSize(100)
+
+ if "--filter" in options:
+ filter_key = options["--filter"].split("=")[0].strip()
+ filter_value = options["--filter"].split("=")[1].strip()
+ params = request.get_query_params()
+ params[filter_key] = filter_value
+ request.set_query_params(params)
+
response = _send_request(conn, request)
- instance_status = None
if response is not None:
instance_list = response.get('Instances').get('Instance')
for item in instance_list:
instance_id = item.get('InstanceId')
instance_name = item.get('InstanceName')
result[instance_id] = (instance_name, None)
+ logging.debug("get nodes list: %s" % result)
return result
def get_power_status(conn, options):
+ logging.debug("start to get power(%s) status" % options["--plug"])
state = get_status(conn, options["--plug"])
if state == "Running":
@@ -81,14 +105,11 @@
status = "off"
else:
status = "unknown"
-
- logging.info("get_power_status: %s" % status)
-
+ logging.debug("the power(%s) status is %s" % (options["--plug"], status))
return status
-
def set_power_status(conn, options):
- logging.info("set_power_status: %s" % options["--action"])
+ logging.info("start to set power(%s) status to %s" % (options["--plug"], options["--action"]))
if (options["--action"]=="off"):
stop_instance(conn, options["--plug"])
@@ -97,7 +118,6 @@
elif (options["--action"]=="reboot"):
reboot_instance(conn, options["--plug"])
-
def define_new_opts():
all_opt["region"] = {
"getopt" : "r:",
@@ -126,17 +146,42 @@
all_opt["ram_role"] = {
"getopt": ":",
"longopt": "ram-role",
- "help": "--ram-role=[name] Ram Role",
+ "help": "--ram-role=[name] Ram Role",
"shortdesc": "Ram Role.",
"required": "0",
"order": 5
}
+ all_opt["credentials_file"] = {
+ "getopt": ":",
+ "longopt": "credentials-file",
+ "help": "--credentials-file=[path] Path to aliyun-cli credentials file",
+ "shortdesc": "Path to credentials file",
+ "required": "0",
+ "order": 6
+ }
+ all_opt["credentials_file_profile"] = {
+ "getopt": ":",
+ "longopt": "credentials-file-profile",
+ "help": "--credentials-file-profile=[profile] Credentials file profile",
+ "shortdesc": "Credentials file profile",
+ "required": "0",
+ "default": "default",
+ "order": 7
+ }
+ all_opt["filter"] = {
+ "getopt": ":",
+ "longopt": "filter",
+ "help": "--filter=[key=value] Filter (e.g. InstanceIds=[\"i-XXYYZZAA1\",\"i-XXYYZZAA2\"]",
+ "shortdesc": "Filter for list-action.",
+ "required": "0",
+ "order": 8
+ }
# Main agent method
def main():
conn = None
- device_opt = ["port", "no_password", "region", "access_key", "secret_key", "ram_role"]
+ device_opt = ["port", "no_password", "region", "access_key", "secret_key", "ram_role", "credentials_file", "credentials_file_profile", "filter"]
atexit.register(atexit_handler)
@@ -164,8 +209,25 @@
ram_role = options["--ram-role"]
role = EcsRamRoleCredential(ram_role)
conn = client.AcsClient(region_id=region, credential=role)
- region_provider.modify_point('Ecs', region, 'ecs.%s.aliyuncs.com' % region)
-
+ elif "--credentials-file" in options and "--credentials-file-profile" in options:
+ import os, configparser
+ try:
+ config = configparser.ConfigParser()
+ config.read(os.path.expanduser(options["--credentials-file"]))
+ access_key = config.get(options["--credentials-file-profile"], "aliyun_access_key_id")
+ secret_key = config.get(options["--credentials-file-profile"], "aliyun_access_key_secret")
+ conn = client.AcsClient(access_key, secret_key, region)
+ except Exception as e:
+ fail_usage("Failed: failed to read credentials file: %s" % e)
+ else:
+ fail_usage("Failed: User credentials are not set. Please set the Access Key and the Secret Key, or configure the RAM role.")
+
+ # Use intranet endpoint to access ECS service
+ try:
+ region_provider.modify_point('Ecs', region, 'ecs.%s.aliyuncs.com' % region)
+ except Exception as e:
+ logging.warning("Failed: failed to modify endpoint to 'ecs.%s.aliyuncs.com': %s" % (region, e))
+
# Operate the fencing device
result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list)
sys.exit(result)

View File

@ -87,7 +87,7 @@
Name: fence-agents
Summary: Set of unified programs capable of host isolation ("fencing")
Version: 4.2.1
Release: 129%{?alphatag:.%{alphatag}}%{?dist}.9
Release: 129%{?alphatag:.%{alphatag}}%{?dist}.10
License: GPLv2+ and LGPLv2+
Group: System Environment/Base
URL: https://github.com/ClusterLabs/fence-agents
@ -317,6 +317,7 @@ Patch144: RHEL-56840-fence_scsi-only-preempt-once-for-mpath-devices.patch
Patch145: RHEL-76492-fence_azure_arm-use-azure-identity.patch
Patch146: RHEL-65025-fence_ibm_powervs-add-private-endpoint-and-token-file-support.patch
Patch147: RHEL-96179-fence_kubevirt-force-off.patch
Patch148: RHEL-99338-fence_aliyun-update.patch
### HA support libs/utils ###
# all archs
@ -553,6 +554,7 @@ BuildRequires: python3-google-api-client python3-pip python3-wheel python3-jinja
%patch -p1 -P 145
%patch -p1 -P 146
%patch -p1 -P 147
%patch -p1 -P 148
# prevent compilation of something that won't get used anyway
sed -i.orig 's|FENCE_ZVM=1|FENCE_ZVM=0|' configure.ac
@ -1599,6 +1601,10 @@ Fence agent for IBM z/VM over IP.
%endif
%changelog
* Mon Jun 23 2025 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-129.10
- fence_aliyun: add credentials file support
Resolves: RHEL-99338
* Fri Jun 13 2025 Oyvind Albrigtsen <oalbrigt@redhat.com> - 4.2.1-129.9
- fence_kubevirt: use hard poweroff
Resolves: RHEL-96179