tests/maproom/scripts/New-S2DSyntheticCluster.ps1
|
<#
.SYNOPSIS Generates a synthetic S2D cluster fixture using IIC (Infinite Improbability Corp) fictional company data for simulation testing. .DESCRIPTION Produces tests/maproom/Fixtures/synthetic-cluster.json without any live connections, CIM sessions, or WinRM. All data follows the mandatory IIC canonical standard. All company data follows the IIC canonical standard defined in: https://azurelocal.github.io/standards/examples .PARAMETER OutputPath Path for the generated fixture. Defaults to tests/maproom/Fixtures/synthetic-cluster.json. .EXAMPLE .\tests\maproom\scripts\New-S2DSyntheticCluster.ps1 .NOTES Output: tests/maproom/Fixtures/synthetic-cluster.json #> [CmdletBinding()] param( [string]$OutputPath = (Join-Path $PSScriptRoot '..\Fixtures\synthetic-cluster.json') ) Set-StrictMode -Version Latest # ============================================================================= # IIC REFERENCE DATA # All data follows the mandatory IIC (Infinite Improbability Corp) standard. # Domain: iic.local | NetBIOS: IMPROBABLE | Public: improbability.cloud # ============================================================================= $iic = @{ Company = 'Infinite Improbability Corp' Abbreviation = 'IIC' Domain = 'iic.local' ClusterName = 'azlocal-iic-s2d-01' NodeNames = @('azl-iic-n01', 'azl-iic-n02', 'azl-iic-n03', 'azl-iic-n04') NodeFqdns = @('azl-iic-n01.iic.local', 'azl-iic-n02.iic.local', 'azl-iic-n03.iic.local', 'azl-iic-n04.iic.local') NodeIPs = @('10.0.0.11', '10.0.0.12', '10.0.0.13', '10.0.0.14') HardwareModel = 'PowerEdge R760' HardwareMfr = 'Dell Inc.' StoragePoolName = 'S2D on azlocal-iic-s2d-01' SubscriptionId = '33333333-3333-3333-3333-333333333333' ResourceGroup = 'rg-iic-compute-01' TenantId = '00000000-0000-0000-0000-000000000000' } # Disk sizes: 4× 3.84 TB NVMe per node (3,840,000,000,000 bytes each) $diskSizeBytes = 3840000000000 $diskTiB = [Math]::Round($diskSizeBytes / 1099511627776, 3) # 3.492 $diskTB = [Math]::Round($diskSizeBytes / 1000000000000, 2) # 3.84 $generatedAt = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ" # ============================================================================= # BUILD: physical disks per node # 4-node cluster, 4× 3.84 TB NVMe capacity per node (no separate cache tier — all-NVMe) # ============================================================================= $allDisks = @() foreach ($i in 0..3) { $nodeName = $iic.NodeNames[$i] foreach ($d in 1..4) { $allDisks += [ordered]@{ NodeName = $nodeName DiskNumber = $d FriendlyName = "INTEL SSDPE2KX040T8" SerialNumber = "IIC{0:D2}N{1:D2}D{2:D1}" -f ($i+1), ($i+1), $d MediaType = "NVMe" BusType = "NVMe" Usage = "Auto-Select" Role = "Capacity" SizeBytes = $diskSizeBytes SizeTiB = $diskTiB SizeTB = $diskTB SizeDisplay = "$diskTiB TiB ($diskTB TB)" Model = "INTEL SSDPE2KX040T8" FirmwareVersion = "VCV10162" Manufacturer = "Intel" HealthStatus = "Healthy" OperationalStatus = "OK" CanPool = $false WearPercentage = (Get-Random -Minimum 5 -Maximum 20) Temperature = (Get-Random -Minimum 32 -Maximum 42) PowerOnHours = (Get-Random -Minimum 10000 -Maximum 20000) ReadErrors = 0 WriteErrors = 0 } } } # ============================================================================= # BUILD: capacity waterfall (4-node, 4× 3.84 TB NVMe, 3-way mirror) # ============================================================================= $rawBytes = $diskSizeBytes * 16 # 16 disks total $rawTiB = [Math]::Round($rawBytes / 1099511627776, 2) $rawTB = [Math]::Round($rawBytes / 1000000000000, 2) $reserveBytes = $diskSizeBytes * 4 # min(4,4) × largest = 4 drives $reserveTiB = [Math]::Round($reserveBytes / 1099511627776, 2) $reserveTB = [Math]::Round($reserveBytes / 1000000000000, 2) $poolOverheadBytes = [long]($rawBytes * 0.005) # ~0.5% metadata $infraVolBytes = 268435456000 # 250 GB $infraVolTiB = [Math]::Round($infraVolBytes / 1099511627776, 2) $infraVolTB = [Math]::Round($infraVolBytes / 1000000000000, 2) $availableBytes = $rawBytes - $poolOverheadBytes - $reserveBytes - $infraVolBytes $availableTiB = [Math]::Round($availableBytes / 1099511627776, 2) $usableBytes = [long]($availableBytes / 3) # 3-way mirror = 33.3% $usableTiB = [Math]::Round($usableBytes / 1099511627776, 2) $usableTB = [Math]::Round($usableBytes / 1000000000000, 2) $waterfall = [ordered]@{ Raw = [ordered]@{ Bytes = $rawBytes TiB = $rawTiB TB = $rawTB Display = "$rawTiB TiB ($rawTB TB)" } AfterPoolOverhead = [ordered]@{ OverheadBytes = $poolOverheadBytes TiB = [Math]::Round(($rawBytes - $poolOverheadBytes) / 1099511627776, 2) } Reserve = [ordered]@{ RecommendedBytes = $reserveBytes RecommendedTiB = $reserveTiB RecommendedTB = $reserveTB ActualBytes = $reserveBytes Status = "Adequate" } InfrastructureVolume = [ordered]@{ PresentBytes = $infraVolBytes PresentTiB = $infraVolTiB PresentTB = $infraVolTB Detected = $true } Available = [ordered]@{ Bytes = $availableBytes TiB = $availableTiB } ResiliencyEfficiency = [ordered]@{ ResiliencyType = "Mirror" NumberOfCopies = 3 EfficiencyPct = 33.3 } Usable = [ordered]@{ Bytes = $usableBytes TiB = $usableTiB TB = $usableTB Display = "$usableTiB TiB ($usableTB TB)" } } # ============================================================================= # BUILD: health checks # ============================================================================= $healthChecks = @( [ordered]@{ CheckName = "ReserveAdequacy"; Severity = "Critical"; Status = "Pass"; Details = "Reserve $reserveTiB TiB meets recommended $reserveTiB TiB" } [ordered]@{ CheckName = "DiskSymmetry"; Severity = "Warning"; Status = "Pass"; Details = "All 4 nodes have 4 disks" } [ordered]@{ CheckName = "VolumeHealth"; Severity = "Critical"; Status = "Pass"; Details = "All volumes Healthy" } [ordered]@{ CheckName = "DiskHealth"; Severity = "Critical"; Status = "Pass"; Details = "All 16 disks Healthy" } [ordered]@{ CheckName = "NvmeWear"; Severity = "Warning"; Status = "Pass"; Details = "All NVMe disks below 80% wear" } [ordered]@{ CheckName = "ThinOvercommit"; Severity = "Warning"; Status = "Pass"; Details = "No thin overcommit detected" } [ordered]@{ CheckName = "FirmwareConsistency"; Severity = "Info"; Status = "Pass"; Details = "All disks on firmware VCV10162" } [ordered]@{ CheckName = "InfraVolume"; Severity = "Info"; Status = "Pass"; Details = "Infrastructure volume detected (250 GB)" } ) # ============================================================================= # ASSEMBLE: full synthetic cluster document # ============================================================================= $synthetic = [ordered]@{ generatedAt = $generatedAt generatedBy = "New-S2DSyntheticCluster.ps1" company = $iic.Company clusterName = $iic.ClusterName clusterFqdn = "$($iic.ClusterName).$($iic.Domain)" nodeCount = 4 nodes = $iic.NodeFqdns storagePoolName = $iic.StoragePoolName physicalDisks = $allDisks capacityWaterfall = $waterfall healthChecks = $healthChecks metadata = [ordered]@{ subscriptionId = $iic.SubscriptionId resourceGroup = $iic.ResourceGroup tenantId = $iic.TenantId hardwareModel = $iic.HardwareModel hardwareMfr = $iic.HardwareMfr } } $outputDir = Split-Path $OutputPath -Parent if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null } $synthetic | ConvertTo-Json -Depth 10 | Set-Content -Path $OutputPath -Encoding UTF8 Write-Host "✅ Synthetic cluster fixture written to: $OutputPath" -ForegroundColor Green Write-Host " Cluster : $($synthetic.clusterName)" -ForegroundColor Gray Write-Host " Nodes : $($synthetic.nodeCount)" -ForegroundColor Gray Write-Host " Disks : $($allDisks.Count) ($diskTB TB NVMe each)" -ForegroundColor Gray Write-Host " Raw Cap : $rawTiB TiB ($rawTB TB)" -ForegroundColor Gray Write-Host " Usable : $usableTiB TiB ($usableTB TB) after 3-way mirror" -ForegroundColor Gray |