外观
DesiredState 参考
DesiredState 是「单节点应运行什么」的完整声明式输入,schema 版本 v1。本目录是其字段的单一事实源。
源码在 packages/dn42_schemas/dn42_schemas/:desired_state.py(顶层、跨字段校验、normalize 钩子)、network.py、routing.py、dns.py、runtime.py、enums.py,示例见 testing.py。
分册
| 文档 | 内容 |
|---|---|
| 节点与接口 spec | NodeSpec、InterfaceSpec、WireGuardPeerSpec |
| Runtime spec | RouterRuntimeSpec 及其全部嵌套:服务、卷、健康检查、构建、端口、underlay、RPKI、Dockerfile |
| BIRD 与路由 spec | Bird2ConfigSpec、BgpSessionSpec、BfdSpec、内部拓扑、large community、RouteLocalPrefSpec |
| DNS spec | DnsSpec、DnsZoneSpec、DnsRecordSpec、DnsForwardSpec |
| Normalize 钩子 | 四个 normalize 钩子(派生 / 注入 / 剥离)、TemplateSetSpec、枚举、完整示例 |
相关:DB 事实如何变成本对象见 materialize 写入路径;地址的源 / 派生 / 副本分类见 地址模型。
StrictModel 不变量
本包所有 schema 对象继承 StrictModel(dn42_schemas/base.py):
extra="forbid"——拒绝任何未知字段。控制面与 agent 之间的任何隐式协议漂移都会在解析期直接报错,而不是被静默忽略。这也是锁步升级纪律的由来。frozen=True——模型不可变。唯一受控豁口是validate_references里 normalize 钩子用object.__setattr__回写runtime/interfaces/bird;其它任何地方都按不可变对象使用。canonical_json()——稳定可复算的 JSON(排序键、紧凑分隔符、ensure_ascii=False),使控制面与 agent 在不同进程对同一对象算出一致的字节序列。canonical_sha256()——上述 canonical JSON 的 SHA-256 十六进制摘要,用于哈希门控与内容寻址。
顶层字段
源码 desired_state.py。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
schema_version | Literal["v1"] | 否(默认 "v1") | schema 版本号;当前只接受 v1 |
generation | int(ge=1) | 是 | 期望状态世代号,通常由控制面递增 |
node | NodeSpec | 是 | 节点身份、ASN、前缀、loopback、link-local |
runtime | RouterRuntimeSpec | 是 | runtime 部署:underlay、Dockerfile、RPKI、服务列表、WG 端口范围 |
bird | Bird2ConfigSpec | 否(默认空配置) | BIRD 2 模板的高层配置 |
interfaces | list[InterfaceSpec] | 否(默认 []) | 节点应创建的接口 |
bgp_sessions | list[BgpSessionSpec] | 否(默认 []) | 节点应建立的 BGP 会话 |
dns | DnsSpec | None | 否(默认 None) | 本地 DNS 服务配置;None 表示不生成 DNS |
templates | TemplateSetSpec | 否(默认值) | 渲染时选用的模板集版本 |
validate_references(model_validator(mode="after"))在解析后跑跨字段校验并执行 normalize 钩子。
跨字段校验规则
DesiredState.validate_references 按顺序执行:
- 接口名唯一——
interfaces[*].name不得重复。 - BGP 会话名唯一——
bgp_sessions[*].name不得重复。 - BGP 会话引用的接口须存在——每个非空
session.interface必须出现在interfaces的名集合里。 - 每 WG 接口单一外部远端 ASN——对
enabled且非内部的会话,按interface聚合remote_asn;同一接口出现多个外部 ASN 即报错。 - 内部拓扑须包含本节点——若
bird.internal_topology存在,node.node_id必须属于routers + private_nodes。
嵌套模型自身的校验在解析期已生效,关键的几条:
| 模型 | 校验 |
|---|---|
NodeSpec | loopback_ipv4 ∈ ipv4_prefixes、loopback_ipv6 ∈ ipv6_prefixes;前缀走 DN42 网络校验;link_local 须为合法 fe80::/10 |
InterfaceSpec | 名 ≤15 字符;wireguard 接口须有 private_key_ref(字面私钥)与 wireguard_peer,非 wireguard 不得有 wireguard_peer |
BgpSessionSpec | neighbor / zone / interface 一致性;IPv6 link-local 邻居须给 %zone 或显式 interface |
InternalTopologySpec | routers / private_nodes / igp_adjacencies 引用的节点都须在 hosts 里 |
RouterRuntimeSpec | 见 runtime.md 的服务校验 |
UnderlayNetworkSpec | ipv6_subnet 设了就必须有 ipv6_gateway |
PortPublishSpec / WireGuardPortRangeSpec | 端口范围一致性与上界校验 |
DnsZoneSpec | 有内联 records 时须同时给 primary_ns 与 admin_email |