New project SQLInsight.Servers: - CimWindowsServerSource (Microsoft.Management.Infrastructure, the API behind Get-CimInstance): WS-Man first, DCOM if that fails, a local session for the agent's own computer, as the agent's identity. Only SELECT queries (CimQueries) and Task Scheduler's read-only GetInfo. CPU and paging from raw counters, disks (Win32_Volume, Win32_LogicalDisk fallback), services, scheduled tasks, and the System/Application logs through the Event Log service (MS-EVEN6, XPath filter on the server) with Win32_NTLogEvent as fallback. - ServerErrors: access denied, missing namespace, RPC/WinRM unreachable and timeouts become "ikke målbar" reasons that name the missing right and the grant script; never local administrator. - WindowsServerTester: tries every area once for the web UI's test. - SyntheticCheckRunner: TCP, HTTP(S) with expected status/text and TLS certificate expiry (also for refused certificates), SQL login + read-only query (re-validated, in a transaction that is always rolled back), DNS, and the Dynamics AX logon: AOS port, then a logon through the .NET Business Connector in a separate Windows PowerShell process with a timeout when it is installed; otherwise the result says clearly that only the port was tested. Tests: error mapping, queries, AX command/parsing, runners against local TCP and HTTP(S) servers with a self-signed certificate, the CIM source against this computer, and the SQL check against the test instance. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
106 lines
4.5 KiB
C#
106 lines
4.5 KiB
C#
using SQLInsight.Core.Servers;
|
|
|
|
namespace SQLInsight.Servers;
|
|
|
|
/// <summary>Opens read-only sessions to Windows servers, as the agent's own identity.</summary>
|
|
public interface IWindowsServerSource
|
|
{
|
|
/// <summary>Connects to <paramref name="host"/>: WS-Man (WinRM) first, DCOM if that fails, or locally for the agent's own computer.</summary>
|
|
/// <exception cref="ServerAccessException">The server could not be reached; the message says why (Danish).</exception>
|
|
Task<IWindowsServerSession> ConnectAsync(string host, CancellationToken cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// One connection to a Windows server. Every method only reads: no method changes, starts or stops anything. Each
|
|
/// throws <see cref="ServerAccessException"/> with the exact reason when its area cannot be read.
|
|
/// </summary>
|
|
public interface IWindowsServerSession : IDisposable
|
|
{
|
|
/// <summary>"WS-Man", "DCOM" or "lokal".</summary>
|
|
string Protocol { get; }
|
|
|
|
/// <summary>Why the preferred protocol was not used (Danish), when it was not.</summary>
|
|
string? ProtocolNote { get; }
|
|
|
|
Task<ServerOsInfo> GetOperatingSystemAsync(CancellationToken cancellationToken);
|
|
|
|
Task<ServerPerformanceRaw> GetPerformanceAsync(DateTimeOffset now, CancellationToken cancellationToken);
|
|
|
|
Task<IReadOnlyList<ServerDisk>> GetDisksAsync(CancellationToken cancellationToken);
|
|
|
|
Task<IReadOnlyList<ServerService>> GetServicesAsync(CancellationToken cancellationToken);
|
|
|
|
/// <summary>One service, or null when it does not exist (or cannot be seen).</summary>
|
|
Task<ServerService?> GetServiceAsync(string name, CancellationToken cancellationToken);
|
|
|
|
/// <summary>Errors and critical events in the System and Application logs since <paramref name="since"/>, oldest first.</summary>
|
|
Task<ServerEventRead> GetEventsAsync(DateTimeOffset since, int max, CancellationToken cancellationToken);
|
|
|
|
/// <summary>Scheduled tasks; run information only for the ones <paramref name="watch"/> selects (at most <paramref name="maxWatched"/>).</summary>
|
|
Task<ServerTaskRead> GetTasksAsync(Func<string, bool> watch, int maxWatched, CancellationToken cancellationToken);
|
|
}
|
|
|
|
/// <param name="Method">How the logs were read: "Event Log (RPC)" or "CIM (Win32_NTLogEvent)".</param>
|
|
/// <param name="Truncated">More events than <c>max</c>: the rest is read next time.</param>
|
|
public sealed record ServerEventRead(IReadOnlyList<ServerEventEntry> Entries, string Method, bool Truncated, string? Note = null);
|
|
|
|
/// <param name="Visible">Every task the agent could see.</param>
|
|
public sealed record ServerTaskRead(IReadOnlyList<ServerTask> Watched, int Visible, string? Note = null);
|
|
|
|
public enum ServerAccessProblem
|
|
{
|
|
/// <summary>The server does not answer (name, network, firewall, WinRM/RPC not running).</summary>
|
|
Unreachable,
|
|
|
|
/// <summary>The server answers, but the agent's account may not read this.</summary>
|
|
AccessDenied,
|
|
|
|
/// <summary>The server does not have it (an older Windows without the class or namespace).</summary>
|
|
NotAvailable,
|
|
|
|
/// <summary>The server did not answer in time.</summary>
|
|
Timeout,
|
|
|
|
/// <summary>The agent cannot do it at all (not on Windows).</summary>
|
|
NotSupported,
|
|
|
|
Other,
|
|
}
|
|
|
|
/// <summary>A Windows server or one of its areas could not be read. The message is Danish and says exactly why and what to do.</summary>
|
|
public sealed class ServerAccessException : Exception
|
|
{
|
|
public ServerAccessException()
|
|
{
|
|
}
|
|
|
|
public ServerAccessException(string message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
public ServerAccessException(string message, Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
}
|
|
|
|
public ServerAccessException(ServerAccessProblem problem, string message, Exception? innerException = null)
|
|
: base(message, innerException)
|
|
{
|
|
Problem = problem;
|
|
}
|
|
|
|
public ServerAccessProblem Problem { get; } = ServerAccessProblem.Other;
|
|
|
|
/// <summary>The Windows error code (HRESULT) when there was one, for support.</summary>
|
|
public uint? Code { get; init; }
|
|
}
|
|
|
|
/// <summary>For an agent that does not run on Windows: Windows servers cannot be read from Linux.</summary>
|
|
public sealed class UnsupportedWindowsServerSource : IWindowsServerSource
|
|
{
|
|
public const string Reason = "Windows-servere kan kun overvåges, når SQLInsight Agent kører på Windows (CIM/WMI og hændelseslog er Windows-protokoller).";
|
|
|
|
public Task<IWindowsServerSession> ConnectAsync(string host, CancellationToken cancellationToken) =>
|
|
throw new ServerAccessException(ServerAccessProblem.NotSupported, Reason);
|
|
}
|