Skip to content

Normalize 钩子、模板集与枚举


Normalize 钩子

validate_references 在所有跨字段校验通过后,按固定顺序执行四个归一化钩子,全部经 object.__setattr__ 回写到 frozen 对象。

每个钩子都是幂等的——序列化后的 DesiredState 回灌给 agent 再次校验不会翻倍注入。

_normalize_wireguard_port_publish_runtime

  1. 收集所有 kind=wireguard 接口的 listen_port,同节点内必须唯一(重复即报错)。
  2. 若设了 runtime.wireguard_port_range,所有 listen_port 必须落在该范围内(越界报错)。
  3. wireguard_port_range 合成一条托管 PortPublishSpechost_ipeffective_host_starteffective_host_endstartend,协议 udp),注入到 router-netns 角色服务的 ports;已存在同 key 则不重复。

未设端口范围或没有 WG 接口时原样返回。

_normalize_bird_control_socket

无条件注入 bird-router 的 /run/bird 可写挂载(source=runtime/bird-runtarget=/run/birdreadonly=False)。

这是 bird-router 的一等不变量:宿主上的 agent 经此直连 bird.ctl 采集路由。已有 /run/bird 挂载则只校验其可写——只读会报错。没有 bird-router 服务时原样返回。

_normalize_dns_runtime

「分配组即启用」的服务面。据 dns 决定框架托管的 CoreDNS 服务:

  • dnsNoneenabled=False剥掉所有 dns 角色服务。DesiredState 里不含即意味着 agent 拆除 CoreDNS。
  • 启用且尚无 dns 角色服务注入 CoreDNS:name=dn42-dnsimage=coredns/coredns:1.12.1command=["-conf", "/etc/coredns/Corefile"]network_mode=service:<router-netns>、挂 coredns → /etc/coredns、依赖 router-netns 与可选的 wg-gateway。没有 router-netns 服务则跳过注入。
  • 已有 dns 服务则保留(去重)。

_normalize_dns_anycast

「分配组即启用」的网络面,与上一钩子同构。dns.bind_addresses 是 DNS 服务地址的唯一真源。

  1. 先按保留名 dns-anycast 剔除 interfacesbird.dummy_interfaces 里的同名残留——单源识别即幂等。
  2. 启用dns 非空、enabled、有 bind_addresses)→ 派生重建:合成一条 dns-anycast dummy 接口承载这些地址(裸 IP 补成 v4 /32、v6 /128),并登记为 track_service=TrueDummyInterfaceSpec,于是 BIRD 的 direct protocol 起源对应前缀、任播地址进 BGP。多节点订阅同一组即拿到相同地址,构成 anycast。
  3. 未启用或无 bind 地址 → 只剔除不重建:地址既不挂内核也不宣告,没提供 DNS 的节点不会黑洞任播流量。

TemplateSetSpec

源码 desired_state.py。决定模板层选用哪套模板目录。

