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). ///