Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fence-agents for openSUSE:Factory 
checked in at 2025-12-03 14:12:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fence-agents (Old)
 and      /work/SRC/openSUSE:Factory/.fence-agents.new.14147 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fence-agents"

Wed Dec  3 14:12:40 2025 rev:89 rq:1320851 
version:4.16.0+git.1763372689.07285aac

Changes:
--------
--- /work/SRC/openSUSE:Factory/fence-agents/fence-agents.changes        
2025-09-24 15:26:51.362853941 +0200
+++ /work/SRC/openSUSE:Factory/.fence-agents.new.14147/fence-agents.changes     
2025-12-03 14:14:00.302307202 +0100
@@ -1,0 +2,8 @@
+Tue Nov 25 13:21:25 UTC 2025 - Peter Varkoly <[email protected]>
+
+- Update to version 4.16.0+git.1763372689.07285aac:
+  * fence_gce: fix node list limit (#640)
+  * fence_hetzner_cloud: add new fence agent for Hetzner Cloud
+  * fence_kubevirt: add fallback to kubernetes.client.rest to avoid errors 
with some versions of the kubernetes library
+
+-------------------------------------------------------------------

Old:
----
  fence-agents-4.16.0+git.1758188595.5a0c09a2.tar.xz

New:
----
  fence-agents-4.16.0+git.1763372689.07285aac.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fence-agents.spec ++++++
--- /var/tmp/diff_new_pack.6ty36R/_old  2025-12-03 14:14:00.882331679 +0100
+++ /var/tmp/diff_new_pack.6ty36R/_new  2025-12-03 14:14:00.886331848 +0100
@@ -19,7 +19,7 @@
 %define agent_list aliyun alom apc apc_snmp aws azure_arm bladecenter brocade 
cisco_mds cisco_ucs drac5 dummy eaton_snmp eaton_ssh emerson eps evacuate gce 
hds_cb hpblade ibmblade ibmz ibm_powervs ibm_vpc ifmib ilo ilo_moonshot ilo_mp 
ilo_ssh intelmodular ipdu ipmilan ironic kdump lpar mpath netio nutanix_ahv 
powerman pve raritan rcd_serial redfish rsa rsb sanbox2 sbd scsi vbox virsh 
vmware vmware_rest wti zvm
 Name:           fence-agents
 Summary:        Set of unified programs capable of host isolation ("fencing")
-Version:        4.16.0+git.1758188595.5a0c09a2
+Version:        4.16.0+git.1763372689.07285aac
 Release:        0
 License:        GPL-2.0-or-later AND LGPL-2.0-or-later
 Group:          Productivity/Clustering/HA

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.6ty36R/_old  2025-12-03 14:14:00.950334549 +0100
+++ /var/tmp/diff_new_pack.6ty36R/_new  2025-12-03 14:14:00.954334717 +0100
@@ -3,6 +3,6 @@
             <param 
name="url">git://github.com/ClusterLabs/fence-agents.git</param>
           <param 
name="changesrevision">8d746be92f191aa289f13a3703031c122a5e6cf3</param></service><service
 name="tar_scm">
                 <param 
name="url">https://github.com/ClusterLabs/fence-agents</param>
-              <param 
name="changesrevision">5a0c09a23986965507f37a350f3bba20df18c79b</param></service></servicedata>
+              <param 
name="changesrevision">07285aacbc2194c6a2dfc7dd62dda9a3308bc243</param></service></servicedata>
 (No newline at EOF)
 

++++++ fence-agents-4.16.0+git.1758188595.5a0c09a2.tar.xz -> 
fence-agents-4.16.0+git.1763372689.07285aac.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/gce/fence_gce.py 
new/fence-agents-4.16.0+git.1763372689.07285aac/agents/gce/fence_gce.py
--- old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/gce/fence_gce.py     
2025-09-18 11:43:15.000000000 +0200
+++ new/fence-agents-4.16.0+git.1763372689.07285aac/agents/gce/fence_gce.py     
2025-11-17 10:44:49.000000000 +0100
@@ -150,15 +150,24 @@
        result = {}
        plug = options["--plug"] if "--plug" in options else ""
        zones = options["--zone"] if "--zone" in options else ""
+       filter = "name="+plug if plug != "" else ""
+       max_results = 1 if options.get("--action") == "monitor" else 500
        if not zones:
                zones = get_zone(conn, options, plug) if "--plugzonemap" not in 
options else options["--plugzonemap"][plug]
        try:
                for zone in zones.split(","):
-                       instanceList = retry_api_execute(options, 
conn.instances().list(
+                       request = conn.instances().list(
                                project=options["--project"],
-                               zone=zone))
+                               zone=zone,
+                               filter=filter,
+                               maxResults=max_results)
+               while request is not None:
+                       instanceList = retry_api_execute(options, request)
+                       if "items" not in instanceList:
+                               break
                        for instance in instanceList["items"]:
                                result[instance["id"]] = (instance["name"], 
translate_status(instance["status"]))
+                       request = 
conn.instances().list_next(previous_request=request, 
previous_response=instanceList)
        except Exception as err:
                fail_fence_agent(options, "Failed: get_nodes_list: 
{}".format(str(err)))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/hetzner_cloud/fence_hetzner_cloud.py
 
new/fence-agents-4.16.0+git.1763372689.07285aac/agents/hetzner_cloud/fence_hetzner_cloud.py
--- 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/hetzner_cloud/fence_hetzner_cloud.py
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/fence-agents-4.16.0+git.1763372689.07285aac/agents/hetzner_cloud/fence_hetzner_cloud.py
 2025-11-17 10:44:49.000000000 +0100
@@ -0,0 +1,218 @@
+#!@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
+
+if sys.version_info[0] > 2:
+    import urllib.parse as urllib
+else:
+    import urllib
+
+FENCE_STATUS_MAP = {True: "on", False: "off"}
+
+HETZNER_SERVER_ACTIONS = {"on": "poweron", "off": "poweroff"}
+
+HETZNER_STATUSES_OFF = ["off"]
+
+
+def connect(opt):
+    conn = pycurl.Curl()
+    conn.base_url = opt["--api-url"]
+    conn.setopt(
+        pycurl.HTTPHEADER, ["Authorization: Bearer 
{}".format(opt["--api-token"])]
+    )
+    conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"]))
+    try:
+        send_command(
+            conn, "/server_types", "GET", query_string={"per_page": 1}, 
pages_max=1
+        )
+    except Exception as e:
+        logging.error(e)
+        fail(EC_LOGIN_DENIED)
+    return conn
+
+
+def disconnect(conn):
+    conn.close()
+
+
+def send_command(conn, path, method, query_string={}, pages_max=None):
+    results = []
+    result = {"meta": {"pagination": {"next_page": 1}}}
+    while result["meta"]["pagination"]["next_page"] and (
+        not pages_max or len(results) < pages_max
+    ):
+        query_string["page"] = result["meta"]["pagination"]["next_page"]
+        result = send_request(conn, path, method, query_string)
+        results.append(result)
+        if method != "GET":
+            break
+    return results
+
+
+def send_request(conn, path, method, query_string={}):
+    url = conn.base_url + path
+    if query_string:
+        url += "?" + urllib.urlencode(query_string)
+    logging.debug("REQUEST {} {}".format(method, url))
+    conn.setopt(pycurl.URL, url.encode("ascii"))
+    web_buffer = io.BytesIO()
+    if method == "GET":
+        conn.setopt(pycurl.POST, 0)
+    elif method == "POST":
+        conn.setopt(pycurl.POSTFIELDS, "")
+    conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write)
+    try:
+        conn.perform()
+    except Exception as e:
+        raise (e)
+    result_status = conn.getinfo(pycurl.HTTP_CODE)
+    result_body = web_buffer.getvalue().decode("UTF-8")
+    web_buffer.close()
+    if len(result_body) > 0:
+        result_body = json.loads(result_body)
+    if 200 <= result_status < 300:
+        logging.debug("RESULT {} {}".format(result_status, result_body))
+    else:
+        raise Exception("RESULT {} {}".format(result_status, result_body))
+    return result_body
+
+
+def get_server(conn, options):
+    try:
+        results = send_command(
+            conn,
+            "/servers",
+            "GET",
+            query_string={"name": options["--plug"]},
+            pages_max=1,
+        )
+    except Exception as e:
+        logging.error(e)
+        fail(EC_STATUS)
+    if len(results) != 1:
+        fail(EC_STATUS)
+    if len(results[0]["servers"]) == 1:
+        return results[0]["servers"][0]
+    else:
+        logging.warning(
+            "SCANNING over all servers! For better performance, set Hetzner 
Cloud server name to Fencing Agent plug (i.e. Pacemaker node name)."
+        )
+        try:
+            results = send_command(conn, "/servers", "GET")
+        except Exception as e:
+            logging.error(e)
+            fail(EC_STATUS)
+        for result in results:
+            if "servers" not in result:
+                fail(EC_STATUS)
+            for server in result["servers"]:
+                ips = [
+                    server["public_net"]["ipv4"]["ip"],
+                    server["public_net"]["ipv6"]["ip"],
+                ] + list(map(lambda e: e["ip"], server["private_net"]))
+                if options["--plug"] in ips:
+                    return server
+            fail(EC_STATUS)
+
+
+def get_power_status(conn, options):
+    server = get_server(conn, options)
+    status = FENCE_STATUS_MAP[server["status"] not in HETZNER_STATUSES_OFF]
+    logging.debug("{} {} -> {}".format(server["id"], server["status"], status))
+    return status
+
+
+def set_power_status(conn, options):
+    server = get_server(conn, options)
+    logging.debug(server)
+    logging.debug(options)
+    path = "/servers/{}/actions/{}".format(
+        server["id"], HETZNER_SERVER_ACTIONS[options["--action"]]
+    )
+    try:
+        send_command(conn, path, "POST")
+    except Exception as e:
+        logging.error(e)
+        fail(EC_STATUS)
+
+
+def get_list(conn, options):
+    outlets = {}
+    try:
+        results = send_command(conn, "/servers", "GET")
+    except Exception as e:
+        logging.error(e)
+        fail(EC_STATUS)
+    for result in results:
+        if "servers" not in result:
+            fail(EC_STATUS)
+        for server in result["servers"]:
+            status = FENCE_STATUS_MAP[server["status"] not in 
HETZNER_STATUSES_OFF]
+            logging.debug("{} {} -> {}".format(server["id"], server["status"], 
status))
+            outlets[server["name"]] = ("", status)
+    return outlets
+
+
+def define_new_opts():
+    all_opt["api_url"] = {
+        "getopt": ":",
+        "longopt": "api-url",
+        "help": "--api-url=[url]                API URL",
+        "default": "https://api.hetzner.cloud/v1";,
+        "required": "0",
+        "shortdesc": "API URL",
+        "order": 0,
+    }
+    all_opt["api_token"] = {
+        "getopt": ":",
+        "longopt": "api-token",
+        "help": "--api-token=[token]            API token",
+        "required": "1",
+        "shortdesc": "API token",
+        "order": 0,
+    }
+
+
+def main():
+    device_opt = [
+        "api_url",
+        "api_token",
+        "no_password",
+        "port",
+        "web",
+    ]
+
+    atexit.register(atexit_handler)
+    define_new_opts()
+
+    all_opt["shell_timeout"]["default"] = "5"
+    all_opt["power_wait"]["default"] = "5"
+
+    options = check_input(device_opt, process_input(device_opt))
+
+    docs = {}
+    docs["shortdesc"] = "Fence agent for Hetzner Cloud"
+    docs["longdesc"] = """fence_hetzner_cloud is a Power Fencing agent which 
can be \
+used with Hetzner Cloud via its API to fence cloud servers."""
+    docs["vendorurl"] = "https://www.hetzner.com";
+    show_docs(options, docs)
+
+    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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/kubevirt/fence_kubevirt.py
 
new/fence-agents-4.16.0+git.1763372689.07285aac/agents/kubevirt/fence_kubevirt.py
--- 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/agents/kubevirt/fence_kubevirt.py
   2025-09-18 11:43:15.000000000 +0200
+++ 
new/fence-agents-4.16.0+git.1763372689.07285aac/agents/kubevirt/fence_kubevirt.py
   2025-11-17 10:44:49.000000000 +0100
@@ -10,7 +10,10 @@
 try:
     from kubernetes.client.exceptions import ApiException
 except ImportError:
-    logging.error("Couldn\'t import kubernetes.client.exceptions.ApiException 
- not found or not accessible")
+    try:
+        from kubernetes.client.rest import ApiException
+    except ImportError:
+        logging.error("Couldn\'t import 
kubernetes.client.exceptions.ApiException or 
kubernetes.client.rest.ApiException - not found or not accessible")
 
 def _get_namespace(options):
     from kubernetes import config
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/fence-agents.spec.in 
new/fence-agents-4.16.0+git.1763372689.07285aac/fence-agents.spec.in
--- old/fence-agents-4.16.0+git.1758188595.5a0c09a2/fence-agents.spec.in        
2025-09-18 11:43:15.000000000 +0200
+++ new/fence-agents-4.16.0+git.1763372689.07285aac/fence-agents.spec.in        
2025-11-17 10:44:49.000000000 +0100
@@ -51,6 +51,7 @@
 fence-agents-eps \\
 fence-agents-gce \\
 fence-agents-hds-cb \\
+fence-agents-hetzner-cloud \\
 fence-agents-heuristics-ping \\
 fence-agents-hpblade \\
 fence-agents-ibmblade \\
@@ -645,6 +646,18 @@
 %{_sbindir}/fence_hds_cb
 %{_mandir}/man8/fence_hds_cb.8*
 
+%package hetzner-cloud
+License: GPL-2.0-or-later AND LGPL-2.0-or-later
+Summary: Fence agent for Hetzner Cloud
+Requires: fence-agents-common = %{version}-%{release}
+BuildArch: noarch
+Obsoletes: fence-agents < 3.1.13
+%description hetzner-cloud
+Fence agent for Hetzner Cloud.
+%files hetzner-cloud
+%{_sbindir}/fence_hetzner_cloud
+%{_mandir}/man8/fence_hetzner_cloud.8*
+
 %package heuristics-ping
 License: GPL-2.0-or-later AND LGPL-2.0-or-later
 Summary: Pseudo fence agent to affect other agents based on ping-heuristics
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/tests/data/metadata/fence_hetzner_cloud.xml
 
new/fence-agents-4.16.0+git.1763372689.07285aac/tests/data/metadata/fence_hetzner_cloud.xml
--- 
old/fence-agents-4.16.0+git.1758188595.5a0c09a2/tests/data/metadata/fence_hetzner_cloud.xml
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/fence-agents-4.16.0+git.1763372689.07285aac/tests/data/metadata/fence_hetzner_cloud.xml
 2025-11-17 10:44:49.000000000 +0100
@@ -0,0 +1,128 @@
+<?xml version="1.0" ?>
+<resource-agent name="fence_hetzner_cloud" shortdesc="Fence agent for Hetzner 
Cloud" >
+<longdesc>fence_hetzner_cloud is a Power Fencing agent which can be used with 
Hetzner Cloud via its API to fence cloud servers.</longdesc>
+<vendor-url>https://www.hetzner.com</vendor-url>
+<parameters>
+       <parameter name="api_token" unique="0" required="1">
+               <getopt mixed="--api-token=[token]" />
+               <content type="string"  />
+               <shortdesc lang="en">API token</shortdesc>
+       </parameter>
+       <parameter name="api_url" unique="0" required="0">
+               <getopt mixed="--api-url=[url]" />
+               <content type="string" default="https://api.hetzner.cloud/v1";  
/>
+               <shortdesc lang="en">API URL</shortdesc>
+       </parameter>
+       <parameter name="action" unique="0" required="1">
+               <getopt mixed="-o, --action=[action]" />
+               <content type="string" default="reboot"  />
+               <shortdesc lang="en">Fencing action</shortdesc>
+       </parameter>
+       <parameter name="plug" unique="0" required="1" obsoletes="port">
+               <getopt mixed="-n, --plug=[id]" />
+               <content type="string"  />
+               <shortdesc lang="en">Physical plug number on device, UUID or 
identification of machine</shortdesc>
+       </parameter>
+       <parameter name="port" unique="0" required="1" deprecated="1">
+               <getopt mixed="-n, --plug=[id]" />
+               <content type="string"  />
+               <shortdesc lang="en">Physical plug number on device, UUID or 
identification of machine</shortdesc>
+       </parameter>
+       <parameter name="quiet" unique="0" required="0">
+               <getopt mixed="-q, --quiet" />
+               <content type="boolean"  />
+               <shortdesc lang="en">Disable logging to stderr. Does not affect 
--verbose or --debug-file or logging to syslog.</shortdesc>
+       </parameter>
+       <parameter name="verbose" unique="0" required="0">
+               <getopt mixed="-v, --verbose" />
+               <content type="boolean"  />
+               <shortdesc lang="en">Verbose mode. Multiple -v flags can be 
stacked on the command line (e.g., -vvv) to increase verbosity.</shortdesc>
+       </parameter>
+       <parameter name="verbose_level" unique="0" required="0">
+               <getopt mixed="--verbose-level" />
+               <content type="integer"  />
+               <shortdesc lang="en">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).</shortdesc>
+       </parameter>
+       <parameter name="debug" unique="0" required="0" deprecated="1">
+               <getopt mixed="-D, --debug-file=[debugfile]" />
+               <content type="string"  />
+               <shortdesc lang="en">Write debug information to given 
file</shortdesc>
+       </parameter>
+       <parameter name="debug_file" unique="0" required="0" obsoletes="debug">
+               <getopt mixed="-D, --debug-file=[debugfile]" />
+               <shortdesc lang="en">Write debug information to given 
file</shortdesc>
+       </parameter>
+       <parameter name="version" unique="0" required="0">
+               <getopt mixed="-V, --version" />
+               <content type="boolean"  />
+               <shortdesc lang="en">Display version information and 
exit</shortdesc>
+       </parameter>
+       <parameter name="help" unique="0" required="0">
+               <getopt mixed="-h, --help" />
+               <content type="boolean"  />
+               <shortdesc lang="en">Display help and exit</shortdesc>
+       </parameter>
+       <parameter name="plug_separator" unique="0" required="0">
+               <getopt mixed="--plug-separator=[char]" />
+               <content type="string" default=","  />
+               <shortdesc lang="en">Separator for plug parameter when 
specifying more than 1 plug</shortdesc>
+       </parameter>
+       <parameter name="separator" unique="0" required="0">
+               <getopt mixed="-C, --separator=[char]" />
+               <content type="string" default=","  />
+               <shortdesc lang="en">Separator for CSV created by 'list' 
operation</shortdesc>
+       </parameter>
+       <parameter name="delay" unique="0" required="0">
+               <getopt mixed="--delay=[seconds]" />
+               <content type="second" default="0"  />
+               <shortdesc lang="en">Wait X seconds before fencing is 
started</shortdesc>
+       </parameter>
+       <parameter name="disable_timeout" unique="0" required="0">
+               <getopt mixed="--disable-timeout=[true/false]" />
+               <content type="string"  />
+               <shortdesc lang="en">Disable timeout (true/false) (default: 
true when run from Pacemaker 2.0+)</shortdesc>
+       </parameter>
+       <parameter name="login_timeout" unique="0" required="0">
+               <getopt mixed="--login-timeout=[seconds]" />
+               <content type="second" default="5"  />
+               <shortdesc lang="en">Wait X seconds for cmd prompt after 
login</shortdesc>
+       </parameter>
+       <parameter name="power_timeout" unique="0" required="0">
+               <getopt mixed="--power-timeout=[seconds]" />
+               <content type="second" default="20"  />
+               <shortdesc lang="en">Test X seconds for status change after 
ON/OFF</shortdesc>
+       </parameter>
+       <parameter name="power_wait" unique="0" required="0">
+               <getopt mixed="--power-wait=[seconds]" />
+               <content type="second" default="5"  />
+               <shortdesc lang="en">Wait X seconds after issuing 
ON/OFF</shortdesc>
+       </parameter>
+       <parameter name="shell_timeout" unique="0" required="0">
+               <getopt mixed="--shell-timeout=[seconds]" />
+               <content type="second" default="5"  />
+               <shortdesc lang="en">Wait X seconds for cmd prompt after 
issuing command</shortdesc>
+       </parameter>
+       <parameter name="stonith_status_sleep" unique="0" required="0">
+               <getopt mixed="--stonith-status-sleep=[seconds]" />
+               <content type="second" default="1"  />
+               <shortdesc lang="en">Sleep X seconds between status calls 
during a STONITH action</shortdesc>
+       </parameter>
+       <parameter name="retry_on" unique="0" required="0">
+               <getopt mixed="--retry-on=[attempts]" />
+               <content type="integer" default="1"  />
+               <shortdesc lang="en">Count of attempts to retry power 
on</shortdesc>
+       </parameter>
+</parameters>
+<actions>
+       <action name="on" automatic="0"/>
+       <action name="off" />
+       <action name="reboot" />
+       <action name="status" />
+       <action name="list" />
+       <action name="list-status" />
+       <action name="monitor" />
+       <action name="metadata" />
+       <action name="manpage" />
+       <action name="validate-all" />
+</actions>
+</resource-agent>

Reply via email to