Managing Power Constraints
Managing Power Constraints
Overview
This use case demonstrates how to schedule and manage datacenter power constraints using the NvGrid API. Grid integrators can schedule future power changes, query scheduled targets, and retrieve feed metadata to respond to grid signals.
Goal
Schedule datacenter power constraint changes in response to grid signals such as curtailment events, demand response programs, or dynamic pricing signals.
Persona: Grid Integration Engineer
Role: Developer integrating commercial grid solutions with DPS for datacenter power management.
Objective: Implement grid signal response by scheduling power constraints and monitoring load target achievement.
Use Case Workflow
Scenario: Grid solution receives a curtailment signal and needs to reduce datacenter power consumption
Workflow Steps:
- Discover Feed Configuration - Query feed metadata to understand power thresholds and current defaults
- Calculate Target Load - Determine appropriate power constraint based on grid signal
- Schedule Load Target - Set power constraint with start/end times
- Query Schedule - Retrieve current and future scheduled targets
- Monitor Status - Track actual power consumption vs target
Theory of Operation
Load Target Scheduling
Load targets define time-based power constraints for datacenter feeds. Each target specifies:
- Interval: Start and end times for the constraint
- Load Constraint: Target power limit (watts, kilowatts, or megawatts), or default (zero) to reset
- Strategy: Algorithm for achieving the target (currently
best_effort) - Feed Tags: Specific feeds to constrain (or all if omitted)
- Correlation ID: Unique identifier for tracking
Setting Constraints:
- Use
--valueand--unitto set a specific power limit - Use
--defaultto reset to the default (zero) constraint - Alternatively,
--value=0with any unit also sets the default constraint --defaultand--valueare mutually exclusive
Schedule Conflict Resolution
gantt
title Load Target Schedule Resolution
dateFormat HH:mm
axisFormat %H:%M
section Default
10MW Default :default1, 12:00, 480m
section Scheduled Targets
6MW Target :active, target1, 16:00, 180m
8MW Target :crit, target2, 17:00, 60m
section Effective Power
10MW (Default) :done, eff1, 12:00, 240m
6MW (Target 1) :done, eff2, 16:00, 60m
8MW (Target 2) :done, eff3, 17:00, 60m
6MW (Target 1) :done, eff4, 18:00, 60m
10MW (Default) :done, eff5, 19:00, 60mResolution Rule: For overlapping intervals, the most recently scheduled target takes precedence.
- Default constraint: 10MW
- Set 6MW from 4pm-7pm → Active 4pm-7pm
- Set 8MW from 5pm-6pm → Overrides to 8MW from 5pm-6pm
- Set 5MW from 6pm-7pm → Reverts to 6MW from 6pm-7pm
- After 7pm → Reverts to 10MW default
Power Reduction Strategy
The best_effort strategy:
- Reduces power from DPM-enabled workloads
- Does not terminate running jobs
- Target may not be achieved if constraint requires job termination
- Restores power when constraints are relaxed
Python Client Setup
This section shows one-time setup required for all Python examples below. Complete these steps once before using any Python code examples.
1. Install the DPS Python API
# Navigate to the Python API directory
cd /path/to/dcpower/api/python3
# Install the package
pip3 install -e .
# Verify installation
python3 -c "from dpsapi.v1 import nvgrid_pb2, nvgrid_pb2_grpc; print('Success')"2. Extract the CA Certificate
# Extract CA certificate from your DPS deployment
kubectl get secret -n <namespace> dps-api-tls -o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/dps-ca.crt
# Security Note: In production, store certificates in a secure location
# with appropriate file permissions (e.g., 0600) and consider using
# a dedicated certificate directory like /etc/ssl/certs/dps/
# Verify the certificate
ls -lh /tmp/dps-ca.crt
# Example for dps-test namespace:
kubectl get secret -n dps-test dps-api-tls -o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/dps-ca.crt3. Setup Authentication Helper
Save this reusable helper for all examples:
# nvgrid_client.py - Reusable authentication helper
import grpc
from dpsapi.v1 import auth_pb2, auth_pb2_grpc
def create_tls_channel(target, authority, ca_cert_path):
"""Create TLS channel with CA certificate"""
with open(ca_cert_path, 'rb') as f:
ca_cert = f.read()
credentials = grpc.ssl_channel_credentials(root_certificates=ca_cert)
options = [
('grpc.default_authority', authority),
('grpc.ssl_target_name_override', authority.split(':')[0]),
]
return grpc.secure_channel(target, credentials, options=options)
def get_auth_token(username, password, host='api.dps', port=443, resolve_to=None, ca_cert_path='/tmp/dps-ca.crt'):
"""Login and get access token"""
target = f'{resolve_to or host}:{port}'
authority = f'{host}:{port}'
channel = create_tls_channel(target, authority, ca_cert_path)
stub = auth_pb2_grpc.AuthServiceStub(channel)
try:
req = auth_pb2.TokenRequest(
password_credential=auth_pb2.PasswordCredential(
username=username,
password=password
)
)
resp = stub.Token(req)
return resp.access_token
finally:
channel.close()
def create_nvgrid_client(token, host='api.dps', port=443, resolve_to=None, ca_cert_path='/tmp/dps-ca.crt'):
"""Create authenticated NvGrid client stub"""
from dpsapi.v1 import nvgrid_pb2_grpc
target = f'{resolve_to or host}:{port}'
authority = f'{host}:{port}'
channel = create_tls_channel(target, authority, ca_cert_path)
stub = nvgrid_pb2_grpc.NvGridStub(channel)
# Return both channel and stub so caller can close channel
return channel, stub
def create_auth_metadata(token):
"""Create gRPC metadata with auth token"""
return [('authorization', f'Bearer {token}')]
def format_load_value(load_value):
"""Format LoadValue for display"""
from dpsapi.v1 import nvgrid_pb2
if not load_value or not load_value.value:
return "not set"
unit_names = {
nvgrid_pb2.LoadValue.UNIT_WATT: 'W',
nvgrid_pb2.LoadValue.UNIT_KILOWATT: 'kW',
nvgrid_pb2.LoadValue.UNIT_MEGAWATT: 'MW'
}
unit = unit_names.get(load_value.unit, 'W')
return f"{load_value.value:.2f} {unit}"Usage in all examples below:
from nvgrid_client import get_auth_token, create_nvgrid_client, create_auth_metadata
# Login once
token = get_auth_token('admin', 'password', host='api.dps-test', resolve_to='127.0.0.1')
# Create client
channel, stub = create_nvgrid_client(token, host='api.dps-test', resolve_to='127.0.0.1')
metadata = create_auth_metadata(token)
# Use stub with metadata in API calls
# ... your code here ...
# Close when done
channel.close()Step-by-Step Integration Guide
Step 1: Discover Feed Configuration
Query feed metadata to understand available feeds and their power thresholds.
Python Example
#!/usr/bin/env python3
"""
Step 1: Discover feed configuration and power thresholds
"""
from google.protobuf import empty_pb2
from dpsapi.v1 import nvgrid_pb2
from nvgrid_client import get_auth_token, create_nvgrid_client, create_auth_metadata
def discover_feeds(username, password):
"""Discover feed configuration from NvGrid API"""
# Login
token = get_auth_token(username, password, host='api.dps', port=443)
# Create NvGrid client
channel, stub = create_nvgrid_client(token, host='api.dps', port=443)
metadata = create_auth_metadata(token)
try:
# Get feed metadata
response = stub.GetMetadata(empty_pb2.Empty(), metadata=metadata)
print(f"Found {len(response.metadata)} power feed(s):\n")
feeds = {}
for feed_tag, entry in response.metadata.items():
feeds[feed_tag] = {
'power_minimum': format_load_value(entry.power_minimum),
'power_maximum': format_load_value(entry.power_maximum),
'default_constraint': format_load_value(entry.default_constraint)
}
print(f"Feed: {feed_tag}")
print(f" Minimum: {feeds[feed_tag]['power_minimum']}")
print(f" Maximum: {feeds[feed_tag]['power_maximum']}")
print(f" Default: {feeds[feed_tag]['default_constraint']}\n")
return feeds
finally:
channel.close()
def format_load_value(load_value):
"""Format LoadValue for display"""
if not load_value or not load_value.value:
return "not set"
unit_names = {
nvgrid_pb2.LoadValue.UNIT_WATT: 'W',
nvgrid_pb2.LoadValue.UNIT_KILOWATT: 'kW',
nvgrid_pb2.LoadValue.UNIT_MEGAWATT: 'MW'
}
unit = unit_names.get(load_value.unit, 'W')
return f"{load_value.value:.2f} {unit}"
# Usage
if __name__ == '__main__':
# Use environment variables for credentials in production
# e.g.
# username = os.getenv('DPS_USERNAME', 'admin')
# password = os.getenv('DPS_PASSWORD')
feeds = discover_feeds('admin', 'password')Step 2: Calculate Target Load
Determine the appropriate power constraint based on the grid signal received.
Python Example
#!/usr/bin/env python3
"""
Step 2: Calculate target load based on grid signal
"""
def calculate_target_load(target_mw, feeds):
"""Calculate target load for all feeds
Args:
target_mw: Target load in megawatts (e.g., 5.0 for 5MW)
feeds: Dict of feed metadata from Step 1
Returns:
Dict with target load per feed in watts
"""
targets = {}
for feed_tag in feeds.keys():
target_watts = target_mw * 1_000_000
targets[feed_tag] = target_watts
print(f"Feed {feed_tag}: {target_mw:.2f} MW")
return targets
# Usage
target_mw = 5.0 # Set load target to 5 megawatts
targets = calculate_target_load(target_mw, feeds)Step 3: Schedule Load Target
Set the power constraint with start and end times.
Python Example
#!/usr/bin/env python3
"""
Step 3: Schedule load target
"""
from datetime import datetime, timedelta
from google.protobuf.timestamp_pb2 import Timestamp
from dpsapi.v1 import nvgrid_pb2
from nvgrid_client import get_auth_token, create_nvgrid_client, create_auth_metadata
def schedule_load_target(stub, metadata, targets, start_time, duration_minutes, correlation_id=None):
"""Schedule load target on NvGrid
Args:
stub: NvGrid client stub
metadata: Auth metadata
targets: Dict of feed_tag -> target_watts
start_time: datetime when constraint starts
duration_minutes: How long constraint is active
correlation_id: Optional tracking ID
"""
end_time = start_time + timedelta(minutes=duration_minutes)
request = nvgrid_pb2.LoadTargetRequest()
for feed_tag, target_watts in targets.items():
target = request.targets.add()
target.interval.start_time.CopyFrom(Timestamp(seconds=int(start_time.timestamp())))
target.interval.end_time.CopyFrom(Timestamp(seconds=int(end_time.timestamp())))
target.load_constraint.value = target_watts / 1_000_000 # Convert to MW
target.load_constraint.unit = nvgrid_pb2.LoadValue.UNIT_MEGAWATT
target.load_target_strategy.best_effort = True
target.feed_tags.append(feed_tag)
if correlation_id:
target.correlation_id = f"{correlation_id}-{feed_tag}"
print(f"Feed {feed_tag}: {target_watts/1_000_000:.2f}MW "
f"({start_time.strftime('%H:%M')} - {end_time.strftime('%H:%M')})")
response = stub.SetLoadTarget(request, metadata=metadata)
if response.code == nvgrid_pb2.LoadTargetResponse.STATUS_CODE_SUCCESS:
print("✅ Load target scheduled successfully")
for detail in response.details:
print(f" Correlation ID: {detail.correlation_id}")
return response
else:
print(f"❌ Failed: {response.diag_msg}")
return None
# Usage
token = get_auth_token('admin', 'password', host='api.dps')
channel, stub = create_nvgrid_client(token, host='api.dps')
metadata = create_auth_metadata(token)
start_time = datetime.now() + timedelta(minutes=5)
schedule_load_target(stub, metadata, targets, start_time, 60, "curtailment-001")
channel.close()Step 4: Query Schedule
Retrieve current and future scheduled load targets.
Python Example
#!/usr/bin/env python3
"""
Step 4: Query load schedule
"""
from datetime import datetime, timedelta
from google.protobuf.timestamp_pb2 import Timestamp
from dpsapi.v1 import nvgrid_pb2
from nvgrid_client import get_auth_token, create_nvgrid_client, create_auth_metadata
def get_load_schedule(stub, metadata, start_time, end_time, feed_tags=None):
"""Query load schedule for a time interval"""
request = nvgrid_pb2.GetLoadScheduleRequest()
request.interval.start_time.CopyFrom(Timestamp(seconds=int(start_time.timestamp())))
request.interval.end_time.CopyFrom(Timestamp(seconds=int(end_time.timestamp())))
if feed_tags:
request.feed_tags.extend(feed_tags)
schedule = stub.GetLoadSchedule(request, metadata=metadata)
print(f"Found {len(schedule.targets)} scheduled targets:")
for i, target in enumerate(schedule.targets, 1):
start_dt = datetime.fromtimestamp(target.interval.start_time.seconds)
end_dt = datetime.fromtimestamp(target.interval.end_time.seconds) if target.interval.HasField('end_time') else None
print(f"\n{i}. {start_dt.strftime('%H:%M')} - {end_dt.strftime('%H:%M') if end_dt else '∞'}")
print(f" Load: {target.load_constraint.value:.2f} MW")
print(f" Feeds: {', '.join(target.feed_tags)}")
print(f" ID: {target.correlation_id}")
return schedule
# Usage
token = get_auth_token('admin', 'password', host='api.dps')
channel, stub = create_nvgrid_client(token, host='api.dps')
metadata = create_auth_metadata(token)
start = datetime.now()
end = start + timedelta(hours=4)
schedule = get_load_schedule(stub, metadata, start, end)
channel.close()Step 5: Monitor Status
Track current power status and target achievement.
Python Example
#!/usr/bin/env python3
"""
Step 5: Monitor current status
"""
from google.protobuf import empty_pb2
from dpsapi.v1 import nvgrid_pb2
from nvgrid_client import get_auth_token, create_nvgrid_client, create_auth_metadata
def get_current_status(stub, metadata):
"""Get current power status and target achievement"""
# Get detailed status
status_response = stub.GetStatus(empty_pb2.Empty(), metadata=metadata)
print(f"Current status for {len(status_response.statuses)} feeds:\n")
for feed_tag, feed_status in status_response.statuses.items():
# Check if target is met
target_watts = load_value_to_watts(feed_status.current_load_target)
calculated_watts = load_value_to_watts(feed_status.current_calculated_load)
target_met = calculated_watts <= target_watts if target_watts > 0 else True
status_str = "ADJUSTING" if feed_status.in_flight else "STABLE"
status_icon = "✅" if target_met else "⚠️"
print(f"Feed: {feed_tag} - {status_str}")
print(f" Target: {target_watts/1_000_000:.2f} MW")
print(f" Actual: {calculated_watts/1_000_000:.2f} MW")
print(f" Status: {status_icon} {'Met' if target_met else 'Not Met'}")
if feed_status.correlation_id:
print(f" ID: {feed_status.correlation_id}")
print()
return status_response.statuses
def load_value_to_watts(load_value):
"""Convert LoadValue to watts"""
if not load_value or not load_value.value:
return 0
multipliers = {
nvgrid_pb2.LoadValue.UNIT_MEGAWATT: 1_000_000,
nvgrid_pb2.LoadValue.UNIT_KILOWATT: 1_000,
nvgrid_pb2.LoadValue.UNIT_WATT: 1
}
return load_value.value * multipliers.get(load_value.unit, 1)
# Usage
token = get_auth_token('admin', 'password', host='api.dps')
channel, stub = create_nvgrid_client(token, host='api.dps')
metadata = create_auth_metadata(token)
status = get_current_status(stub, metadata)
channel.close()Configuration and Setup
Choose the testing approach that fits your needs:
Option 1: Automated Testing (Recommended for Comprehensive Testing)
# Deploy environment and run grid simulation
task sdk # Creates cluster with simulated datacenter
task sim:grid END_AFTER=600 # Runs 10-minute grid integration testChoose Your Simulation Type:
| Command | What It Tests | Use When |
|---|---|---|
task sim:grid |
Grid integration only | Testing grid API and load scheduling in isolation |
task sim |
Grid + resource groups | Testing grid under realistic workload churn |
Grid-only simulation:
task sim:grid END_AFTER=600 # 10 minutes, default load patternsSchedules 2-5 concurrent load targets, randomizes patterns across power feeds.
Combined simulation:
task sim END_AFTER=600 # 10 minutes, grid + workload lifecycleTests grid integration while resource groups are continuously created and deleted.
Advanced Configuration:
# Extended test with high load range
task sim:grid END_AFTER=3600 MIN_LOAD_PERCENT=70 MAX_LOAD_PERCENT=95
# Frequent schedule changes for rapid testing
task sim:grid INTERVAL=60 MIN_DURATION=120 MAX_DURATION=300
# Combined test with aggressive workload churn
task sim END_AFTER=1800 MAX_RGS=25 MIN_DURATION=60 MAX_DURATION=180Documentation: Grid Simulation • Combined Simulation
Option 2: Manual Testing (Useful for Learning and Debugging)
Step-by-step setup with manual API control:
# 1. Deploy DPS development environment
task dev:up
task dev:deploy
# 2. Verify NvGrid API connection
dpsctl --host api.dps --port 443 --insecure-tls-skip-verify nvgrid get-metadata
# 3. Query current status
dpsctl --host api.dps --port 443 --insecure-tls-skip-verify nvgrid get-statusManual API Commands:
# Get feed metadata
dpsctl nvgrid get-metadata
# Schedule a single load target (6MW for 1 hour)
dpsctl nvgrid set-load-target \
--value=6 \
--unit=megawatt \
--start-time="2025-10-24T15:00:00Z" \
--end-time="2025-10-24T16:00:00Z" \
--best-effort=true
# Reset to default constraint (using --default)
dpsctl nvgrid set-load-target \
--default \
--start-time="2025-10-24T18:00:00Z" \
--end-time="2025-10-24T20:00:00Z"
# Reset to default constraint (using --value=0, equivalent)
dpsctl nvgrid set-load-target \
--value=0 \
--unit=watt \
--start-time="2025-10-24T18:00:00Z" \
--end-time="2025-10-24T20:00:00Z"
# Query load schedule
dpsctl nvgrid get-schedule \
--start-time="2025-10-24T14:00:00Z" \
--end-time="2025-10-24T18:00:00Z"
# Get current load target
dpsctl nvgrid get-current
# Get current status
dpsctl nvgrid get-statusObservable Metrics
NvGrid exposes the following Prometheus metrics:
API Performance
| Metric | Type | Labels | Description |
|---|---|---|---|
grpc_duration |
histogram (ms) | method, status_code |
API response time |
grpc_requests |
counter | method, status_code |
Total API request count |
Power Management
| Metric | Type | Labels | Description |
|---|---|---|---|
nvgrid_system_status_load_target_watts |
gauge (W) | feed_tag, load_unit |
Current load target |
nvgrid_system_status_calculated_load_watts |
gauge (W) | feed_tag, load_unit |
Actual power consumption |
nvgrid_system_status_in_flight |
gauge | feed_tag |
Power event status (0/1) |
nvgrid_feed_schedule_load_target_watts |
gauge (W) | feed_tag, start_time, end_time, load_unit, status |
Scheduled targets (status: active, scheduled, expired) |
Derived Metrics: Compare target vs calculated load for constraint accuracy. Monitor schedule status transitions for adherence tracking.
Event Correlation: Query grid_feed_schedule and nvgrid_system_status database tables for correlation_id fields to track grid signals through the power management pipeline.
This power constraint management integration provides grid solutions with precise control over datacenter power consumption in response to utility grid signals.