From b2c4a583f7e28ca984ff8e5ea1f009e4ce7d49b1 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Sat, 22 Nov 2025 23:44:01 +0000 Subject: [PATCH] Fix pvecm status parsing for QDevice flags (#738) --- cmd/pulse-sensor-proxy/ssh.go | 26 +++++--- cmd/pulse-sensor-proxy/ssh_parsing_test.go | 77 ++++++++++++++++++++++ scripts/install-sensor-proxy.sh | 6 +- scripts/pulse-sensor-cleanup.sh | 2 +- 4 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 cmd/pulse-sensor-proxy/ssh_parsing_test.go diff --git a/cmd/pulse-sensor-proxy/ssh.go b/cmd/pulse-sensor-proxy/ssh.go index 7fe6195..ff718ad 100644 --- a/cmd/pulse-sensor-proxy/ssh.go +++ b/cmd/pulse-sensor-proxy/ssh.go @@ -732,12 +732,13 @@ func discoverClusterNodes() ([]string, error) { } // Parse output to extract IP addresses - // Format example: - // 0x00000001 1 192.168.0.134 - // 0x00000003 1 192.168.0.5 (local) + return parseClusterNodes(out.String()) +} +// parseClusterNodes parses pvecm status output to extract IP addresses +func parseClusterNodes(output string) ([]string, error) { var nodes []string - lines := strings.Split(out.String(), "\n") + lines := strings.Split(output, "\n") for _, line := range lines { // Look for lines with hex ID and IP address if !strings.Contains(line, "0x") { @@ -750,11 +751,18 @@ func discoverClusterNodes() ([]string, error) { continue } - // Third field should be the IP address - ip := fields[2] - // Basic validation that it looks like an IP - if strings.Contains(ip, ".") { - nodes = append(nodes, ip) + // Iterate through fields to find the IP address + for _, field := range fields { + // Skip hex ID + if strings.HasPrefix(field, "0x") { + continue + } + + // Check if it's a valid IP + if ip := net.ParseIP(field); ip != nil { + nodes = append(nodes, field) + break + } } } diff --git a/cmd/pulse-sensor-proxy/ssh_parsing_test.go b/cmd/pulse-sensor-proxy/ssh_parsing_test.go new file mode 100644 index 0000000..ced9c58 --- /dev/null +++ b/cmd/pulse-sensor-proxy/ssh_parsing_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "testing" +) + +func TestParseClusterNodes(t *testing.T) { + tests := []struct { + name string + output string + want []string + wantErr bool + }{ + { + name: "standard output", + output: `Membership information +---------------------- + Nodeid Votes Name +0x00000001 1 192.168.1.10 +0x00000002 1 192.168.1.11`, + want: []string{"192.168.1.10", "192.168.1.11"}, + }, + { + name: "output with QDevice flags", + output: `Membership information +---------------------- + Nodeid Votes Name +0x00000001 1 A,V,NMW 192.168.1.10 +0x00000002 1 A,V,NMW 192.168.1.11`, + want: []string{"192.168.1.10", "192.168.1.11"}, + }, + { + name: "output with local suffix", + output: `Membership information +---------------------- + Nodeid Votes Name +0x00000001 1 192.168.1.10 (local) +0x00000002 1 192.168.1.11`, + want: []string{"192.168.1.10", "192.168.1.11"}, + }, + { + name: "mixed output", + output: `Membership information +---------------------- + Nodeid Votes Name +0x00000001 1 A,V,NMW 192.168.1.10 (local) +0x00000002 1 192.168.1.11`, + want: []string{"192.168.1.10", "192.168.1.11"}, + }, + { + name: "no nodes", + output: "some random text", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseClusterNodes(tt.output) + if (err != nil) != tt.wantErr { + t.Errorf("parseClusterNodes() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if len(got) != len(tt.want) { + t.Errorf("parseClusterNodes() got = %v, want %v", got, tt.want) + } else { + for i := range got { + if got[i] != tt.want[i] { + t.Errorf("parseClusterNodes() got[%d] = %v, want %v", i, got[i], tt.want[i]) + } + } + } + } + }) + } +} diff --git a/scripts/install-sensor-proxy.sh b/scripts/install-sensor-proxy.sh index ee72686..ffa34b4 100755 --- a/scripts/install-sensor-proxy.sh +++ b/scripts/install-sensor-proxy.sh @@ -805,7 +805,7 @@ cleanup_cluster_authorized_keys_manual() { if command -v pvecm >/dev/null 2>&1; then while IFS= read -r node_ip; do [[ -n "$node_ip" ]] && nodes+=("$node_ip") - done < <(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}' || true) + done < <(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {for(i=1;i<=NF;i++) if($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $i}' || true) fi if [[ ${#nodes[@]} -eq 0 ]]; then @@ -2646,7 +2646,7 @@ if [[ -z "$HOST" ]]; then # Discover cluster nodes if command -v pvecm >/dev/null 2>&1; then - CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}' || true) + CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {for(i=1;i<=NF;i++) if($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $i}' || true) if [[ -n "$CLUSTER_NODES" ]]; then for node_ip in $CLUSTER_NODES; do @@ -2955,7 +2955,7 @@ print_info "Proxy public key: ${PROXY_PUBLIC_KEY:0:50}..." # Discover cluster nodes if command -v pvecm >/dev/null 2>&1; then # Extract node IPs from pvecm status - CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}' || true) + CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {for(i=1;i<=NF;i++) if($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $i}' || true) if [[ -n "$CLUSTER_NODES" ]]; then print_info "Discovered cluster nodes: $(echo $CLUSTER_NODES | tr '\n' ' ')" diff --git a/scripts/pulse-sensor-cleanup.sh b/scripts/pulse-sensor-cleanup.sh index d283277..3e78051 100755 --- a/scripts/pulse-sensor-cleanup.sh +++ b/scripts/pulse-sensor-cleanup.sh @@ -50,7 +50,7 @@ if [[ -z "$HOST" ]]; then # Discover cluster nodes if command -v pvecm >/dev/null 2>&1; then - CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $3}') + CLUSTER_NODES=$(pvecm status 2>/dev/null | awk '/0x[0-9a-f]+.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {for(i=1;i<=NF;i++) if($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $i}') if [[ -n "$CLUSTER_NODES" ]]; then for node_ip in $CLUSTER_NODES; do