Skip to content

feat(planner): DGD-owned GPU power caps and budget-aware scaling - #12012

Merged
kaim-eng merged 52 commits into
mainfrom
feat/dgd-owned-power-budget
Aug 4, 2026
Merged

feat(planner): DGD-owned GPU power caps and budget-aware scaling#12012
kaim-eng merged 52 commits into
mainfrom
feat/dgd-owned-power-budget

Conversation

@kaim-eng

@kaim-eng kaim-eng commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Add Kubernetes-only, power-aware scaling to the Planner. Per-GPU caps are owned by DynamoGraphDeployment (DGD) worker podTemplate annotations, propagated to Pods by the operator, and enforced by the Power Agent. The Planner is read-only with respect to those caps: it resolves each role's per-replica power cost at startup and clamps scaling proposals against total_gpu_power_limit.

This replaces the Planner-owned Pod PATCH design from #9683, #9684, and #9685. The power path has no Pod write surface and requires no pods/patch RBAC.

Details

Power ownership and admission contract

  • Worker components declare dynamo.nvidia.com/gpu-power-limit on their DGD podTemplate.
  • The operator propagates the annotation to worker Pods; the Power Agent applies the requested per-GPU cap.
  • After the DGD and worker rollout settle, the Planner verifies the annotation on every relevant labelled, non-terminal Pod, resolves the effective scalar nvidia.com/gpu count and multinode.nodeCount, and caches watts per logical replica.
  • The operator's validating webhook rejects adding, removing, or changing the power annotation on an existing component. For a power-annotated component, it also makes the effective scalar GPU count and node count immutable and rejects DRA-backed GPU allocation.
  • The shipped Helm configuration uses failurePolicy: Fail by default. Phase-1 cached accounting assumes this admission contract is enforced.
  • Changing a cached power input requires deleting and recreating the DGD and restarting the Planner against the replacement deployment.

Power awareness is supported for Kubernetes disaggregated Planner deployments. Configuration validation requires environment=kubernetes, mode=disagg, and a positive total_gpu_power_limit.

Budget and rollout behavior

  • Deployment state carries the startup-cached per-GPU cap and per-replica watts for each required role.
  • Initialization is strict and fail-closed, including a minimum-footprint check for min_endpoint.
  • Runtime refresh does not re-read the power annotation. It uses one DGD GET and one DGD-scoped Pod LIST, dispatched off the async planning loop, to derive ready counts and deployment-wide rollout stability.
  • The final projection applies the GPU budget first and the power budget second.
  • The power budget is a ceiling: it may reduce a proposal below the GPU floor when necessary, never upscales decode, preserves partial-proposal provenance, and stages opposing rebalances so scale-up does not rely on unfinished scale-down capacity.
  • While either power-relevant role is rolling, scale-ups are held at ready count. Baseline-filled peer targets are removed using the original proposal mask so an unrelated in-flight desired count is not overwritten.

The total budget is a projection over requested caps, not measured hardware draw. Effective-cap feedback and dynamic cap retargeting are outside this phase.

Additional cleanup

This PR also removes unused diagnostic-projection helpers from engine_adapter.py and its module-local __all__. Repository-wide search found no call sites.

Where should reviewers start?

  1. Admission invariant:
    • deploy/operator/internal/webhook/validation/dynamographdeployment.go
    • deploy/operator/internal/webhook/validation/dynamographdeployment_helpers.go
    • deploy/operator/internal/webhook/validation/dynamographdeployment_validation_envtest_test.go
  2. DGD parsing and startup lifecycle:
    • components/src/dynamo/planner/monitoring/dgd_services.py
    • components/src/dynamo/planner/environment/base.py
    • components/src/dynamo/planner/connectors/kubernetes.py
  3. Final budget boundary and proposal provenance:
    • components/src/dynamo/planner/core/budget.py
    • components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py
    • components/src/dynamo/planner/plugins/orchestrator/pipeline.py
  4. Behavioral coverage:
    • components/src/dynamo/planner/tests/unit/test_power_budget.py
    • components/src/dynamo/planner/tests/unit/test_power_environment.py
    • components/src/dynamo/planner/tests/unit/test_kubernetes_connector.py
    • deploy/operator/internal/webhook/validation/dynamographdeployment_validation_envtest_test.go

