Skip to content

Pre-flight checks — validando o laboratório antes das execuções

Leia no seu idioma: English · Português · Español

Status do escopo (pós-congelamento de escopo 2026-05-10) — Ver ARCHITECTURE.md para os 37 MÓDULOs canônicos + 7 Test Kinds + arquitetura de safety DOM/CPOS/PIE-PA. ADRs 0014, 0019-0025 cobrem adições pós-Freeze. O motor de Pre-flight checks é um validador read-only que executa contra os dispositivos DUT API registrados ANTES do operador disparar a execução de um Test Plan. Ele captura cedo as condições de "lab fora do estado esperado" para que execuções não produzam dados forensicamente inúteis.

O princípio: garbage in, garbage out. Se o NGFW tem mudanças de deploy pendentes, ou se a política de decrypt está desligada quando o plan exige decrypt-on, os números de p99 resultantes não dizem nada de útil. O pre-flight recusa iniciar a execução em vez de permitir que ela produza dados enganosos.

Como se encaixa no fluxo do operador

Operator picks plan → Runs preflight → Reviews failures → Fixes lab state →
Triggers snapshot → Re-runs preflight → All green → Starts the actual test run

O pre-flight é invocado manualmente hoje (POST endpoint). Em um PR futuro (PR-D), o motor de Test Plan vai gatear o run-start em um preflight bem-sucedido automaticamente.

API

POST /api/test-runs/preflight

curl -X POST "https://dashboard.example/api/test-runs/preflight" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "content-type: application/json" \
  -d '{"planIdentifier": "BASELINE-SLO-30M"}'

Resposta (sucesso — 200):

{
  "planIdentifier": "BASELINE-SLO-30M",
  "ranAt": "2026-05-06T14:35:00.000Z",
  "checksRun": 8,
  "checksPassed": 8,
  "checksFailed": 0,
  "checksSkipped": 0,
  "pass": true,
  "checks": [
    {
      "checkId": "ngfw-deploy-clean",
      "description": "NGFW deploy state is DEPLOYED — no pending config changes",
      "deviceHostname": "ftd-1.lab.example.com",
      "vendor": "cisco-ftd",
      "pass": true,
      "detail": "state=DEPLOYED — no pending changes",
      "evidence": {
        "snapshotId": "...",
        "payloadSha256": "abc123...",
        "collectedAt": "2026-05-06T14:34:42.000Z"
      }
    },
    ...
  ],
  "summary": "8/8 checks passed — lab is ready for plan BASELINE-SLO-30M"
}

Resposta (qualquer check falhou — 422):

{
  "planIdentifier": "BASELINE-SLO-30M",
  "ranAt": "2026-05-06T14:35:00.000Z",
  "checksRun": 8,
  "checksPassed": 6,
  "checksFailed": 1,
  "checksSkipped": 1,
  "pass": false,
  "checks": [
    ...
    {
      "checkId": "ngfw-decrypt-state-matches-plan",
      "deviceHostname": "ftd-1.lab.example.com",
      "vendor": "cisco-ftd",
      "pass": false,
      "detail": "plan requires decrypt-on but no decrypt rules configured",
      "evidence": { ... }
    }
  ],
  "summary": "1 check(s) failed, 1 skipped (missing snapshots) — review details"
}

Catálogo de checks (atual)

Check ID Aplica-se a Endpoint label O que valida
ngfw-deploy-clean Cisco FTD deploy_status state == 'DEPLOYED' (sem mudanças pendentes)
ngfw-decrypt-state-matches-plan Cisco FTD + Fortinet decrypt_policy Se o plan exige decrypt-on, ao menos uma regra configurada; se decrypt-off, zero regras
ntp-source-configured Todos os vendors ntp_config Dispositivo tem ao menos um servidor NTP configurado
ngfw-ha-state-sane Cisco FTD ha_status Estado HA é um de ACTIVE / STANDBY_READY / NEGOTIATION / JOIN
snapshot-fresh Todos os vendors system_info Snapshot mais recente tem menos de 60 min

O catálogo é extensível — adicionar um novo check é anexar uma entrada em lib/preflight/checks.ts. Sem mudanças no engine.

O que os checks retornam

Cada check retorna um de três estados:

Resultado Significado Ação do operador
pass: true + evidence Check avaliado contra um snapshot, tudo certo Nenhuma — prossiga
pass: false + evidence Check avaliado contra um snapshot, FALHA Corrija o estado do dispositivo, dispare um snapshot manual, re-execute
pass: false + evidence: null Skipped — não existe snapshot para esse dispositivo + label Dispare um snapshot manual primeiro

