Private/Get-VmPowerScheduleState.ps1
|
function Get-VmPowerScheduleState { <# .SYNOPSIS What a schedule says a machine should be right now, and how long it has said it. .DESCRIPTION The decision engine needs a desired state, not a list of future events. A machine that should have started at 07:30 and did not is still supposed to be running at 09:00, and an engine that only looked at what is due in the next hour would never notice. So this looks backwards. The most recent action at or before the moment asked about decides: Start means Up, Deallocate means Down. It uses the same occurrence engine the calendar prints, which is the point - a desired state derived from different code than the preview is a desired state nobody can check. Exception dates are skipped rather than treated as an action, so a machine keeps the state it had going into the holiday instead of flipping on a day the schedule said to do nothing. The lookback is forty days because a schedule can be monthly. Before that there is nothing to go on, and Unknown is the honest answer: not knowing is not a reason to act. .PARAMETER Schedule A schedule in the long form. .PARAMETER AtUtc The moment to answer for. .PARAMETER LookbackDays How far back to search for the last action. #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] $Schedule, [Parameter()] [datetime]$AtUtc = [datetime]::UtcNow, [Parameter()] [ValidateRange(1, 400)] [int]$LookbackDays = 40 ) $at = switch ($AtUtc.Kind) { ([System.DateTimeKind]::Utc) { $AtUtc } ([System.DateTimeKind]::Local) { $AtUtc.ToUniversalTime() } default { [datetime]::SpecifyKind($AtUtc, [System.DateTimeKind]::Utc) } } $occurrences = Get-VmPowerScheduleOccurrence -Schedule $Schedule -FromUtc $at.AddDays(-$LookbackDays) -Days ($LookbackDays + 1) $last = @($occurrences | Where-Object { -not $_.Skipped -and $_.Utc -le $at }) | Select-Object -Last 1 if (-not $last) { return [pscustomobject]@{ State = 'Unknown'; MinutesSince = [int]::MaxValue; At = $null } } $state = switch ($last.Action) { 'Start' { 'Up' } 'Deallocate' { 'Down' } default { 'Unknown' } } # The age matters as much as the state. A machine that is down twenty minutes after a scheduled # start is a start that failed; the same machine six hours later is one somebody turned off. [pscustomobject]@{ State = $state MinutesSince = [int][Math]::Floor(($at - $last.Utc).TotalMinutes) At = $last.Utc } } |