| name | validate-side-by-side |
| description | Use when the user asks to "validate the migration", "compare side by side", "did my panels translate correctly", "verify the numbers match", or "check parity" — checks whether a migrated Kibana dashboard matches the original Grafana/Datadog source panel-by-panel, numerically where Elasticsearch's native PROMQL oracle applies and structural-only otherwise. Runs read-only queries against the target cluster and writes nothing to the source. For an overall coverage summary use report-migration-coverage; to understand panels that failed use explain-migration-gaps. |
Validate side by side
Audience: operators of the published obs-migrate CLI (PyPI/uvx), using public docs and their real source + Elastic/Kibana — not a repo lab harness.
Goal: run obs-migrate compare to check per-panel parity against the source — numerically where the native PROMQL oracle applies, and as a structural-only row (never hidden) for Datadog, non-PromQL, or no-oracle panels. The command issues read-only _query requests against the target Elasticsearch cluster; it writes nothing to Grafana or Datadog and does not re-run migration.
Prerequisites (install)
These skills help operators of the published CLI (not a repo checkout).
If obs-migrate is missing or doctor is not Ready, follow
install-obs-migrate first — that skill owns PyPI/uvx/pip, extras, and
Python/uv gotchas. Do not invent alternate install commands here.
uvx --from 'elastic-observability-migration[all]' obs-migrate doctor
Source/Elastic credentials: connect-to-o11y-source (and your env exports).
Command
obs-migrate compare \
--artifact-dir <output-dir>/dashboards \
--es-url "$ELASTICSEARCH_ENDPOINT" \
--api-key "$KEY" \
--report-out <output-dir>/dashboards/comparison_report.json
--es-url / --api-key default to ELASTICSEARCH_ENDPOINT|ES_URL and KEY. Repeat --artifact-dir to merge multiple migrate runs. Add --ca-cert / --insecure for TLS, and tune --index, --step-seconds, --window-minutes, or --report-out when the default oracle window does not match the dashboard. Full flag list and defaults: https://github.com/elastic/observability-migration-platform/blob/main/docs/command-contract.md.
Optional package-native scorecard that also runs emitted-query acceptance (and optionally compare in-process):
obs-migrate verify \
--artifact-dir <output-dir>/dashboards \
--es-url "$ELASTICSEARCH_ENDPOINT" \
--api-key "$KEY" \
--compare \
--report-out <output-dir>/dashboards/verify_report.json
What "verified" means (honest table)
| Source / cluster | Mode | Verdicts | What it proves |
|---|
| PromQL / Grafana on a cluster with native PROMQL | Numeric (mode=native_oracle) | STRICT_PASS (≤1% max relative error), FUZZY_PASS (≤5%), SHAPE_PASS (5% < error ≤ 25%), FAIL (no overlap, or relative error above 25%), SKIP, ERROR | Translated ES |
Panels whose packets carry live source-vs-target verdicts (obs-migrate migrate --source datadog --source-execution --validate) | Live source (mode=live_source) | SOURCE_PASS, SOURCE_DRIFT, SOURCE_FAIL (fails the run), ERROR (target broken) | The source API's own numbers vs the target ES |
| Datadog panels without live comparison, non-PromQL panels, or clusters without native PROMQL | Structural (mode=structural) | STRUCTURAL | Semantic gate only — not numerically verified; the command checked shape/metadata, not bucket-by-bucket numbers |
Never describe a STRUCTURAL row as numeric proof. Never hide the structural fallback behind exit code 0.
Deterministic flow (optional)
When live telemetry is sparse or mismatched, seed synthetic data both sides can read, compare, then clean up:
obs-migrate seed-sample-data \
--artifact-dir <output-dir>/dashboards \
--es-url "$ELASTICSEARCH_ENDPOINT" \
--api-key "$KEY"
obs-migrate compare \
--artifact-dir <output-dir>/dashboards \
--es-url "$ELASTICSEARCH_ENDPOINT" \
--api-key "$KEY" \
--report-out <output-dir>/dashboards/comparison_report.json
obs-migrate remove-sample-data \
--artifact-dir <output-dir>/dashboards \
--es-url "$ELASTICSEARCH_ENDPOINT" \
--api-key "$KEY" \
--confirm
seed-sample-data and remove-sample-data are ES-only (they touch the target cluster, not the source). remove-sample-data is dry-run by default — pass --confirm to actually delete seeder-owned streams. Auth flag is --api-key (not --es-api-key).
Reading the result
The command writes comparison_report.json (machine-readable) and a sibling comparison_report.md with a panel-by-panel table: dashboard, panel, mode, verdict, max relative error, native/translated/common series counts, reason. Numeric JSON rows also carry native_series/translated_series/common_series and notes, and every FAIL or SKIP has a populated reason. --report-out defaults to comparison_report.json in the current working directory — pass an explicit path under the artifact dir to keep the report beside the migration artifacts.
Exit codes:
2 — Elasticsearch unreachable or invalid input (missing credentials, bad/missing verification_packets.json).
1 — at least one panel parity check returned FAIL or ERROR (or a live source comparison returned SOURCE_FAIL).
0 — otherwise (including runs where every row is STRUCTURAL, SOURCE_DRIFT, SKIP, or non-FAIL numeric verdicts).
Besides FAIL / SOURCE_FAIL / ERROR (which set exit 1), verdicts SKIP, SHAPE_PASS, and SOURCE_DRIFT do not fail the run but still warrant a look — route them to explain-migration-gaps or re-check --window-minutes / --step-seconds / target telemetry before trusting an all-green exit code. SHAPE_PASS is bounded at 25% relative error; above that the row is FAIL. Still read max_relative_error on every SHAPE_PASS row — a 20% miss is a pass-shaped name, not numeric proof.
Route panels with verdict FAIL / SOURCE_FAIL or structural rows the user expected to be numerically verified to the explain-migration-gaps skill for rebuild guidance. Note that STRUCTURAL can also hide panels that migrated with accepted approximations (migrated_with_warnings / Datadog warning) — structural shape ≠ semantic fidelity; use explain-migration-gaps when the user expected numeric proof. For a shareable headline scorecard (not per-panel parity), use report-migration-coverage.
Beyond compare (operator follow-ups)
obs-migrate compare proves oracle / live-source parity where applicable; it does not prove Lens UI render. When an operator asks "will it show up correctly in Kibana?":
obs-migrate verify (emitted-query acceptance + optional --compare)
grafana-validate-uploaded and/or open the dashboard in Kibana view mode
debug-uploaded-kibana-dashboard for a single broken panel
- Optional lab/repo gates listed by
obs-migrate verify and in https://github.com/elastic/observability-migration-platform/blob/main/docs/testing.md — not required for the normal operator path
Honest limits / Do NOT
- Exit
0 with all-STRUCTURAL rows is NOT numeric proof — you only confirmed structural compatibility, not that numbers match. Datadog without --source-execution typically lands here.
SHAPE_PASS is not numeric proof. It means labels overlap and relative error is between 5% and 25%. Read max_relative_error. Above 25% the row is FAIL.
- A
FAIL / SOURCE_DRIFT may be a data-window or step mismatch, not a translation bug — re-run with --window-minutes and --step-seconds aligned to the dashboard (and consider seed-sample-data) before declaring a translation defect.
- Do not claim Datadog panels were numerically verified via the PROMQL oracle — without live source packets they degrade to
STRUCTURAL; with --source-execution --validate they use SOURCE_* verdicts instead.
- Do not write to the source — compare is read-only on the target cluster; it does not prove the uploaded Kibana dashboard renders in the UI (empty panels may still be missing telemetry).
See also
install-obs-migrate — install/doctor when the CLI is missing or not Ready.
report-migration-coverage skill — shareable coverage summary from migrate artifacts.
explain-migration-gaps skill — why a panel did not migrate cleanly and how to rebuild it.
debug-uploaded-kibana-dashboard skill — UI render failures after upload.
prepare-production-cutover skill — go/no-go using compare + render-audit + coverage.
obs-migrate seed-sample-data / obs-migrate remove-sample-data — optional deterministic data setup and teardown (https://github.com/elastic/observability-migration-platform/blob/main/docs/command-contract.md).
- For Datadog,
obs-migrate migrate --source datadog --source-execution --validate fills the verification packets' source_execution/comparison blocks so a later obs-migrate compare can emit SOURCE_* verdicts (needs DD creds and comparable telemetry on both sides).
https://github.com/elastic/observability-migration-platform/blob/main/docs/command-contract.md — full compare, seed, and remove flag reference for the installed version.
https://github.com/elastic/observability-migration-platform/blob/main/docs/testing.md — layered verifier and render-audit gates.