P4Runtime客户端库
项目描述
Finsy P4Runtime控制器库
Finsy是一个用Python编写的P4Runtime控制器库,使用asyncio。Finsy包括对gNMI的支持。
请查看示例目录中的某些演示程序。
安装
Finsy需要Python 3.10或更高版本。要安装最新版本,请输入pip install finsy
。
P4Runtime脚本
使用Finsy,您可以编写一个Python脚本,用于读取/写入单个交换机的P4Runtime实体。
以下是一个完整的示例,用于从交换机检索P4Info
import finsy as fy
async def main():
async with fy.Switch("sw1", "127.0.0.1:50001") as sw1:
# Print out a description of the switch's P4Info, if one is configured.
print(sw1.p4info)
fy.run(main())
以下是一个打印所有非默认表条目的示例。
import finsy as fy
async def main():
async with fy.Switch("sw1", "127.0.0.1:50001") as sw1:
# Do a wildcard read for table entries.
async for entry in sw1.read(fy.P4TableEntry()):
print(entry)
fy.run(main())
P4Runtime控制器
您还可以编写一个独立管理多个交换机的P4Runtime控制器。您的控制器可以通过更改P4表的内容来响应交换机的事件。
每个交换机由一个异步的ready_handler
函数管理。您的ready_handler
函数可以读取或更新交换机中的各种P4Runtime实体。它还可以创建任务来监听数据包或摘要。
当您向交换机写入P4Runtime更新时,您使用一元运算符(+、-、~)来指定操作:插入(+)、删除(-)或修改(~)。
import finsy as fy
async def ready_handler(sw: fy.Switch):
await sw.delete_all()
await sw.write(
[
# Insert (+) multicast group with ports 1, 2, 3 and CONTROLLER.
+fy.P4MulticastGroupEntry(1, replicas=[1, 2, 3, 255]),
# Modify (~) default table entry to flood all unmatched packets.
~fy.P4TableEntry(
"ipv4",
action=fy.Action("flood"),
is_default_action=True,
),
]
)
async for packet in sw.read_packets():
print(f"{sw.name}: {packet}")
使用SwitchOptions
类来指定每个交换机的设置,包括p4info/p4blob和ready_handler
。使用Controller
类来驱动多个交换机连接。每个交换机在P4Runtime连接建立后会回调到您的ready_handler
函数。
from pathlib import Path
options = fy.SwitchOptions(
p4info=Path("hello.p4info.txt"),
p4blob=Path("hello.json"),
ready_handler=ready_handler,
)
controller = fy.Controller([
fy.Switch("sw1", "127.0.0.1:50001", options),
fy.Switch("sw2", "127.0.0.1:50002", options),
fy.Switch("sw3", "127.0.0.1:50003", options),
])
fy.run(controller.run())
您的ready_handler
可以使用Switch.create_task
方法启动并发任务。以这种方式创建的任务将由交换机对象管理其生命周期。
如果交换机断开连接或其角色更改为备份,则运行您的ready_handler
(及其启动的所有任务)的任务将被取消,并且ready_handler
将重新开始。
有关更多示例,请参阅示例目录。
交换机读写API
Switch
类提供了与P4Runtime交换机交互的API。您将使用一个“准备处理程序”函数来控制Switch对象。准备处理程序是在交换机准备好接受命令时被调用的异步函数。
您的ready_handler
通常会向交换机写入一些控制实体,然后监听传入的事件并对它们做出反应,进行更多写入。您偶尔也可以从交换机读取实体。
当您的ready_handler
被调用时,已经建立了一个P4Runtime通道,完成了客户端仲裁,并将管道配置为在SwitchOptions
中指定的配置。
以下是一个示例骨架程序。ready_handler
被命名为ready()
。
async def ready(switch: fy.Switch):
# Check if switch is the primary. If not, we may want to proceed
# in read-only mode. In this example, ignore switch if it's a backup.
if not switch.is_primary:
return
# If we're reconnecting to a switch, it will already have runtime state.
# In this example, we just delete all entities and start over.
await switch.delete_all()
# Provision the pipeline with one or more `write` transactions. Each
# `write` is a single WriteRequest which may contain multiple updates.
await switch.write(
# [Next section will cover what goes here.]
)
# Listen for events and respond to them. This "infinite" loop will
# continue until the Switch disconnects, changes primary/backup status,
# or the controller is stopped.
async for packet in switch.read_packets():
await handle_packet(switch, packet)
Switch
类提供了一个switch.create_task
方法来启动一个受管理任务。任务允许您在同一个交换机上执行并发操作。我们可以将上面的最后一段代码(无限循环读取数据包)作为单独的任务来编写。准备处理程序函数可以提前返回;它创建的任何任务仍然会运行。
写入
使用write()
方法写入一个或多个P4Runtime更新和数据包。
P4Runtime更新支持三种操作之一:INSERT、MODIFY或DELETE。某些实体支持所有三种操作。其他实体仅支持MODIFY。
实体 | 允许的操作 | 相关类 |
---|---|---|
|
INSERT、MODIFY、DELETE | Match 、Action 、IndirectAction 、P4MeterConfig 、P4CounterData 、P4MeterCounterData |
|
INSERT、MODIFY、DELETE | |
|
INSERT、MODIFY、DELETE |
|
|
INSERT、MODIFY、DELETE | |
|
INSERT、MODIFY、DELETE | |
|
INSERT、MODIFY、DELETE | |
|
INSERT、MODIFY、DELETE | |
|
MODIFY | |
|
MODIFY |
|
|
MODIFY |
|
|
MODIFY | P4MeterConfig 、P4MeterCounterData |
|
MODIFY | |
|
MODIFY |
|
插入/修改/删除更新
要指定操作,请使用一元运算符+
(INSERT)、~
(MODIFY)或-
(DELETE)。如果您没有指定操作,则write
将引发ValueError
异常。
以下是一个示例,说明如何在同一个WriteRequest中插入和删除两个不同的实体。
await switch.write([
+fy.P4TableEntry( # unary + means INSERT
"ipv4",
match=fy.Match(dest="192.168.1.0/24"),
action=fy.Action("forward", port=1),
),
-fy.P4TableEntry( # unary - means DELETE
"ipv4",
match=fy.Match(dest="192.168.2.0/24"),
action=fy.Action("forward", port=2),
),
])
您不应该在同一个WriteRequest中插入、修改或删除相同的条目。
如果您对所有实体执行的是相同的操作,则可以使用Switch的insert
、delete
或modify
方法。
await switch.insert([
fy.P4MulticastGroupEntry(1, replicas=[1, 2, 3]),
fy.P4MulticastGroupEntry(2, replicas=[4, 5, 6]),
])
仅修改更新
对于仅支持修改操作的实体,您无需指定操作。(您可以可选地使用~
。)
await switch.write([
fy.P4RegisterEntry("reg1", index=0, data=0),
fy.P4RegisterEntry("reg1", index=1, data=1),
fy.P4RegisterEntry("reg1", index=2, data=2),
])
您还可以使用modify
方法
await switch.modify([
fy.P4RegisterEntry("reg1", index=0, data=0),
fy.P4RegisterEntry("reg1", index=1, data=1),
fy.P4RegisterEntry("reg1", index=2, data=2),
])
如果您将仅修改实体传递给insert
或delete
方法,则P4Runtime服务器将返回错误。
发送数据包
使用write
方法发送数据包。
await switch.write([fy.P4PacketOut(b"a payload.....", port=3)])
您可以在同一个调用中包含其他实体。任何非更新对象(例如 P4PacketOut、P4DigestListAck)将 在 WriteRequest 之前发送。
监听数据包
要接收数据包,请使用异步迭代器 Switch.read_packets()
。在这个例子中,pkt
是一个 P4PacketIn
对象。
read_packets
可以过滤特定 eth_type
。
# Read packets filtering only for ARP (eth_type == 0x0806).
async for pkt in switch.read_packets(eth_types={0x0806}):
# You can access the packet payload `pkt.payload` or any metadata value,
# e.g. `pkt['ingress_port']`
print(pkt.payload)
print(pkt['ingress_port'])
监听摘要
要接收摘要,请使用异步迭代器 Switch.read_digests
。您必须指定您的 P4 程序中摘要的名称。
async for digest in switch.read_digests("digest_t"):
# You can access the digest metadata e.g. `digest['ingress_port']`
# Your code may need to update table entries based on the digest data.
# To ack the digest, write `digest.ack()`.
await switch.write([entry, ...])
await switch.write([digest.ack()])
要确认摘要条目,您可以编写 digest.ack()
。
监听空闲超时
要接收空闲超时通知,请使用异步迭代器 Switch.read_idle_timeouts
。您将接收到一个包含多个表条目的 P4IdleTimeoutNotification
,每个超时的条目都有一个。
async for timeout in switch.read_idle_timeouts():
for entry in timeout.table_entry:
print(timeout.timestamp, entry)
其他事件
P4 交换机可能使用 EventEmitter
API 报告其他事件。有关事件类型,请参阅 SwitchEvent
类。每个交换机都有一个 switch.ee
属性,允许您的代码注册事件回调。
开发和测试
执行以下步骤以设置您的本地环境进行 Finsy 开发,或尝试 codespace。Finsy 需要 Python 3.10 或更高版本。如果没有安装 poetry,请按照 这些说明 安装它。
克隆并准备虚拟环境
poetry install
命令将所有开发依赖项安装到虚拟环境(venv)中。
$ git clone https://github.com/byllyfish/finsy.git
$ cd finsy
$ python3 -m venv .venv
$ poetry install
运行单元测试
从存储库的顶级目录运行 pytest 时,您将运行单元测试。
$ poetry run pytest
运行集成测试
当您在 examples
目录中运行 pytest 时,您将运行集成测试而不是单元测试。集成测试在 Mininet 网络上运行示例程序。需要 Docker 或 podman。
$ cd examples
$ poetry run pytest
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。