The Waldur Site Agent uses Pydantic for robust YAML configuration validation, providing type safety, clear error messages, and extensible plugin-specific validation.
The validation system consists of two layers:
All configurations are validated using Pydantic models with enum-based validation:
sentry_dsn: "https://key@o123.ingest.sentry.io/456" # URL validation
timezone: "UTC"
offerings:
- name: "My SLURM Cluster"
waldur_api_url: "https://waldur.example.com/api/" # URL validation + auto-normalization
waldur_api_token: "your_token_here"
waldur_offering_uuid: "uuid-here"
backend_type: "slurm" # Auto-lowercased
backend_components:
cpu:
measured_unit: "k-Hours" # Required string
accounting_type: "usage" # Enum: "usage" or "limit"
label: "CPU" # Required string
unit_factor: 60000 # Optional float
limit: 1000 # Optional float
Automatic Validation:
name, waldur_api_url, waldur_api_token, waldur_offering_uuid, backend_typewaldur_api_url must be valid HTTP/HTTPS URL (auto-adds trailing slash)accounting_type must be “usage” or “limit”backend_type automatically lowercasedOptional URL Validation:
None)The accounting_type field uses a validated enum:
from waldur_site_agent.common.structures import AccountingType
# Valid values
AccountingType.USAGE # "usage"
AccountingType.LIMIT # "limit"
Benefits:
Plugins can provide their own Pydantic schemas to validate plugin-specific configuration fields:
pyproject.tomlCreate schemas.py in your plugin:
from __future__ import annotations
from enum import Enum
from typing import Optional
from pydantic import ConfigDict, Field, field_validator
from waldur_site_agent.common.plugin_schemas import (
PluginBackendSettingsSchema,
PluginComponentSchema,
)
class MyPeriodType(Enum):
"""Period types for my plugin."""
MONTHLY = "monthly"
QUARTERLY = "quarterly"
ANNUAL = "annual"
class MyComponentSchema(PluginComponentSchema):
"""My plugin-specific component validation."""
model_config = ConfigDict(extra="allow") # Allow core fields
# Plugin-specific fields
my_period_type: Optional[MyPeriodType] = Field(
default=None,
description="Period type for my plugin features"
)
my_custom_ratio: Optional[float] = Field(
default=None,
description="Custom ratio (0.0-1.0)"
)
@field_validator("my_custom_ratio")
@classmethod
def validate_ratio(cls, v: Optional[float]) -> Optional[float]:
"""Validate custom ratio is between 0.0 and 1.0."""
if v is not None and (v < 0.0 or v > 1.0):
msg = "my_custom_ratio must be between 0.0 and 1.0"
raise ValueError(msg)
return v
Add to your plugin’s pyproject.toml:
[project.entry-points."waldur_site_agent.component_schemas"]
my-plugin = "waldur_site_agent_my_plugin.schemas:MyComponentSchema"
[project.entry-points."waldur_site_agent.backend_settings_schemas"]
my-plugin = "waldur_site_agent_my_plugin.schemas:MyBackendSettingsSchema"
Your plugin-specific fields are now validated:
offerings:
- name: "My Plugin Offering"
waldur_api_url: "https://waldur.example.com/api/"
waldur_api_token: "token"
waldur_offering_uuid: "uuid"
backend_type: "my-plugin"
backend_components:
cpu:
# Core fields (validated by BackendComponent)
measured_unit: "Hours"
accounting_type: "usage" # AccountingType enum
label: "CPU"
# Plugin fields (validated by MyComponentSchema)
my_period_type: "quarterly" # MyPeriodType enum
my_custom_ratio: 0.25 # 0.0-1.0 validation
✅ Correct approach:
from pydantic import ConfigDict
class MySchema(PluginComponentSchema):
model_config = ConfigDict(extra="allow") # Works on all Python versions
❌ Avoid:
from typing import ClassVar
class MySchema(PluginComponentSchema):
model_config: ClassVar = {"extra": "allow"} # Fails on Python 3.9
✅ Better approach:
class BackendType(Enum):
SLURM = "slurm"
MUP = "mup"
backend_type: Optional[BackendType] = Field(default=None)
❌ Avoid:
@field_validator("backend_type")
@classmethod
def validate_backend_type(cls, v):
if v not in {"slurm", "mup"}:
raise ValueError("Invalid backend type")
return v
The SLURM plugin demonstrates real-world plugin validation:
class PeriodType(Enum):
MONTHLY = "monthly"
QUARTERLY = "quarterly"
ANNUAL = "annual"
class SlurmComponentSchema(PluginComponentSchema):
model_config = ConfigDict(extra="allow")
period_type: Optional[PeriodType] = Field(default=None)
carryover_enabled: Optional[bool] = Field(default=None)
grace_ratio: Optional[float] = Field(default=None)
Stop configuration loading with clear error messages:
ValidationError: 2 validation errors for Offering
waldur_api_url
Value error, waldur_api_url must start with http:// or https://
accounting_type
Input should be 'usage' or 'limit'
Log warnings but continue with configuration loading:
Warning: Plugin schema validation failed for slurm.cpu: 1 validation error
period_type: Input should be 'monthly', 'quarterly' or 'annual'
This validation system provides robust configuration management while maintaining clean separation between core and plugin concerns.