Private/New-InfraPulseComparison.ps1
|
function New-InfraPulseComparison { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ComputerName, [AllowNull()] [psobject]$ReferenceReport, [AllowNull()] [psobject]$DifferenceReport, [switch]$ExcludeUnchanged ) $changes = New-Object System.Collections.Generic.List[object] $referenceResults = if ($null -ne $ReferenceReport) { @($ReferenceReport.Results) } else { @() } $differenceResults = if ($null -ne $DifferenceReport) { @($DifferenceReport.Results) } else { @() } $differencePool = New-Object System.Collections.Generic.List[object] foreach ($result in $differenceResults) { [void]$differencePool.Add($result) } foreach ($referenceResult in $referenceResults) { $match = $null foreach ($candidate in $differencePool) { if ( [string]$candidate.CheckName -eq [string]$referenceResult.CheckName -and [string]$candidate.Target -eq [string]$referenceResult.Target ) { $match = $candidate break } } if ($null -eq $match) { [void]$changes.Add((New-InfraPulseResultChange -ChangeType 'NotComparable' -ComputerName $ComputerName -ReferenceResult $referenceResult -DifferenceResult $null)) continue } [void]$differencePool.Remove($match) [void]$changes.Add((New-InfraPulseResultChange -ChangeType (Get-InfraPulseChangeType -ReferenceResult $referenceResult -DifferenceResult $match) -ComputerName $ComputerName -ReferenceResult $referenceResult -DifferenceResult $match)) } foreach ($differenceResult in $differencePool) { $changeType = if ([string]$differenceResult.Status -in @('Warning', 'Critical', 'Unknown')) { 'NewFinding' } else { 'Added' } [void]$changes.Add((New-InfraPulseResultChange -ChangeType $changeType -ComputerName $ComputerName -ReferenceResult $null -DifferenceResult $differenceResult)) } $allChanges = @($changes.ToArray()) $counts = [ordered]@{ Total = $allChanges.Count NewFinding = 0 Regressed = 0 Resolved = 0 Improved = 0 Changed = 0 NotComparable = 0 Added = 0 Unchanged = 0 } foreach ($change in $allChanges) { $counts[[string]$change.ChangeType] = [int]$counts[[string]$change.ChangeType] + 1 } $visibleChanges = if ($ExcludeUnchanged) { @($allChanges | Where-Object { $_.ChangeType -ne 'Unchanged' }) } else { $allChanges } $referenceMetadata = ConvertTo-InfraPulseComparisonSide -Report $ReferenceReport $differenceMetadata = ConvertTo-InfraPulseComparisonSide -Report $DifferenceReport $configurationMatches = $null if ( $null -ne $ReferenceReport -and $null -ne $DifferenceReport -and -not [string]::IsNullOrWhiteSpace([string]$referenceMetadata.ConfigurationFingerprint) -and -not [string]::IsNullOrWhiteSpace([string]$differenceMetadata.ConfigurationFingerprint) ) { $configurationMatches = [string]$referenceMetadata.ConfigurationFingerprint -eq [string]$differenceMetadata.ConfigurationFingerprint } $comparison = [pscustomobject][ordered]@{ SchemaVersion = '1.1' Tool = 'InfraPulse' ToolVersion = $script:InfraPulseModuleVersion ComputerName = $ComputerName GeneratedAtUtc = [DateTime]::UtcNow Comparable = ($null -ne $ReferenceReport -and $null -ne $DifferenceReport) ConfigurationMatches = $configurationMatches HasRegressions = ([int]$counts.NewFinding + [int]$counts.Regressed) -gt 0 Reference = $referenceMetadata Difference = $differenceMetadata Summary = [pscustomobject]$counts Changes = $visibleChanges } $comparison.PSObject.TypeNames.Insert(0, 'InfraPulse.Comparison') return $comparison } function ConvertTo-InfraPulseComparisonSide { [CmdletBinding()] param( [AllowNull()] [psobject]$Report ) if ($null -eq $Report) { return $null } $metadata = [ordered]@{ RunId = '' GeneratedAtUtc = $null OverallStatus = [string]$Report.OverallStatus ConfigurationFingerprint = '' } if ($null -ne $Report.PSObject.Properties['RunId']) { $metadata.RunId = [string]$Report.RunId } if ($null -ne $Report.PSObject.Properties['GeneratedAtUtc']) { $metadata.GeneratedAtUtc = $Report.GeneratedAtUtc } if ($null -ne $Report.PSObject.Properties['ConfigurationFingerprint']) { $metadata.ConfigurationFingerprint = [string]$Report.ConfigurationFingerprint } return [pscustomobject]$metadata } function Get-InfraPulseStatusSeverity { [CmdletBinding()] param( [AllowNull()] [string]$Status ) switch ($Status) { 'Critical' { return 4 } 'Warning' { return 3 } 'Unknown' { return 2 } 'Healthy' { return 1 } 'Skipped' { return 0 } default { return 2 } } } function Get-InfraPulseChangeType { [CmdletBinding()] param( [Parameter(Mandatory)] [psobject]$ReferenceResult, [Parameter(Mandatory)] [psobject]$DifferenceResult ) $referenceStatus = [string]$ReferenceResult.Status $differenceStatus = [string]$DifferenceResult.Status if ($referenceStatus -eq $differenceStatus) { if (Test-InfraPulseResultChanged -ReferenceResult $ReferenceResult -DifferenceResult $DifferenceResult) { return 'Changed' } return 'Unchanged' } $referenceSeverity = Get-InfraPulseStatusSeverity -Status $referenceStatus $differenceSeverity = Get-InfraPulseStatusSeverity -Status $differenceStatus if ($differenceSeverity -gt $referenceSeverity) { return 'Regressed' } if ($differenceStatus -eq 'Healthy' -and $referenceStatus -in @('Warning', 'Critical', 'Unknown')) { return 'Resolved' } return 'Improved' } function Test-InfraPulseResultChanged { [CmdletBinding()] param( [Parameter(Mandatory)] [psobject]$ReferenceResult, [Parameter(Mandatory)] [psobject]$DifferenceResult ) if ([string]$ReferenceResult.ObservedValue -ne [string]$DifferenceResult.ObservedValue) { return $true } $referenceEvidence = ConvertTo-InfraPulseComparableEvidence -Evidence $ReferenceResult.Evidence $differenceEvidence = ConvertTo-InfraPulseComparableEvidence -Evidence $DifferenceResult.Evidence return $referenceEvidence -cne $differenceEvidence } function ConvertTo-InfraPulseComparableEvidence { # Timing values and event samples differ between otherwise identical runs, # so they are excluded before evidence is compared. [CmdletBinding()] param( [AllowNull()] [object]$Evidence ) if ($null -eq $Evidence) { return '' } $volatileKeys = @('DurationMs', 'TimeoutMs', 'HandshakeMs', 'Samples', 'CollectedAtUtc', 'QueryError') $normalized = ConvertTo-InfraPulseSerializableValue -Value $Evidence if ($normalized -is [System.Collections.IDictionary]) { foreach ($volatileKey in $volatileKeys) { if ($normalized.Contains($volatileKey)) { $normalized.Remove($volatileKey) } } } elseif ($normalized -is [System.Management.Automation.PSCustomObject]) { foreach ($volatileKey in $volatileKeys) { $property = $normalized.PSObject.Properties[$volatileKey] if ($null -ne $property) { $normalized.PSObject.Properties.Remove($volatileKey) } } } return ConvertTo-Json -InputObject $normalized -Depth 8 -Compress } function New-InfraPulseResultChange { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('NewFinding', 'Regressed', 'Resolved', 'Improved', 'Changed', 'NotComparable', 'Added', 'Unchanged')] [string]$ChangeType, [Parameter(Mandatory)] [string]$ComputerName, [AllowNull()] [psobject]$ReferenceResult, [AllowNull()] [psobject]$DifferenceResult ) $template = if ($null -ne $DifferenceResult) { $DifferenceResult } else { $ReferenceResult } $change = [pscustomobject][ordered]@{ ChangeType = $ChangeType ComputerName = $ComputerName CheckName = [string]$template.CheckName Category = [string]$template.Category Target = [string]$template.Target ReferenceStatus = if ($null -ne $ReferenceResult) { [string]$ReferenceResult.Status } else { $null } DifferenceStatus = if ($null -ne $DifferenceResult) { [string]$DifferenceResult.Status } else { $null } ReferenceObservedValue = if ($null -ne $ReferenceResult) { $ReferenceResult.ObservedValue } else { $null } DifferenceObservedValue = if ($null -ne $DifferenceResult) { $DifferenceResult.ObservedValue } else { $null } ReferenceMessage = if ($null -ne $ReferenceResult) { [string]$ReferenceResult.Message } else { $null } DifferenceMessage = if ($null -ne $DifferenceResult) { [string]$DifferenceResult.Message } else { $null } EvidenceChanged = if ($null -ne $ReferenceResult -and $null -ne $DifferenceResult) { Test-InfraPulseResultChanged -ReferenceResult $ReferenceResult -DifferenceResult $DifferenceResult } else { $null } } $change.PSObject.TypeNames.Insert(0, 'InfraPulse.ResultChange') return $change } |