diff --git a/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs b/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs
index a66f603..e288816 100644
--- a/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs
+++ b/src/PSProxmoxVE.Core/Services/ClusterConfigService.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Authentication;
using PSProxmoxVE.Core.Client;
+using PSProxmoxVE.Core.Exceptions;
using PSProxmoxVE.Core.Models.Cluster;
using PSProxmoxVE.Core.Utilities;
@@ -16,6 +18,9 @@ namespace PSProxmoxVE.Core.Services
{
private readonly IPveHttpClient? _injectedClient;
+ private static readonly TimeSpan DefaultQuorumTimeout = TimeSpan.FromSeconds(60);
+ private static readonly TimeSpan QuorumPollInterval = TimeSpan.FromSeconds(2);
+
///
/// Initializes a new instance of the class.
///
@@ -378,6 +383,48 @@ namespace PSProxmoxVE.Core.Services
}
}
+ ///
+ /// Blocks until the cluster reports quorum (GET /cluster/status, quorate = 1).
+ ///
+ /// The authenticated PVE session.
+ /// Maximum time to wait. Defaults to 60 seconds.
+ ///
+ /// The cluster-create task completes before corosync converges; until the node
+ /// is quorate it rejects joins with "cluster not ready - no quorum?". API errors
+ /// during that window are transient and are retried until the deadline.
+ ///
+ /// Quorum was not reached before the deadline.
+ public void WaitForQuorum(PveSession session, TimeSpan? timeout = null)
+ {
+ if (session == null) throw new ArgumentNullException(nameof(session));
+
+ var effectiveTimeout = timeout ?? DefaultQuorumTimeout;
+ var deadline = DateTime.UtcNow.Add(effectiveTimeout);
+
+ while (true)
+ {
+ try
+ {
+ foreach (var entry in GetClusterStatus(session))
+ {
+ if (string.Equals(entry.Type, "cluster", StringComparison.OrdinalIgnoreCase)
+ && entry.Quorate == 1)
+ return;
+ }
+ }
+ catch (PveApiException)
+ {
+ // pmxcfs and corosync restart while the cluster forms.
+ }
+
+ if (DateTime.UtcNow >= deadline)
+ throw new TimeoutException(
+ $"Cluster did not reach quorum within {effectiveTimeout.TotalSeconds:0} seconds.");
+
+ Thread.Sleep(QuorumPollInterval);
+ }
+ }
+
///
/// Returns the next available VM/CT ID (GET /cluster/nextid).
///