mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-09-03 18:55:33 +00:00
feat: add ClusterConfigService.WaitForQuorum
Polls GET /cluster/status for the cluster entry with quorate = 1, bounded by a timeout (60 s default) and tolerating PveApiException while pmxcfs and corosync restart during cluster formation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
a4ebcec753
commit
6881e5c53c
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using PSProxmoxVE.Core.Authentication;
|
using PSProxmoxVE.Core.Authentication;
|
||||||
using PSProxmoxVE.Core.Client;
|
using PSProxmoxVE.Core.Client;
|
||||||
|
using PSProxmoxVE.Core.Exceptions;
|
||||||
using PSProxmoxVE.Core.Models.Cluster;
|
using PSProxmoxVE.Core.Models.Cluster;
|
||||||
using PSProxmoxVE.Core.Utilities;
|
using PSProxmoxVE.Core.Utilities;
|
||||||
|
|
||||||
@@ -16,6 +18,9 @@ namespace PSProxmoxVE.Core.Services
|
|||||||
{
|
{
|
||||||
private readonly IPveHttpClient? _injectedClient;
|
private readonly IPveHttpClient? _injectedClient;
|
||||||
|
|
||||||
|
private static readonly TimeSpan DefaultQuorumTimeout = TimeSpan.FromSeconds(60);
|
||||||
|
private static readonly TimeSpan QuorumPollInterval = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ClusterConfigService"/> class.
|
/// Initializes a new instance of the <see cref="ClusterConfigService"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -378,6 +383,48 @@ namespace PSProxmoxVE.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Blocks until the cluster reports quorum (GET /cluster/status, quorate = 1).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="session">The authenticated PVE session.</param>
|
||||||
|
/// <param name="timeout">Maximum time to wait. Defaults to 60 seconds.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
/// <exception cref="TimeoutException">Quorum was not reached before the deadline.</exception>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the next available VM/CT ID (GET /cluster/nextid).
|
/// Returns the next available VM/CT ID (GET /cluster/nextid).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user