using SQLInsight.Core.Collection;
using SQLInsight.Core.Servers;
namespace SQLInsight.Rules.Servers;
///
/// Thresholds of the Windows server rules (SQLInsight:Servers in the agent's appsettings.json). Not sealed: the agent's
/// options add how and how often to collect.
///
public class ServerRuleSettings
{
public CpuThresholds Cpu { get; set; } = new();
public MemoryThresholds Memory { get; set; } = new();
public DiskThresholds Disks { get; set; } = new();
public ServiceThresholds Services { get; set; } = new();
public EventLogThresholds EventLog { get; set; } = new();
public TaskThresholds Tasks { get; set; } = new();
/// A server not restarted for more than this many days is a (low) finding: it most likely lacks Windows updates. 0: off.
public int UptimeDays { get; set; }
}
public sealed class CpuThresholds
{
/// Average CPU at or above this (percent) …
public double HighPercent { get; set; } = 90;
/// … for this many minutes is a finding (SQLI-WIN-001).
public int SustainedMinutes { get; set; } = 15;
}
public sealed class MemoryThresholds
{
/// Available memory below this share (percent) …
public double AvailablePercent { get; set; } = 5;
/// … and below this many MB is a finding (SQLI-WIN-002). Both, so big SQL Server hosts with little left by design are not flagged.
public double AvailableMegabytes { get; set; } = 1024;
/// Hard page faults per second above this, while memory is low, is paging (SQLI-WIN-003).
public double PagesPerSecond { get; set; } = 1000;
/// Committed memory at or above this share of the commit limit (percent) is a finding (SQLI-WIN-004).
public double CommitPercent { get; set; } = 90;
}
public sealed class DiskThresholds
{
/// Free space below this (percent) is a finding (SQLI-WIN-005) …
public double FreePercent { get; set; } = 10;
/// … High below this …
public double CriticalFreePercent { get; set; } = 5;
/// … unless there is at least this much free (GB): a big volume with a small share free can be fine.
public double EnoughFreeGigabytes { get; set; } = 50;
/// A volume forecast to be full within this many days is a finding (SQLI-WIN-006); within 7 days High, 2 days Critical.
public int ForecastDays { get; set; } = 30;
/// How many days of SQLInsight's own samples the forecast is computed from.
public int ForecastHistoryDays { get; set; } = 14;
}
public sealed class ServiceThresholds
{
///
/// Services that may be stopped although they start automatically (they stop by design), in addition to the
/// built-in list. Name, or a prefix ending in *.
///
public IList Ignore { get; } = [];
/// Right after a restart services are still starting: no finding for this many minutes.
public int StartupGraceMinutes { get; set; } = 15;
///
/// At most this many findings for stopped automatic services per server (SQLI-WIN-007), by name; the rest are named in
/// one summary finding. Between 1 and 100.
///
public int MaxFindings { get; set; } = 15;
}
public sealed class EventLogThresholds
{
/// An ordinary error must repeat this many times in 24 hours before it is a finding; the important kinds count at once.
public int RepeatThreshold { get; set; } = 5;
/// Events left out as noise, in addition to the built-in list: "Kilde:id", "Kilde" or "Kilde*".
public IList Ignore { get; } = [];
/// At most this many event findings per server, most important first; the rest are named in one summary finding. Between 1 and 100.
public int MaxFindings { get; set; } = 15;
/// At most this many events per log are read in one collection; the rest is read next time.
public int MaxEventsPerCollection { get; set; } = 2000;
}
public sealed class TaskThresholds
{
/// Also watch Microsoft's own tasks (\Microsoft\…). Off: there are hundreds, and their failures are rarely yours.
public bool IncludeMicrosoftTasks { get; set; }
/// A task whose next run time is more than this many minutes in the past has not run as planned (SQLI-WIN-013).
public int LateMinutes { get; set; } = 60;
/// At most this many tasks per server are watched (their run information is read one at a time).
public int MaxWatched { get; set; } = 50;
///
/// Leave out the tasks programs create per user (a user's SID in the name) or to update themselves (OneDrive, Edge,
/// Google, User_Feed_Synchronization; ). On by default.
///
public bool IgnoreUserAndUpdaterTasks { get; set; } = true;
/// Tasks never watched (path\name, or a prefix ending in *).
public IList Ignore { get; } = [];
/// Whether the task (its full path) is watched: the agent's filter when it reads tasks, and the rules' too.
public bool Watches(string fullName)
{
ArgumentNullException.ThrowIfNull(fullName);
return (IncludeMicrosoftTasks || !TaskResults.IsMicrosoftTask(fullName))
&& !(IgnoreUserAndUpdaterTasks && TaskResults.IsUserOrUpdaterTask(fullName))
&& !WindowsServerRules.Matches(fullName, Ignore);
}
}
/// What the Windows server rules see: the server, the latest data per area, and the thresholds.
public sealed record ServerRuleContext(ServerIdentity Server, DateTimeOffset Now, CollectedData Data, ServerRuleSettings Settings)
{
/// The interval performance samples are taken at, for "sustained" averages.
public TimeSpan PerformanceInterval { get; init; } = ServerArea.DefaultInterval(ServerArea.Performance);
}