208 lines
8.2 KiB
C#
208 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace BizTalkCheckmkPulse
|
|
{
|
|
/// <summary>
|
|
/// Validiert die vom ersten Providerlauf erzeugten Runtime-Artefakte mit denselben
|
|
/// Parsern und Grenzen, die auch der laufende Consumer verwendet.
|
|
/// </summary>
|
|
internal static class RuntimeValidator
|
|
{
|
|
public static RuntimeValidationResult Validate(
|
|
MonitoringOptions options,
|
|
CheckmkLocalFormatter formatter,
|
|
DateTime notBeforeUtc,
|
|
string expectedIdentity,
|
|
DateTime utcNow)
|
|
{
|
|
if (options == null) throw new ArgumentNullException("options");
|
|
if (formatter == null) throw new ArgumentNullException("formatter");
|
|
|
|
var snapshot = new SnapshotStore(options.SnapshotPath, options.SnapshotMaxBytes)
|
|
.Read(utcNow, TimeSpan.FromSeconds(options.SnapshotMaxAgeSeconds));
|
|
if (!snapshot.IsSuccess)
|
|
{
|
|
return RuntimeValidationResult.Failed("Snapshot ist ungueltig: " + snapshot.Error);
|
|
}
|
|
|
|
var boundary = notBeforeUtc.ToUniversalTime();
|
|
if (snapshot.GeneratedUtc < boundary)
|
|
{
|
|
return RuntimeValidationResult.Failed(
|
|
"Snapshot stammt nicht aus dem gestarteten Providerlauf. generatedUtc="
|
|
+ snapshot.GeneratedUtc.ToString("o") + ", requiredUtc=" + boundary.ToString("o") + ".");
|
|
}
|
|
|
|
if (!string.Equals(snapshot.Identity, expectedIdentity, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return RuntimeValidationResult.Failed(
|
|
"Snapshot wurde von einer unerwarteten Identitaet erzeugt. expected="
|
|
+ expectedIdentity + ", actual=" + (snapshot.Identity ?? "(leer)") + ".");
|
|
}
|
|
|
|
string serviceError;
|
|
string[] unknownServiceLines;
|
|
var expectedServices = formatter.FormatSelfTest().Select(ExtractServiceName).ToArray();
|
|
if (!ContainsExactStableContract(
|
|
snapshot.Lines,
|
|
expectedServices,
|
|
out unknownServiceLines,
|
|
out serviceError))
|
|
{
|
|
return RuntimeValidationResult.Failed(serviceError);
|
|
}
|
|
|
|
var acceptedEndpointUnknownName = options.ServiceName("Endpoint Reachability");
|
|
var blockingUnknown = unknownServiceLines
|
|
.Where(x => !options.ProbeEndpointConnectivity
|
|
|| !string.Equals(
|
|
ExtractServiceName(x),
|
|
acceptedEndpointUnknownName,
|
|
StringComparison.Ordinal))
|
|
.Select(ExtractServiceName)
|
|
.OrderBy(x => x, StringComparer.Ordinal)
|
|
.ToArray();
|
|
if (blockingUnknown.Length != 0)
|
|
{
|
|
return RuntimeValidationResult.Failed(
|
|
"Snapshot enthaelt blockierende UNKNOWN-Zustaende in Kernservices. UNKNOWN=["
|
|
+ string.Join(", ", blockingUnknown) + "].");
|
|
}
|
|
|
|
DateTime? catalogUtc = null;
|
|
if (options.ProbeEndpointConnectivity)
|
|
{
|
|
EndpointCatalog catalog;
|
|
try
|
|
{
|
|
catalog = new EndpointCatalogStore(
|
|
options.EndpointCatalogPath,
|
|
options.EndpointCatalogMaxBytes,
|
|
options.EndpointCatalogMaxEntries)
|
|
.Read(options.EnvironmentName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return RuntimeValidationResult.Failed(
|
|
"Endpoint-Katalog ist ungueltig: " + ex.GetType().Name + ": " + ex.Message);
|
|
}
|
|
|
|
if (catalog.SynchronizedUtc < boundary)
|
|
{
|
|
return RuntimeValidationResult.Failed(
|
|
"Endpoint-Katalog wurde beim Installationslauf nicht aktualisiert. synchronizedUtc="
|
|
+ catalog.SynchronizedUtc.ToString("o") + ", requiredUtc=" + boundary.ToString("o") + ".");
|
|
}
|
|
|
|
if (catalog.SynchronizedUtc > utcNow.ToUniversalTime().AddMinutes(5))
|
|
{
|
|
return RuntimeValidationResult.Failed("Endpoint-Katalog-Zeitstempel liegt unplausibel in der Zukunft.");
|
|
}
|
|
|
|
catalogUtc = catalog.SynchronizedUtc;
|
|
}
|
|
|
|
return RuntimeValidationResult.Success(
|
|
snapshot.GeneratedUtc,
|
|
catalogUtc,
|
|
expectedServices.Length,
|
|
unknownServiceLines);
|
|
}
|
|
|
|
internal static bool ContainsExactStableContract(
|
|
IEnumerable<string> lines,
|
|
IEnumerable<string> expectedServices,
|
|
out string[] unknownServiceLines,
|
|
out string error)
|
|
{
|
|
unknownServiceLines = new string[0];
|
|
string[] actual;
|
|
try
|
|
{
|
|
actual = (lines ?? Enumerable.Empty<string>()).Select(ExtractServiceName).ToArray();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
error = "Snapshot-Servicevertrag kann nicht gelesen werden: " + ex.Message;
|
|
return false;
|
|
}
|
|
|
|
if (actual.Distinct(StringComparer.Ordinal).Count() != actual.Length)
|
|
{
|
|
error = "Snapshot enthaelt doppelte Checkmk-Servicenamen.";
|
|
return false;
|
|
}
|
|
|
|
var actualSet = new HashSet<string>(actual, StringComparer.Ordinal);
|
|
var expected = (expectedServices ?? Enumerable.Empty<string>()).ToArray();
|
|
var missing = expected
|
|
.Where(x => !actualSet.Contains(x))
|
|
.OrderBy(x => x, StringComparer.Ordinal)
|
|
.ToArray();
|
|
if (missing.Length != 0)
|
|
{
|
|
error = "Snapshot enthaelt nicht alle stabilen Checkmk-Services. Fehlend=["
|
|
+ string.Join(", ", missing) + "].";
|
|
return false;
|
|
}
|
|
|
|
unknownServiceLines = (lines ?? Enumerable.Empty<string>())
|
|
.Where(x => x != null && x.StartsWith("3 ", StringComparison.Ordinal))
|
|
.Where(x => expected.Contains(ExtractServiceName(x), StringComparer.Ordinal))
|
|
.OrderBy(ExtractServiceName, StringComparer.Ordinal)
|
|
.ToArray();
|
|
|
|
error = string.Empty;
|
|
return true;
|
|
}
|
|
|
|
private static string ExtractServiceName(string line)
|
|
{
|
|
var firstQuote = (line ?? string.Empty).IndexOf('"');
|
|
var secondQuote = firstQuote < 0 ? -1 : line.IndexOf('"', firstQuote + 1);
|
|
if (firstQuote < 0 || secondQuote <= firstQuote + 1)
|
|
throw new InvalidOperationException("Ungueltige Local-Check-Zeile.");
|
|
return line.Substring(firstQuote + 1, secondQuote - firstQuote - 1);
|
|
}
|
|
}
|
|
|
|
internal sealed class RuntimeValidationResult
|
|
{
|
|
private RuntimeValidationResult() { }
|
|
|
|
public bool IsSuccess { get; private set; }
|
|
public string Error { get; private set; }
|
|
public DateTime GeneratedUtc { get; private set; }
|
|
public DateTime? CatalogSynchronizedUtc { get; private set; }
|
|
public int StableServiceCount { get; private set; }
|
|
public IReadOnlyList<string> AcceptedUnknownLines { get; private set; }
|
|
|
|
public static RuntimeValidationResult Success(
|
|
DateTime generatedUtc,
|
|
DateTime? catalogUtc,
|
|
int serviceCount,
|
|
IEnumerable<string> acceptedUnknownLines)
|
|
{
|
|
return new RuntimeValidationResult
|
|
{
|
|
IsSuccess = true,
|
|
GeneratedUtc = generatedUtc,
|
|
CatalogSynchronizedUtc = catalogUtc,
|
|
StableServiceCount = serviceCount,
|
|
AcceptedUnknownLines = (acceptedUnknownLines ?? Enumerable.Empty<string>()).ToArray()
|
|
};
|
|
}
|
|
|
|
public static RuntimeValidationResult Failed(string error)
|
|
{
|
|
return new RuntimeValidationResult
|
|
{
|
|
Error = error,
|
|
AcceptedUnknownLines = new string[0]
|
|
};
|
|
}
|
|
}
|
|
}
|