Configuration Validation
The agent validates its YAML configuration with Pydantic when it starts. This page says what is checked, which problems stop the agent and which only log a warning, how to read the errors, and what loading the file does not catch. The last section is for plugin authors who want their plugin's settings validated.
What happens when the file is loaded
First the top-level keys are checked (fatal). Then, for every offering, in this order:
- Plugin-specific component fields are checked against the plugin's component schema, if the
plugin registered one under the offering's
backend_type(warning only). - Each entry under
backend_componentsis checked against the core component model (fatal). backend_settingsis checked against the plugin's settings schema, if the plugin registered one under the offering'sbackend_type(warning only).- The offering itself is checked (fatal).
Loading stops at the first model that fails, so a file with problems in several places reports them one place at a time.
Fatal: the agent does not start
| Check | Rule |
|---|---|
| Required offering keys | name, waldur_api_url, waldur_offering_uuid, backend_type |
| Credentials | waldur_api_token, or all three of oidc_token_url, oidc_client_id, oidc_client_secret; a partial OIDC set is rejected even when a token is also set |
| STOMP and OIDC | stomp_enabled: true requires waldur_api_token |
| URLs | waldur_api_url and oidc_token_url must start with http:// or https://; a missing trailing / on waldur_api_url is added |
| Components | measured_unit, accounting_type and label are required; accounting_type is one of usage, limit, one |
timezone |
must be a known IANA zone name |
log_level |
one of DEBUG, INFO, WARNING, ERROR, CRITICAL (any case) |
reporting_periods |
integer from 1 to 12 |
sentry_dsn, elastic_apm_server_url |
a valid URL when set (an empty string means unset) |
log_shipping |
ship_interval_seconds ≥ 10, buffer_size_mb ≥ 1 |
backend_type is lowercased on load.
Warning only: the agent starts anyway
A plugin settings or component schema failure is logged and the agent continues with the values exactly as written:
1 | |
Treat these warnings as errors: the backend usually fails later, when it first reads the setting.
Not checked at all
- Unknown keys are ignored at the top level and inside offerings, so a misspelt optional key silently keeps its default. Unknown keys inside a component are kept and passed to the backend (plugins use them for their own component fields) — a misspelt one is simply never read. Plugin settings schemas may allow extra keys too; the SLURM one does.
*_backendnames are not checked on load. An unregistered name such ascscs-dwdi(the registered ones arecscs-dwdi-compute,-storageand-inference) logsUnsupported backend type for reporting_backend: …when that process starts. An offering with no*_backendat all is silently skipped byorder_process;membership_synclogsUnable to create backend for <offering>for it, andreportstops the whole process with that error.- Nothing is contacted. Credentials, the offering UUID and the backend are not tried until the
agent runs.
waldur_site_diagnostics -c <config>tries them: it queries Waldur with each offering's credentials and runs the order processing backend's diagnostics. It exits 1 when those backend diagnostics fail orcluster_namedoes not match the offering'sbackend_id. Waldur errors are logged; if the offering itself cannot be fetched (rejected token, unknown UUID) the command then stops with a traceback, so read the output rather than relying on the exit status.
Reading the errors
A fatal error is raised as ValueError: Configuration validation failed: followed by Pydantic's
report, which names the model, the field and the reason:
1 2 3 | |
The model name tells you where to look:
| Model in the message | Where the problem is |
|---|---|
RootConfiguration |
a top-level key (timezone, log_level, …) |
Offering |
an offering key; a rule over several keys has no field line, only the message |
BackendComponent |
an entry under backend_components |
Messages for the cross-field rules:
1 2 3 | |
A missing required key reads Field required [type=missing, …] under the key's name; an invalid
accounting_type reads Input should be 'usage', 'limit' or 'one'.
For plugin authors: validation schemas
A plugin can validate its own backend_settings and its plugin-specific component fields by
registering Pydantic models. Schemas are looked up by the offering's backend_type only —
not by order_processing_backend, membership_sync_backend or reporting_backend — so register
the schema under the name operators put in backend_type.
Base classes
Both base classes live in waldur_site_agent.common.plugin_schemas and forbid unknown fields by
default (extra="forbid"), so a typo in a plugin key is reported:
PluginBackendSettingsSchemavalidates the wholebackend_settingsmapping.HomedirSettingsSchemaextends it with the home-directory settings core reads (enable_user_homedir_account_creation,default_homedir_umask,homedir_base_path,homedir_quota); inherit from it if your backend creates home directories.PluginComponentSchemavalidates only the component fields the core model does not know. Core stripsmeasured_unit,unit_factor,accounting_type,labeland the other core fields before calling it, so the schema declares just the plugin's own fields.
The Waldur federation plugin is a working example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Registering the schemas
In the plugin's pyproject.toml, under the same name as the backend entry point:
1 2 3 4 5 | |
What validation does with your schema
- On success,
backend_settingsis replaced bymodel_dump(exclude_unset=True)of your model, so defaults declared in the schema are not filled in — read them with a default in the backend too. - On failure, the error is logged as a warning and the raw values are passed through unchanged
(see Warning only). Do not rely on the schema to stop a
misconfigured agent; raise a
BackendErrorfrom the backend's constructor for settings it cannot work without.
Python 3.9 compatibility
Plugins must run on Python 3.9. Set model_config = ConfigDict(...) rather than a
ClassVar dict, and use Optional[X] / Union[X, Y] instead of X | Y in field annotations.