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 2026-02-25 21:12:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fence-agents (Old) and /work/SRC/openSUSE:Factory/.fence-agents.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "fence-agents" Wed Feb 25 21:12:40 2026 rev:92 rq:1335070 version:4.17.0+git.1769693386.608dd62 Changes: -------- --- /work/SRC/openSUSE:Factory/fence-agents/fence-agents.changes 2025-12-25 19:57:55.751934437 +0100 +++ /work/SRC/openSUSE:Factory/.fence-agents.new.1977/fence-agents.changes 2026-02-25 21:21:54.786179795 +0100 @@ -1,0 +2,9 @@ +Mon Feb 23 19:09:44 UTC 2026 - [email protected] + +- Update to version 4.17.0+git.1769693386.608dd62: + * fence_ibm_vpc: fix indentation + * fence_dummy: add recorder mode for testing fence_recorder protocol (#647) + * Enhancement: better alpine compatibility (#648) + * fence_scsi: add "mpath_register_method" parameter to allow only registering to the main multipath device + +------------------------------------------------------------------- Old: ---- fence-agents-4.16.0+git.1765293331.d30fb54b.tar.xz New: ---- fence-agents-4.17.0+git.1769693386.608dd62.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fence-agents.spec ++++++ --- /var/tmp/diff_new_pack.in7I3K/_old 2026-02-25 21:21:55.430206239 +0100 +++ /var/tmp/diff_new_pack.in7I3K/_new 2026-02-25 21:21:55.430206239 +0100 @@ -1,6 +1,7 @@ # # spec file for package fence-agents # +# Copyright (c) 2026 SUSE LLC # Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties @@ -19,7 +20,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.1765293331.d30fb54b +Version: 4.17.0+git.1769693386.608dd62 Release: 0 License: GPL-2.0-or-later AND LGPL-2.0-or-later Group: Productivity/Clustering/HA ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.in7I3K/_old 2026-02-25 21:21:55.486208539 +0100 +++ /var/tmp/diff_new_pack.in7I3K/_new 2026-02-25 21:21:55.490208703 +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">d30fb54bb7dce92af8c67ca01b3e410552c4183b</param></service></servicedata> + <param name="changesrevision">608dd62f4d213f8583e96fc5b2e2bd65b0d25108</param></service></servicedata> (No newline at EOF) ++++++ fence-agents-4.16.0+git.1765293331.d30fb54b.tar.xz -> fence-agents-4.17.0+git.1769693386.608dd62.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/.gitignore new/fence-agents-4.17.0+git.1769693386.608dd62/.gitignore --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/.gitignore 2026-01-29 14:29:46.000000000 +0100 @@ -0,0 +1,75 @@ +*.orig +*.rej +*.swp +Makefile.in +aclocal.m4 +autoconf +autoheader +autom4te.cache +automake +autoscan.log +compile +configure +configure.scan +config.guess +config.log +config.sub +config.status +Makefile +depcomp +install-sh +libtoolize +ltmain.sh +libtool +make/stamp-h1 + +# ignore "libtoolized" m4 files, but keep our (enumerated) ones +/m4/* +!/m4/ac_python_module.m4 +!/m4/ax*.m4 + +# make/release.mk related litter +/.tarball-version +/tag-* + +make/config.h* +missing +*.pc +.deps +.libs +*.o +*.la +*.lo +lib/*.py* +lib/__pycache__ +!lib/*.py.py +!lib/check_used_options.py +agents/*/fence_* +agents/*/.dirstamp +!agents/*/fence_*.py +!agents/*/fence_*.c +!agents/*/fence_*.h +!agents/kdump/fence_kdump_send.8 +!agents/manual/fence_ack_manual.8 +!agents/zvm/fence_zvm_man_page +.fence*.tmp +fence-agents* +.version +tests/devices.d/* + +y.tab.* +*.o +*.lo +*.la +.deps +.libs +.*version +*.swp +agents/virt/client/fence_virt +agents/virt/client/fence_xvm +agents/virt/common/libfence_virt.a +agents/virt/config/config.c +agents/virt/config/libsimpleconfig.a +agents/virt/fence_virtd.service +agents/virt/server/fence_virtd +fence-virt-* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/dummy/README.md new/fence-agents-4.17.0+git.1769693386.608dd62/agents/dummy/README.md --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/dummy/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/dummy/README.md 2026-01-29 14:29:46.000000000 +0100 @@ -0,0 +1,352 @@ +# fence_dummy - Testing and Simulation Fence Agent + +## Overview + +`fence_dummy` is a fence agent for development, testing, and simulation. It supports three operating modes: + +1. **file** - Status stored in a file (default) +2. **fail** - Simulated failure scenarios +3. **recorder** - Request/response coordination with external systems + +--- + +--- + +## File Mode (Default) + +The **file mode** (default) stores power status in a simple text file, making it useful for basic testing without complex infrastructure. + +### How It Works + +- Reads/writes node power state from/to a status file +- `on` action: Writes "on" to the file +- `off` action: Writes "off" to the file +- `status` action: Reads current state from file (returns "off" if file missing) +- File persists across invocations for state tracking + +### Use Cases + +- **Basic fence agent testing** in development +- **Simple state tracking** without external dependencies +- **Unit testing** fence workflows +- **Learning fence agent behavior** in isolation + +### Configuration + +```bash +# Direct CLI usage +fence_dummy --plug node1 --action off --status-file /tmp/node1.status +fence_dummy --plug node1 --action status --status-file /tmp/node1.status + +# Create STONITH resource +pcs stonith create file-fence fence_dummy \ + plug=compute-node-1 \ + status_file=/tmp/compute-node-1.status +``` + +### Options (File Mode) + +| Option | Default | Description | +| ------ | ------- | ----------- | +| `--type` | `file` | Mode (default, can be omitted) | +| `--plug` | (optional) | Node identifier (not used for file operations) | +| `--status-file` | `/tmp/fence_dummy.status` | File storing power state | + +### Example + +```bash +# Turn off node +$ fence_dummy --plug node1 --action off --status-file /tmp/node1.status +$ cat /tmp/node1.status +off + +# Check status +$ fence_dummy --plug node1 --action status --status-file /tmp/node1.status +Status: OFF + +# Turn on node +$ fence_dummy --plug node1 --action on --status-file /tmp/node1.status +$ cat /tmp/node1.status +on +``` + +--- + +## Fail Mode + +The **fail mode** (`--type=fail`) simulates fence device failures and unexpected behaviors for testing error handling. + +### How It Works + +- Returns power state **opposite** of what's expected +- `status` returns random on/off state for specified plug +- `list` returns a predefined list of fake outlets +- Useful for testing Pacemaker error recovery and retry logic + +### Use Cases + +- **Testing error handling** in cluster configurations +- **Validating retry logic** in Pacemaker +- **Simulating unreliable fence devices** during development +- **Chaos engineering** for HA clusters + +### Configuration + +```bash +# Direct CLI usage +fence_dummy --plug node1 --type=fail --action status + +# Create STONITH resource (NOT recommended for production!) +pcs stonith create fail-fence fence_dummy \ + type=fail \ + plug=compute-node-1 +``` + +### Options (Fail Mode) + +| Option | Default | Description | +| ------ | ------- | ----------- | +| `--type` | `file` | Set to `fail` for failure simulation | +| `--plug` | (required) | Target outlet/node | + +### Behavior + +- **`list` action**: Returns fake outlet list `["1", "2"]` +- **`status` action**: Returns random state (inconsistent results) +- **`on`/`off` actions**: Succeed but state queries will be inconsistent +- **`monitor` action**: Returns success (agent is "working") + +### Example + +```bash +$ fence_dummy --plug 1 --type=fail --action list +1,2 + +$ fence_dummy --plug 1 --type=fail --action status +Status: ON + +$ fence_dummy --plug 1 --type=fail --action status +Status: OFF # Inconsistent! +``` + +--- + +## Recorder Mode + +The **recorder mode** (`--type=recorder`) implements the fence_recorder request/response pattern for coordinating fencing with external systems via files. + +### How It Works + +```text +Pacemaker → fence_dummy (recorder mode) + ↓ + Write request file + ↓ + Wait for response + ↓ + External system writes response + ↓ + Read response & exit +``` + +### Use Cases + +- **Testing fence_recorder protocol** without external system dependencies +- **Simulating external fence coordination** in development +- **CI/CD testing** of fence request/response workflows +- **Validating external fence responders** before production + +### Configuration + +```bash +# Create directories +sudo mkdir -p /var/run/fence_dummy/{requests,responses} +sudo mkdir -p /var/log/cluster + +# Create STONITH resource +pcs stonith create test-fence fence_dummy \ + type=recorder \ + plug=compute-node-1 \ + request_path=/var/run/fence_dummy/requests \ + response_path=/var/run/fence_dummy/responses \ + log_path=/var/log/cluster \ + recorder_timeout=30 +``` + +### Options (Recorder Mode) + +| Option | Default | Description | +| ------ | ------- | ----------- | +| `--type` | `file` | Set to `recorder` for request/response mode | +| `--plug` | (required) | Target node to fence | +| `--request-path` | `/var/run/fence_dummy/requests` | Directory for fence requests | +| `--response-path` | `/var/run/fence_dummy/responses` | Directory for fence responses | +| `--recorder-timeout` | `60` | Seconds to wait for response | +| `--recorder-poll-interval` | `0.5` | Seconds between response checks | +| `--log-path` | `/var/log/cluster` | Directory for fence event logs | + +### Logging (Recorder Mode) + +Recorder mode enables structured logging to track fence operations: + +- **Log file**: `<log-path>/fence-events.log` (also to stderr) +- **Format**: `[YYYY-MM-DD HH:MM:SS] [LEVEL] message` +- **Events logged**: + - Fence action requested + - Fence action completed/failed + - Request/response file operations + - Errors and timeouts + +Example log entries: + +```text +[2026-01-14 13:45:10] [INFO] Fence event: action=off, target=compute-node-1, status=requested, details=Fence action off requested +[2026-01-14 13:45:15] [INFO] Fence response received: success=True, message=Fence operation completed successfully, timestamp=2026-01-14T13:45:15-06:00 +[2026-01-14 13:45:15] [INFO] Fence event: action=off, target=compute-node-1, status=completed, details=Fence action off completed successfully: Fence operation completed successfully +``` + +> **Note**: In recorder mode, the agent uses a synchronous pattern to wait for the external response. +> +> - `off`: Writes a request and waits for a success response. +> - `reboot`: Executed as `off` then `on`. This generates an `off` request (wait for success) followed by an `on` action (no-op). +> - `on`, `status`, `monitor`: These are no-ops that always return success/on to satisfy Pacemaker requirements, but do not generate request files. + +### Request File Format + +**File**: `<request-path>/<node-name>-<uuid>.json` + +```json +{ + "request_id": "550e8400-e29b-41d4-a716-446655440000", + "timestamp": "2026-01-06T14:30:00-06:00", + "action": "off", + "target_node": "compute-node-1", + "recorder_node": "control-node" +} +``` + +### Response File Format + +**File**: `<response-path>/<node-name>-<uuid>.json` + +```json +{ + "request_id": "550e8400-e29b-41d4-a716-446655440000", + "timestamp": "2026-01-06T14:30:05-06:00", + "success": true, + "message": "Fence operation completed successfully", + "action_performed": "off", + "target_node": "compute-node-1", + "recorder_node": "responder-system" +} +``` + +### Example: Simulating an External Responder + +```bash +#!/bin/bash +# Simple fence responder for testing + +REQUEST_DIR="/var/run/fence_dummy/requests" +RESPONSE_DIR="/var/run/fence_dummy/responses" + +while true; do + for request in "$REQUEST_DIR"/*.json; do + [ -e "$request" ] || continue + + # Extract request_id and node name from request file + request_id=$(jq -r '.request_id' "$request") + target_node=$(jq -r '.target_node' "$request") + + # Write response + response_file="$RESPONSE_DIR/$(basename "$request")" + timestamp=$(date +"%Y-%m-%dT%H:%M:%S%z" | sed 's/\([0-9][0-9]\)$/:\1/') + cat > "$response_file" <<EOF +{ + "request_id": "$request_id", + "timestamp": "$timestamp", + "success": true, + "message": "Simulated fence of $target_node", + "action_performed": "off", + "target_node": "$target_node", + "recorder_node": "$(hostname)" +} +EOF + + echo "Responded to fence request for $target_node" + rm "$request" + done + sleep 1 +done +``` + +--- + +## Common Options (All Modes) + +### Random Sleep + +Simulate slow fence devices by adding random delays: + +| Option | Default | Description | +| ------ | ------- | ----------- | +| `--random_sleep_range` | (disabled) | Maximum sleep time in seconds (random 1-N) | + +```bash +# Sleep 1-30 seconds before taking action (any mode) +fence_dummy --plug node1 --action off --random_sleep_range=30 + +# Combine with recorder mode +fence_dummy --type=recorder --plug node1 --action off \ + --random_sleep_range=10 \ + --request-path=/var/run/fence_dummy/requests \ + --response-path=/var/run/fence_dummy/responses +``` + +### Standard Fence Actions + +All modes support standard fence agent actions: + +- **`on`**: Turn on the node/outlet +- **`off`**: Turn off the node/outlet (primary fence action) +- **`reboot`**: Reboot the node (implemented as off + on) +- **`status`**: Query current power state +- **`monitor`**: Check if fence agent is functioning (always succeeds) +- **`list`**: List available outlets (fail mode only) +- **`metadata`**: Output XML metadata for Pacemaker + +--- + +## Comparison of Modes + +| Feature | File Mode | Fail Mode | Recorder Mode | +| ------- | --------- | --------- | ------------- | +| **Default** | ✓ | | | +| **State Persistence** | File-based | None | External system | +| **External Dependencies** | None | None | Response writer | +| **Reliable Results** | ✓ | ✗ (intentionally) | ✓ (if responder works) | +| **Async Operation** | No | No | Yes (waits for response) | +| **Logging** | Minimal | Minimal | Structured logs | +| **Use Case** | Basic testing | Error simulation | External coordination | +| **Production Use** | Testing only | Never | Testing only | + +--- + +## Installation + +```bash +# Build from source +./autogen.sh +./configure +make +sudo make install + +# Verify installation +fence_dummy -o metadata +``` + +## See Also + +- `fence_recorder` - Production fence agent for NNF storage coordination +- [Pacemaker documentation](https://clusterlabs.org/pacemaker/) +- Fence agent development: `/usr/share/doc/fence-agents/` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/dummy/fence_dummy.py new/fence-agents-4.17.0+git.1769693386.608dd62/agents/dummy/fence_dummy.py --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/dummy/fence_dummy.py 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/dummy/fence_dummy.py 2026-01-29 14:29:46.000000000 +0100 @@ -4,12 +4,25 @@ import logging import time import atexit +import os +import json +import socket +import uuid +from datetime import datetime + sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -from fencing import fail_usage, run_delay +from fencing import fail, fail_usage, run_delay plug_status = "on" +# Defaults for recorder mode +DEFAULT_REQUEST_PATH = "@FENCETMPDIR@/fence_dummy/requests" +DEFAULT_RESPONSE_PATH = "@FENCETMPDIR@/fence_dummy/responses" +DEFAULT_RECORDER_TIMEOUT = 60 +DEFAULT_RECORDER_POLL_INTERVAL = 0.5 +DEFAULT_LOG_PATH = "@LOGDIR@" + def get_power_status_file(conn, options): del conn @@ -71,11 +84,188 @@ return result +# Recorder mode logging functions +def setup_recorder_logging(log_path): + """Initialize logging with the specified log directory (recorder mode only) + + Adds an additional file handler to log fence events to a separate file. + Keeps the default root logger format for pacemaker.log compatibility. + + Note: The root logger level must be set to INFO to allow INFO messages + through to handlers. fencing.py sets it to WARNING by default unless + --verbose is specified. + """ + os.makedirs(log_path, exist_ok=True) + fence_log = os.path.join(log_path, "fence-events.log") + + # Add file handler for fence events (keeps existing root logger handlers) + file_handler = logging.FileHandler(fence_log) + file_handler.setLevel(logging.INFO) + file_handler.setFormatter(logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')) + + # Set root logger level to INFO to allow our INFO messages through + # (fencing.py defaults to WARNING unless --verbose is set) + root_logger = logging.getLogger() + if root_logger.level > logging.INFO: + root_logger.setLevel(logging.INFO) + root_logger.addHandler(file_handler) + + return log_path, fence_log + +def record_fence_event(action, target_node, status, details=""): + """Record fencing event to log""" + logging.info(f"Fence event: action={action}, target={target_node}, status={status}, details={details}") + +# Recorder mode functions +def write_fence_request_recorder(action, target_node, request_dir): + """Write fence request file (atomic rename pattern)""" + os.makedirs(request_dir, exist_ok=True) + + request_id = str(uuid.uuid4()) + filename = f"{target_node}-{request_id}.json" + temp_file = os.path.join(request_dir, f".{filename}.tmp") + final_file = os.path.join(request_dir, filename) + + request_data = { + "request_id": request_id, + "timestamp": datetime.now().astimezone().isoformat(), + "action": action, + "target_node": target_node, + "recorder_node": socket.gethostname() + } + + try: + with open(temp_file, 'w') as f: + json.dump(request_data, f, indent=2) + os.rename(temp_file, final_file) + logging.info(f"Wrote fence request: {final_file}") + return request_id + except Exception as e: + logging.error(f"Failed to write fence request: {e}") + try: + os.remove(temp_file) + except OSError: + pass + return None + +def wait_for_fence_response_recorder(request_id, target_node, response_dir, timeout, poll_interval): + """Wait for external component to write response file""" + os.makedirs(response_dir, exist_ok=True) + + response_file = os.path.join(response_dir, f"{target_node}-{request_id}.json") + start_time = time.time() + + logging.info(f"Waiting for fence response: {response_file} (timeout={timeout}s)") + + while time.time() - start_time < timeout: + try: + if os.path.exists(response_file): + file_size = os.path.getsize(response_file) + if file_size > 1024 * 1024: # 1MB limit + logging.error(f"Response file too large: {file_size} bytes") + return False, "Response file exceeds size limit" + + with open(response_file, 'r') as f: + response_data = json.load(f) + + success = response_data.get("success", False) + message = response_data.get("message", "Fence operation completed") + timestamp = response_data.get("timestamp", "unknown") + response_target = response_data.get("target_node", "unknown") + response_recorder = response_data.get("recorder_node", "unknown") + + logging.info(f"Fence response received: success={success}, message={message}, timestamp={timestamp}") + logging.info(f"Response metadata: target={response_target}, recorder={response_recorder}") + return success, message + except (FileNotFoundError, json.JSONDecodeError, Exception) as e: + logging.debug(f"Waiting for response: {e}") + + time.sleep(poll_interval) + + logging.error(f"Fence response timeout after {timeout}s") + return False, f"Fence operation timed out after {timeout}s" + +def get_power_status_recorder(conn, options): + """Recorder mode get_power_status - not used with sync pattern""" + del conn + # This shouldn't be called when using sync_set_power_fn pattern + # Return 'on' as fallback + return "on" + +def sync_set_power_status_recorder(conn, options): + """Recorder mode sync_set_power_status - write request and wait for response. + + Returns True on success, False on failure. Using sync pattern avoids + the post-action power state verification that would fail since we can't + query the actual power state of fenced nodes. + """ + del conn + + action = options["--action"] + target_node = options["--plug"] + request_path = options["--request-path"] + response_path = options["--response-path"] + timeout = int(options["--recorder-timeout"]) + poll_interval = float(options["--recorder-poll-interval"]) + + # Only handle off/reboot actions (not on/status/monitor) + if action not in ["off", "reboot"]: + return True + + # Record fence event initiation + record_fence_event( + action, + target_node, + "requested", + f"Fence action {action} requested" + ) + + # Write request + request_id = write_fence_request_recorder(action, target_node, request_path) + if not request_id: + record_fence_event( + action, + target_node, + "failed", + "Failed to create fence request file" + ) + return False + + # Wait for response + success, message = wait_for_fence_response_recorder( + request_id, target_node, response_path, timeout, poll_interval + ) + + if not success: + logging.error(f"Fence operation failed: {message}") + record_fence_event( + action, + target_node, + "failed", + f"Fence action {action} failed: {message}" + ) + return False + + # Record successful completion + record_fence_event( + action, + target_node, + "completed", + f"Fence action {action} completed successfully: {message}" + ) + + return True + def main(): - device_opt = ["no_password", "status_file", "random_sleep_range", "type", "no_port"] + device_opt = ["no_password", "status_file", "random_sleep_range", "type", "port", "no_port", + "request_path", "response_path", "recorder_timeout", "recorder_poll_interval", "log_path"] atexit.register(atexit_handler) + # Port is optional - no_port in device_opt handles runtime validation, + # but we also need to set required=0 for correct metadata generation + all_opt["port"]["required"] = "0" + all_opt["status_file"] = { "getopt" : ":", "longopt" : "status-file", @@ -98,21 +288,77 @@ all_opt["type"] = { "getopt" : ":", "longopt" : "type", - "help":"--type=[type] Possible types are: file and fail", + "help":"--type=[type] Possible types are: file, fail, and recorder", "required" : "0", "shortdesc" : "Type of the dummy fence agent", "default" : "file", "order": 1 } + all_opt["request_path"] = { + "getopt" : ":", + "longopt" : "request-path", + "help":"--request-path=[path] Directory for fence request files (recorder mode)", + "required" : "0", + "shortdesc" : "Request directory path for recorder mode", + "default" : DEFAULT_REQUEST_PATH, + "order": 1 + } + + all_opt["response_path"] = { + "getopt" : ":", + "longopt" : "response-path", + "help":"--response-path=[path] Directory for fence response files (recorder mode)", + "required" : "0", + "shortdesc" : "Response directory path for recorder mode", + "default" : DEFAULT_RESPONSE_PATH, + "order": 1 + } + + all_opt["recorder_timeout"] = { + "getopt" : ":", + "longopt" : "recorder-timeout", + "help":"--recorder-timeout=[seconds] Timeout for external response (recorder mode)", + "required" : "0", + "shortdesc" : "Response timeout for recorder mode", + "default" : str(DEFAULT_RECORDER_TIMEOUT), + "order": 1 + } + + all_opt["recorder_poll_interval"] = { + "getopt" : ":", + "longopt" : "recorder-poll-interval", + "help":"--recorder-poll-interval=[sec] Poll interval for response check (recorder mode)", + "required" : "0", + "shortdesc" : "Poll interval for recorder mode", + "default" : str(DEFAULT_RECORDER_POLL_INTERVAL), + "order": 1 + } + + all_opt["log_path"] = { + "getopt" : ":", + "longopt" : "log-path", + "help":"--log-path=[path] Directory for fence event logs (recorder mode)", + "required" : "0", + "shortdesc" : "Log directory path for fence events", + "default" : DEFAULT_LOG_PATH, + "order": 1 + } + options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Dummy fence agent" - docs["longdesc"] = "fence_dummy" + docs["longdesc"] = "fence_dummy is a fake fence agent for testing. " \ + "It supports three modes: 'file' (status in file), 'fail' (simulated failures), " \ + "and 'recorder' (request/response coordination with external systems)." docs["vendorurl"] = "http://www.example.com" show_docs(options, docs) + # Setup persistent logging for recorder mode only + if options.get("--type") == "recorder": + setup_recorder_logging(options["--log-path"]) + run_delay(options) # random sleep for testing @@ -124,6 +370,12 @@ if options["--type"] == "fail": result = fence_action(None, options, set_power_status_fail, get_power_status_fail, get_outlets_fail) + elif options["--type"] == "recorder": + # Use sync_set_power_fn pattern to avoid post-action power state verification + # (we can't query actual power state - the response file confirms the action) + # Note: fence_action signature is (conn, options, set_power_fn, get_power_fn, get_outlet_list, reboot_cycle_fn, sync_set_power_fn) + # The 7th parameter is defined in lib/fencing.py.py but not in agents/autodetect/fencing.py + result = fence_action(None, options, None, get_power_status_recorder, None, None, sync_set_power_status_recorder) # type: ignore[call-arg] else: result = fence_action(None, options, set_power_status_file, get_power_status_file, None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/ibm_vpc/fence_ibm_vpc.py new/fence-agents-4.17.0+git.1769693386.608dd62/agents/ibm_vpc/fence_ibm_vpc.py --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/ibm_vpc/fence_ibm_vpc.py 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/ibm_vpc/fence_ibm_vpc.py 2026-01-29 14:29:46.000000000 +0100 @@ -16,8 +16,8 @@ "stopping": "unknown", "restarting": "unknown", "pending": "unknown", - "deleting": "unknown", - "failed": "unknown", + "deleting": "unknown", + "failed": "unknown", } def get_list(conn, options): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/fence_kdump.c new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/fence_kdump.c --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/fence_kdump.c 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/fence_kdump.c 2026-01-29 14:29:46.000000000 +0100 @@ -229,7 +229,7 @@ do_action_metadata (const char *self) { fprintf (stdout, "<?xml version=\"1.0\" ?>\n"); - fprintf (stdout, "<resource-agent name=\"%s\"", basename (self)); + fprintf (stdout, "<resource-agent name=\"%s\"", basename ((char *)self)); fprintf (stdout, " shortdesc=\"fencing agent for use with kdump crash recovery service\">\n"); fprintf (stdout, "<longdesc>"); fprintf (stdout, "fence_kdump is an I/O fencing agent to be used with the kdump\n" @@ -335,7 +335,7 @@ static void print_usage (const char *self) { - fprintf (stdout, "Usage: %s [options]\n", basename (self)); + fprintf (stdout, "Usage: %s [options]\n", basename ((char *)self)); fprintf (stdout, "\n"); fprintf (stdout, "Options:\n"); fprintf (stdout, "\n"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/fence_kdump_send.c new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/fence_kdump_send.c --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/fence_kdump_send.c 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/fence_kdump_send.c 2026-01-29 14:29:46.000000000 +0100 @@ -72,7 +72,7 @@ static void print_usage (const char *self) { - fprintf (stdout, "Usage: %s [options] [nodes]\n", basename (self)); + fprintf (stdout, "Usage: %s [options] [nodes]\n", basename ((char *)self)); fprintf (stdout, "\n"); fprintf (stdout, "Options:\n"); fprintf (stdout, "\n"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/version.h new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/version.h --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/kdump/version.h 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/kdump/version.h 2026-01-29 14:29:46.000000000 +0100 @@ -24,10 +24,12 @@ #define FENCE_KDUMP_VERSION "0.1" +#include <libgen.h> + static inline void print_version (const char *self) { - fprintf (stdout, "%s %s\n", basename (self), FENCE_KDUMP_VERSION); + fprintf (stdout, "%s %s\n", basename ((char *)self), FENCE_KDUMP_VERSION); } #endif /* _FENCE_KDUMP_VERSION_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/scsi/fence_scsi.py new/fence-agents-4.17.0+git.1769693386.608dd62/agents/scsi/fence_scsi.py --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/scsi/fence_scsi.py 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/scsi/fence_scsi.py 2026-01-29 14:29:46.000000000 +0100 @@ -133,7 +133,7 @@ def register_dev(options, dev, key, do_preempt=True): dev = os.path.realpath(dev) - if re.search(r"^dm", dev[5:]): + if options.get("--mpath-register-method") == "multi" and re.search(r"^dm", dev[5:]): devices = get_mpath_slaves(dev) register_dev(options, devices[0], key) for device in devices[1:]: @@ -384,6 +384,15 @@ "shortdesc" : "Use the APTPL flag for registrations. This option is only used for the 'on' action.", "order": 1 } + all_opt["mpath_register_method"] = { + "getopt" : ":", + "longopt" : "mpath-register-method", + "help" : "--mpath-register-method=[multi|single] Register key to all multipath sub devices (multi), or directly to the main multipath device (single).", + "required" : "0", + "shortdesc" : "Multipath register method (multi/single).", + "default": "multi", + "order": 3 + } all_opt["readonly"] = { "getopt" : "", "longopt" : "readonly", @@ -523,8 +532,8 @@ device_opt = ["no_login", "no_password", "devices", "nodename", "port",\ "no_port", "key", "aptpl", "fabric_fencing", "on_target", "corosync_cmap_path",\ - "sg_persist_path", "sg_turs_path", "readonly", "suppress-errors", "logfile", "vgs_path",\ - "force_on", "key_value"] + "sg_persist_path", "sg_turs_path", "mpath_register_method", "readonly", \ + "suppress-errors", "logfile", "vgs_path","force_on", "key_value"] define_new_opts() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/daemon_init.c new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/daemon_init.c --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/daemon_init.c 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/daemon_init.c 2026-01-29 14:29:46.000000000 +0100 @@ -23,7 +23,7 @@ #include <fcntl.h> #include <dirent.h> #include <sys/mman.h> -#include <sys/errno.h> +#include <errno.h> #include <libgen.h> #include <signal.h> #include <syslog.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/virt-serial.c new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/virt-serial.c --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/virt-serial.c 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/virt-serial.c 2026-01-29 14:29:46.000000000 +0100 @@ -12,7 +12,7 @@ #include <fcntl.h> #include <sys/types.h> -#include <sys/poll.h> +#include <poll.h> #include <libvirt/libvirt.h> #include <libxml/xmlreader.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/virt-sockets.c new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/virt-sockets.c --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/agents/virt/server/virt-sockets.c 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/agents/virt/server/virt-sockets.c 2026-01-29 14:29:46.000000000 +0100 @@ -8,6 +8,7 @@ #include <sys/socket.h> #include <sys/un.h> #include <stdlib.h> +#include <string.h> #include <errno.h> #include <fcntl.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/tests/data/metadata/fence_dummy.xml new/fence-agents-4.17.0+git.1769693386.608dd62/tests/data/metadata/fence_dummy.xml --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/tests/data/metadata/fence_dummy.xml 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/tests/data/metadata/fence_dummy.xml 2026-01-29 14:29:46.000000000 +0100 @@ -1,6 +1,6 @@ <?xml version="1.0" ?> <resource-agent name="fence_dummy" shortdesc="Dummy fence agent" > -<longdesc>fence_dummy</longdesc> +<longdesc>fence_dummy is a fake fence agent for testing. It supports three modes: 'file' (status in file), 'fail' (simulated failures), and 'recorder' (request/response coordination with external systems).</longdesc> <vendor-url>http://www.example.com</vendor-url> <parameters> <parameter name="action" unique="0" required="1"> @@ -8,11 +8,43 @@ <content type="string" default="reboot" /> <shortdesc lang="en">Fencing action</shortdesc> </parameter> + <parameter name="log_path" unique="0" required="0"> + <getopt mixed="--log-path=[path]" /> + <shortdesc lang="en">Log directory path for fence events</shortdesc> + </parameter> + <parameter name="plug" unique="0" required="0" 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="0" 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="random_sleep_range" unique="0" required="0"> <getopt mixed="--random_sleep_range=[seconds]" /> <content type="string" /> <shortdesc lang="en">Issue a sleep between 1 and X seconds. Used for testing.</shortdesc> </parameter> + <parameter name="recorder_poll_interval" unique="0" required="0"> + <getopt mixed="--recorder-poll-interval=[sec]" /> + <content type="string" default="0.5" /> + <shortdesc lang="en">Poll interval for recorder mode</shortdesc> + </parameter> + <parameter name="recorder_timeout" unique="0" required="0"> + <getopt mixed="--recorder-timeout=[seconds]" /> + <content type="string" default="60" /> + <shortdesc lang="en">Response timeout for recorder mode</shortdesc> + </parameter> + <parameter name="request_path" unique="0" required="0"> + <getopt mixed="--request-path=[path]" /> + <shortdesc lang="en">Request directory path for recorder mode</shortdesc> + </parameter> + <parameter name="response_path" unique="0" required="0"> + <getopt mixed="--response-path=[path]" /> + <shortdesc lang="en">Response directory path for recorder mode</shortdesc> + </parameter> <parameter name="status_file" unique="0" required="0"> <getopt mixed="--status-file=[file]" /> <shortdesc lang="en">File with status</shortdesc> @@ -61,6 +93,11 @@ <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" /> @@ -107,6 +144,8 @@ <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" /> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fence-agents-4.16.0+git.1765293331.d30fb54b/tests/data/metadata/fence_scsi.xml new/fence-agents-4.17.0+git.1769693386.608dd62/tests/data/metadata/fence_scsi.xml --- old/fence-agents-4.16.0+git.1765293331.d30fb54b/tests/data/metadata/fence_scsi.xml 2025-12-09 16:15:31.000000000 +0100 +++ new/fence-agents-4.17.0+git.1769693386.608dd62/tests/data/metadata/fence_scsi.xml 2026-01-29 14:29:46.000000000 +0100 @@ -36,6 +36,11 @@ <content type="string" /> <shortdesc lang="en">Name of the node to be fenced. The node name is used to generate the key value used for the current operation. This option will be ignored when used with the -k option.</shortdesc> </parameter> + <parameter name="mpath_register_method" unique="0" required="0"> + <getopt mixed="--mpath-register-method=[multi|single]" /> + <content type="string" default="multi" /> + <shortdesc lang="en">Multipath register method (multi/single).</shortdesc> + </parameter> <parameter name="readonly" unique="0" required="0"> <getopt mixed="--readonly" /> <content type="boolean" />