字段类型必填说明
birdstr否(默认 "config-bird2/v1"BIRD 模板集版本
wireguardstr否(默认 "config-wireguard/v1"WireGuard 模板集版本
corednsstr | None否(默认 "config-coredns/v1"CoreDNS 模板集版本;None 表示不使用
dockerstr否(默认 "config-docker/v1"Docker 构建产物模板集版本
scriptsstr否(默认 "config-scripts/v1"启动与应用脚本模板集版本

枚举

源码 enums.py,全部继承 (str, Enum),可直接接受原始字符串输入并序列化为可读字符串。

枚举取值用途
InterfaceKinddummy / wireguard / underlayInterfaceSpec.kind
ServiceRolerouter-netns / wg-gateway / bird-router / rpki-cache / dns / debug-shellRuntimeServiceSpec.role;决定必需挂载与默认 IP 推导
AddressFamilyipv4 / ipv6 / mp-bgpBgpSessionSpec.address_family
ImportLimitActionblock / restart / disable / warn导入上限超限动作

enums.py 还含 ApplyStatusBootstrapStatusAgentCapabilityRuntimeResourceStatusDriftSeverityNodeHealthObservationStatus 等,但那些属于 agent 上报与控制面观测协议,不出现在 DesiredState 里。


完整示例

摘自 packages/dn42_schemas/dn42_schemas/testing.pybuild_hkg1_example_state()(golden 样本),为可读性按 JSON 缩略并加注释。

注意 DNS 任播地址只写在 dns.bind_addresses——dns-anycast 接口与 dummy 登记由 normalize 钩子派生,手写在 interfacesbird.dummy_interfaces 里。

jsonc
{
  "schema_version": "v1",
  "generation": 1,
  "node": {
    "node_id": "edge1",
    "site": "hkg",
    "region": "asia-east",            // Dn42OriginRegionCommunity
    "asn": 4242420000,
    "router_id": "172.20.0.62",
    "ipv4_prefixes": ["172.20.0.0/26"],
    "ipv6_prefixes": ["fdce:1111:2222::/48"],
    "loopback_ipv4": "172.20.0.62",   // 必须 ∈ ipv4_prefixes
    "loopback_ipv6": "fdce:1111:2222:9500::1"
  },
  "runtime": {
    "underlay": { "subnet": "10.254.42.0/24", "gateway": "10.254.42.1" },
    "router_dockerfile": { "base_image": "debian:13-slim", "debian_mirror": "deb.debian.org" },
    "services": [
      { "name": "dn42-router-netns", "role": "router-netns",
        "build": { "target": "netns" }, "command": ["sleep", "infinity"],
        "cap_add": ["NET_ADMIN", "NET_RAW"], "devices": ["/dev/net/tun:/dev/net/tun"] },
      { "name": "dn42-wg-gateway", "role": "wg-gateway",
        "build": { "target": "wg-gateway" },
        "command": ["/opt/dn42/scripts/wg/start-wg-gateway.sh"],
        "network_mode": "service:dn42-router-netns",
        "volumes": [ { "source": "wireguard", "target": "/etc/wireguard" },
                     { "source": "scripts", "target": "/opt/dn42/scripts" } ],
        "depends_on": ["dn42-router-netns"] },
      { "name": "dn42-bird-router", "role": "bird-router",
        "build": { "target": "bird-router" },
        "command": ["/opt/dn42/scripts/bird/start-bird-router.sh"],
        "network_mode": "service:dn42-router-netns",
        "volumes": [ { "source": "bird", "target": "/etc/bird" },
                     { "source": "scripts", "target": "/opt/dn42/scripts" },
                     // 可省略,_normalize_bird_control_socket 会补
                     { "source": "runtime/bird-run", "target": "/run/bird", "readonly": false } ],
        "depends_on": ["dn42-router-netns", "dn42-wg-gateway", "dn42-rpki-cache"] },
      { "name": "dn42-rpki-cache", "role": "rpki-cache", "image": "rpki/stayrtr:latest",
        "command": ["-checktime=false", "-cache=https://dn42.burble.com/roa/dn42_roa_46.json"] }
    ]
  },
  "bird": {
    "region": "asia-east",
    "large_communities": { "origin_node_id": 62 },
    "internal_topology": {
      "routers": ["edge1", "edge2"],          // 必须含本节点 edge1
      "hosts": {
        "edge1": { "ownip": "172.20.0.62", "ownip6": "fdce:1111:2222:9500::1" },
        "edge2": { "ownip": "198.18.1.3",  "ownip6": "fdce:1111:2222:ff01::3" }
      },
      "igp_adjacencies": [ { "node": "edge2", "cost": 10 } ]
    }
  },
  "interfaces": [
    { "name": "dn42-lo", "kind": "dummy", "mtu": null,
      "addresses": ["172.20.0.62/32", "fdce:1111:2222:9500::1/128"] },
    { "name": "as4242420001", "kind": "wireguard",
      // 字面私钥(base64 32 字节);实际由 materializer 从节点密钥表注入
      "private_key_ref": "s2ljcc2rBbcmSbpSlQO3xZK20RqSxPFOcYM39Ge678M=",
      "addresses": ["172.20.0.62/32", "fdce:1111:2222:9500::1/128"],
      "peer_routes": ["172.20.0.105/32", "fdce:1111:2222:dead::11/128"],
      "wireguard_peer": { "public_key": "+aFW7xRRTwOZ6w0EmrvqN4ng2QcFA0/9Wdu9GkdwJgQ=",
                          "allowed_ips": ["0.0.0.0/0", "::/0"] } }
    // …igp-edge2 内部 WG 接口略
  ],
  "bgp_sessions": [
    { "name": "demopeer_4242420001_ex01_v4", "remote_asn": 4242420001,
      "neighbor": "172.20.0.105", "source_address": "172.20.0.62",
      "address_family": "ipv4", "interface": "as4242420001" }
    // …_v6 会话略
  ],
  "dns": {
    "bind_addresses": ["172.20.0.20", "172.20.0.22",
                       "fdce:1111:2222::20", "fdce:1111:2222::22"],
    "zones": [ { "zone": "example.dn42", "records_ref": "zone://example.dn42" },
               { "zone": "0.20.172.in-addr.arpa", "records_ref": "zone://0.20.172" } ],
    "forwards": [ { "zone": "dn42", "upstreams": ["172.20.0.53"] } ]
  },
  "templates": { "bird": "config-bird2/v1", "wireguard": "config-wireguard/v1",
                 "coredns": "config-coredns/v1", "docker": "config-docker/v1",
                 "scripts": "config-scripts/v1" }
}

validate_references 之后,因 dns.enabled 默认为真且有 bind_addresses

  • interfaces 多出一条 dns-anycast dummy 接口(地址 172.20.0.20/32…::20/128 等);
  • bird.dummy_interfaces 多出 dns-anycast: { track_service: true }
  • runtime.services 多出 dn42-dns(CoreDNS)服务;
  • bird-router 的 /run/bird 可写挂载即使省略也会被补齐。