Related issues

Supersedes the unmerged Planner-owned Pod-PATCH stack: #9683, #9684, and #9685.

Validation

Current tip: 354a0240c9491d7953b97f59a11b20c1eb3377fb.

Passing on the current tip:

  • Pre-merge checks, pre-commit, CODEOWNERS, DCO, and CodeQL
  • Operator Integration and the Operator image job
  • Helm Chart Tests
  • Power Agent
  • Planner compliance on amd64 and arm64
  • Fern configuration, links, and docs preview

The full CI run is still in progress. The Planner CPU-test jobs on amd64 and arm64 are currently failing in the parallelized CPU-test step; detailed logs are pending workflow completion and must be investigated or retried before merge.

@kaim-eng
kaim-eng requested review from a team as code owners July 22, 2026 14:33
@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py Outdated
Comment thread components/src/dynamo/planner/monitoring/dgd_services.py Outdated
@github-actions github-actions Bot added documentation Improvements or additions to documentation deployment::k8s Relates to dynamo deployment in kubernetes planner labels Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The planner adds Kubernetes-only power-aware scaling. It reads DGD-owned GPU power annotations, validates and tracks per-role power data, clamps projected replica counts against a total budget, publishes power metrics, and adds tests, documentation, and an example deployment.

Changes

Power-aware scaling

