Record project continuation changes
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
param(
|
||||
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
|
||||
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
|
||||
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
|
||||
[string]$EntryNodeName = "test-1",
|
||||
[string]$RelayNodeName = "test-3",
|
||||
[string]$ExitNodeName = "test-2",
|
||||
[string]$ResultPath = "artifacts\c18z12-service-channel-route-quality-smoke-result.json"
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
|
||||
$runId = "c18z12-" + (Get-Date -Format "yyyyMMdd-HHmmss")
|
||||
$resourceId = "vpn-$runId"
|
||||
|
||||
function Invoke-Api {
|
||||
param(
|
||||
[string]$Method,
|
||||
[string]$Path,
|
||||
[object]$Body = $null
|
||||
)
|
||||
$uri = "$ApiBaseUrl$Path"
|
||||
try {
|
||||
if ($null -eq $Body) {
|
||||
return Invoke-RestMethod -Method $Method -Uri $uri -TimeoutSec 30
|
||||
}
|
||||
return Invoke-RestMethod -Method $Method -Uri $uri -ContentType "application/json" -Body ($Body | ConvertTo-Json -Depth 80) -TimeoutSec 30
|
||||
}
|
||||
catch {
|
||||
$statusCode = $null
|
||||
if ($_.Exception.Response) {
|
||||
$statusCode = [int]$_.Exception.Response.StatusCode
|
||||
}
|
||||
$details = $_.ErrorDetails.Message
|
||||
if (-not $details) {
|
||||
$details = $_.Exception.Message
|
||||
}
|
||||
throw "$Method $Path failed with HTTP $statusCode`: $details"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-NodeByName {
|
||||
param([string]$Name)
|
||||
$nodes = (Invoke-Api -Method GET -Path "/clusters/$ClusterID/nodes?actor_user_id=$ActorUserID").nodes
|
||||
$node = @($nodes | Where-Object { $_.name -eq $Name }) | Select-Object -First 1
|
||||
if ($null -eq $node) {
|
||||
throw "Node '$Name' was not found in cluster $ClusterID"
|
||||
}
|
||||
return $node
|
||||
}
|
||||
|
||||
function Get-SmokeRouteLabel {
|
||||
param([object]$RouteIntent)
|
||||
if ($null -eq $RouteIntent -or $null -eq $RouteIntent.PSObject.Properties["policy"]) {
|
||||
return ""
|
||||
}
|
||||
$policy = $RouteIntent.policy
|
||||
if ($null -eq $policy -or $null -eq $policy.PSObject.Properties["metadata"]) {
|
||||
return ""
|
||||
}
|
||||
$metadata = $policy.metadata
|
||||
if ($null -eq $metadata) {
|
||||
return ""
|
||||
}
|
||||
$smoke = $metadata.PSObject.Properties["smoke"]
|
||||
if ($null -eq $smoke) {
|
||||
return ""
|
||||
}
|
||||
return [string]$smoke.Value
|
||||
}
|
||||
|
||||
function Clear-SmokeRouteIntents {
|
||||
$items = (Invoke-Api -Method GET -Path "/clusters/$ClusterID/mesh/route-intents?actor_user_id=$ActorUserID").route_intents
|
||||
foreach ($item in @($items)) {
|
||||
if ([string]$item.lifecycle_status -ne "active") {
|
||||
continue
|
||||
}
|
||||
if ([string]$item.service_class -ne "vpn_packets") {
|
||||
continue
|
||||
}
|
||||
if ((Get-SmokeRouteLabel -RouteIntent $item) -ne "c18z12_service_channel_route_quality") {
|
||||
continue
|
||||
}
|
||||
Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents/$($item.id)/expire" -Body @{ actor_user_id = $ActorUserID } | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function New-RouteIntent {
|
||||
param(
|
||||
[string]$SourceNodeID,
|
||||
[string]$DestinationNodeID,
|
||||
[string[]]$Hops,
|
||||
[int]$Priority,
|
||||
[string]$Label
|
||||
)
|
||||
$expiresAt = (Get-Date).ToUniversalTime().AddMinutes(10).ToString("o")
|
||||
return Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents" -Body @{
|
||||
actor_user_id = $ActorUserID
|
||||
source_selector = @{ node_id = $SourceNodeID }
|
||||
destination_selector = @{ node_id = $DestinationNodeID }
|
||||
service_class = "vpn_packets"
|
||||
priority = $Priority
|
||||
policy = @{
|
||||
synthetic_enabled = $true
|
||||
route_version = "$runId-$Label"
|
||||
policy_version = "$runId-$Label"
|
||||
peer_directory_version = "$runId-$Label"
|
||||
hops = @($Hops)
|
||||
allowed_channels = @("vpn_packet", "fabric_control")
|
||||
max_ttl = 8
|
||||
max_hops = 8
|
||||
expires_at = $expiresAt
|
||||
metadata = @{
|
||||
smoke = "c18z12_service_channel_route_quality"
|
||||
run_id = $runId
|
||||
label = $Label
|
||||
route_quality_smoke = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function New-ServiceChannelLease {
|
||||
param(
|
||||
[string]$EntryNodeID,
|
||||
[string]$ExitNodeID
|
||||
)
|
||||
return (Invoke-Api -Method POST -Path "/clusters/$ClusterID/fabric/service-channels/leases" -Body @{
|
||||
actor_user_id = $ActorUserID
|
||||
organization_id = "org-c18z12-smoke"
|
||||
user_id = $ActorUserID
|
||||
resource_id = $resourceId
|
||||
service_class = "vpn_packets"
|
||||
entry_node_ids = @($EntryNodeID)
|
||||
exit_node_ids = @($ExitNodeID)
|
||||
preferred_entry_node_id = $EntryNodeID
|
||||
preferred_exit_node_id = $ExitNodeID
|
||||
allowed_channels = @("vpn_packet", "bulk", "control")
|
||||
ttl_seconds = 300
|
||||
metadata = @{
|
||||
smoke = "c18z12_service_channel_route_quality"
|
||||
run_id = $runId
|
||||
}
|
||||
}).fabric_service_channel_lease
|
||||
}
|
||||
|
||||
function Send-QualityHeartbeat {
|
||||
param(
|
||||
[string]$EntryNodeID,
|
||||
[string]$SlowRouteID,
|
||||
[string]$FastRouteID
|
||||
)
|
||||
$observedAt = (Get-Date).ToUniversalTime().ToString("o")
|
||||
return Invoke-Api -Method POST -Path "/clusters/$ClusterID/nodes/$EntryNodeID/heartbeats" -Body @{
|
||||
health_status = "healthy"
|
||||
reported_version = "0.2.185"
|
||||
capabilities = @{
|
||||
fabric_service_channel_route_manager = $true
|
||||
fabric_service_channel_route_quality_feedback = $true
|
||||
}
|
||||
service_states = @{ smoke = "c18z12_route_quality_feedback" }
|
||||
metadata = @{
|
||||
fabric_service_channel_runtime_report = @{
|
||||
schema_version = "c18l.fabric_service_channel_runtime_report.v1"
|
||||
cluster_id = $ClusterID
|
||||
local_node_id = $EntryNodeID
|
||||
observed_at = $observedAt
|
||||
ingress = @{
|
||||
flow_scheduler = @{
|
||||
schema_version = "rap.fabric_flow_scheduler.v1"
|
||||
service_neutral = $true
|
||||
service_mode = "application_protocol_agnostic"
|
||||
channel_stats = @{
|
||||
"quality-fast" = @{
|
||||
last_route_id = $FastRouteID
|
||||
last_next_hop = "fast"
|
||||
last_send_duration_ms = 8
|
||||
consecutive_failures = 0
|
||||
stall_count = 0
|
||||
route_rebuild_recommended = $false
|
||||
degraded_fallback_recommended = $false
|
||||
}
|
||||
"quality-slow" = @{
|
||||
last_route_id = $SlowRouteID
|
||||
last_next_hop = "slow"
|
||||
last_send_duration_ms = 900
|
||||
consecutive_failures = 0
|
||||
stall_count = 0
|
||||
route_rebuild_recommended = $false
|
||||
degraded_fallback_recommended = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$entryNode = Get-NodeByName -Name $EntryNodeName
|
||||
$relayNode = Get-NodeByName -Name $RelayNodeName
|
||||
$exitNode = Get-NodeByName -Name $ExitNodeName
|
||||
$slowRouteID = ""
|
||||
$fastRouteID = ""
|
||||
$result = $null
|
||||
|
||||
try {
|
||||
Clear-SmokeRouteIntents
|
||||
$slowIntent = New-RouteIntent `
|
||||
-SourceNodeID $entryNode.id `
|
||||
-DestinationNodeID $exitNode.id `
|
||||
-Hops @($entryNode.id, $relayNode.id, $exitNode.id) `
|
||||
-Priority 2000000000 `
|
||||
-Label "slow-high-priority"
|
||||
$fastIntent = New-RouteIntent `
|
||||
-SourceNodeID $entryNode.id `
|
||||
-DestinationNodeID $exitNode.id `
|
||||
-Hops @($entryNode.id, $exitNode.id) `
|
||||
-Priority 1999999950 `
|
||||
-Label "fast-lower-priority"
|
||||
$slowRouteID = $slowIntent.route_intent.id
|
||||
$fastRouteID = $fastIntent.route_intent.id
|
||||
|
||||
$initialLease = New-ServiceChannelLease -EntryNodeID $entryNode.id -ExitNodeID $exitNode.id
|
||||
if ($initialLease.status -ne "ready" -or [string]$initialLease.primary_route.route_id -ne $slowRouteID) {
|
||||
throw "Initial lease should select higher-priority slow route '$slowRouteID': status=$($initialLease.status) route=$($initialLease.primary_route.route_id)"
|
||||
}
|
||||
|
||||
$qualityHeartbeat = Send-QualityHeartbeat -EntryNodeID $entryNode.id -SlowRouteID $slowRouteID -FastRouteID $fastRouteID
|
||||
Start-Sleep -Seconds 2
|
||||
$qualityLease = New-ServiceChannelLease -EntryNodeID $entryNode.id -ExitNodeID $exitNode.id
|
||||
if ($qualityLease.status -ne "ready" -or [string]$qualityLease.primary_route.route_id -ne $fastRouteID) {
|
||||
throw "Quality lease should select fast route '$fastRouteID': status=$($qualityLease.status) route=$($qualityLease.primary_route.route_id) score=$($qualityLease.primary_route.path_score)"
|
||||
}
|
||||
|
||||
$expiredSlow = Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents/$slowRouteID/expire" -Body @{ actor_user_id = $ActorUserID }
|
||||
$expiredFast = Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents/$fastRouteID/expire" -Body @{ actor_user_id = $ActorUserID }
|
||||
|
||||
$result = [ordered]@{
|
||||
schema_version = "c18z12.service_channel_route_quality_smoke.v1"
|
||||
run_id = $runId
|
||||
base_url = $ApiBaseUrl
|
||||
cluster_id = $ClusterID
|
||||
entry_node = @{ name = $entryNode.name; id = $entryNode.id }
|
||||
relay_node = @{ name = $relayNode.name; id = $relayNode.id }
|
||||
exit_node = @{ name = $exitNode.name; id = $exitNode.id }
|
||||
resource_id = $resourceId
|
||||
route_intents = @{
|
||||
slow_route_id = $slowRouteID
|
||||
fast_route_id = $fastRouteID
|
||||
slow_hops = @($entryNode.id, $relayNode.id, $exitNode.id)
|
||||
fast_hops = @($entryNode.id, $exitNode.id)
|
||||
expired_slow_status = $expiredSlow.route_intent.lifecycle_status
|
||||
expired_fast_status = $expiredFast.route_intent.lifecycle_status
|
||||
}
|
||||
initial_lease = @{
|
||||
status = $initialLease.status
|
||||
primary_route_id = $initialLease.primary_route.route_id
|
||||
primary_path_score = $initialLease.primary_route.path_score
|
||||
score_reasons = $initialLease.primary_route.score_reasons
|
||||
}
|
||||
quality_lease = @{
|
||||
status = $qualityLease.status
|
||||
primary_route_id = $qualityLease.primary_route.route_id
|
||||
primary_path_score = $qualityLease.primary_route.path_score
|
||||
score_reasons = $qualityLease.primary_route.score_reasons
|
||||
}
|
||||
feedback = @{
|
||||
heartbeat_status = $qualityHeartbeat.heartbeat.health_status
|
||||
fast_last_send_duration_ms = 8
|
||||
slow_last_send_duration_ms = 900
|
||||
}
|
||||
passed = $true
|
||||
checks = [ordered]@{
|
||||
initial_prefers_high_priority_slow_route = ([string]$initialLease.primary_route.route_id -eq $slowRouteID)
|
||||
quality_prefers_fast_route = ([string]$qualityLease.primary_route.route_id -eq $fastRouteID)
|
||||
fast_route_has_quality_reason = (@($qualityLease.primary_route.score_reasons | Where-Object { $_ -eq "service_channel_quality_latency_le_10ms" }).Count -ge 1)
|
||||
route_intents_expired = ($expiredSlow.route_intent.lifecycle_status -eq "expired" -and $expiredFast.route_intent.lifecycle_status -eq "expired")
|
||||
}
|
||||
}
|
||||
$failedChecks = @($result.checks.GetEnumerator() | Where-Object { $_.Value -ne $true })
|
||||
if ($failedChecks.Count -gt 0) {
|
||||
throw "C18Z12 failed checks: $($failedChecks.Name -join ', ')"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ($slowRouteID) {
|
||||
try { Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents/$slowRouteID/expire" -Body @{ actor_user_id = $ActorUserID } | Out-Null } catch {}
|
||||
}
|
||||
if ($fastRouteID) {
|
||||
try { Invoke-Api -Method POST -Path "/clusters/$ClusterID/mesh/route-intents/$fastRouteID/expire" -Body @{ actor_user_id = $ActorUserID } | Out-Null } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
$resultFullPath = Join-Path $repoRoot $ResultPath
|
||||
$resultDir = Split-Path -Parent $resultFullPath
|
||||
if (-not (Test-Path $resultDir)) {
|
||||
New-Item -ItemType Directory -Path $resultDir | Out-Null
|
||||
}
|
||||
$result | ConvertTo-Json -Depth 100 | Set-Content -Path $resultFullPath -Encoding UTF8
|
||||
Write-Host "C18Z12 service-channel route quality smoke passed. Result: $resultFullPath"
|
||||
$result
|
||||
Reference in New Issue
Block a user