Evidence é o campo mais importante — ele cita o SHA-256 exato do snapshot + collected_at. O mesmo SHA-256 vai aparecer nos anexos do Test Run Report quando o PR-D for entregue, então o chain-of-custody fica intacto.

Por que pre-flight importa

Sem pre-flight, este é um cenário típico:

O operador dispara BASELINE-SLO-30M esperando decrypt-on. O NGFW teve sua política de decrypt desabilitada por outro engenheiro 30 minutos atrás. A execução de 30 minutos completa; o p99 parece suspeitosamente baixo. O operador só percebe que algo está errado ao comparar com a execução da semana passada. A run é inválida. O engagement perde 30 min + a credibilidade do relatório.

Com pre-flight:

O operador roda preflight primeiro. O check ngfw-decrypt-state-matches-plan falha: "plan requires decrypt-on but no decrypt rules configured". O operador abre o console do NGFW (ou dispara uma write-op via a API futura), habilita decrypt, snapshot, re-roda preflight, tudo verde, inicia a run. Os 30 minutos são gastos em uma execução válida.

Fluxo do operador — sequência completa

# 1. Confirm devices are registered
curl -H "Authorization: Bearer $ADMIN_TOKEN" \
  "https://dashboard.example/api/admin/dut/devices"

# 2. Trigger fresh snapshots (so preflight sees current state)
for id in $(curl -s -H "Authorization: Bearer $ADMIN_TOKEN" \
  "https://dashboard.example/api/admin/dut/devices" \
  | jq -r '.devices[].id'); do
  curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
    "https://dashboard.example/api/admin/dut/devices/$id/snapshot"
done

# 3. Run pre-flight
curl -X POST "https://dashboard.example/api/test-runs/preflight" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "content-type: application/json" \
  -d '{"planIdentifier": "BASELINE-SLO-30M"}'

# 4. If pass: true → proceed with run trigger (existing flow)
# 5. If pass: false → review checks[].detail, fix the lab, go to step 2

Adicionando um novo check

O catálogo de checks em lib/preflight/checks.ts é data-driven. Para adicionar um check:

{
  id: 'my-new-check',
  description: 'What this check validates in operator-friendly prose',
  endpointLabel: 'system_info', // which snapshot label the check needs
  appliesTo: (device, plan) => {
    // Return true if this check is relevant for this device + plan combo
    return device.vendor === 'cisco-ftd' && plan.someField === 'someValue';
  },
  evaluate: (snapshot, plan) => {
    // Inspect snapshot.payloadJson and decide pass/fail
    const fieldValue = (snapshot.payloadJson as any)?.someField;
    if (fieldValue === 'expected') {
      return { pass: true, detail: 'value matches expectation' };
    }
    return { pass: false, detail: `value=${fieldValue}, expected 'expected'` };
  }
}

Sem mudanças no engine. O runner descobre o novo check automaticamente.

Limitações

Escopo honesto:

  • Read-only — pre-flight NÃO dispara snapshots por conta própria. O operador os dispara via o endpoint existente POST /api/admin/dut/devices/{id}/snapshot
  • Snapshot stale tolerado até 60 min — o check snapshot-fresh falha se mais antigo. Ajuste editando o check, OU rode um snapshot novo antes do preflight
  • Sem run-blocking automático ainda — PR-D vai integrar o preflight ao fluxo de run-start do Test Plan (recusar iniciar se o preflight falhar)
  • Checks de Cisco UCS ainda não conectados — o adapter UCS está na fila do PR #199. Quando mergeado, checks específicos de UCS (sem critical faults, thermal sane) serão adicionados
  • Sem write/remediation — pre-flight reporta estado mas não corrige. F-1 / F-2 (write ops) no API_FEATURE_CATALOG.md cobrem essa capacidade futura

O que o pre-flight NÃO substitui

  • Julgamento do operador para preocupações não-checáveis (conexões de cabo, camada física, contratos de suporte de vendor)
  • O TLS Decrypt Mode Probe (que é independente do estado da API — o probe pode detectar "decrypt está configurado mas de algum modo não está realmente decifrando tráfego", o que checks só-de-API não conseguem)
  • Verificação de time-sync (script separado check-time-sync.sh — pre-flight eventualmente vai chamá-lo como um check)

Relacionados