Layer / File(s) Summary
Configuration contract
components/src/dynamo/planner/config/*, components/src/dynamo/planner/tests/unit/test_planner_config.py
Adds power-awareness settings, migration validation for removed power-write fields, and Kubernetes/budget requirements.
DGD power resolution
components/src/dynamo/planner/connectors/*, components/src/dynamo/planner/monitoring/dgd_services.py, components/src/dynamo/planner/errors.py, components/src/dynamo/planner/tests/unit/test_dgd_power_annotation.py, components/src/dynamo/planner/tests/unit/test_actuation_knobs.py
Resolves per-role GPU caps and watts per replica from worker podTemplate annotations, with typed errors and no Pod mutation surfaces.
Environment state lifecycle
components/src/dynamo/planner/environment/*, components/src/dynamo/planner/core/util.py, components/src/dynamo/planner/tests/unit/test_power_environment.py
Adds initialization-time strict validation and runtime conservative handling for power-cap changes, failures, and scale-up blocking.
Budget enforcement and metrics
components/src/dynamo/planner/core/*, components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py, components/src/dynamo/planner/monitoring/planner_metrics.py, components/src/dynamo/planner/tests/unit/test_power_budget.py, components/src/dynamo/planner/tests/unit/test_metric_publication.py, components/src/dynamo/planner/tests/integration/test_power_no_mutation.py
Projects watts, clamps final replica proposals after GPU budgeting, holds scale-up during relevant rollouts, and publishes power-budget gauges.
Contracts and examples
deploy/power-agent/*, deploy/helm/charts/*, docs/components/planner/planner-examples.md, examples/power-aware-budget/*, components/src/dynamo/planner/tests/unit/test_power_annotation_contract.py
Documents DGD and Power Agent annotation ownership and adds a complete power-aware budget example.
Estimated code review effort: 5 (Critical) ~120 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the PR's main changes: DGD-owned GPU power caps and budget-aware Planner scaling.
Description check ✅ Passed The description covers the overview, implementation details, review starting points, related issues, and validation status.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/src/dynamo/planner/monitoring/dgd_services.py`:
- Around line 267-270: Update the exception handling in the watts parsing block
to explicitly chain PowerAnnotationInvalidError from the caught ValueError or
TypeError using from; preserve the existing validation error and message while
retaining the original parse failure as its cause.

In `@components/src/dynamo/planner/tests/integration/test_power_no_mutation.py`:
- Around line 139-141: Update the Step 3 test setup around WorkerCounts and
_apply_final_budget so expected_num_prefill and expected_num_decode are
explicitly set to stable target counts matching the proposed (4, 4) scale-up.
This must prevent _hold_scale_up_during_rollout from reducing the proposal
before apply_power_budget runs, allowing the test to exercise proportional
clamping against the 5000 W budget.

In `@examples/power-aware-budget/README.md`:
- Around line 1-6: Update the documentation header in the example README to
follow the repository’s Fern convention: replace the HTML SPDX comment with the
required YAML frontmatter, and change the body-level “DGD-owned power caps and
budget-aware scaling” H1 to an `##` heading. Follow the metadata structure
specified by docs/AGENTS.md.
- Line 18: Fix the blank line between the two blockquote callouts in the README
by either replacing it with a `>` separator line or removing it, while
preserving both callouts and making the content MD028-compliant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6db45d29-47a9-425d-a4d4-de48c37bb54c

📥 Commits

Reviewing files that changed from the base of the PR and between 5f62ed5 and 34259a4.

📒 Files selected for processing (32)
  • components/src/dynamo/planner/config/defaults.py
  • components/src/dynamo/planner/config/planner_config.py
  • components/src/dynamo/planner/connectors/base.py
  • components/src/dynamo/planner/connectors/kubernetes.py
  • components/src/dynamo/planner/connectors/virtual.py
  • components/src/dynamo/planner/core/base.py
  • components/src/dynamo/planner/core/budget.py
  • components/src/dynamo/planner/core/types.py
  • components/src/dynamo/planner/core/util.py
  • components/src/dynamo/planner/environment/base.py
  • components/src/dynamo/planner/environment/state.py
  • components/src/dynamo/planner/errors.py
  • components/src/dynamo/planner/monitoring/dgd_services.py
  • components/src/dynamo/planner/monitoring/planner_metrics.py
  • components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py
  • components/src/dynamo/planner/tests/integration/test_power_no_mutation.py
  • components/src/dynamo/planner/tests/unit/test_actuation_knobs.py
  • components/src/dynamo/planner/tests/unit/test_dgd_power_annotation.py
  • components/src/dynamo/planner/tests/unit/test_metric_publication.py
  • components/src/dynamo/planner/tests/unit/test_planner_config.py
  • components/src/dynamo/planner/tests/unit/test_power_annotation_contract.py
  • components/src/dynamo/planner/tests/unit/test_power_budget.py
  • components/src/dynamo/planner/tests/unit/test_power_environment.py
  • deploy/helm/charts/platform/components/operator/templates/planner.yaml
  • deploy/helm/charts/power-agent/values.yaml
  • deploy/power-agent/power_agent.py
  • deploy/power-agent/tests/test_annotation_scope.py
  • deploy/power-agent/tests/test_multi_dgd_topology.py
  • docs/components/planner/planner-examples.md
  • examples/power-aware-budget/README.md
  • examples/power-aware-budget/dgd.yaml
  • examples/power-aware-budget/planner_config.json

Comment thread components/src/dynamo/planner/monitoring/dgd_services.py Outdated
Comment thread components/src/dynamo/planner/tests/integration/test_power_no_mutation.py Outdated
Comment thread examples/power-aware-budget/README.md
Comment thread examples/power-aware-budget/README.md
@kaim-eng
kaim-eng requested review from a team as code owners July 22, 2026 15:06
@kaim-eng

Copy link
Copy Markdown
Contributor Author

/ok to test 52363b6

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@datadog-official

datadog-official Bot commented Jul 22, 2026

Copy link
Copy Markdown

Pipelines

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 47.57% (-2.28%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: deb67c3 | Docs | Datadog PR Page | Give us feedback!

@kaim-eng

Copy link
Copy Markdown
Contributor Author

/ok to test 57d2efd

@kaim-eng

Copy link
Copy Markdown
Contributor Author

Review nudge for CODEOWNER stamps on tip 57d2efdc9a#12012

What this PR does: planner stops PATCHing Pod power caps; it reads dynamo.nvidia.com/gpu-power-limit from DGD worker podTemplates, projects watts, and clamps scale-up against total_gpu_power_limit. Enforcement stays with operator + Power Agent.

Current state: short CI green / near-green; full CI re-triggered via /ok to test 57d2efdc9a.

Please stamp your area when you have a chance:

  • @ai-dynamo/dynamo-planner-codeowners — planner config/core/env/budget clamp, power tests, examples/power-aware-budget/
  • @ai-dynamo/dynamo-operator-codeowners — power-agent + helm (deploy/power-agent/, planner/power-agent helm templates), k8s connector co-own
  • @ai-dynamo/dynamo-observability-codeowners — planner_metrics.py co-own (power diagnostic metric surface)
  • @ai-dynamo/dynamo-docs-codeowners — docs/components/planner/planner-examples.md
  • @ai-dynamo/dynamo-ops-codeowners — .github/codeowners/areas.yaml ownership change for the new example path
  • @ai-dynamo/dynamo-process-codeowners — generated CODEOWNERS regeneration

Happy to walk any of the above live if useful.

@tedzhouhk tedzhouhk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One blocking correctness issue in the power-budget actuation path.

Comment thread components/src/dynamo/planner/core/budget.py

@tedzhouhk tedzhouhk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional planner findings: three blocking scope/correctness issues and four non-blocking cross-cutting behavior changes.

Comment thread components/src/dynamo/planner/config/planner_config.py Outdated
Comment thread components/src/dynamo/planner/environment/base.py Outdated
Comment thread components/src/dynamo/planner/environment/base.py Outdated
Comment thread components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py Outdated
Comment thread components/src/dynamo/planner/plugins/orchestrator/engine_adapter.py Outdated
Comment thread components/src/dynamo/planner/monitoring/dgd_services.py Outdated
Comment thread components/src/dynamo/planner/connectors/base.py
kaim-eng added a commit that referenced this pull request Jul 23, 2026
When settled (4P,1D) fits the budget but parallel rollouts would peak
above the ceiling (e.g. (1P,4D)->(4P,1D)), defer scale-up legs via
peak_parallel_watts and emit scale-downs first. Also implement
GlobalPlannerConnector.get_component_power_configs for mypy.

Fixes Ted P1 on #12012 and the dynamo-runtime / mypy CI failure.

Signed-off-by: Kai Ma <kaim@nvidia.com>
kaim-eng added a commit that referenced this pull request Jul 23, 2026
…refresh

Address Ted review on #12012: read DGD power annotations once at startup
after worker readiness, remove runtime re-read/sticky scale-up blocking, restore
origin/main refresh fail-stop when power awareness is off, drop
get_component_power_configs from PlannerConnector, and localize agg
type:worker resolution to the power parser.

Signed-off-by: Kai Ma <kaim@nvidia.com>
Comment thread components/src/dynamo/planner/core/budget.py Outdated
@kaim-eng

Copy link
Copy Markdown
Contributor Author

All 16 review threads on tip 0e7d6eb663 now have replies and are resolved:

  • Ted's P1/P2 items — fixed and pushed (staged rebalance, init-only static caps, fail-stop refresh, scope trims, plus a fail-closed runtime topology guard so stale power_watts_per_replica cannot under-count after a GPUs-per-replica change)
  • Dmitry's dead-branch nit — fixed
  • CodeRabbit B904 / clamp-test — fixed; README frontmatter + MD028 intentionally rejected per example-README style guide (Fern frontmatter rules are for docs/, not examples/)
  • Devin / review-agent notes — fixed (mask gating + localized generic-worker fallback)

Short CI is green; merge is still blocked only on REVIEW_REQUIRED.

@tedzhouhk — when you have a chance, could you re-review the tip and stamp if the earlier findings look closed?
@ai-dynamo/dynamo-operator-codeowners @ai-dynamo/dynamo-observability-codeowners — still showing as requested CODEOWNER teams; a stamp on your areas would unblock merge.

@kaim-eng

Copy link
Copy Markdown
Contributor Author

/ok to test 5fe990d

@kaim-eng

Copy link
Copy Markdown
Contributor Author

/ok to test 84a8316

@kaim-eng

Copy link
Copy Markdown
Contributor Author

C:/Program Files/Git/ok to test 3983b3d

@kaim-eng

Copy link
Copy Markdown
Contributor Author

/ok to test 3983b3d

sttts and others added 9 commits July 31, 2026 20:34
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Dr. Stefan Schimanski <sschimanski@nvidia.com>
Signed-off-by: Kai Ma <kaim@nvidia.com>

# Conflicts:
#	.lycheeignore
@kaim-eng

kaim-eng commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

C:/Program Files/Git/ok to test 354a024

@kaim-eng

kaim-eng commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 354a024

…gained get_power_aware_worker_counts

All eight test_power_environment failures on amd64/arm64 stem from
_POWER_AWARE_REQUIRED expanding to four methods when
get_power_aware_worker_counts was added, while the test mocks only
supplied three.

- _power_controller(): add get_power_aware_worker_counts =
  AsyncMock(return_value=(1, 1, True)); update docstring "three"→"four"
- Positive-case and three negative-case protocol tests: add the fourth
  method so each test exercises exactly the defect its name claims
- test_init_requires_power_capable_connector: widen match to
  "PowerAwareConnector" to track the updated error message
- test_initialize_caches_caps_from_settled_snapshot_not_lagging_get:
  add get_power_aware_worker_counts for both _refresh_deployment_state
  calls inside initialize()

Production: align _resolve_power_configs error message with the other
two guards (list all four methods); update stale "three methods" comment
in kubernetes.py.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Kai Ma <kaim@nvidia.com>
@kaim-eng

kaim-eng commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test a7000d8

…ate time

- Add validateDGDPowerLimitValue helper: rejects non-integer and
  non-positive values at admission so a bad annotation surfaces as a
  rejected DGD rather than a failing Planner Pod against an immutable
  annotation.
- Extend dgdDRAPath to scan InitContainers in addition to Containers,
  closing a theoretical bypass of the DRA + power incompatibility guard.
- Add four envtest cases covering zero, negative, non-integer, and
  init-container DRA scenarios.
- Fix stale docstring on _resolve_one_power_service in dgd_services.py.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Kai Ma <kaim@nvidia.com>
@kaim-eng

kaim-eng commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

C:/Program Files/Git/ok to test 6dc517e

validateDynamoGraphDeploymentSpec hit cyclomatic complexity 31 (limit
30) after the inline power-value and DRA checks were added. Extract
both checks into validateDGDComponentPowerAnnotation, reducing the
parent function complexity by 3.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Kai Ma <kaim@nvidia.com>
@kaim-eng

kaim-eng commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

C:/Program Files/Git/ok to test deb67c3

@kaim-eng

kaim-eng commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test deb67c3

1 similar comment
@kaim-eng

kaim-eng commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test deb67c3

@jthomson04 jthomson04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving planner_metrics.py observability changes.

@dmitry-tokarev-nv dmitry-tokarev-nv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only approving process changes from DevOps side.

@kaim-eng
kaim-eng merged commit f4bf628 into main Aug 4, 2026
258 of 265 checks passed
@kaim-eng
kaim-eng deleted the feat/dgd-owned-power-budget branch August 4, 2026 19:56
dagil-nvidia added a commit that referenced this pull request Aug 7, 2026
areas.yaml auto-merged; only the generated CODEOWNERS conflicted. Resolved
by regenerating from the merged areas.yaml rather than hand-merging, so the
committed artifact is reproducible and the workflow's regenerate-and-diff
step passes.

Main gained ownership entries from #12488, #12012, #11874 and #11923 while
this branch was in review, plus #12361, which co-owns the docs publish
workflow and the link-checker config with docs. All survive: fern-docs.yml
and .lycheeignore still resolve to ops and docs.

This branch's own additions survive too: CODEOWNERS carries all 23 areas and
areas.yaml carries ops and process.

Validation: strict full-tree gate exits 0 at 5200/5200 owned with no stale
globs; 150 tests pass.

Signed-off-by: Dan Gil <dagil@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deployment::k8s Relates to dynamo deployment in kubernetes documentation Improvements or additions to documentation feat planner size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants