Skip to content

Python 公共 API 契约

本文固定首版的调用形状与公开边界,配合模型、使用示例和生命周期实施。离线核心、RTR、在线 Client、热配置及事件的真实签名与字段见源码生成 API 参考;持久化和共享读取已有实现;HTTP 服务、远程 SDK 与诊断入口见各专题;实现及最终验收状态以关联提交和报告为准。行为调整必须同步本契约和调用示例。

公开模块

模块 公开入口
rpkiparrot Client、MemoryStore、__version__
rpkiparrot.models 数据契约列出的输入、载荷、快照、状态、结果和事件类型
rpkiparrot.errors 数据契约列出的异常及 ErrorInfo
rpkiparrot.config 配置契约中的冻结配置类(含 SshConfig),不自动读取环境或文件
rpkiparrot.readers parse_json、read_json、load_snapshot、JsonReader、JsonFormatAdapter
rpkiparrot.metrics 显式实例 MetricsCollector,包装纯验证且不使用全局指标
rpkiparrot.validation 单条及批量 ROV、ASPA、BMP 入口
rpkiparrot.query 覆盖查询、遍历、provider、来源差异、导出
rpkiparrot.rtr.codec PDU 类型、encode_pdu、decode_pdu、PduDecoder
rpkiparrot.rtr.client 低层 RtrSession 和 SourceSink,可独立于高级管理器使用
rpkiparrot.transports TransportFactory、ConnectedTransport
rpkiparrot.persistence PersistenceBackend、SQLiteBackend、DuckDBBackend、状态/回执类型、state_from_snapshot 与 state_digest;具体驱动延迟导入
rpkiparrot.shared SQLite SharedSnapshotClient,只读且不创建来源连接
rpkiparrot.service create_app、ServiceConfig;可选 asyncio 服务工厂
rpkiparrot.remote 可选 HTTP RemoteClient,见 HTTP 契约
rpkiparrot.diagnostics Recorder、replay 和诊断结果

模块内部对象不因没有下划线就自动成为受支持接口。实现时维护明确导出清单。CLI、service 的处理函数以及内部索引、事务构建器不作为第三方扩展点;服务的应用工厂是公开可选入口。

同步离线入口

已实现同步入口的签名、类型、错误及字段直接从源码生成:readers 提供 parse_json/read_json/load_snapshot,MemoryStore 负责来源原子替换与获取,validation 提供单条和批量 ROV/ASPA 及独立 BMP 分析。可执行流程见离线示例和验证示例。

parse_json 的 str 始终是 JSON 文本,不猜测文件路径;字节必须是 UTF-8。read_json 是明确的阻塞文件便利入口。dict 输入无法还原重复 JSON key,应在结果诊断中标明这项检查不适用。格式适配器和大小约束见输入契约。

MemoryStore 默认在线、单一合并组。context=None 等于 online;不使用当前时间填补缺失导出时间。replace_source 是完整替换并返回提交快照;失败不改变旧状态。remove_source 对未知 ID 幂等,不制造新 generation。没有来源时 snapshot 仍可取得,但验证会报告不可用。

MemoryStore 的写方法限一个拥有线程。snapshot 在拥有线程中处理到期并产生新视图;返回的不可变快照可供多个线程执行同步查询。异步应用由 Client 负责隔离大规模解析、索引构建和阻塞 I/O。

查询与导出

所有查询签名见源码参考:covering_vrps、iter_vrps、aspa_providers、iter_aspas、compare_sources 和 export_snapshot。

ASPA 查询与验证的 afi: Afi | None = None 显式选择 Afi.IPV4(IANA 1)或 Afi.IPV6(IANA 2)。aspa_providers、iter_aspas、validate_aspa 和 analyze_bmp_path 使用同一参数;批量输入 AspaInput.afi 逐项选择。结果保留 afi。省略 AFI 仅在两族授权、来源支持、能力、选中组与固定视图期限完全相同时有效,否则 InputError 要求显式选择,不能跨族合并或推测 IPv4。AS0 的压制在每一族内独立进行。compare_sources 的原始 ASPA 差异保留 AFI。

查询顺序确定:VRP 按地址族、网络地址整数、前缀长度、max_length、ASN 排序;ASPA 按 customer ASN 排序,来源按 ID,providers 按 ASN。iter_aspas 每个生效 customer 返回一次完整合并的 AspaProviders;source_id 只选择该来源参与支持的 customer,providers 仍是生效并集,不伪装成原始单来源集合。迭代器固定快照,每次 next 检查时效,不自动切换到新快照。调用方可直接丢弃迭代器释放引用,无隐藏文件句柄。

compare_sources 返回两来源的原始状态、载荷差异、能力与期限,不裁决可信性。export_snapshot 导出完整版本化状态,使用严格总量限制,不能将部分输出当成功。超出便捷接口限制时明确失败;文件/HTTP 适配器的流式输出也必须绑定同一快照并标记传输完整性。

covering_vrps、iter_vrps、iter_aspas 和 aspa_providers 查询生效视图,须检查相应能力与期限;source_id 筛选只筛支持来源,不自动启用备用组。原始来源差异、状态和完整原始导出允许包含过期记录,但必须保留原期限和不可用标志,不能把诊断可读误作可验证。

嵌入式客户端

class Client:
    def __init__(
        self,
        config: ClientConfig,
        *,
        transports: Mapping[str, TransportFactory] | None = None,
        readers: Mapping[str, JsonReader] | None = None,
        persistence: PersistenceBackend | None = None,
    ) -> None: ...

    async def __aenter__(self) -> Client: ...
    async def __aexit__(self, exc_type, exc, tb) -> None: ...
    async def run(self, *, task_status=TASK_STATUS_IGNORED) -> None: ...
    async def wait_ready(
        self,
        *,
        required: frozenset[PayloadKind],
        timeout: float = 30.0,
        aspa_afi: Afi | None = None,
    ) -> Snapshot: ...
    async def apply_config(
        self,
        config: ClientConfig,
        *,
        expected_revision: ConfigRevision,
        transports: Mapping[str, TransportFactory] | None = None,
        readers: Mapping[str, JsonReader] | None = None,
    ) -> ConfigReceipt: ...
    async def get_snapshot(self) -> Snapshot: ...
    def watch(self) -> AbstractAsyncContextManager[Subscription]: ...
    def get_status(self) -> StatusReport: ...
    def get_metrics(self) -> MetricsReport: ...
    async def flush(self, *, timeout: float | None = None) -> PersistReceipt: ...
    async def aclose(self) -> None: ...

构造不启动网络、线程、数据库或事件循环。进入上下文检查配置、可选依赖和后端并完成恢复;由宿主 await task_group.start(client.run) 启动任务。task_status.started 表示运行设施启动完成,不等于载荷就绪。required 必须非空,且限首版公开可验证的 VRP/ASPA。

注入 persistence 与 config.persistence 互斥,防止意外创建两个后端;注入后端自身负责保存路径等配置,Client 仍验证 capabilities。transports 的键必须对应已配置 RTR 来源 ID,未知 ID 或给非 RTR 来源注入传输均为 ConfigurationError。

readers 按 JSON 文件/HTTP 来源 ID 注入显式 JsonReader;未知 ID、非 JSON 来源或未知 format 均报 ConfigurationError。不提供绑定时使用内置 reader,解析结果仍须通过统一完整性、时效和资源检查。构造时复制注入映射;同一有状态 reader 的调用串行化,避免在线并发破坏适配器状态。

apply_config 在运行中原子增删来源并修改配置,包含比较配置版本、旧任务隔离、取消和字段变更边界;完整契约见运行中配置变更。它不等待新增来源就绪,不能用于切换数据库路径或后端。

get_snapshot 返回当前一致快照,不隐式等待首次同步;到期处理需要重建时受管理地完成后返回。wait_ready 仅返回指定能力可用的快照,已同步为空也算就绪;aspa_afi=None 要求两族各自可用,但不要求授权相同,指定 AFI 则只等待该族,且 required 必须含 ASPA;超时异常含当前状态。状态和指标读取不执行网络或 SQL。

watch 上下文进入时原子注册订阅并提供 subscription.initial: Snapshot;初始快照可未就绪。subscription.initial_status 在同一注册点提供当前 StatusReport,包括快照发布后仅通过状态事件观测到的连接/持久化变化;Client 和 SharedSnapshotClient 创建的订阅必定提供此字段。快照自身的 sources 固定为发布时元数据,不为当前连接观测改写同一快照 ID 的内容。之后异步迭代产生 SnapshotEvent | StatusEvent。丢失连续性抛 ResyncRequiredError,直到关闭旧订阅都不再交付普通事件。

flush 固定调用时的内存版本,等待该版本或其后继已成功持久化;返回实际回执。timeout=None 捕获当前 PersistenceConfig.flush_timeout,默认 600 秒;注入后端且没有持久化配置时同样为 600 秒。显式 timeout 优先,在途等待不被热配置重置。等待预算在取得目标快照后进入持久化等待时使用,不是整个方法的硬性墙钟上限。未配置持久化时报 ConfigurationError。关闭是否等待最后持久化、失败如何报告,见生命周期和持久化文档。

SharedSnapshotClient 接受 SharedReaderConfig,提供相同的 async context、run、wait_ready、get_snapshot、watch、get_status、get_metrics、aclose 签名,不提供 flush 或来源写接口。自有身份和 upstream_id 见持久化契约。

扩展协议和独立模块

接口 首版必要契约
TransportFactory async connect(endpoint: Endpoint) -> ByteStream;每次成功交付新流,所有权移交会话;失败须清理半开连接
ConnectedTransport 显式注入单个现有流及 owns_stream;不能重建时结束会话,不能假装支持重连
JsonFormatAdapter format_id: str;parse(document: Mapping[str, object]) -> ParsedDataset;同步、无 I/O;用户显式注入,不扫描任意插件
SourceSink async publish(update: SourceUpdate) -> None 与 async report(status: SourceInfo) -> None;publish 返回前完成一次原子接纳,取消或失败不得发布半份;report 中 unavailable 能力表示须原子撤销来源,只有连接重试不撤销有效数据
RtrSession RtrSession(config, *, transport_factory=None, limits=None, restored=None);async context、async run(sink, *, task_status=...) 与 async aclose();应用管理任务,生命周期与 Client 相同
PersistenceBackend 同步 open/read_state/commit/close,完整定义见持久化契约

内置传输与 ConnectedTransport 显式参与会话准备/关闭生命周期。普通自定义 TransportFactory 仍只要求 connect,宿主拥有其其他生命周期,不能仅凭同名 prepare/aclose 方法被会话管理。ConnectedTransport.prepare 检查单次流尚未使用,不转移所有权;关闭失败时会话保留资源句柄并报告 TransportError(code="close_failed"),可以重试 aclose。

RTR 已结束或被拒绝的查询在调用 report 前释放来源候选额度;接替查询在发送前重新准入。Client 将必要撤销作为维护工作单独准入,已经开始的撤销不能被另一项维护当作未提交的输入候选取消。正常 EOD 的 publish 仍持有候选额度直至接纳结束。

受管理协议检查可能在取消已经挂起后才确定致命错误,因此 report 可能在该状态下进入。独立 SourceSink 应保护自身必要的信任撤销;Client 对这项必要清理屏蔽取消,仍按同一构建预算排队,并在关闭时等待其原子提交。撤销的生效点仍是完整快照提交,在此之前其他已准入工作可以提交旧视图;不能把正在处理的 report 当成已经完成的撤销。report 成功返回后,当前视图必须已经反映撤销或该来源已经被配置替换/删除。

Client 对已完成撤销来源的重复不可用报告只更新连接观察,不重复提交撤销或抢占其他来源的候选。此时仍不恢复可用性;新的有效 EOD 接纳和适用的协议降级继续按独立提交处理。

RtrSession 进入上下文时完成传输依赖及 TLS/SSH 凭据准备;run 的 task_status.started 只表示任务已受宿主管理,不表示缓存数据就绪。SourceUpdate 必须包含完整 dataset 与来源原始 last_sync_at/expires_at;RTR 以有效 EOD 的接收时刻作为 generated_at 和 last_sync_at,保留 metadata.rtr 中的 refresh_interval/retry_interval/expire_interval 供恢复校验。查询捕获当次 limits、query_timeout 与排序策略。restored 接受宿主选择的完整 SourceUpdate;不因恢复而发布载荷或延长期限。身份、启用载荷与能力、协议元数据、原始计时参数或预算不匹配时报告 recovery_rejected 并改用 Reset Query;已到期状态也只允许 Reset Query,完整有效状态才允许 Serial Query。来源发布成功返回后才推进会话的 session/serial;若 sink 已完整提交而调用因异常或取消未获确认,本次 run 结束且不可重用,宿主须从自己的提交状态取得已接纳 SourceUpdate 后再显式恢复。report 的 fatal unavailable 或 expired 状态须在返回前撤销对应来源;纯连接重试保留原期限内的数据。不可重连的既有流结束后 run 返回,所有权和取消清理保持 生命周期契约 的要求。可执行示例见 低层 RTR 会话。

自定义 JSON 适配器通过 parse_json(..., format=adapter.format_id) 的显式 reader 实例注册入口实现:JsonReader(adapters: Sequence[JsonFormatAdapter]) 提供同签名 parse_json/read_json;模块级函数仅使用内置适配器,禁止修改全局注册表。JsonReader 也列入 readers 的公开清单。

PDU 公开类包括 SerialNotify、SerialQuery、ResetQuery、CacheResponse、Ipv4Prefix、Ipv6Prefix、EndOfData、CacheReset、RouterKey、ErrorReport、AspaPdu;区分协议载荷 AspaPdu 与领域 Aspa。全部为冻结、关键字参数数据类,version 显式必填;Pdu 为上述类型的联合,pdu_type/length 为派生只读字段。SerialNotify/SerialQuery 带 session_id/serial,CacheResponse 带 session_id;前缀类带 announce/prefix/max_length/asn;EndOfData 带 session_id/serial/refresh_interval/retry_interval/expire_interval;RouterKey 带 announce/asn/ski/spki;ErrorReport 带 error_code/encapsulated_pdu/text(后两者默认为空);AspaPdu 带 announce/customer/providers/afi,version 必须为 2,afi 默认 None 为 -27 双族载荷,显式 Afi.IPV4/IPV6 为 -10 单族载荷。RouterKey/ErrorReport 构造只表示通用结构合法的候选,具体 profile 的上限、错误码及封装规则由编码入口继续检查。字段类型、范围及示例进入源码生成参考。

encode_pdu(pdu, *, limits: Limits | None = None, v2_profile: str = "8210bis-27") -> bytes、decode_pdu(data: bytes, *, limits: Limits | None = None, v2_profile: str = "8210bis-27") -> Pdu 处理恰好一个完整 PDU;PduDecoder(*, limits=None, v2_profile="8210bis-27") 的 feed(data: bytes) -> tuple[Pdu, ...] 保存不足帧,finish() -> None 在残帧时失败。其他支持 profile 为显式 8210bis-10 与 8210bis-13,不可自动探测;非法 profile 或模型 profile/AFI 与所选 profile 不匹配报 InputError。AspaPdu(..., afi=None, v2_profile=None) 省略 profile 时维持 None AFI→-27、具体 AFI→-10 的模型构造规则,运行时规范化为profile字符串;无AFI且16字节的 -13必须显式设置,不通过解码结果猜测。一次 feed 失败不返回该调用先前帧,解码器失败或 finish 后不再接收新字节;成功 finish 可重复调用。构造错误为 InputError、线格式错误为 ProtocolError、配置防护超限为 ResourceLimitError;ProtocolError.details.wire_error_code 独立保存线错误码,解码错误另有 received_type/reply_allowed 防止 ErrorReport 循环。状态、协商、顺序、计时有效区间和事务合法性由会话层验证;完整规则见 PDU 契约。

依赖方向

models/errors 不依赖网络、数据库、CLI 或服务。codec/readers/validation/query 只依赖核心模型与纯计算辅助模块;存储负责索引和事务,不导入具体传输。sources 和 Client 组合存储、会话、事件与后端协议;具体数据库适配器实现协议。CLI、HTTP 和远程序列化在边界转换输入,调用公共能力。

规范化 SourceUpdate 是来源到存储的提交边界。公共扩展接口使用 typing.Protocol,避免依赖特定基类或第三方框架。首版不承诺替换任意内部索引,也不增加通用插件发现系统。