From f1f82a4ac96b4cd08d57b9afd8b9c32b87e622c6 Mon Sep 17 00:00:00 2001 From: Riff Date: Sun, 5 Jan 2025 13:35:34 -0800 Subject: [PATCH] Translate all pages into English. (#32) The Chinese version is temporarily removed and will be added back as translation, so we can use English as our main version. This will help more people on getting started on SONiC. --- .gitignore | 1 + README.md | 10 +- book.toml | 4 +- justfile | 12 +- po/cn.po | 6334 ++++++++ po/en.po | 12452 ---------------- po/messages.pot | 10271 +++++++------ src/1-1-install.md | 84 +- src/1-2-hello-world-virtually.md | 101 +- src/1-3-command-cheatsheet.md | 45 +- src/1-intro.md | 44 +- src/2-1-database.md | 40 +- src/2-2-services-intro.md | 88 +- src/2-3-key-containers.md | 80 +- src/2-4-sai-intro.md | 44 +- src/2-core-components-intro.md | 16 +- src/3-1-code-repos.md | 156 +- src/3-2-build.md | 223 + src/3-2-compile.md | 214 - src/3-3-testing.md | 2 +- src/3-4-1-sai-debugging.md | 1 + src/3-4-debugging.md | 2 +- src/3-4-sai-debugging.md | 1 - src/3-dev-guide.md | 2 +- src/4-1-1-exec.md | 14 +- src/4-1-2-netlink.md | 28 +- src/4-1-communicate-via-kernel.md | 1 + src/4-1-communication-with-kernel.md | 1 - src/4-2-1-redis-wrappers.md | 37 +- src/4-2-2-redis-messaging-layer.md | 336 - src/4-2-2-subscribe-state-table.md | 81 + src/4-2-3-notification-producer-consumer.md | 64 + src/4-2-4-producer-consumer-table.md | 146 + src/4-2-5-producer-consumer-state-table.md | 141 + src/4-2-communication-thru-redis.md | 1 - src/4-2-redis-based-channels.md | 8 + src/4-3-zmq-based-channels.md | 1 + src/4-3-zmq-messaging.md | 1 - src/4-4-orch-layer.md | 16 +- src/4-5-event-polling-and-error-handling.md | 38 +- src/4-communications.md | 20 +- src/5-1-syncd-and-sai.md | 315 +- ...1-bgp-command-impl.md => 5-2-1-bgp-cli.md} | 46 +- src/5-2-1-bgp-commands.md | 1 + src/5-2-2-bgp-route-update-workflow.md | 1484 -- src/5-2-2-route-update-in-frr.md | 758 + src/5-2-3-route-update-in-sonic.md | 757 + src/5-2-bgp.md | 14 +- src/5-core-components.md | 10 +- src/6-1-cold-boot.md | 2 +- src/6-2-fast-boot.md | 2 +- src/6-3-warm-boot.md | 2 +- src/6-boot.md | 2 +- src/SUMMARY.md | 66 +- theme/index.hbs | 6 +- 55 files changed, 14201 insertions(+), 20425 deletions(-) create mode 100644 po/cn.po delete mode 100644 po/en.po create mode 100644 src/3-2-build.md delete mode 100644 src/3-2-compile.md create mode 100644 src/3-4-1-sai-debugging.md delete mode 100644 src/3-4-sai-debugging.md create mode 100644 src/4-1-communicate-via-kernel.md delete mode 100644 src/4-1-communication-with-kernel.md delete mode 100644 src/4-2-2-redis-messaging-layer.md create mode 100644 src/4-2-2-subscribe-state-table.md create mode 100644 src/4-2-3-notification-producer-consumer.md create mode 100644 src/4-2-4-producer-consumer-table.md create mode 100644 src/4-2-5-producer-consumer-state-table.md delete mode 100644 src/4-2-communication-thru-redis.md create mode 100644 src/4-2-redis-based-channels.md create mode 100644 src/4-3-zmq-based-channels.md delete mode 100644 src/4-3-zmq-messaging.md rename src/{5-2-1-bgp-command-impl.md => 5-2-1-bgp-cli.md} (71%) create mode 100644 src/5-2-1-bgp-commands.md delete mode 100644 src/5-2-2-bgp-route-update-workflow.md create mode 100644 src/5-2-2-route-update-in-frr.md create mode 100644 src/5-2-3-route-update-in-sonic.md diff --git a/.gitignore b/.gitignore index 7b8b44a..0fcb26f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode/ book po/*~ bin.tar.gz diff --git a/README.md b/README.md index 41a03cc..fe159a0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Build Pipeline](https://img.shields.io/github/actions/workflow/status/r12f/sonic-book/mdbook.yml) -Version: [Chinese](https://r12f.com/sonic-book) | [English (Machine Translated, WIP)](https://r12f.com/sonic-book/en/) +Version: [English](https://r12f.com/sonic-book/en/) | [Chinese, WIP](https://r12f.com/sonic-book/cn/) > What is SONiC? > @@ -14,16 +14,16 @@ You might be interested in SONiC because it is powerful enough to suite your nee If you are in this situation, then this book is for you. -"Getting Started with SONiC" / "SONiC入门指南" is a book that intended to help people actually getting started on [SONiC](https://sonicfoundation.dev/). It contains a series of tutorials that will guide you through the process of building a SONiC image, deploying it on a switch or virtually, and using it to do some basic network operations to get hands on, as well as introducing the high level architecture, code base, and typical workflows to help you understand how it works internally and get started on development. +"Getting Started with SONiC" is a book that intended to help people actually getting started on [SONiC](https://sonicfoundation.dev/). It contains a series of tutorials that will guide you through the process of building a SONiC image, deploying it on a switch or virtually, and using it to do some basic network operations to get hands on, as well as introducing the high level architecture, code base, and typical workflows to help you understand how it works internally and get started on development. -The book is currently in [Chinese(中文)](https://r12f.com/sonic-book) and English version is still working in progress. If you like this books, please give it a star, or join the effort of authoring, bug fixing or translations by submitting PRs. +If you like this book, please help give it a star, or join the effort of authoring, bug fixing or translations by submitting PRs. ## How to build ### Prerequisites 1. Install `just` by following the [installation guide](https://github.com/casey/just#installation). We use `just` instead of `make`, because it is easier to manage and use. -2. Install powershell by following the [installation guide](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.3). This is because we use powershell as our make file script engine, so we can run our book on any platform. +2. Install powershell by following the [installation guide](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.3). This is because we use powershell as our make file script engine, so we can run our book on any platform. 3. Run `just init` for installing mdbook and related pluins. This is one time initialization. ### Build @@ -44,4 +44,4 @@ Huge thanks to the following friends for their help and contribution, without yo ## License -This book is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). \ No newline at end of file +This book is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). diff --git a/book.toml b/book.toml index 8a4faa5..d8b9211 100644 --- a/book.toml +++ b/book.toml @@ -1,9 +1,9 @@ [book] authors = ["r12f"] -language = "cn" +language = "en" multilingual = false src = "src" -title = "SONiC入门指南" +title = "Getting Started with SONiC" [build] extra-watch-dirs = ["po"] diff --git a/justfile b/justfile index 8421c0f..7c52f45 100644 --- a/justfile +++ b/justfile @@ -41,7 +41,7 @@ install-gh-bin GITHUB_PATH BIN_NAME: build: @just _log-head "Building book ..." mdbook build - just po-build en + just po-build cn serve: @just _log-head "Starting mdbook server ..." @@ -51,19 +51,19 @@ po-extract: @just _log-head "Extracting messages.pot file from source ..." $env:MDBOOK_OUTPUT='{"xgettext": {"pot-file": "messages.pot"}}'; mdbook build -d po; $env:MDBOOK_OUTPUT=$null -po-update PO='en': +po-update PO='cn': @just _log-head "Updating po files for language {{PO}} ..." msgmerge --update po/{{PO}}.po po/messages.pot -po-build PO='en': +po-build PO='cn': @just _log-head "Building book for language {{PO}} ..." $env:MDBOOK_BOOK__LANGUAGE="{{PO}}"; mdbook build -d book/{{PO}}; $env:MDBOOK_BOOK__LANGUAGE=$null -po-serve PO='en': +po-serve PO='cn': @just _log-head "Starting mdbook server with translated {{PO}} book ..." $env:MDBOOK_BOOK__LANGUAGE="{{PO}}"; mdbook serve -d book/{{PO}} -n 0.0.0.0; $env:MDBOOK_BOOK__LANGUAGE=$null -po-tr PO='en': +po-tr PO='cn': @just _log-head "Starting translating {{PO}} book ..." potr -p ./po/{{PO}}.po -e deepl -t {{PO}} @@ -80,4 +80,4 @@ _log-error LOG_LINE: @just _log-inner "{{COLOR_RED}}" "ERROR" "{{LOG_LINE}}" _log-inner COLOR LOG_LEVEL LOG_LINE: - @if ("{{LOG_LINE}}" -eq "") { echo ""; } else { Write-Host -ForegroundColor "{{COLOR}}" "[$(Get-Date -UFormat '%Y-%m-%d %H:%M:%S')][{{LOG_LEVEL}}] {{LOG_LINE}}"; } \ No newline at end of file + @if ("{{LOG_LINE}}" -eq "") { echo ""; } else { Write-Host -ForegroundColor "{{COLOR}}" "[$(Get-Date -UFormat '%Y-%m-%d %H:%M:%S')][{{LOG_LEVEL}}] {{LOG_LINE}}"; } diff --git a/po/cn.po b/po/cn.po new file mode 100644 index 0000000..3b09776 --- /dev/null +++ b/po/cn.po @@ -0,0 +1,6334 @@ +msgid "" +msgstr "" +"Project-Id-Version: SONiC入门指南\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2023-06-25 20:04-0700\n" +"Last-Translator: r12f \n" +"Language-Team: Chinese\n" +"Language: cn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src\SUMMARY.md:1 +msgid "Summary" +msgstr "总结" + +#: src\SUMMARY.md:3 src\1-intro.md:1 +msgid "Getting Started with SONiC" +msgstr "SONiC入门指南" + +#: src\SUMMARY.md:4 src\1-1-install.md:1 +msgid "Installation" +msgstr "安装" + +#: src\SUMMARY.md:5 +msgid "Hello World! Virtually" +msgstr "虚拟环境中的Hello World!" + +#: src\SUMMARY.md:6 +msgid "Commands Cheatsheet (WIP)" +msgstr "命令速查表 (WIP)" + +#: src\SUMMARY.md:7 +msgid "Core Components Introduction" +msgstr "核心组件介绍" + +#: src\SUMMARY.md:8 +msgid "Redis Database" +msgstr "Redis数据库" + +#: src\SUMMARY.md:9 +msgid "Services and Workflows" +msgstr "服务和工作流" + +#: src\SUMMARY.md:10 +msgid "Key Containers" +msgstr "关键容器" + +#: src\SUMMARY.md:11 src\2-4-sai-intro.md:1 +msgid "SAI" +msgstr "SAI" + +#: src\SUMMARY.md:12 src\3-dev-guide.md:1 +msgid "Developer Guide" +msgstr "开发者指南" + +#: src\SUMMARY.md:13 src\3-1-code-repos.md:1 +msgid "Code Repositories" +msgstr "代码仓库" + +#: src\SUMMARY.md:14 src\3-2-build.md:1 +msgid "Build" +msgstr "编译" + +#: src\SUMMARY.md:15 +msgid "Testing (WIP)" +msgstr "测试 (WIP)" + +#: src\SUMMARY.md:16 +msgid "Debugging (WIP)" +msgstr "调试 (WIP)" + +#: src\SUMMARY.md:17 +msgid "SAI Debugging (WIP)" +msgstr "SAI调试 (WIP)" + +#: src\SUMMARY.md:18 +msgid "Service Communication" +msgstr "服务间通信" + +#: src\SUMMARY.md:19 src\4-1-communicate-via-kernel.md:1 +msgid "Communicate via Kernel" +msgstr "通过内核通信" + +#: src\SUMMARY.md:20 src\4-1-1-exec.md:1 +msgid "Command Line Invocation" +msgstr "命令行调用" + +#: src\SUMMARY.md:21 src\4-1-2-netlink.md:1 +msgid "Netlink" +msgstr "Netlink" + +#: src\SUMMARY.md:22 src\4-2-redis-based-channels.md:1 +msgid "Redis-based Channels" +msgstr "基于Redis的通道" + +#: src\SUMMARY.md:23 src\4-2-1-redis-wrappers.md:1 +msgid "Redis Wrappers" +msgstr "Redis封装" + +#: src\SUMMARY.md:24 src\4-2-2-subscribe-state-table.md:1 +msgid "SubscribeStateTable" +msgstr "SubscribeStateTable" + +#: src\SUMMARY.md:25 +msgid "NotificationProducer/Consumer" +msgstr "NotificationProducer/Consumer" + +#: src\SUMMARY.md:26 +msgid "Producer/ConsumerTable" +msgstr "Producer/ConsumerTable" + +#: src\SUMMARY.md:27 +msgid "Producer/ConsumerStateTable" +msgstr "Producer/ConsumerStateTable" + +#: src\SUMMARY.md:28 +msgid "ZMQ-based Channels (WIP)" +msgstr "基于ZMQ的通道 (WIP)" + +#: src\SUMMARY.md:29 +msgid "Orch Layer" +msgstr "Orch层" + +#: src\SUMMARY.md:30 +msgid "Event Polling and Error Handling" +msgstr "事件轮询和错误处理" + +#: src\SUMMARY.md:31 +msgid "Core Components Deep Dive" +msgstr "深入核心组件" + +#: src\SUMMARY.md:32 src\5-1-syncd-and-sai.md:1 +msgid "Syncd and SAI" +msgstr "Syncd和SAI" + +#: src\SUMMARY.md:33 src\5-2-bgp.md:1 +msgid "BGP" +msgstr "BGP" + +#: src\SUMMARY.md:34 src\5-2-1-bgp-cli.md:1 +msgid "BGP CLI and vtysh" +msgstr "BGP CLI和vtysh" + +#: src\SUMMARY.md:35 src\5-2-2-route-update-in-frr.md:1 +msgid "Route Update in FRR" +msgstr "FRR中的路由更新" + +#: src\SUMMARY.md:36 src\5-2-3-route-update-in-sonic.md:1 +msgid "Route Update in SONiC" +msgstr "SONiC中的路由更新" + +#: src\SUMMARY.md:37 +msgid "Boot Process (WIP)" +msgstr "启动过程 (WIP)" + +#: src\SUMMARY.md:38 +msgid "Cold Boot (WIP)" +msgstr "冷启动 (WIP)" + +#: src\SUMMARY.md:39 +msgid "Fast Boot (WIP)" +msgstr "快速启动 (WIP)" + +#: src\SUMMARY.md:40 +msgid "Warm Boot (WIP)" +msgstr "热启动 (WIP)" + +#: src\1-1-install.md:3 +msgid "" +"If you already own a switch or are planning to purchase one and install " +"SONiC on it, please read this section carefully. Otherwise, feel free to " +"skip it. :D" +msgstr "" + +#: src\1-1-install.md:5 +msgid "Switch Selection and SONiC Installation" +msgstr "" + +#: src\1-1-install.md:7 +msgid "" +"First, please confirm if your switch supports SONiC. The list of currently " +"supported switch models can be found [here](https://sonic-net.github.io/" +"SONiC/Supported-Devices-and-Platforms.html). If your switch model is not on " +"the list, you will need to contact the manufacturer to see if they have " +"plans to support SONiC. There are many switches that do not support SONiC, " +"such as:" +msgstr "" + +#: src\1-1-install.md:9 +msgid "" +"Regular switches for home use. These switches have relatively low hardware " +"configurations (even if they support high bandwidth, such as [MikroTik " +"CRS504-4XQ-IN](https://mikrotik.com/product/crs504_4xq_in), which supports " +"100GbE networks but only has 16MB of flash storage and 64MB of RAM, so it " +"can basically only run its own RouterOS)." +msgstr "" + +#: src\1-1-install.md:10 +msgid "" +"Some data center switches may not support SONiC due to their outdated models " +"and lack of manufacturer plans." +msgstr "" + +#: src\1-1-install.md:12 +msgid "" +"Regarding the installation process, since each manufacturer's switch design " +"is different, the underlying interfaces are also different, so the " +"installation methods vary. These differences mainly focus on two areas:" +msgstr "" + +#: src\1-1-install.md:14 +msgid "" +"Each manufacturer will have their own [SONiC Build](https://sonic-net.github." +"io/SONiC/Supported-Devices-and-Platforms.html), and some manufacturers will " +"extend development on top of SONiC to support more features for their " +"switches, such as [Dell Enterprise SONiC](https://www.dell.com/en-us/shop/" +"povw/sonic) and [EdgeCore Enterprise SONiC](https://www.edge-core.com/sonic." +"php). Therefore, you need to choose the corresponding version based on your " +"switch model." +msgstr "" + +#: src\1-1-install.md:15 +msgid "" +"Each manufacturer's switch will also support different installation methods, " +"some using USB to flash the ROM directly, and some using ONIE for " +"installation. This configuration needs to be done according to your specific " +"switch." +msgstr "" + +#: src\1-1-install.md:17 +msgid "" +"Although the installation methods may vary, the overall steps are similar. " +"Please contact your manufacturer to obtain the corresponding installation " +"documentation and follow the instructions to complete the installation." +msgstr "" + +#: src\1-1-install.md:19 +msgid "Configure the Switch" +msgstr "" + +#: src\1-1-install.md:21 +msgid "" +"After installation, we need to perform some basic settings. Some settings " +"are common, and we will summarize them here." +msgstr "" + +#: src\1-1-install.md:23 +msgid "Set the admin password" +msgstr "" + +#: src\1-1-install.md:25 +msgid "" +"The default SONiC account and password is `admin` and `YourPaSsWoRd`. Using " +"default password is obviously not secure. To change the password, we can run " +"the following command:" +msgstr "" + +#: src\1-1-install.md:31 +msgid "Set fan speed" +msgstr "" + +#: src\1-1-install.md:33 +msgid "" +"Data center switches are usually very noisy! For example, the switch I use " +"is Arista 7050QX-32S, which has 4 fans that can spin up to 17000 RPM. Even " +"if it is placed in the garage, the high-frequency whining can still be heard " +"behind 3 walls on the second floor. Therefore, if you are using it at home, " +"it is recommended to adjust the fan speed." +msgstr "" + +#: src\1-1-install.md:35 +msgid "" +"Unfortunately, [SONiC does not have CLI control over fan speed](https://" +"github.com/sonic-net/SONiC/blob/master/thermal-control-design.md), so we " +"need to manually modify the configuration file in the pmon container to " +"adjust the fan speed." +msgstr "" + +#: src\1-1-install.md:38 +msgid "# Enter the pmon container" +msgstr "" + +#: src\1-1-install.md:40 +msgid "" +"# Use pwmconfig to detect all PWM fans and create a configuration file. The " +"configuration file will be created at /etc/fancontrol." +msgstr "" + +#: src\1-1-install.md:43 +msgid "" +"# Start fancontrol and make sure it works. If it doesn't work, you can run " +"fancontrol directly to see what's wrong." +msgstr "" + +#: src\1-1-install.md:45 src\1-1-install.md:46 +msgid "1" +msgstr "" + +#: src\1-1-install.md:47 +msgid "# Exit the pmon container" +msgstr "" + +#: src\1-1-install.md:50 +msgid "" +"# Copy the configuration file from the container to the host, so that the " +"configuration will not be lost after reboot." +msgstr "" + +#: src\1-1-install.md:51 +msgid "" +"# This command needs to know what is the model of your switch. For example, " +"the command I need to run here is as follows. If your switch model is " +"different, please modify it accordingly." +msgstr "" + +#: src\1-1-install.md:56 +msgid "Set the Switch Management Port IP" +msgstr "" + +#: src\1-1-install.md:58 +msgid "" +"Data center switches usually can be connected via Serial Console, but its " +"speed is very slow. Therefore, after installation, it is better to set up " +"the Management Port as soon as possible, then use SSH connection." +msgstr "" + +#: src\1-1-install.md:60 +msgid "" +"Generally, the management port is named eth0, so we can use SONiC's " +"configuration command to set it up:" +msgstr "" + +#: src\1-1-install.md:63 +msgid "# sudo config interface ip add eth0 " +msgstr "" + +#: src\1-1-install.md:63 +msgid "# IPv4" +msgstr "" + +#: src\1-1-install.md:66 +msgid "# IPv6" +msgstr "" + +#: src\1-1-install.md:71 +msgid "Create Network Configuration" +msgstr "" + +#: src\1-1-install.md:73 +msgid "" +"A newly installed SONiC switch will have a default network configuration, " +"which has many issues, such as using 10.0.0.0 IP on Ethernet0, as shown " +"below:" +msgstr "" + +#: src\1-1-install.md:84 +msgid "" +"Therefore, we need to update the ports with a new network configuration. A " +"simple method is to create a VLAN and use VLAN Routing:" +msgstr "" + +#: src\1-1-install.md:87 +msgid "# Create untagged VLAN" +msgstr "" + +#: src\1-1-install.md:89 +msgid "# Add IP to VLAN" +msgstr "" + +#: src\1-1-install.md:92 +msgid "# Remove all default IP settings" +msgstr "" + +#: src\1-1-install.md:94 +msgid "'{print \"sudo config interface ip remove\", $1, $2}'" +msgstr "" + +#: src\1-1-install.md:95 +msgid "# Add all ports to the new VLAN" +msgstr "" + +#: src\1-1-install.md:97 +msgid "'{print \"sudo config vlan member add -u 2\", $1}'" +msgstr "" + +#: src\1-1-install.md:98 +msgid "" +"# Enable proxy ARP, so the switch can respond to ARP requests from hosts" +msgstr "" + +#: src\1-1-install.md:101 +msgid "# Save the config, so it will be persistent after reboot" +msgstr "" + +#: src\1-1-install.md:106 +msgid "That's it! Now we can use `show vlan brief` to check it:" +msgstr "" + +#: src\1-1-install.md:119 +msgid "Configure the Host" +msgstr "" + +#: src\1-1-install.md:121 +msgid "" +"If you only have one host at home using multiple NICs to connect to the " +"switch for testing, we need to update some settings on the host to ensure " +"that traffic flows through the NIC and the switch. Otherwise, feel free to " +"skip this step." +msgstr "" + +#: src\1-1-install.md:123 +msgid "" +"There are many online guides for this, such as using DNAT and SNAT in " +"iptables to create a virtual address. However, after some experiments, I " +"found that the simplest way is to move one of the NICs to a new network " +"namespace, even if it uses the same IP subnet, it will still work." +msgstr "" + +#: src\1-1-install.md:125 +msgid "" +"For example, if I use Netronome Agilio CX 2x40GbE at home, it will create " +"two interfaces: `enp66s0np0` and `enp66s0np1`. Here, we can move " +"`enp66s0np1` to a new network namespace and configure the IP address:" +msgstr "" + +#: src\1-1-install.md:128 +msgid "# Create a new network namespace" +msgstr "" + +#: src\1-1-install.md:130 +msgid "# Move the interface to the new namespace" +msgstr "" + +#: src\1-1-install.md:133 +msgid "# Setting up IP and default routes" +msgstr "" + +#: src\1-1-install.md:140 +msgid "" +"That's it! We can start testing it using iperf and confirm on the switch:" +msgstr "" + +#: src\1-1-install.md:143 +msgid "# On the host (enp66s0np0 has IP 10.2.0.10 assigned)" +msgstr "" + +#: src\1-1-install.md:145 +msgid "# Test within the new network namespace" +msgstr "" + +#: src\1-1-install.md:155 +msgid "# Confirm on the switch" +msgstr "" + +#: src\1-1-install.md:167 +msgid "" +"[SONiC Thermal Control Design](https://github.com/sonic-net/SONiC/blob/" +"master/thermal-control-design.md)" +msgstr "" + +#: src\1-1-install.md:168 +msgid "" +"[Dell Enterprise SONiC Distribution](https://www.dell.com/en-us/shop/povw/" +"sonic)" +msgstr "" + +#: src\1-1-install.md:169 +msgid "" +"[Edgecore Enterprise SONiC Distribution](https://www.edge-core.com/sonic." +"php)" +msgstr "" + +#: src\1-1-install.md:170 +msgid "[Mikrotik CRS504-4XQ-IN](https://mikrotik.com/product/crs504_4xq_in)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:1 +msgid "Hello, World! Virtually" +msgstr "" + +#: src\1-2-hello-world-virtually.md:3 +msgid "" +"Although SONiC is powerful, most of the time the price of a switch that " +"supports SONiC OS is not cheap. If you just want to try SONiC without " +"spending money on a hardware device, then this chapter is a must-read. In " +"this chapter, we will summarize how to build a virtual SONiC lab using GNS3 " +"locally, allowing you to quickly experience the basic functionality of SONiC." +msgstr "" + +#: src\1-2-hello-world-virtually.md:5 +msgid "" +"There are several ways to run SONiC locally, such as docker + vswitch, p4 " +"software switch, etc. For first-time users, using GNS3 may be the most " +"convenient way, so here, we will use GNS3 as an example to explain how to " +"build a SONiC lab locally. So, let's get started!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:7 +msgid "Prepare GNS3" +msgstr "" + +#: src\1-2-hello-world-virtually.md:9 +msgid "" +"First, in order to easily and intuitively establish a virtual network for " +"testing, we need to install GNS3." +msgstr "" + +#: src\1-2-hello-world-virtually.md:11 +msgid "" +"[GNS3, short for Graphical Network Simulator 3, is (obviously) a graphical " +"network simulation software](https://www.gns3.com/). It supports various " +"virtualization technologies such as QEMU, VMware, VirtualBox, etc. With it, " +"when we build a virtual network, we don't have to run many commands manually " +"or write scripts. Most of the work can be done through its GUI, which is " +"very convenient." +msgstr "" + +#: src\1-2-hello-world-virtually.md:13 +msgid "Install Dependencies" +msgstr "" + +#: src\1-2-hello-world-virtually.md:15 +msgid "" +"Before installing it, we need to install several other software: docker, " +"wireshark, putty, qemu, ubridge, libvirt, and bridge-utils. If you have " +"already installed them, you can skip this step." +msgstr "" + +#: src\1-2-hello-world-virtually.md:17 +msgid "" +"First is Docker. You can install it by following the instructions in this " +"link: [https://docs.docker.com/engine/install/](https://docs.docker.com/" +"engine/install/)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:19 +msgid "" +"Installing the others on Ubuntu is very simple, just execute the following " +"command. Note that during the installation of ubridge and Wireshark, you " +"will be asked if you want to create the wireshark user group to bypass sudo. " +"Be sure to choose Yes." +msgstr "" + +#: src\1-2-hello-world-virtually.md:25 +msgid "Once completed, we can proceed to install GNS3." +msgstr "" + +#: src\1-2-hello-world-virtually.md:27 +msgid "Install GNS3" +msgstr "" + +#: src\1-2-hello-world-virtually.md:30 +msgid "" +"On Ubuntu, the installation of GNS3 is very simple, just execute the " +"following commands:" +msgstr "" + +#: src\1-2-hello-world-virtually.md:38 +msgid "" +"Then add your user to the following groups, so that GNS3 can access docker, " +"wireshark, and other functionalities without using sudo." +msgstr "" + +#: src\1-2-hello-world-virtually.md:46 +msgid "" +"If you are not using Ubuntu, you can refer to [their official documentation]" +"(https://docs.gns3.com/docs/getting-started/installation/linux/) for more " +"detailed installation instructions." +msgstr "" + +#: src\1-2-hello-world-virtually.md:48 +msgid "Prepare the SONiC Image" +msgstr "" + +#: src\1-2-hello-world-virtually.md:50 +msgid "" +"Before testing, we need a SONiC image. Since SONiC supports various vendors " +"with different underlying implementations, each vendor has their own image. " +"In our case, since we are creating a virtual environment, we can use the " +"VSwitch-based image to create virtual switches: sonic-vs.img.gz." +msgstr "" + +#: src\1-2-hello-world-virtually.md:52 +msgid "" +"[The SONiC image project is located here](https://github.com/sonic-net/sonic-" +"buildimage). Although we can compile it ourselves, the process can be slow. " +"To save time, we can directly [download the latest image from here](https://" +"sonic-build.azurewebsites.net/ui/sonic/pipelines/142/builds?" +"branchName=master). Just find the latest successful build and download the " +"sonic-vs.img.gz file from the Artifacts section." +msgstr "" + +#: src\1-2-hello-world-virtually.md:54 +msgid "Next, let's prepare the project:" +msgstr "" + +#: src\1-2-hello-world-virtually.md:59 +msgid "" +"# Place the downloaded image in this directory and then run the following " +"command to extract it." +msgstr "" + +#: src\1-2-hello-world-virtually.md:62 +msgid "" +"# The following command will generate the GNS3 image configuration file." +msgstr "" + +#: src\1-2-hello-world-virtually.md:67 +msgid "" +"After executing the above commands, you can run the `ls` command to see the " +"required image file." +msgstr "" + +#: src\1-2-hello-world-virtually.md:74 +msgid "# <= This is the GNS3 image configuration file" +msgstr "" + +#: src\1-2-hello-world-virtually.md:75 +msgid "# <= This is the image we extracted" +msgstr "" + +#: src\1-2-hello-world-virtually.md:79 +msgid "Import the Image" +msgstr "" + +#: src\1-2-hello-world-virtually.md:81 +msgid "" +"Now, run `gns3` in the command line to start GNS3. If you are SSHed into " +"another machine, you can try enabling X11 forwarding so that you can run " +"GNS3 remotely, with the GUI displayed locally on your machine. And I made " +"this working using MobaXterm on my local machine." +msgstr "" + +#: src\1-2-hello-world-virtually.md:83 +msgid "" +"Once it's up and running, GNS3 will prompt us to create a project. It's " +"simple, just enter a directory. If you are using X11 forwarding, please note " +"that this directory is on your remote server, not local." +msgstr "" + +#: src\1-2-hello-world-virtually.md:85 +msgid "![](assets/chapter-1/gns3-new-project.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:87 +msgid "" +"Next, we can import the image we just generated by going to `File -> Import " +"appliance`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:89 +msgid "![](assets/chapter-1/gns3-import-appliance-menu.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:91 +msgid "" +"Select the `SONiC-latest.gns3a` image configuration file we just generated " +"and click `Next`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:93 +msgid "![](assets/chapter-1/gns3-import-appliance-select-image.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:95 +msgid "Now you can see our image, click `Next`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:97 +msgid "![](assets/chapter-1/gns3-import-appliance-image.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:99 +msgid "" +"At this point, the image import process will start, which may be slow " +"because GNS3 needs to convert the image to qcow2 format and place it in our " +"project directory. Once completed, we can see our image." +msgstr "" + +#: src\1-2-hello-world-virtually.md:101 +msgid "![](assets/chapter-1/gns3-import-appliance-done.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:103 +msgid "Great! We're done!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:105 +msgid "Create the Network" +msgstr "" + +#: src\1-2-hello-world-virtually.md:107 +msgid "Alright! Now that everything is set up, let's create a virtual network!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:109 +msgid "" +"The GNS3 graphical interface is very user-friendly. Basically, open the " +"sidebar, drag in the switch, drag in the VPC, and then connect the ports. " +"After connecting, remember to click the Play button at the top to start the " +"network simulation. We won't go into much detail here, let's just look at " +"the pictures." +msgstr "" + +#: src\1-2-hello-world-virtually.md:111 +msgid "![](assets/chapter-1/gns3-console.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:113 +msgid "" +"Next, right-click on the switch, select `Custom Console`, then select Putty " +"to open the console of the switch we saw earlier. Here, the default username " +"and password for SONiC are `admin` and `YourPaSsWoRd`. Once logged in, we " +"can run familiar commands like `show interfaces status` or `show ip " +"interface` to check the network status. Here, we can also see that the " +"status of the first two interfaces we connected is `up`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:115 +msgid "Configure the Network" +msgstr "" + +#: src\1-2-hello-world-virtually.md:117 +msgid "" +"In SONiC software switches, the default ports use the 10.0.0.x subnet (as " +"shown below) with neighbor paired." +msgstr "" + +#: src\1-2-hello-world-virtually.md:128 +msgid "" +"Similar to what we mentioned in [installation](./1-1-install.md), we are " +"going to create a simple network by creating a small VLAN and including our " +"ports in it (in this case, Ethernet4 and Ethernet8):" +msgstr "" + +#: src\1-2-hello-world-virtually.md:131 +msgid "# Remove old config" +msgstr "" + +#: src\1-2-hello-world-virtually.md:134 +msgid "# Create VLAN with id 2" +msgstr "" + +#: src\1-2-hello-world-virtually.md:137 +msgid "# Add ports to VLAN" +msgstr "" + +#: src\1-2-hello-world-virtually.md:141 +msgid "# Add IP address to VLAN" +msgstr "" + +#: src\1-2-hello-world-virtually.md:146 +msgid "Now, our VLAN is created, and we can use `show vlan brief` to check:" +msgstr "" + +#: src\1-2-hello-world-virtually.md:152 +msgid "" +"==========+==============+===========+================+=============" +"+=======================+" +msgstr "" + +#: src\1-2-hello-world-virtually.md:158 +msgid "Now, let's assign a 10.0.0.x IP address to each host." +msgstr "" + +#: src\1-2-hello-world-virtually.md:161 +msgid "# VPC1" +msgstr "" + +#: src\1-2-hello-world-virtually.md:163 +msgid "# VPC2" +msgstr "" + +#: src\1-2-hello-world-virtually.md:168 +msgid "Alright, let's start the ping!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:170 +msgid "![](assets/chapter-1/gns3-ping.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:172 +msgid "It works!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:174 +msgid "Packet Capture" +msgstr "" + +#: src\1-2-hello-world-virtually.md:176 +msgid "" +"Before installing GNS3, we installed Wireshark so that we can capture " +"packets within the virtual network created by GNS3. To start capturing, " +"simply right-click on the link you want to capture on and select `Start " +"capture`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:178 +msgid "![](assets/chapter-1/gns3-capture.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:180 +msgid "" +"After a moment, Wireshark will automatically open and display all the " +"packets in real-time. Very convenient!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:182 +msgid "![](assets/chapter-1/gns3-capture-live.png)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:184 +msgid "More Networks" +msgstr "" + +#: src\1-2-hello-world-virtually.md:186 +msgid "" +"In addition to the simplest network setup we discussed above, we can " +"actually use GNS3 to build much more complex networks for testing, such as " +"multi-layer ECMP + eBGP, and more. XFlow Research has published a very " +"detailed document that covers these topics. Interested folks can refer to " +"the document: [SONiC Deployment and Testing Using GNS3](https://" +"xflowresearch.com/wp-content/uploads/2023/05/SONiC-Deployment-and-Testing-" +"Using-GNS3.pdf)." +msgstr "" + +#: src\1-2-hello-world-virtually.md:190 +msgid "[GNS3](https://www.gns3.com/)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:191 +msgid "" +"[GNS3 Linux Install](https://docs.gns3.com/docs/getting-started/installation/" +"linux/)" +msgstr "" + +#: src\1-2-hello-world-virtually.md:192 +msgid "" +"[SONiC Deployment and Testing Using GNS3](https://xflowresearch.com/wp-" +"content/uploads/2023/05/SONiC-Deployment-and-Testing-Using-GNS3.pdf)" +msgstr "" + +#: src\1-3-command-cheatsheet.md:2 +msgid "Common Commands" +msgstr "" + +#: src\1-3-command-cheatsheet.md:4 +msgid "" +"To help us check and configure the state of SONiC, SONiC provides a large " +"number of CLI commands for us to use. These commands are mostly divided into " +"two categories: `show` and `config`. Their formats are generally similar, " +"mostly following the format below:" +msgstr "" + +#: src\1-3-command-cheatsheet.md:11 +msgid "" +"The SONiC documentation provides a very detailed list of commands: [SONiC " +"Command Line Interface Guide](https://github.com/sonic-net/sonic-utilities/" +"blob/master/doc/Command-Reference.md), but due to the large number of " +"commands, it is not very convenient for us to ramp up, so we listed some of " +"the most commonly used commands and explanations for reference." +msgstr "" + +#: src\1-3-command-cheatsheet.md:34 +msgid "Basic system information" +msgstr "" + +#: src\1-3-command-cheatsheet.md:37 +msgid "# Show system version, platform info and docker containers" +msgstr "" + +#: src\1-3-command-cheatsheet.md:39 +msgid "# Show system uptime" +msgstr "" + +#: src\1-3-command-cheatsheet.md:42 +msgid "# Show platform information, such as HWSKU" +msgstr "" + +#: src\1-3-command-cheatsheet.md:47 +msgid "Config" +msgstr "" + +#: src\1-3-command-cheatsheet.md:50 +msgid "# Reload all config." +msgstr "" + +#: src\1-3-command-cheatsheet.md:50 +msgid "" +"# WARNING: This will restart almost all services and will cause network " +"interruption." +msgstr "" + +#: src\1-3-command-cheatsheet.md:53 +msgid "" +"# Save the current config from redis DB to disk, which makes the config " +"persistent across reboots." +msgstr "" + +#: src\1-3-command-cheatsheet.md:54 +msgid "# NOTE: The config file is saved to `/etc/sonic/config_db.json`" +msgstr "" + +#: src\1-3-command-cheatsheet.md:59 +msgid "Docker Related" +msgstr "" + +#: src\1-3-command-cheatsheet.md:62 +msgid "# Show all docker containers" +msgstr "" + +#: src\1-3-command-cheatsheet.md:64 +msgid "# Show processes running in a container" +msgstr "" + +#: src\1-3-command-cheatsheet.md:67 +msgid "# Enter the container" +msgstr "" + +#: src\1-3-command-cheatsheet.md:85 +msgid "Interfaces / IPs" +msgstr "" + +#: src\1-3-command-cheatsheet.md:98 +msgid "MAC / ARP / NDP" +msgstr "" + +#: src\1-3-command-cheatsheet.md:101 +msgid "# Show MAC (FDB) entries" +msgstr "" + +#: src\1-3-command-cheatsheet.md:103 +msgid "# Show IP ARP table" +msgstr "" + +#: src\1-3-command-cheatsheet.md:106 +msgid "# Show IPv6 NDP table" +msgstr "" + +#: src\1-3-command-cheatsheet.md:111 +msgid "BGP / Routes" +msgstr "" + +#: src\1-3-command-cheatsheet.md:128 +msgid "LLDP" +msgstr "" + +#: src\1-3-command-cheatsheet.md:131 +msgid "# Show LLDP neighbors in table format" +msgstr "" + +#: src\1-3-command-cheatsheet.md:133 +msgid "# Show LLDP neighbors details" +msgstr "" + +#: src\1-3-command-cheatsheet.md:138 +msgid "VLAN" +msgstr "" + +#: src\1-3-command-cheatsheet.md:144 +msgid "QoS Related" +msgstr "" + +#: src\1-3-command-cheatsheet.md:147 +msgid "# Show PFC watchdog stats" +msgstr "" + +#: src\1-3-command-cheatsheet.md:152 +msgid "ACL" +msgstr "" + +#: src\1-3-command-cheatsheet.md:159 +msgid "MUXcable / Dual ToR" +msgstr "" + +#: src\1-3-command-cheatsheet.md:161 +msgid "Muxcable mode" +msgstr "" + +#: src\1-3-command-cheatsheet.md:168 +msgid "Muxcable config" +msgstr "" + +#: src\1-3-command-cheatsheet.md:174 +msgid "Muxcable status" +msgstr "" + +#: src\1-3-command-cheatsheet.md:180 +msgid "Muxcable firmware" +msgstr "" + +#: src\1-3-command-cheatsheet.md:183 +msgid "# Firmware version:" +msgstr "" + +#: src\1-3-command-cheatsheet.md:185 +msgid "# Firmware download" +msgstr "" + +#: src\1-3-command-cheatsheet.md:186 +msgid "# config muxcable firmware download " +msgstr "" + +#: src\1-3-command-cheatsheet.md:189 +msgid "# Rollback:" +msgstr "" + +#: src\1-3-command-cheatsheet.md:190 +msgid "# config muxcable firmware rollback " +msgstr "" + +#: src\1-3-command-cheatsheet.md:197 +msgid "" +"[SONiC Command Line Interface Guide](https://github.com/sonic-net/sonic-" +"utilities/blob/master/doc/Command-Reference.md)" +msgstr "" + +#: src\2-core-components-intro.md:1 +msgid "Core components" +msgstr "" + +#: src\2-core-components-intro.md:3 +msgid "" +"We might feel that a switch is a simple network device, but in fact, there " +"could be many components running on the switch." +msgstr "" + +#: src\2-core-components-intro.md:5 +msgid "" +"Since SONiC decoupled all its services using Redis, it can be difficult to " +"understand the relationships between services by simpling tracking the code. " +"To get started on SONiC quickly, it is better to first establish a high-" +"level model, and then delve into the details of each component. Therefore, " +"before diving into other parts, we will first give a brief introduction to " +"each component to help everyone build a rough overall model." +msgstr "" + +#: src\2-core-components-intro.md:13 +msgid "" +"In addition, to help us get started, we placed the SONiC architecture " +"diagram here again as a reference:" +msgstr "" + +#: src\2-core-components-intro.md:15 +msgid "![](assets/chapter-2/sonic-arch.png)" +msgstr "" + +#: src\2-core-components-intro.md:21 src\2-1-database.md:78 +#: src\2-2-services-intro.md:76 src\2-3-key-containers.md:183 +#: src\2-4-sai-intro.md:166 src\3-1-code-repos.md:135 +#: src\4-communications.md:21 src\4-2-1-redis-wrappers.md:34 +#: src\4-2-2-subscribe-state-table.md:73 +#: src\4-2-3-notification-producer-consumer.md:54 +#: src\4-2-4-producer-consumer-table.md:130 +#: src\4-2-5-producer-consumer-state-table.md:125 src\4-4-orch-layer.md:36 +#: src\4-5-event-polling-and-error-handling.md:123 +#: src\5-1-syncd-and-sai.md:824 src\5-2-bgp.md:34 src\5-2-1-bgp-cli.md:91 +#: src\5-2-2-route-update-in-frr.md:737 src\5-2-3-route-update-in-sonic.md:736 +msgid "" +"[SONiC Architecture](https://github.com/sonic-net/SONiC/wiki/Architecture)" +msgstr "" + +#: src\2-core-components-intro.md:22 src\2-4-sai-intro.md:167 +#: src\3-1-code-repos.md:138 +msgid "[SAI API](https://github.com/opencomputeproject/SAI/wiki/SAI-APIs)" +msgstr "" + +#: src\2-core-components-intro.md:23 src\2-4-sai-intro.md:168 +msgid "" +"[Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " +"Hardware for SDN](http://yuba.stanford.edu/~grg/docs/sdn-chip-sigcomm-2013." +"pdf)" +msgstr "" + +#: src\2-1-database.md:1 +msgid "Redis database" +msgstr "" + +#: src\2-1-database.md:3 +msgid "" +"First and foremost, the core service in SONiC is undoubtedly the central " +"database - Redis! It has two major purposes: storing the configuration and " +"state of all services, and providing a communication channel for these " +"services." +msgstr "" + +#: src\2-1-database.md:5 +msgid "" +"To provide these functionalities, SONiC creates a database instance in Redis " +"named `sonic-db`. The configuration and database partitioning information " +"can be found in `/var/run/redis/sonic-db/database_config.json`:" +msgstr "" + +#: src\2-1-database.md:10 +msgid "\"INSTANCES\"" +msgstr "" + +#: src\2-1-database.md:11 src\2-1-database.md:19 src\2-1-database.md:20 +#: src\2-1-database.md:21 src\2-1-database.md:22 src\2-1-database.md:23 +#: src\2-1-database.md:24 src\2-1-database.md:25 src\2-1-database.md:26 +#: src\2-1-database.md:27 src\2-1-database.md:28 src\2-1-database.md:29 +#: src\2-1-database.md:30 src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"redis\"" +msgstr "" + +#: src\2-1-database.md:12 +msgid "\"hostname\"" +msgstr "" + +#: src\2-1-database.md:12 +msgid "\"127.0.0.1\"" +msgstr "" + +#: src\2-1-database.md:13 +msgid "\"port\"" +msgstr "" + +#: src\2-1-database.md:14 +msgid "\"unix_socket_path\"" +msgstr "" + +#: src\2-1-database.md:14 +msgid "\"/var/run/redis/redis.sock\"" +msgstr "" + +#: src\2-1-database.md:15 +msgid "\"persistence_for_warm_boot\"" +msgstr "" + +#: src\2-1-database.md:15 +msgid "\"yes\"" +msgstr "" + +#: src\2-1-database.md:18 +msgid "\"DATABASES\"" +msgstr "" + +#: src\2-1-database.md:19 src\5-2-3-route-update-in-sonic.md:50 +msgid "\"APPL_DB\"" +msgstr "" + +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"id\"" +msgstr "" + +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"separator\"" +msgstr "" + +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:24 src\2-1-database.md:25 +#: src\2-1-database.md:29 src\2-1-database.md:30 src\2-1-database.md:31 +#: src\2-1-database.md:32 src\5-1-syncd-and-sai.md:498 +#: src\5-2-3-route-update-in-sonic.md:682 +msgid "\":\"" +msgstr "" + +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"instance\"" +msgstr "" + +#: src\2-1-database.md:20 +msgid "\"ASIC_DB\"" +msgstr "" + +#: src\2-1-database.md:21 +msgid "\"COUNTERS_DB\"" +msgstr "" + +#: src\2-1-database.md:22 +msgid "\"LOGLEVEL_DB\"" +msgstr "" + +#: src\2-1-database.md:23 +msgid "\"CONFIG_DB\"" +msgstr "" + +#: src\2-1-database.md:23 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 +msgid "\"|\"" +msgstr "" + +#: src\2-1-database.md:24 +msgid "\"PFC_WD_DB\"" +msgstr "" + +#: src\2-1-database.md:25 +msgid "\"FLEX_COUNTER_DB\"" +msgstr "" + +#: src\2-1-database.md:26 +msgid "\"STATE_DB\"" +msgstr "" + +#: src\2-1-database.md:27 +msgid "\"SNMP_OVERLAY_DB\"" +msgstr "" + +#: src\2-1-database.md:28 +msgid "\"RESTAPI_DB\"" +msgstr "" + +#: src\2-1-database.md:29 +msgid "\"GB_ASIC_DB\"" +msgstr "" + +#: src\2-1-database.md:30 +msgid "\"GB_COUNTERS_DB\"" +msgstr "" + +#: src\2-1-database.md:31 +msgid "\"GB_FLEX_COUNTER_DB\"" +msgstr "" + +#: src\2-1-database.md:32 +msgid "\"APPL_STATE_DB\"" +msgstr "" + +#: src\2-1-database.md:34 +msgid "\"VERSION\"" +msgstr "" + +#: src\2-1-database.md:34 +msgid "\"1.0\"" +msgstr "" + +#: src\2-1-database.md:38 +msgid "" +"Although we can see that there are about a dozen databases in SONiC, most of " +"the time we only need to focus on the following most important ones:" +msgstr "" + +#: src\2-1-database.md:40 +msgid "" +"**CONFIG_DB (ID = 4)**: Stores the **configuration** of all services, such " +"as port configuration, VLAN configuration, etc. It represents the data model " +"of the **desired state of the switch** as intended by the user. This is also " +"the main object of operation when all CLI and external applications modify " +"the configuration." +msgstr "" + +#: src\2-1-database.md:41 +msgid "" +"**APPL_DB (Application DB, ID = 0)**: Stores **internal state information of " +"all services**. It contains two types of information:" +msgstr "" + +#: src\2-1-database.md:42 +msgid "" +"One is calculated by each service after reading the configuration " +"information from CONFIG_DB, which can be understood as the **desired state " +"of the switch** (Goal State) but from the perspective of each service." +msgstr "" + +#: src\2-1-database.md:43 +msgid "" +"The other is when the ASIC state changes and is written back, some services " +"write directly to APPL_DB instead of the STATE_DB we will introduce next. " +"This information can be understood as the **current state of the switch** as " +"perceived by each service." +msgstr "" + +#: src\2-1-database.md:44 +msgid "" +"**STATE_DB (ID = 6)**: Stores the **current state** of various components of " +"the switch. When a service in SONiC receives a state change from STATE_DB " +"and finds it inconsistent with the Goal State, SONiC will reapply the " +"configuration until the two states are consistent. (Of course, for those " +"states written back to APPL_DB, the service will monitor changes in APPL_DB " +"instead of STATE_DB.)" +msgstr "" + +#: src\2-1-database.md:45 +msgid "" +"**ASIC_DB (ID = 1)**: Stores the **desired state information** of the switch " +"ASIC in SONiC, such as ACL, routing, etc. Unlike APPL_DB, the data model in " +"this database is designed for ASIC rather than service abstraction. This " +"design facilitates the development of SAI and ASIC drivers by various " +"vendors." +msgstr "" + +#: src\2-1-database.md:47 +msgid "" +"Now, we have an intuitive question: with so many services in the switch, are " +"all configurations and states stored in a single database without isolation? " +"What if two services use the same Redis Key? This is a very good question, " +"and SONiC's solution is straightforward: continue to partition each database " +"into tables!" +msgstr "" + +#: src\2-1-database.md:49 +msgid "" +"We know that Redis does not have the concept of tables within each database " +"but uses key-value pairs to store data. Therefore, to further partition " +"tables, SONiC's solution is to include the table name in the key and " +"separate the table and key with a delimiter. The `separator` field in the " +"configuration file above serves this purpose. For example, the state of the " +"`Ethernet4` port in the `PORT_TABLE` table in `APPL_DB` can be accessed " +"using `PORT_TABLE:Ethernet4` as follows:" +msgstr "" + +#: src\2-1-database.md:56 +msgid "\"admin_status\"" +msgstr "" + +#: src\2-1-database.md:57 src\2-1-database.md:71 src\4-1-2-netlink.md:68 +msgid "\"up\"" +msgstr "" + +#: src\2-1-database.md:58 +msgid "\"alias\"" +msgstr "" + +#: src\2-1-database.md:59 +msgid "\"Ethernet6/1\"" +msgstr "" + +#: src\2-1-database.md:60 +msgid "\"index\"" +msgstr "" + +#: src\2-1-database.md:61 +msgid "\"6\"" +msgstr "" + +#: src\2-1-database.md:62 +msgid "\"lanes\"" +msgstr "" + +#: src\2-1-database.md:63 +msgid "\"13,14,15,16\"" +msgstr "" + +#: src\2-1-database.md:64 +msgid "\"mtu\"" +msgstr "" + +#: src\2-1-database.md:65 +msgid "\"9100\"" +msgstr "" + +#: src\2-1-database.md:66 +msgid "\"speed\"" +msgstr "" + +#: src\2-1-database.md:67 +msgid "\"40000\"" +msgstr "" + +#: src\2-1-database.md:68 +msgid "\"description\"" +msgstr "" + +#: src\2-1-database.md:69 src\5-2-3-route-update-in-sonic.md:307 +msgid "\"\"" +msgstr "" + +#: src\2-1-database.md:70 src\4-1-2-netlink.md:68 +msgid "\"oper_status\"" +msgstr "" + +#: src\2-1-database.md:74 +msgid "" +"Of course, in SONiC, not only the data model but also the communication " +"mechanism uses a similar method to achieve \"table\" level isolation." +msgstr "" + +#: src\2-2-services-intro.md:2 +msgid "Introduction to Services and Workflows" +msgstr "" + +#: src\2-2-services-intro.md:4 +msgid "" +"There are many services (daemon processes) in SONiC, around twenty to " +"thirty. They start with the switch and keep running until the switch is shut " +"down. If we want to quickly understand how SONiC works, diving into each " +"service one by one is obviously not a good option. Therefore, it is better " +"to categorize these services and control flows on high level to help us " +"build a big picture." +msgstr "" + +#: src\2-2-services-intro.md:10 +msgid "Service Categories" +msgstr "" + +#: src\2-2-services-intro.md:12 +msgid "" +"Generally speaking, the services in SONiC can be divided into the following " +"categories: `*syncd`, `*mgrd`, feature implementations, `orchagent`, and " +"`syncd`." +msgstr "" + +#: src\2-2-services-intro.md:14 +msgid "`*syncd` Services" +msgstr "" + +#: src\2-2-services-intro.md:16 +msgid "" +"These services have names ending with `syncd`. They perform similar tasks: " +"synchronizing hardware states to Redis, usually into APPL_DB or STATE_DB." +msgstr "" + +#: src\2-2-services-intro.md:18 +msgid "" +"For example, `portsyncd` listens to netlink events and synchronizes the " +"status of all ports in the switch to STATE_DB, while `natsyncd` listens to " +"netlink events and synchronizes all NAT statuses in the switch to APPL_DB." +msgstr "" + +#: src\2-2-services-intro.md:20 +msgid "`*mgrd` Services" +msgstr "" + +#: src\2-2-services-intro.md:22 +msgid "" +"These services have names ending with `mgrd`. As the name suggests, these " +"are \"Manager\" services responsible for configuring various hardware, " +"opposite to `*syncd`. Their logic mainly consists of two parts:" +msgstr "" + +#: src\2-2-services-intro.md:24 +msgid "" +"**Configuration Deployment**: Responsible for reading configuration files " +"and listening to configuration and state changes in Redis (mainly CONFIG_DB, " +"APPL_DB, and STATE_DB), then pushing these changes to the switch hardware. " +"The method of pushing varies depending on the target, either by updating " +"APPL_DB and publishing update messages or directly calling Linux command " +"lines to modify the system. For example, `nbrmgr` listens to changes in " +"CONFIG_DB, APPL_DB, and STATE_DB for neighbors and modifies neighbors and " +"routes using netlink and command lines, while `intfmgr` not only calls " +"command lines but also updates some states to APPL_DB." +msgstr "" + +#: src\2-2-services-intro.md:25 +msgid "" +"**State Synchronization**: For services that need reconciliation, `*mgrd` " +"also listens to state changes in STATE_DB. If it finds that the hardware " +"state is inconsistent with the expected state, it will re-initiate the " +"configuration process to set the hardware state to the expected state. These " +"state changes in STATE_DB are usually pushed by `*syncd` services. For " +"example, `intfmgr` listens to port up/down status and MTU changes pushed by " +"`portsyncd` in STATE_DB. If it finds inconsistencies with the expected state " +"stored in its memory, it will re-deploy the configuration." +msgstr "" + +#: src\2-2-services-intro.md:27 +msgid "`orchagent` Service" +msgstr "" + +#: src\2-2-services-intro.md:29 +msgid "" +"This is the most important service in SONiC. Unlike other services that are " +"responsible for one or two specific functions, `orchagent`, as the " +"orchestrator of the switch ASIC state, checks all states from `*syncd` " +"services in the database, integrates them, and deploys them to ASIC_DB, " +"which is used to store the switch ASIC configuration. These states are " +"eventually received by `syncd`, which calls the SAI API through the SAI " +"implementation and ASIC SDK provided by various vendors to interact with the " +"ASIC, ultimately deploying the configuration to the switch hardware." +msgstr "" + +#: src\2-2-services-intro.md:31 +msgid "Feature Implementation Services" +msgstr "" + +#: src\2-2-services-intro.md:33 +msgid "" +"Some features are not implemented by the OS itself but by specific " +"processes, such as BGP or some external-facing interfaces. These services " +"often have names ending with `d`, indicating daemon, such as `bgpd`, " +"`lldpd`, `snmpd`, `teamd`, etc., or simply the name of the feature, such as " +"`fancontrol`." +msgstr "" + +#: src\2-2-services-intro.md:35 +msgid "`syncd` Service" +msgstr "" + +#: src\2-2-services-intro.md:37 +msgid "" +"The `syncd` service is downstream of `orchagent`. Although its name is " +"`syncd`, it shoulders the work of both `*mgrd` and `*syncd` for the ASIC." +msgstr "" + +#: src\2-2-services-intro.md:39 +msgid "" +"First, as `*mgrd`, it listens to state changes in ASIC_DB. Once detected, it " +"retrieves the new state and calls the SAI API to deploy the configuration to " +"the switch hardware." +msgstr "" + +#: src\2-2-services-intro.md:40 +msgid "" +"Then, as `*syncd`, if the ASIC sends any notifications to SONiC, it will " +"send these notifications to Redis as messages, allowing `orchagent` and " +"`*mgrd` services to obtain these changes and process them. The types of " +"these notifications can be found in [SwitchNotifications.h](https://github." +"com/sonic-net/sonic-sairedis/blob/master/syncd/SwitchNotifications.h)." +msgstr "" + +#: src\2-2-services-intro.md:42 +msgid "Control Flow Between Services" +msgstr "" + +#: src\2-2-services-intro.md:44 +msgid "" +"With service categories, we can now better understand the services in SONiC. " +"To get started, it is crucial to understand the control flow between " +"services. Based on the above categories, we can divide the main control " +"flows into two categories: configuration deployment and state " +"synchronization." +msgstr "" + +#: src\2-2-services-intro.md:46 +msgid "Configuration Deployment" +msgstr "" + +#: src\2-2-services-intro.md:48 +msgid "The configuration deployment process generally follows these steps:" +msgstr "" + +#: src\2-2-services-intro.md:50 +msgid "" +"**Modify Configuration**: Users can modify configurations through CLI or " +"REST API. These configurations are written to CONFIG_DB and send update " +"notifications through Redis. Alternatively, external programs can modify " +"configurations through specific interfaces, such as the BGP API. These " +"configurations are sent to `*mgrd` services through internal TCP sockets." +msgstr "" + +#: src\2-2-services-intro.md:51 +msgid "" +"**`*mgrd` Deploys Configuration**: Services listen to configuration changes " +"in CONFIG_DB and then push these configurations to the switch hardware. " +"There are two main scenarios (which can coexist):" +msgstr "" + +#: src\2-2-services-intro.md:52 +msgid "**Direct Deployment**:" +msgstr "" + +#: src\2-2-services-intro.md:53 +msgid "" +"`*mgrd` services directly call Linux command lines or modify system " +"configurations through netlink." +msgstr "" + +#: src\2-2-services-intro.md:54 +msgid "" +"`*syncd` services listen to system configuration changes through netlink or " +"other methods and push these changes to STATE_DB or APPL_DB." +msgstr "" + +#: src\2-2-services-intro.md:55 +msgid "" +"`*mgrd` services listen to configuration changes in STATE_DB or APPL_DB, " +"compare these configurations with those stored in their memory, and if " +"inconsistencies are found, they re-call command lines or netlink to modify " +"system configurations until they are consistent." +msgstr "" + +#: src\2-2-services-intro.md:56 +msgid "**Indirect Deployment**:" +msgstr "" + +#: src\2-2-services-intro.md:57 +msgid "" +"`*mgrd` pushes states to APPL_DB and sends update notifications through " +"Redis." +msgstr "" + +#: src\2-2-services-intro.md:58 +msgid "" +"`orchagent` listens to configuration changes, calculates the state the ASIC " +"should achieve based on all related states, and deploys it to ASIC_DB." +msgstr "" + +#: src\2-2-services-intro.md:59 +msgid "" +"`syncd` listens to changes in ASIC_DB and updates the switch ASIC " +"configuration through the unified SAI API interface by calling the ASIC " +"Driver." +msgstr "" + +#: src\2-2-services-intro.md:61 +msgid "" +"Configuration initialization is similar to configuration deployment but " +"involves reading configuration files when services start, which will not be " +"expanded here." +msgstr "" + +#: src\2-2-services-intro.md:63 +msgid "State Synchronization" +msgstr "" + +#: src\2-2-services-intro.md:65 +msgid "" +"If situations arise, such as a port failure or changes in the ASIC state, " +"state updates and synchronization are needed. The process generally follows " +"these steps:" +msgstr "" + +#: src\2-2-services-intro.md:67 +msgid "" +"**Detect State Changes**: These state changes mainly come from `*syncd` " +"services (netlink, etc.) and `syncd` services ([SAI Switch Notification]" +"(https://github.com/sonic-net/sonic-sairedis/blob/master/syncd/" +"SwitchNotifications.h)). After detecting changes, these services send them " +"to STATE_DB or APPL_DB." +msgstr "" + +#: src\2-2-services-intro.md:68 +msgid "" +"**Process State Changes**: `orchagent` and `*mgrd` services listen to these " +"changes, process them, and re-deploy new configurations to the system " +"through command lines and netlink or to ASIC_DB for `syncd` services to " +"update the ASIC again." +msgstr "" + +#: src\2-2-services-intro.md:70 +msgid "Specific Examples" +msgstr "" + +#: src\2-2-services-intro.md:72 +msgid "" +"The official SONiC documentation provides several typical examples of " +"control flow. Interested readers can refer to [SONiC Subsystem Interactions]" +"(https://github.com/sonic-net/SONiC/wiki/Architecture#sonic-subsystems-" +"interactions). In the workflow chapter, we will also expand on some very " +"common workflows." +msgstr "" + +#: src\2-3-key-containers.md:1 +msgid "Key containers" +msgstr "" + +#: src\2-3-key-containers.md:3 +msgid "" +"One of the most distinctive features of SONiC's design is containerization." +msgstr "" + +#: src\2-3-key-containers.md:5 +msgid "" +"From the design diagram of SONiC, we can see that all services in SONiC run " +"in the form of containers. After logging into the switch, we can use the " +"`docker ps` command to see all containers that are currently running:" +msgstr "" + +#: src\2-3-key-containers.md:10 src\2-3-key-containers.md:11 +#: src\2-3-key-containers.md:16 src\2-3-key-containers.md:17 +#: src\2-3-key-containers.md:19 +msgid "\"/usr/local/bin/supe\"" +msgstr "" + +#: src\2-3-key-containers.md:12 +msgid "\"/usr/bin/docker-lld\"" +msgstr "" + +#: src\2-3-key-containers.md:13 src\2-3-key-containers.md:15 +msgid "\"/usr/bin/docker_ini\"" +msgstr "" + +#: src\2-3-key-containers.md:14 src\2-3-key-containers.md:18 +msgid "\"/usr/bin/docker-ini\"" +msgstr "" + +#: src\2-3-key-containers.md:20 +msgid "\"/usr/local/bin/dock\"" +msgstr "" + +#: src\2-3-key-containers.md:23 +msgid "Here we will briefly introduce these containers." +msgstr "" + +#: src\2-3-key-containers.md:25 +msgid "Database Container: `database`" +msgstr "" + +#: src\2-3-key-containers.md:27 +msgid "" +"This container contains the central database - Redis, which we have " +"mentioned multiple times. It stores all the configuration and status of the " +"switch, and SONiC also uses it to provide the underlying communication " +"mechanism to various services." +msgstr "" + +#: src\2-3-key-containers.md:29 +msgid "" +"By entering this container via Docker, we can see the running Redis process:" +msgstr "" + +#: src\2-3-key-containers.md:44 +msgid "How does other container access this Redis database?" +msgstr "" + +#: src\2-3-key-containers.md:46 +msgid "" +"The answer is through Unix Socket. We can see this Unix Socket in the " +"database container, which is mapped from the `/var/run/redis` directory on " +"the switch." +msgstr "" + +#: src\2-3-key-containers.md:49 +msgid "# In database container" +msgstr "" + +#: src\2-3-key-containers.md:52 +msgid "# On host" +msgstr "" + +#: src\2-3-key-containers.md:58 +msgid "" +"Then SONiC maps `/var/run/redis` folder into all relavent containers, " +"allowing other services to access the central database. For example, the " +"swss container:" +msgstr "" + +#: src\2-3-key-containers.md:63 +msgid "\"HostConfig\"" +msgstr "" + +#: src\2-3-key-containers.md:64 +msgid "\"Binds\"" +msgstr "" + +#: src\2-3-key-containers.md:66 +msgid "\"/var/run/redis:/var/run/redis:rw\"" +msgstr "" + +#: src\2-3-key-containers.md:72 +msgid "SWitch State Service Container: `swss`" +msgstr "" + +#: src\2-3-key-containers.md:74 +msgid "" +"This container can be considered the most critical container in SONiC. **It " +"is the brain of SONiC**, running numerous `*syncd` and `*mgrd` services to " +"manage various configurations of the switch, such as Port, neighbor, ARP, " +"VLAN, Tunnel, etc. Additionally, it runs the `orchagent`, which handles many " +"configurations and state changes related to the ASIC." +msgstr "" + +#: src\2-3-key-containers.md:76 +msgid "" +"We have already discussed the general functions and processes of these " +"services, so we won't repeat them here. We can use the `ps` command to see " +"the services running in this container:" +msgstr "" + +#: src\2-3-key-containers.md:101 +msgid "ASIC Management Container: `syncd`" +msgstr "" + +#: src\2-3-key-containers.md:103 +msgid "" +"This container is mainly used for managing the ASIC on the switch, running " +"the `syncd` service. The SAI (Switch Abstraction Interface) implementation " +"and ASIC Driver provided by various vendors are placed in this container. It " +"allows SONiC to support multiple different ASICs without modifying the upper-" +"layer services. In other words, without this container, SONiC would be a " +"brain in a jar, capable of only thinking but nothing else." +msgstr "" + +#: src\2-3-key-containers.md:105 +msgid "" +"We don't have too many services running in the syncd container, mainly " +"syncd. We can check them using the `ps` command, and in the `/usr/lib` " +"directory, we can find the enormous SAI file compiled to support the ASIC:" +msgstr "" + +#: src\2-3-key-containers.md:125 +msgid "Feature Containers" +msgstr "" + +#: src\2-3-key-containers.md:127 +msgid "" +"There are many containers in SONiC designed to implement specific features. " +"These containers usually have special external interfaces (non-SONiC CLI and " +"REST API) and implementations (non-OS or ASIC), such as:" +msgstr "" + +#: src\2-3-key-containers.md:129 +msgid "" +"`bgp`: Container for implementing the BGP and other routing protocol (Border " +"Gateway Protocol)" +msgstr "" + +#: src\2-3-key-containers.md:130 +msgid "" +"`lldp`: Container for implementing the LLDP protocol (Link Layer Discovery " +"Protocol)" +msgstr "" + +#: src\2-3-key-containers.md:131 +msgid "`teamd`: Container for implementing Link Aggregation" +msgstr "" + +#: src\2-3-key-containers.md:132 +msgid "" +"`snmp`: Container for implementing the SNMP protocol (Simple Network " +"Management Protocol)" +msgstr "" + +#: src\2-3-key-containers.md:134 +msgid "" +"Similar to SWSS, these containers also run the services we mentioned earlier " +"to adapt to SONiC's architecture:" +msgstr "" + +#: src\2-3-key-containers.md:136 +msgid "" +"Configuration management and deployment (similar to `*mgrd`): `lldpmgrd`, " +"`zebra` (bgp)" +msgstr "" + +#: src\2-3-key-containers.md:137 +msgid "" +"State synchronization (similar to `*syncd`): `lldpsyncd`, `fpmsyncd` (bgp), " +"`teamsyncd`" +msgstr "" + +#: src\2-3-key-containers.md:138 +msgid "" +"Service implementation or external interface (`*d`): `lldpd`, `bgpd`, " +"`teamd`, `snmpd`" +msgstr "" + +#: src\2-3-key-containers.md:140 +msgid "Management Service Container: `mgmt-framework`" +msgstr "" + +#: src\2-3-key-containers.md:142 +msgid "" +"In previous chapters, we have seen how to use SONiC's CLI to configure some " +"aspects of the switch. However, in a production environment, manually " +"logging into the switch and using the CLI to configure all switches is " +"unrealistic. Therefore, SONiC provides a REST API to solve this problem. " +"This REST API is implemented in the `mgmt-framework` container. We can check " +"it using the `ps` command:" +msgstr "" + +#: src\2-3-key-containers.md:153 +msgid "" +"In addition to the REST API, SONiC can also be managed through other methods " +"such as gNMI, all of which run in this container. The overall architecture " +"is shown in the figure below [\\[2\\]](https://github.com/sonic-net/SONiC/" +"blob/master/doc/mgmt/Management%20Framework.md):" +msgstr "" + +#: src\2-3-key-containers.md:155 +msgid "![](assets/chapter-2/sonic-mgmt-framework.jpg)" +msgstr "" + +#: src\2-3-key-containers.md:157 +msgid "" +"Here we can also see that the CLI we use can be implemented by calling this " +"REST API at the bottom layer." +msgstr "" + +#: src\2-3-key-containers.md:159 +msgid "Platform Monitor Container: `pmon`" +msgstr "" + +#: src\2-3-key-containers.md:161 +msgid "" +"The services in this container are mainly used to monitor the basic hardware " +"status of the switch, such as temperature, power supply, fans, SFP events, " +"etc. Similarly, we can use the `ps` command to check the services running in " +"this container:" +msgstr "" + +#: src\2-3-key-containers.md:179 +msgid "" +"The purpose of most of these services can be told from their names. The only " +"one that is not so obvious is `xcvrd`, where xcv stands for transceiver. It " +"is used to monitor the optical modules of the switch, such as SFP, QSFP, etc." +msgstr "" + +#: src\2-3-key-containers.md:184 src\3-1-code-repos.md:137 +msgid "" +"[SONiC Management Framework](https://github.com/sonic-net/SONiC/blob/master/" +"doc/mgmt/Management%20Framework.md)" +msgstr "" + +#: src\2-4-sai-intro.md:3 +msgid "" +"SAI (Switch Abstraction Interface) is the cornerstone of SONiC, while " +"enables it to support multiple hardware platforms. In [this SAI API document]" +"(https://github.com/opencomputeproject/SAI/wiki/SAI-APIs), we can see all " +"the interfaces it defines." +msgstr "" + +#: src\2-4-sai-intro.md:5 +msgid "" +"[In the core container section, we mentioned that SAI runs in the `syncd` " +"container](./2-3-key-containers.html). However, unlike other components, it " +"is not a service but a set of common header files and dynamic link libraries " +"(.so). All abstract interfaces are defined as C language header files in the " +"[OCP SAI repository](https://github.com/opencomputeproject/SAI), and the " +"hardware vendors provides the .so files that implement the SAI interfaces." +msgstr "" + +#: src\2-4-sai-intro.md:7 +msgid "SAI Interface" +msgstr "" + +#: src\2-4-sai-intro.md:9 +msgid "" +"To make things more intuitive, let's take a small portion of the code to " +"show how SAI interfaces look like and how it works, as follows:" +msgstr "" + +#: src\2-4-sai-intro.md:12 +msgid "// File: meta/saimetadata.h" +msgstr "" + +#: src\2-4-sai-intro.md:18 +msgid "// File: inc/saiswitch.h" +msgstr "" + +#: src\2-4-sai-intro.md:28 +msgid "// File: inc/saiport.h" +msgstr "" + +#: src\2-4-sai-intro.md:40 +msgid "" +"The `sai_apis_t` structure is a collection of interfaces for all SAI " +"modules, with each member being a pointer to a specific module's interface " +"list. For example, `sai_switch_api_t` defines all the interfaces for the SAI " +"Switch module, and its definition can be found in `inc/saiswitch.h`. " +"Similarly, the interface definitions for the SAI Port module can be found in " +"`inc/saiport.h`." +msgstr "" + +#: src\2-4-sai-intro.md:42 +msgid "SAI Initialization" +msgstr "" + +#: src\2-4-sai-intro.md:44 +msgid "" +"SAI initialization is essentially about obtaining these function pointers so " +"that we can operate the ASIC through the SAI interfaces." +msgstr "" + +#: src\2-4-sai-intro.md:46 +msgid "" +"The main functions involved in SAI initialization are defined in `inc/sai.h`:" +msgstr "" + +#: src\2-4-sai-intro.md:48 +msgid "`sai_api_initialize`: Initialize SAI" +msgstr "" + +#: src\2-4-sai-intro.md:49 +msgid "" +"`sai_api_query`: Pass in the type of SAI API to get the corresponding " +"interface list" +msgstr "" + +#: src\2-4-sai-intro.md:51 +msgid "" +"Although most vendors' SAI implementations are closed-source, Mellanox has " +"open-sourced its SAI implementation, allowing us to gain a deeper " +"understanding of how SAI works." +msgstr "" + +#: src\2-4-sai-intro.md:53 +msgid "" +"For example, the `sai_api_initialize` function simply sets two global " +"variables and returns `SAI_STATUS_SUCCESS`:" +msgstr "" + +#: src\2-4-sai-intro.md:56 src\2-4-sai-intro.md:73 +msgid "" +"// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/" +"src/mlnx_sai_interfacequery.c" +msgstr "" + +#: src\2-4-sai-intro.md:62 +msgid "// Validate parameters here (code omitted)" +msgstr "" + +#: src\2-4-sai-intro.md:70 +msgid "" +"After initialization, we can use the `sai_api_query` function to query the " +"corresponding interface list by passing in the type of API, where each " +"interface list is actually a global variable:" +msgstr "" + +#: src\2-4-sai-intro.md:83 +msgid "" +"// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/" +"src/mlnx_sai_interfacequery_eth.c" +msgstr "" + +#: src\2-4-sai-intro.md:103 +msgid "" +"// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/" +"src/mlnx_sai_bridge.c" +msgstr "" + +#: src\2-4-sai-intro.md:113 src\5-1-syncd-and-sai.md:395 +#: src\5-1-syncd-and-sai.md:592 +msgid "" +"// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/" +"src/mlnx_sai_switch.c" +msgstr "" + +#: src\2-4-sai-intro.md:124 +msgid "Using SAI" +msgstr "" + +#: src\2-4-sai-intro.md:126 +msgid "" +"In the `syncd` container, SONiC starts the `syncd` service at startup, which " +"loads the SAI component present in the system. This component is provided by " +"various vendors, who implement the SAI interfaces based on their hardware " +"platforms, allowing SONiC to use a unified upper-layer logic to control " +"various hardware platforms." +msgstr "" + +#: src\2-4-sai-intro.md:128 +msgid "We can verify this using the `ps`, `ls`, and `nm` commands:" +msgstr "" + +#: src\2-4-sai-intro.md:131 +msgid "# Enter into syncd container" +msgstr "" + +#: src\2-4-sai-intro.md:133 +msgid "# List all processes. We will only see syncd process here." +msgstr "" + +#: src\2-4-sai-intro.md:141 +msgid "# Find all libsai*.so.* files." +msgstr "" + +#: src\2-4-sai-intro.md:152 +msgid "" +"# Copy the file out of switch and check libsai.so on your own dev machine." +msgstr "" + +#: src\2-4-sai-intro.md:153 +msgid "# We will see the most important SAI export functions here." +msgstr "" + +#: src\2-4-sai-intro.md:169 +msgid "" +"[Github: sonic-net/sonic-sairedis](https://github.com/sonic-net/sonic-" +"sairedis/)" +msgstr "" + +#: src\2-4-sai-intro.md:170 +msgid "" +"[Github: opencomputeproject/SAI](https://github.com/opencomputeproject/SAI)" +msgstr "" + +#: src\2-4-sai-intro.md:171 +msgid "" +"[Arista 7050QX Series 10/40G Data Center Switches Data Sheet](https://www." +"arista.com/assets/data/pdf/Datasheets/7050QX-32_32S_Datasheet_S.pdf)" +msgstr "" + +#: src\2-4-sai-intro.md:172 src\5-1-syncd-and-sai.md:826 +msgid "" +"[Github repo: Nvidia (Mellanox) SAI implementation](https://github.com/" +"Mellanox/SAI-Implementation/tree/master)" +msgstr "" + +#: src\3-1-code-repos.md:3 +msgid "" +"The code of SONiC is hosted on the [sonic-net account on GitHub](https://" +"github.com/sonic-net), with over 30 repositories. It can be a bit " +"overwhelming at first, but don't worry, we'll go through them together here." +msgstr "" + +#: src\3-1-code-repos.md:5 +msgid "Core Repositories" +msgstr "" + +#: src\3-1-code-repos.md:7 +msgid "" +"First, let's look at the two most important core repositories in SONiC: " +"SONiC and sonic-buildimage." +msgstr "" + +#: src\3-1-code-repos.md:9 +msgid "Landing Repository: `SONiC`" +msgstr "" + +#: src\3-1-code-repos.md:11 +msgid "" +msgstr "" + +#: src\3-1-code-repos.md:13 +msgid "" +"This repository contains the SONiC Landing Page and a large number of " +"documents, Wiki, tutorials, slides from past talks, and so on. This " +"repository is the most commonly used by newcomers, but note that there is " +"**no code** in this repository, only documentation." +msgstr "" + +#: src\3-1-code-repos.md:15 +msgid "Image Build Repository: `sonic-buildimage`" +msgstr "" + +#: src\3-1-code-repos.md:17 +msgid "" +msgstr "" + +#: src\3-1-code-repos.md:19 +msgid "" +"Why is this build repository so important to us? Unlike other projects, " +"**the build repository of SONiC is actually its main repository**! This " +"repository contains:" +msgstr "" + +#: src\3-1-code-repos.md:21 +msgid "" +"All the feature implementation repositories, in the form of git submodules " +"(under the `src` directory)." +msgstr "" + +#: src\3-1-code-repos.md:22 +msgid "" +"Support files for each device from switch manufactures (under the `device` " +"directory), such as device configuration files for each model of switch, " +"scripts, and so on. For example, my switch is an Arista 7050QX-32S, so I can " +"find its support files in the `device/arista/x86_64-arista_7050_qx32s` " +"directory." +msgstr "" + +#: src\3-1-code-repos.md:23 +msgid "" +"Support files provided by all ASIC chip manufacturers (in the `platform` " +"directory), such as drivers, BSP, and low-level support scripts for each " +"platform. Here we can see support files from almost all major chip " +"manufacturers, such as Broadcom, Mellanox, etc., as well as implementations " +"for simulated software switches, such as vs and p4. But for protecting IPs " +"from each vendor, most of the time, the repo only contains the Makefiles " +"that downloads these things for build purpose." +msgstr "" + +#: src\3-1-code-repos.md:24 +msgid "" +"Dockerfiles for building all container images used by SONiC (in the " +"`dockers` directory)." +msgstr "" + +#: src\3-1-code-repos.md:25 +msgid "" +"Various general configuration files and scripts (in the `files` directory)." +msgstr "" + +#: src\3-1-code-repos.md:26 +msgid "" +"Dockerfiles for the build containers used for building (in the `sonic-slave-" +"*` directories)." +msgstr "" + +#: src\3-1-code-repos.md:27 +msgid "And more..." +msgstr "" + +#: src\3-1-code-repos.md:29 +msgid "" +"Because this repository brings all related resources together, we basically " +"only need to checkout this single repository to get all SONiC's code. It " +"makes searching and navigating the code much more convenient than checking " +"out the repos one by one!" +msgstr "" + +#: src\3-1-code-repos.md:31 +msgid "Feature Repositories" +msgstr "" + +#: src\3-1-code-repos.md:33 +msgid "" +"In addition to the core repositories, SONiC also has many feature " +"repositories, which contain the implementations of various containers and " +"services. These repositories are imported as submodules in the `src` " +"directory of sonic-buildimage. If we would like to modify and contribute to " +"SONiC, we also need to understand them." +msgstr "" + +#: src\3-1-code-repos.md:35 +msgid "SWSS (Switch State Service) Related Repositories" +msgstr "" + +#: src\3-1-code-repos.md:37 +msgid "" +"As introduced in the previous section, the SWSS container is the brain of " +"SONiC. In SONiC, it consists of two repositories: [sonic-swss-common]" +"(https://github.com/sonic-net/sonic-swss-common) and [sonic-swss](https://" +"github.com/sonic-net/sonic-swss)." +msgstr "" + +#: src\3-1-code-repos.md:39 +msgid "SWSS Common Library: `sonic-swss-common`" +msgstr "" + +#: src\3-1-code-repos.md:41 +msgid "" +"The first one is the common library: `sonic-swss-common` ()." +msgstr "" + +#: src\3-1-code-repos.md:43 +msgid "" +"This repository contains all the common functionalities needed by `*mgrd` " +"and `*syncd` services, such as logger, JSON, netlink encapsulation, Redis " +"operations, and various inter-service communication mechanisms based on " +"Redis. Although it was initially intended for swss services, its extensive " +"functionalities have led to its use in many other repositories, such as " +"`swss-sairedis` and `swss-restapi`." +msgstr "" + +#: src\3-1-code-repos.md:45 +msgid "Main SWSS Repository: `sonic-swss`" +msgstr "" + +#: src\3-1-code-repos.md:47 +msgid "" +"Next is the main SWSS repository: `sonic-swss` ()." +msgstr "" + +#: src\3-1-code-repos.md:49 +msgid "In this repository, we can find:" +msgstr "" + +#: src\3-1-code-repos.md:51 +msgid "" +"Most of the `*mgrd` and `*syncd` services: `orchagent`, `portsyncd/portmgrd/" +"intfmgrd`, `neighsyncd/nbrmgrd`, `natsyncd/natmgrd`, `buffermgrd`, " +"`coppmgrd`, `macsecmgrd`, `sflowmgrd`, `tunnelmgrd`, `vlanmgrd`, `vrfmgrd`, " +"`vxlanmgrd`, and more." +msgstr "" + +#: src\3-1-code-repos.md:52 +msgid "" +"`swssconfig`: Located in the `swssconfig` directory, used to restore FDB and " +"ARP tables during fast reboot." +msgstr "" + +#: src\3-1-code-repos.md:53 +msgid "" +"`swssplayer`: Also in the `swssconfig` directory, used to record all " +"configuration operations performed through SWSS, allowing us to replay them " +"for troubleshooting and debugging." +msgstr "" + +#: src\3-1-code-repos.md:54 +msgid "" +"Even some services not in the SWSS container, such as `fpmsyncd` (BGP " +"container) and `teamsyncd/teammgrd` (teamd container)." +msgstr "" + +#: src\3-1-code-repos.md:56 +msgid "SAI/Platform Related Repositories" +msgstr "" + +#: src\3-1-code-repos.md:58 +msgid "" +"Next is the Switch Abstraction Interface (SAI). [Although SAI was proposed " +"by Microsoft and released version 0.1 in March 2015](https://www.opencompute." +"org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf), [by " +"September 2015, before SONiC had even released its first version, it was " +"already accepted by OCP as a public standard](https://azure.microsoft.com/en-" +"us/blog/switch-abstraction-interface-sai-officially-accepted-by-the-open-" +"compute-project-ocp/). This shows how quickly SONiC and SAI was getting " +"supports from the community and vendors." +msgstr "" + +#: src\3-1-code-repos.md:60 +msgid "Overall, the SAI code is divided into two parts:" +msgstr "" + +#: src\3-1-code-repos.md:62 +msgid "" +"OpenComputeProject/SAI under OCP: . This repository contains all the code related to the SAI standard, " +"including SAI header files, behavior models, test cases, documentation, and " +"more." +msgstr "" + +#: src\3-1-code-repos.md:63 +msgid "" +"sonic-sairedis under SONiC: . " +"This repository contains all the code used by SONiC to interact with SAI, " +"such as the syncd service and various debugging tools like `saiplayer` for " +"replay and `saidump` for exporting ASIC states." +msgstr "" + +#: src\3-1-code-repos.md:65 +msgid "" +"In addition to these two repositories, there is another platform-related " +"repository, such as [sonic-platform-vpp](https://github.com/sonic-net/sonic-" +"platform-vpp), which uses SAI interfaces to implement data plane " +"functionalities through VPP, essentially acting as a high-performance soft " +"switch. I personally feel it might be merged into the buildimage repository " +"in the future as part of the platform directory." +msgstr "" + +#: src\3-1-code-repos.md:67 +msgid "Management Service (mgmt) Related Repositories" +msgstr "" + +#: src\3-1-code-repos.md:69 +msgid "" +"Next are all the repositories related to [management services](https://" +"github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%20Framework.md) " +"in SONiC:" +msgstr "" + +#: src\3-1-code-repos.md:71 src\3-1-code-repos.md:87 +msgid "Name" +msgstr "" + +#: src\3-1-code-repos.md:71 src\3-1-code-repos.md:87 src\3-1-code-repos.md:96 +msgid "Description" +msgstr "" + +#: src\3-1-code-repos.md:73 +msgid "[sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common)" +msgstr "" + +#: src\3-1-code-repos.md:73 +msgid "" +"Base library for management services, containing `translib`, YANG model-" +"related code" +msgstr "" + +#: src\3-1-code-repos.md:74 +msgid "" +"[sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework)" +msgstr "" + +#: src\3-1-code-repos.md:74 +msgid "" +"REST Server implemented in Go, acting as the REST Gateway in the " +"architecture diagram below (process name: `rest_server`)" +msgstr "" + +#: src\3-1-code-repos.md:75 +msgid "[sonic-gnmi](https://github.com/sonic-net/sonic-gnmi)" +msgstr "" + +#: src\3-1-code-repos.md:75 +msgid "" +"Similar to sonic-mgmt-framework, this is the gNMI (gRPC Network Management " +"Interface) Server based on gRPC in the architecture diagram below" +msgstr "" + +#: src\3-1-code-repos.md:76 +msgid "[sonic-restapi](https://github.com/sonic-net/sonic-restapi)" +msgstr "" + +#: src\3-1-code-repos.md:76 +msgid "" +"Another configuration management REST Server implemented in Go. Unlike mgmt-" +"framework, this server directly operates on CONFIG_DB upon receiving " +"messages, instead of using translib (not shown in the diagram, process name: " +"`go-server-server`)" +msgstr "" + +#: src\3-1-code-repos.md:77 +msgid "[sonic-mgmt](https://github.com/sonic-net/sonic-mgmt)" +msgstr "" + +#: src\3-1-code-repos.md:77 +msgid "" +"Various automation scripts (in the `ansible` directory), tests (in the " +"`tests` directory), test bed setup and test reporting (in the " +"`test_reporting` directory), and more" +msgstr "" + +#: src\3-1-code-repos.md:79 +msgid "" +"Here is the architecture diagram of SONiC management services for reference " +"[\\[4\\]](https://github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%" +"20Framework.md):" +msgstr "" + +#: src\3-1-code-repos.md:81 +msgid "![](assets/chapter-3/sonic-mgmt-framework.jpg)" +msgstr "" + +#: src\3-1-code-repos.md:83 +msgid "" +"Platform Monitoring Related Repositories: `sonic-platform-common` and `sonic-" +"platform-daemons`" +msgstr "" + +#: src\3-1-code-repos.md:85 +msgid "" +"The following two repositories are related to platform monitoring and " +"control, such as LEDs, fans, power supplies, thermal control, and more:" +msgstr "" + +#: src\3-1-code-repos.md:89 +msgid "" +"[sonic-platform-common](https://github.com/sonic-net/sonic-platform-common)" +msgstr "" + +#: src\3-1-code-repos.md:89 +msgid "" +"A base package provided to manufacturers, defining interfaces for accessing " +"fans, LEDs, power management, thermal control, and other modules, all " +"implemented in Python" +msgstr "" + +#: src\3-1-code-repos.md:90 +msgid "" +"[sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-daemons)" +msgstr "" + +#: src\3-1-code-repos.md:90 +msgid "" +"Contains various monitoring services running in the pmon container in SONiC, " +"such as `chassisd`, `ledd`, `pcied`, `psud`, `syseepromd`, `thermalctld`, " +"`xcvrd`, `ycabled`. All these services are implemented in Python, and used " +"for monitoring and controlling the platform modules, by calling the " +"interface implementations provided by manufacturers." +msgstr "" + +#: src\3-1-code-repos.md:92 +msgid "Other Feature Repositories" +msgstr "" + +#: src\3-1-code-repos.md:94 +msgid "" +"In addition to the repositories above, SONiC has many repositories " +"implementing various functionalities. They can be services or libraries " +"described in the table below:" +msgstr "" + +#: src\3-1-code-repos.md:96 +msgid "Repository" +msgstr "" + +#: src\3-1-code-repos.md:98 +msgid "[sonic-frr](https://github.com/sonic-net/sonic-frr)" +msgstr "" + +#: src\3-1-code-repos.md:98 +msgid "" +"FRRouting, implementing various routing protocols, so in this repository, we " +"can find implementations of routing-related processes like `bgpd`, `zebra`, " +"etc." +msgstr "" + +#: src\3-1-code-repos.md:99 +msgid "[sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent)" +msgstr "" + +#: src\3-1-code-repos.md:99 +msgid "" +"Implementation of [AgentX](https://www.ietf.org/rfc/rfc2741.txt) SNMP " +"subagent (`sonic_ax_impl`), used to connect to the Redis database and " +"provide various information needed by snmpd. It can be understood as the " +"control plane of snmpd, while snmpd is the data plane, responding to " +"external SNMP requests" +msgstr "" + +#: src\3-1-code-repos.md:100 +msgid "[sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd)" +msgstr "" + +#: src\3-1-code-repos.md:100 +msgid "" +"Dual ToR support, checking the status of links and controlling ToR " +"connections" +msgstr "" + +#: src\3-1-code-repos.md:101 +msgid "[sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay)" +msgstr "" + +#: src\3-1-code-repos.md:101 +msgid "DHCP relay agent" +msgstr "" + +#: src\3-1-code-repos.md:102 +msgid "[sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon)" +msgstr "" + +#: src\3-1-code-repos.md:102 +msgid "Monitors the status of DHCP and reports to the central Redis database" +msgstr "" + +#: src\3-1-code-repos.md:103 +msgid "[sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd)" +msgstr "" + +#: src\3-1-code-repos.md:103 +msgid "" +"`lldp_syncd` service, but the repository name is not well-chosen, called " +"dbsyncd" +msgstr "" + +#: src\3-1-code-repos.md:104 +msgid "[sonic-pins](https://github.com/sonic-net/sonic-pins)" +msgstr "" + +#: src\3-1-code-repos.md:104 +msgid "" +"Google's P4-based network stack support (P4 Integrated Network Stack, PINS). " +"More information can be found on the [PINS website](https://opennetworking." +"org/pins/)" +msgstr "" + +#: src\3-1-code-repos.md:105 +msgid "[sonic-stp](https://github.com/sonic-net/sonic-stp)" +msgstr "" + +#: src\3-1-code-repos.md:105 +msgid "STP (Spanning Tree Protocol) support" +msgstr "" + +#: src\3-1-code-repos.md:106 +msgid "[sonic-ztp](https://github.com/sonic-net/sonic-ztp)" +msgstr "" + +#: src\3-1-code-repos.md:106 +msgid "" +"[Zero Touch Provisioning](https://github.com/sonic-net/SONiC/blob/master/doc/" +"ztp/ztp.md)" +msgstr "" + +#: src\3-1-code-repos.md:107 +msgid "[DASH](https://github.com/sonic-net/DASH)" +msgstr "" + +#: src\3-1-code-repos.md:107 +msgid "" +"[Disaggregated API for SONiC Hosts](https://github.com/sonic-net/DASH/blob/" +"main/documentation/general/dash-high-level-design.md)" +msgstr "" + +#: src\3-1-code-repos.md:108 +msgid "[sonic-host-services](https://github.com/sonic-net/sonic-host-services)" +msgstr "" + +#: src\3-1-code-repos.md:108 +msgid "" +"Services running on the host, providing support to services in containers " +"via dbus, such as saving and reloading configurations, saving dumps, etc., " +"similar to a host broker" +msgstr "" + +#: src\3-1-code-repos.md:109 +msgid "[sonic-fips](https://github.com/sonic-net/sonic-fips)" +msgstr "" + +#: src\3-1-code-repos.md:109 +msgid "" +"FIPS (Federal Information Processing Standards) support, containing various " +"patch files added to support FIPS standards" +msgstr "" + +#: src\3-1-code-repos.md:110 +msgid "" +"[sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant)" +msgstr "" + +#: src\3-1-code-repos.md:110 +msgid "Support for various wireless network protocols" +msgstr "" + +#: src\3-1-code-repos.md:112 +msgid "Tooling Repository: `sonic-utilities`" +msgstr "" + +#: src\3-1-code-repos.md:114 +msgid "" +msgstr "" + +#: src\3-1-code-repos.md:116 +msgid "This repository contains all the command-line tools for SONiC:" +msgstr "" + +#: src\3-1-code-repos.md:118 +msgid "" +"`config`, `show`, `clear` directories: These are the implementations of the " +"three main SONiC CLI commands. Note that the specific command " +"implementations may not necessarily be in these directories; many commands " +"are implemented by calling other commands, with these directories providing " +"an entry point." +msgstr "" + +#: src\3-1-code-repos.md:119 +msgid "" +"`scripts`, `sfputil`, `psuutil`, `pcieutil`, `fwutil`, `ssdutil`, " +"`acl_loader` directories: These directories provide many tool commands, but " +"most are not directly used by users; instead, they are called by commands in " +"the `config`, `show`, and `clear` directories. For example, the `show " +"platform fan` command is implemented by calling the `fanshow` command in the " +"`scripts` directory." +msgstr "" + +#: src\3-1-code-repos.md:120 +msgid "" +"`utilities_common`, `flow_counter_util`, `syslog_util` directories: Similar " +"to the above, but they provide base classes that can be directly imported " +"and called in Python." +msgstr "" + +#: src\3-1-code-repos.md:121 +msgid "" +"There are also many other commands: `fdbutil`, `pddf_fanutil`, " +"`pddf_ledutil`, `pddf_psuutil`, `pddf_thermalutil`, etc., used to view and " +"control the status of various modules." +msgstr "" + +#: src\3-1-code-repos.md:122 +msgid "" +"`connect` and `consutil` directories: Commands in these directories are used " +"to connect to and manage other SONiC devices." +msgstr "" + +#: src\3-1-code-repos.md:123 +msgid "" +"`crm` directory: Used to configure and view [CRM (Critical Resource " +"Monitoring)](https://github.com/sonic-net/SONiC/wiki/Critical-Resource-" +"Monitoring-High-Level-Design) in SONiC. This command is not included in the " +"`config` and `show` commands, so users can use it directly." +msgstr "" + +#: src\3-1-code-repos.md:124 +msgid "" +"`pfc` directory: Used to configure and view PFC (Priority-based Flow " +"Control) in SONiC." +msgstr "" + +#: src\3-1-code-repos.md:125 +msgid "" +"`pfcwd` directory: Used to configure and view [PFC Watch Dog](https://github." +"com/sonic-net/SONiC/wiki/PFC-Watchdog) in SONiC, such as starting, stopping, " +"modifying polling intervals, and more." +msgstr "" + +#: src\3-1-code-repos.md:127 +msgid "Kernel Patches: sonic-linux-kernel" +msgstr "" + +#: src\3-1-code-repos.md:129 +msgid "" +msgstr "" + +#: src\3-1-code-repos.md:131 +msgid "" +"Although SONiC is based on Debian, the default Debian kernel may not " +"necessarily run SONiC, such as certain modules not being enabled by default " +"or issues with older drivers. Therefore, SONiC requires some modifications " +"to the Linux kernel. This repository is used to store all the kernel patches." +msgstr "" + +#: src\3-1-code-repos.md:136 +msgid "" +"[SONiC Source Repositories](https://github.com/sonic-net/SONiC/blob/master/" +"sourcecode.md)" +msgstr "" + +#: src\3-1-code-repos.md:139 src\3-1-code-repos.md:141 +msgid "" +"[SONiC Critical Resource Monitoring](https://github.com/sonic-net/SONiC/wiki/" +"Critical-Resource-Monitoring-High-Level-Design)" +msgstr "" + +#: src\3-1-code-repos.md:140 +msgid "" +"[SONiC Zero Touch Provisioning](https://github.com/sonic-net/SONiC/blob/" +"master/doc/ztp/ztp.md)" +msgstr "" + +#: src\3-1-code-repos.md:142 +msgid "[SONiC P4 Integrated Network Stack](https://opennetworking.org/pins/)" +msgstr "" + +#: src\3-1-code-repos.md:143 +msgid "" +"[SONiC Disaggregated API for Switch Hosts](https://github.com/sonic-net/DASH/" +"blob/main/documentation/general/dash-high-level-design.md)" +msgstr "" + +#: src\3-1-code-repos.md:144 +msgid "" +"[SAI spec for OCP](https://www.opencompute.org/documents/switch-abstraction-" +"interface-ocp-specification-v0-2-pdf)" +msgstr "" + +#: src\3-1-code-repos.md:145 +msgid "[PFC Watchdog](https://github.com/sonic-net/SONiC/wiki/PFC-Watchdog)" +msgstr "" + +#: src\3-2-build.md:3 +msgid "" +"To ensure that we can successfully build SONiC on any platform as well, " +"SONiC leverages docker to build its build environment. It installs all tools " +"and dependencies in a docker container of the corresponding Debian version, " +"mounts its code into the container, and then start the build process inside " +"the container. This way, we can easily build SONiC on any platform without " +"worrying about dependency mismatches. For example, some packages in Debian " +"have higher versions than in Ubuntu, which might cause unexpected errors " +"during build time or runtime." +msgstr "" + +#: src\3-2-build.md:5 +msgid "Setup the Build Environment" +msgstr "" + +#: src\3-2-build.md:7 +msgid "Install Docker" +msgstr "" + +#: src\3-2-build.md:9 +msgid "" +"To support the containerized build environment, the first step is to ensure " +"that Docker is installed on our machine." +msgstr "" + +#: src\3-2-build.md:11 +msgid "" +"You can refer to the [official documentation](https://docs.docker.com/engine/" +"install/) for Docker installation methods. Here, we briefly introduce the " +"installation method for Ubuntu." +msgstr "" + +#: src\3-2-build.md:13 +msgid "" +"First, we need to add docker's source and certificate to the apt source list:" +msgstr "" + +#: src\3-2-build.md:24 +msgid "\"deb [arch=\"" +msgstr "" + +#: src\3-2-build.md:24 +msgid "" +"\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/" +"ubuntu \\" +msgstr "" + +#: src\3-2-build.md:25 +msgid "\"$VERSION_CODENAME\"" +msgstr "" + +#: src\3-2-build.md:25 +msgid "\" stable\"" +msgstr "" + +#: src\3-2-build.md:29 +msgid "Then, we can quickly install docker via apt:" +msgstr "" + +#: src\3-2-build.md:36 +msgid "" +"After installing docker, we need to add the current user to the docker user " +"group and **log out and log back in**. This way, we can run any docker " +"commands without sudo! **This is very important** because subsequent SONiC " +"builds do not allow the use of sudo." +msgstr "" + +#: src\3-2-build.md:42 +msgid "" +"After installation, don't forget to verify the installation with the " +"following command (note, no sudo is needed here!):" +msgstr "" + +#: src\3-2-build.md:48 +msgid "Install Other Dependencies" +msgstr "" + +#: src\3-2-build.md:55 +msgid "Pull the Code" +msgstr "" + +#: src\3-2-build.md:57 +msgid "" +"In [Chapter 3.1 Code Repositories](./3-1-code-repos), we mentioned that the " +"main repository of SONiC is [sonic-buildimage](https://github.com/sonic-net/" +"sonic-buildimage). It is also the only repo we need to focus on for now." +msgstr "" + +#: src\3-2-build.md:59 +msgid "" +"Since this repository includes all other build-related repositories as " +"submodules, we need to use the `--recurse-submodules` option when pulling " +"the code with git:" +msgstr "" + +#: src\3-2-build.md:65 +msgid "" +"If you forget to pull the submodules when pulling the code, you can make up " +"for it with the following command:" +msgstr "" + +#: src\3-2-build.md:71 +msgid "" +"After the code is downloaded, or for an existing repo, we can initialize the " +"compilation environment with the following command. This command updates all " +"current submodules to the required versions to help us successfully compile:" +msgstr "" + +#: src\3-2-build.md:78 +msgid "Set Your Target Platform" +msgstr "" + +#: src\3-2-build.md:80 +msgid "" +"[Although SONiC supports many different types of switches](https://sonic-net." +"github.io/SONiC/Supported-Devices-and-Platforms.html), different models of " +"switches use different ASICs, which means different drivers and SDKs. " +"Although SONiC uses SAI to hide these differences and provide a unified " +"interface for the upper layers. However, we need to set target platform " +"correctly to ensure that the right SAI will be used, so the SONiC we build " +"can run on our target devices." +msgstr "" + +#: src\3-2-build.md:82 +msgid "Currently, SONiC mainly supports the following platforms:" +msgstr "" + +#: src\3-2-build.md:84 +msgid "broadcom" +msgstr "" + +#: src\3-2-build.md:85 +msgid "mellanox" +msgstr "" + +#: src\3-2-build.md:86 +msgid "marvell" +msgstr "" + +#: src\3-2-build.md:87 +msgid "barefoot" +msgstr "" + +#: src\3-2-build.md:88 +msgid "cavium" +msgstr "" + +#: src\3-2-build.md:89 +msgid "centec" +msgstr "" + +#: src\3-2-build.md:90 +msgid "nephos" +msgstr "" + +#: src\3-2-build.md:91 +msgid "innovium" +msgstr "" + +#: src\3-2-build.md:92 +msgid "vs" +msgstr "" + +#: src\3-2-build.md:94 +msgid "" +"After confirming the target platform, we can configure our build environment " +"with the following command:" +msgstr "" + +#: src\3-2-build.md:97 +msgid "# e.g.: make PLATFORM=mellanox configure" +msgstr "" + +#: src\3-2-build.md:114 +msgid "Build the Code" +msgstr "" + +#: src\3-2-build.md:116 +msgid "Build All Code" +msgstr "" + +#: src\3-2-build.md:118 +msgid "After setting the platform, we can start compiling the code:" +msgstr "" + +#: src\3-2-build.md:121 +msgid "# The number of jobs can be the number of cores on your machine." +msgstr "" + +#: src\3-2-build.md:121 +msgid "" +"# Say, if you have 16 cores, then feel free to set it to 16 to speed up the " +"build." +msgstr "" + +#: src\3-2-build.md:132 +msgid "Build Debug Image" +msgstr "" + +#: src\3-2-build.md:134 +msgid "" +"To improve the debug experience, SONiC also supports building debug image. " +"During build, SONiC will make sure the symbols are kept and debug tools are " +"installed inside all the containers, such as gdb. This will help us debug " +"the code more easily." +msgstr "" + +#: src\3-2-build.md:136 +msgid "" +"To build the debug image, we can use `INSTALL_DEBUG_TOOLS` build option:" +msgstr "" + +#: src\3-2-build.md:139 +msgid "y" +msgstr "" + +#: src\3-2-build.md:142 +msgid "Build Specific Package" +msgstr "" + +#: src\3-2-build.md:144 +msgid "" +"From SONiC's Build Pipeline, we can see that compiling the entire project is " +"very time-consuming. Most of the time, our code changes only affect a small " +"part of the code. So, is there a way to reduce our compilation workload? " +"Gladly, yes! We can specify the make target to build only the target or " +"package we need." +msgstr "" + +#: src\3-2-build.md:146 +msgid "" +"In SONiC, the files generated by each subproject can be found in the " +"`target` directory. For example:" +msgstr "" + +#: src\3-2-build.md:148 +msgid "" +"Docker containers: target/.gz, e.g., `target/docker-orchagent." +"gz`" +msgstr "" + +#: src\3-2-build.md:149 +msgid "" +"Deb packages: target/debs//.deb, e.g., `target/debs/" +"bullseye/libswsscommon_1.0.0_amd64.deb`" +msgstr "" + +#: src\3-2-build.md:150 +msgid "" +"Python wheels: target/python-wheels//.whl, e.g., " +"`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl`" +msgstr "" + +#: src\3-2-build.md:152 +msgid "" +"After figuring out the package we need to build, we can delete its generated " +"files and then call the make command again. Here we use `libswsscommon` as " +"an example:" +msgstr "" + +#: src\3-2-build.md:155 +msgid "# Remove the deb package for bullseye" +msgstr "" + +#: src\3-2-build.md:157 +msgid "# Build the deb package for bullseye" +msgstr "" + +#: src\3-2-build.md:162 +msgid "Check and Handle Build Errors" +msgstr "" + +#: src\3-2-build.md:164 +msgid "" +"If an error occurs during the build process, we can check the log file of " +"the failed project to find the specific reason. In SONiC, each subproject " +"generates its related log file, which can be easily found in the `target` " +"directory, such as:" +msgstr "" + +#: src\3-2-build.md:175 +msgid "" +"If we don't want to check the log files every time, then fix errors and " +"recompile in the root directory, SONiC provides another more convenient way " +"that allows us to stay in the docker builder after build. This way, we can " +"directly go to the corresponding directory to run the `make` command to " +"recompile the things you need:" +msgstr "" + +#: src\3-2-build.md:178 +msgid "# KEEP_SLAVE_ON=yes make " +msgstr "" + +#: src\3-2-build.md:179 src\3-2-build.md:180 +msgid "yes" +msgstr "" + +#: src\3-2-build.md:183 +msgid "" +"```admonish note\n" +"Some parts of the code in some repositories will not be build during full " +"build. For example, gtest in `sonic-swss-common`. So, when using this way to " +"recompile, please make sure to check the original repository's build " +"guidance to avoid errors, such as: .\n" +"```" +msgstr "" + +#: src\3-2-build.md:187 +msgid "Get the Correct Image File" +msgstr "" + +#: src\3-2-build.md:189 +msgid "" +"After compilation, we can find the image files we need in the `target` " +"directory. However, there will be many different types of SONiC images, so " +"which one should we use? This mainly depends on what kind of BootLoader or " +"Installer the switch uses. The mapping is as below:" +msgstr "" + +#: src\3-2-build.md:191 +msgid "Bootloader" +msgstr "" + +#: src\3-2-build.md:191 +msgid "Suffix" +msgstr "" + +#: src\3-2-build.md:193 +msgid "Aboot" +msgstr "" + +#: src\3-2-build.md:193 +msgid ".swi" +msgstr "" + +#: src\3-2-build.md:194 +msgid "ONIE" +msgstr "" + +#: src\3-2-build.md:194 +msgid ".bin" +msgstr "" + +#: src\3-2-build.md:195 +msgid "Grub" +msgstr "" + +#: src\3-2-build.md:195 +msgid ".img.gz" +msgstr "" + +#: src\3-2-build.md:197 +msgid "Partial Upgrade" +msgstr "" + +#: src\3-2-build.md:199 +msgid "" +"Obviously, during development, build the image and then performing a full " +"installation each time is very inefficient. So, we could choose not to " +"install the image but directly upgrading certain deb packages as partial " +"upgrade, which could improving our development efficiency." +msgstr "" + +#: src\3-2-build.md:201 +msgid "" +"First, we can upload the deb package to the `/etc/sonic` directory of the " +"switch. The files in this directory will be mapped to the `/etc/sonic` " +"directory of all containers. Then, we can enter the container and use the " +"`dpkg` command to install the deb package, as follows:" +msgstr "" + +#: src\3-2-build.md:204 +msgid "# Enter the docker container" +msgstr "" + +#: src\3-2-build.md:206 +msgid "# Install deb package" +msgstr "" + +#: src\3-2-build.md:213 +msgid "" +"[SONiC Build Guide](https://github.com/sonic-net/sonic-buildimage/blob/" +"master/README.md)" +msgstr "" + +#: src\3-2-build.md:214 +msgid "[Install Docker Engine](https://docs.docker.com/engine/install/)" +msgstr "" + +#: src\3-2-build.md:215 +msgid "" +"[Github repo: sonic-buildimage](https://github.com/sonic-net/sonic-" +"buildimage)" +msgstr "" + +#: src\3-2-build.md:217 +msgid "" +"[Wrapper for starting make inside sonic-slave container](https://github.com/" +"sonic-net/sonic-buildimage/blob/master/Makefile.work)" +msgstr "" + +#: src\3-3-testing.md:1 +msgid "Testing" +msgstr "" + +#: src\3-4-debugging.md:1 +msgid "Debugging" +msgstr "" + +#: src\3-4-1-sai-debugging.md:1 +msgid "Debugging SAI" +msgstr "" + +#: src\4-communications.md:1 +msgid "Communication" +msgstr "" + +#: src\4-communications.md:3 +msgid "" +"There are three main communication mechanisms in SONiC: communication using " +"kernel, Redis-based inter-service communication, and ZMQ-based inter-service " +"communication." +msgstr "" + +#: src\4-communications.md:5 +msgid "" +"There are two main methods for communication using kernel: command line " +"calls and Netlink messages." +msgstr "" + +#: src\4-communications.md:6 +msgid "" +"Redis-based inter-service communication: There are 4 different communication " +"channel based on Redis - SubscriberStateTable, NotificationProducer/" +"Consumer, Producer/ConsumerTable, and Producer/ConsumerStateTable. Although " +"they are all based on Redis, their use case can be very different." +msgstr "" + +#: src\4-communications.md:7 +msgid "" +"ZMQ-based inter-service communication: This communication mechanism is " +"currently only used in the communication between `orchagent` and `syncd`." +msgstr "" + +#: src\4-communications.md:15 +msgid "" +"The implementation of all these basic communication mechanisms is in the " +"`common` directory of the [sonic-swss-common](https://github.com/sonic-net/" +"sonic-swss-common) repo. Additionally, to facilitate the use of various " +"services, SONiC has build a wrapper layer called Orch in [sonic-swss]" +"(https://github.com/sonic-net/sonic-swss), which helps simplify the upper-" +"layer services." +msgstr "" + +#: src\4-communications.md:17 +msgid "" +"In this chapter, we will dive into the implementation of these communication " +"mechanisms!" +msgstr "" + +#: src\4-communications.md:22 src\4-2-1-redis-wrappers.md:35 +#: src\4-2-2-subscribe-state-table.md:74 +#: src\4-2-3-notification-producer-consumer.md:55 +#: src\4-2-4-producer-consumer-table.md:131 +#: src\4-2-5-producer-consumer-state-table.md:126 src\4-4-orch-layer.md:37 +#: src\4-5-event-polling-and-error-handling.md:124 src\5-2-bgp.md:35 +#: src\5-2-2-route-update-in-frr.md:738 src\5-2-3-route-update-in-sonic.md:737 +msgid "[Github repo: sonic-swss](https://github.com/sonic-net/sonic-swss)" +msgstr "" + +#: src\4-communications.md:23 src\4-1-1-exec.md:38 src\4-1-2-netlink.md:78 +#: src\4-2-1-redis-wrappers.md:36 src\4-2-2-subscribe-state-table.md:75 +#: src\4-2-3-notification-producer-consumer.md:56 +#: src\4-2-4-producer-consumer-table.md:132 +#: src\4-2-5-producer-consumer-state-table.md:127 src\4-4-orch-layer.md:38 +#: src\4-5-event-polling-and-error-handling.md:125 +#: src\5-2-2-route-update-in-frr.md:739 src\5-2-3-route-update-in-sonic.md:738 +msgid "" +"[Github repo: sonic-swss-common](https://github.com/sonic-net/sonic-swss-" +"common)" +msgstr "" + +#: src\4-1-1-exec.md:3 +msgid "" +"The simplest way SONiC communicates with the kernel is through command-line " +"calls, which are implemented in [common/exec.h](https://github.com/sonic-net/" +"sonic-swss-common/blob/master/common/exec.h). The interface is straight-" +"forward:" +msgstr "" + +#: src\4-1-1-exec.md:6 +msgid "// File: common/exec.h" +msgstr "" + +#: src\4-1-1-exec.md:6 +msgid "// Namespace: swss" +msgstr "" + +#: src\4-1-1-exec.md:11 +msgid "" +"Here, `cmd` is the command to execute, and `stdout` captures the command " +"output. The `exec` function is a synchronous call that blocks until the " +"command finishes. Internally, it creates a child process via `popen` and " +"retrieves output via `fgets`. However, **although this function returns " +"output, it is rarely used in practice**. Most code only checks the return " +"value for success, and sometimes even error logs won't be logged in the " +"output." +msgstr "" + +#: src\4-1-1-exec.md:13 +msgid "" +"Despite its simplicity, this function is widely used, especially in various " +"`*mgrd` services. For instance, `portmgrd` calls it to set each port's " +"status:" +msgstr "" + +#: src\4-1-1-exec.md:16 +msgid "// File: sonic-swss - cfgmgr/portmgr.cpp" +msgstr "" + +#: src\4-1-1-exec.md:22 +msgid "// ip link set dev [up|down]" +msgstr "" + +#: src\4-1-1-exec.md:23 +msgid "\" link set dev \"" +msgstr "" + +#: src\4-1-1-exec.md:23 +msgid "\" up\"" +msgstr "" + +#: src\4-1-1-exec.md:23 +msgid "\" down\"" +msgstr "" + +#: src\4-1-1-exec.md:27 src\4-1-2-netlink.md:27 src\4-1-2-netlink.md:36 +#: src\4-1-2-netlink.md:43 src\4-1-2-netlink.md:59 src\4-1-2-netlink.md:65 +#: src\4-1-2-netlink.md:72 src\4-2-2-subscribe-state-table.md:28 +#: src\4-2-2-subscribe-state-table.md:42 src\4-2-2-subscribe-state-table.md:51 +#: src\4-2-2-subscribe-state-table.md:58 +#: src\4-2-3-notification-producer-consumer.md:28 +#: src\4-2-3-notification-producer-consumer.md:40 +#: src\4-2-3-notification-producer-consumer.md:44 +#: src\4-2-4-producer-consumer-table.md:42 +#: src\4-2-4-producer-consumer-table.md:90 +#: src\4-2-4-producer-consumer-table.md:108 +#: src\4-2-5-producer-consumer-state-table.md:32 +#: src\4-2-5-producer-consumer-state-table.md:60 +#: src\4-2-5-producer-consumer-state-table.md:63 +#: src\4-5-event-polling-and-error-handling.md:27 +#: src\4-5-event-polling-and-error-handling.md:29 +#: src\4-5-event-polling-and-error-handling.md:62 +#: src\4-5-event-polling-and-error-handling.md:83 +msgid "// ..." +msgstr "" + +#: src\4-1-2-netlink.md:3 +msgid "" +"Netlinkis the message-based communication mechanism provided by Linux kernel " +"and used between the kernel and user-space processes. It is implemented via " +"socket and custom protocol families. It can be used to deliver various types " +"of kernel messages, including network device status, routing table updates, " +"firewall rule changes, and system resource usage. SONiC's `*sync` services " +"heavily utilize Netlink to monitor changes of network devices in the system, " +"synchronize the latest status to Redis, and notify other services to make " +"corresponding updates." +msgstr "" + +#: src\4-1-2-netlink.md:5 +msgid "" +"The main implementation of netlink communication channel is done by these " +"files:" +msgstr "" + +#: src\4-1-2-netlink.md:7 +msgid "" +"[common/netmsg.\\*](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/netmsg.h)" +msgstr "" + +#: src\4-1-2-netlink.md:8 +msgid "" +"[common/netlink.\\*](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/netlink.h)" +msgstr "" + +#: src\4-1-2-netlink.md:9 +msgid "" +"[common/netdispatcher.\\*](https://github.com/sonic-net/sonic-swss-common/" +"blob/master/common/netdispatcher.h)." +msgstr "" + +#: src\4-1-2-netlink.md:11 +msgid "The class diagram is as follows:" +msgstr "" + +#: src\4-1-2-netlink.md:13 +msgid "![](assets/chapter-4/netlink.png)" +msgstr "" + +#: src\4-1-2-netlink.md:15 +msgid "In this diagram:" +msgstr "" + +#: src\4-1-2-netlink.md:17 +msgid "" +"**Netlink**: Wraps the netlink socket interface and provides an interface " +"for sending netlink messages and a callback for receiving messages." +msgstr "" + +#: src\4-1-2-netlink.md:18 +msgid "" +"**NetDispatcher**: A singleton that provides an interface for registering " +"handlers. When a raw netlink message is received, it calls NetDispatcher to " +"parse them into `nl_object` objects and then dispatches them to the " +"corresponding handler based on the message type." +msgstr "" + +#: src\4-1-2-netlink.md:19 +msgid "" +"**NetMsg**: The base class for netlink message handlers, which only provides " +"the `onMsg` interface without a default implementation." +msgstr "" + +#: src\4-1-2-netlink.md:21 +msgid "" +"For example, when `portsyncd` starts, it creates a `Netlink` object to " +"listen for link-related status changes and implements the `NetMsg` interface " +"to handle the link messages. The specific implementation is as follows:" +msgstr "" + +#: src\4-1-2-netlink.md:24 +msgid "// File: sonic-swss - portsyncd/portsyncd.cpp" +msgstr "" + +#: src\4-1-2-netlink.md:29 +msgid "// Create Netlink object to listen to link messages" +msgstr "" + +#: src\4-1-2-netlink.md:33 +msgid "" +"// Here SONiC requests a full dump of the current state to get the status of " +"all links" +msgstr "" + +#: src\4-1-2-netlink.md:35 +msgid "\"Listen to link messages...\"" +msgstr "" + +#: src\4-1-2-netlink.md:38 +msgid "// Register handler for link messages" +msgstr "" + +#: src\4-1-2-netlink.md:47 +msgid "" +"The `LinkSync` class above is an implementation of `NetMsg`, providing the " +"`onMsg` interface for handling link messages:" +msgstr "" + +#: src\4-1-2-netlink.md:50 +msgid "// File: sonic-swss - portsyncd/linksync.h" +msgstr "" + +#: src\4-1-2-netlink.md:56 +msgid "// NetMsg interface" +msgstr "" + +#: src\4-1-2-netlink.md:61 +msgid "// File: sonic-swss - portsyncd/linksync.cpp" +msgstr "" + +#: src\4-1-2-netlink.md:67 +msgid "// Write link state to Redis DB" +msgstr "" + +#: src\4-1-2-netlink.md:68 +msgid "\"down\"" +msgstr "" + +#: src\4-2-redis-based-channels.md:3 +msgid "" +"To facilitate communication between services, SONiC provides a messaging " +"layer that is built on top of the Redis. On high-level, it contains 2 layers:" +msgstr "" + +#: src\4-2-redis-based-channels.md:5 +msgid "" +"First layer wraps frequenctly used redis operations and provide table " +"abstraction on top of it." +msgstr "" + +#: src\4-2-redis-based-channels.md:6 +msgid "" +"Second layer provides different channels for inter-service communication to " +"satisfy various communication channel requirements." +msgstr "" + +#: src\4-2-redis-based-channels.md:8 +msgid "Now, let's dive into them one by one." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:3 +msgid "Redis Database Operation Layer" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:5 +msgid "" +"The first layer, which is also the lowest layer, is the Redis database " +"operation layer. It wraps various basic commands, such as DB connection, " +"command execution, event notification callback interfaces, etc. The specific " +"class diagram is as follows:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:7 +msgid "![](assets/chapter-4/redis-ops.png)" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:9 +msgid "Among them:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:11 +msgid "" +"**[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/dbconnector.h)**: Wraps and maintains the connection to Redis, and " +"closes the connection when it is destroyed." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:12 +msgid "" +"**[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/dbconnector.h)**: Wraps all the underlying Redis commands used, such " +"as `SET`, `GET`, `DEL`, etc." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:13 +msgid "" +"**[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/redistran.h)**: Wraps Redis transaction operations, used to " +"execute multiple commands in a transaction, such as `MULTI`, `EXEC`, etc." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:14 +msgid "" +"**[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/redispipeline.h)**: Wraps the hiredis redisAppendFormattedCommand " +"API, providing an asynchronous interface for executing Redis commands " +"similar to a queue (although most usage methods are still synchronous). It " +"is also one of the few classes that wraps the `SCRIPT LOAD` command, used to " +"load Lua scripts in Redis to implement stored procedures. Most classes in " +"SONiC that need to execute Lua scripts will use this class for loading and " +"calling." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:15 +msgid "" +"**[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/redisselect.h)**: Implements the Selectable interface to support the " +"epoll-based event notification mechanism (Event Polling). Mainly used to " +"trigger epoll callbacks when we receive a reply from Redis (we will " +"introduce this in more detail later)." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:16 +msgid "" +"**[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/dbconnector.h)**: This class is a \"static class\" that mainly " +"implements the reading and parsing of the SONiC DB configuration file. Other " +"database operation classes will use this class to obtain any configuration " +"information if needed." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:18 +msgid "Table Abstraction Layer" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:20 +msgid "" +"Above the Redis database operation layer is the table abstraction layer " +"established by SONiC using the keys in Redis. Since the format of each Redis " +"key is ``, SONiC needs to craft or parse " +"it, when accessing the database. For more details on how the database is " +"designed, please refer to [the database section for more information](/posts/" +"sonic-2-key-components/#database)." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:22 +msgid "The main class diagram of related classes is as follows:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:24 +msgid "![](assets/chapter-4/table-abstraction.png)" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:26 +msgid "In this diagram, we have three key classes:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:28 +msgid "" +"**[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/" +"common/table.h)**: This class is the base class for all tables. It mainly " +"wraps the basic information of the table, such as the table name, Redis key " +"packaging, the name of the channel used for communication when each table is " +"modified, etc." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:29 +msgid "" +"**[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/common/" +"table.h)**: This class wraps the CRUD operations for each table. It contains " +"the table name and separator, so the final key can be constructed when " +"called." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:30 +msgid "" +"**[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/consumertablebase.h)**: This class is the base class for " +"various SubscriptionTables. It mainly wraps a simple queue and its pop " +"operation (yes, only pop, no push, because it is for consumers only), for " +"upper layer calls." +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:3 +msgid "" +"The most straight-forward redis-based communication channel is " +"[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/subscriberstatetable.h)." +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:5 +msgid "" +"The idea is to use the built-in keyspace notification mechanism of the Redis " +"database [\\[4\\]](https://redis.io/docs/manual/keyspace-notifications/). " +"When any value in the Redis database changes, Redis sends two keyspace event " +"notifications: one is `` on `__keyspace@__:` and the other " +"is `` on `__keyevent@__:`. For example, deleting a key in " +"database `0` triggers:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:12 +msgid "" +"`SubscriberStateTable` listens for the first event notification and then " +"calls the corresponding callback function. The main classes related to it " +"are shown in this diagram, where we can see it inherits from " +"ConsumerTableBase because it is a consumer of Redis messages:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:14 +msgid "![](assets/chapter-4/subscriber-state-table.png)" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:16 +msgid "Initialization" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:18 +msgid "" +"From the initialization code, we can see how it subscribes to Redis event " +"notifications:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:21 src\4-2-2-subscribe-state-table.md:39 +msgid "// File: sonic-swss-common - common/subscriberstatetable.cpp" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:25 +msgid "\"__keyspace@\"" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:26 +msgid "\"__:\"" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:26 +msgid "\"*\"" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:31 +msgid "Event handling" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:33 +msgid "" +"`SubscriberStateTable` handles the event reception and distribution in two " +"main functions:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:35 +msgid "" +"`readData()`: Reads pending events from Redis and puts them into the " +"ConsumerTableBase queue" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:36 +msgid "" +"`pops()`: Retrieves the raw events from the queue, parses and passes them to " +"the caller via function parameters" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:55 +msgid "/*prefix*/" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:60 +msgid "// Pop from m_keyspace_event_buffer, which is filled by readData()" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:63 +msgid "// Parsing here ..." +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:76 +#: src\4-2-3-notification-producer-consumer.md:57 +#: src\4-2-4-producer-consumer-table.md:133 +#: src\4-2-5-producer-consumer-state-table.md:128 +msgid "" +"[Redis keyspace notifications](https://redis.io/docs/manual/keyspace-" +"notifications/)" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:1 +msgid "NotificationProducer / NotificationConsumer" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:3 +msgid "" +"When it comes to message communication, there is no way we can bypass " +"message queues. And this is the second communication channel in SONiC - " +"[NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/" +"master/common/notificationproducer.h) and [NotificationConsumer](https://" +"github.com/sonic-net/sonic-swss-common/blob/master/common/" +"notificationconsumer.h)." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:5 +msgid "" +"This communication channel is implemented using Redis's built-in PubSub " +"mechanism, wrapping the `PUBLISH` and `SUBSCRIBE` commands. However, because " +"`PUBLISH` needs everything being send to be serialized in the command, due " +"to API limitations [\\[5\\]](https://redis.io/docs/reference/clients/), " +"these commands are not suitable for passing large data. Hence, in SONiC, it " +"is only used in limited places, such as simple notification scenarios (e.g., " +"timeout checks or restart checks in orchagent), which won't have large " +"payload, such as user configurations or data:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:7 +msgid "![](assets/chapter-4/notification-producer-consumer.png)" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:9 +msgid "" +"In this communication channel, the producer side performs two main tasks:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:11 +msgid "Package the message into JSON format." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:12 +msgid "Call Redis command `PUBLISH` to send it." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:14 +msgid "" +"Because `PUBLISH` can only carry a single message, the \"op\" and \"data\" " +"fields are placed at the front of \"values\", then the `buildJson` function " +"is called to package them into a JSON array:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:19 +msgid "" +"// Pack the op and data into values array, then pack everything into a JSON " +"string as the message" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:25 +msgid "// Publish message to Redis channel" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:27 +msgid "\"PUBLISH %s %s\"" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:35 +msgid "" +"The consumer side uses the `SUBSCRIBE` command to receive all notifications:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:46 +msgid "// Subscribe to Redis channel" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:47 +msgid "\"SUBSCRIBE \"" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:58 +#: src\4-2-4-producer-consumer-table.md:137 +#: src\4-2-5-producer-consumer-state-table.md:132 +msgid "[Redis client handling](https://redis.io/docs/reference/clients/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:1 +msgid "ProducerTable / ConsumerTable" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:3 +msgid "" +"Although `NotificationProducer` and `NotificationConsumer` is straight-" +"forward, but they are not suitable for passing large data. Therefore, SONiC " +"provides another message-queue-based communication mechanism that works in " +"similar way - [ProducerTable](https://github.com/sonic-net/sonic-swss-common/" +"blob/master/common/producertable.h) and [ConsumerTable](https://github.com/" +"sonic-net/sonic-swss-common/blob/master/common/consumertable.h)." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:5 +msgid "" +"This channel leverages the Redis list to pass the message. Unlike " +"Notification, which has limited message capacity, it stores all the message " +"data in a Redis list with a very slim custom messsage format. This solves " +"the message size limitation in Notification. In SONiC, it is mainly used in " +"FlexCounter, the `syncd` service, and `ASIC_DB`." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:7 +msgid "Message format" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:9 +msgid "" +"In this channel, a message is a triplet (`Key`, `FieldValuePairs`, `Op`) and " +"will be pushed into the Redis list (Key = `_KEY_VALUE_OP_QUEUE`) " +"as 3 list items:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:11 +msgid "" +"`Key` is table name and key (e.g., `SAI_OBJECT_TYPE_SWITCH:" +"oid:0x21000000000000`)." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:12 +msgid "" +"`FieldValuePairs` are the field that needs to be updated in the database and " +"their values, which is serialized into a JSON string: `\"[\\\"Field1\\\", \\" +"\"Value1\\\", \\\"Field2\\\", \\\"Value2\\\", ...]\"`." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:13 +msgid "`Op` is the operation to be performed (e.g., Set, Get, Del, etc.)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:15 +msgid "" +"Once the message is pushed into the Redis list, a notification will be " +"published to a specific channel (Key = `_CHANNEL`) with only a " +"single character \"G\" as payload, indicating that there is a new message in " +"the list." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:17 +msgid "" +"So, when using this channel, we can imaging the actual data stored in the " +"Redis:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:19 +msgid "In the channel: `[\"G\", \"G\", ...]`" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:20 +msgid "" +"In the list: `[\"Key1\", \"FieldValuePairs1\", \"Op1\", \"Key2\", " +"\"FieldValuePairs2\", \"Op2\", ...]`" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:22 +msgid "Queue operations" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:24 +msgid "" +"Using this message format, `ProducerTable` and `ConsumerTable` provides two " +"queue operations:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:26 +msgid "" +"Enqueue: `ProducerTable` uses a Lua script to atomically write the message " +"triplet into the Redis list and then publishes an update notification to a " +"specific channel." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:27 +msgid "" +"Pop: `ConsumerTable` also uses a Lua script to atomically read the message " +"triplet from the message queue and writes the requested changes to the " +"database during the read process." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:33 +msgid "" +"Its main class diagram is shown below. In ProducerTable, `m_shaEnqueue` and " +"in ConsumerTable, `m_shaPop` are the two Lua scripts we mentioned. After " +"they are loaded, you can call them atomically via `EVALSHA`:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:35 +msgid "![](assets/chapter-4/producer-consumer-table.png)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:37 +msgid "" +"The core logic of ProducerTable is as follows, showing how values are packed " +"into JSON and how `EVALSHA` is used to call Lua scripts:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:40 +msgid "// File: sonic-swss-common - common/producertable.cpp" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:45 +msgid "\"redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:46 +msgid "\"redis.call('PUBLISH', KEYS[2], ARGV[4]);\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:53 +msgid "\"S\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:58 +msgid "\"{}\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:58 +msgid "\"D\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:61 +msgid "/* prefix */" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:66 +msgid "\"EVALSHA %s 2 %s %s %s %s %s %s\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:73 +msgid "\"G\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:79 +msgid "" +"On the other side, ConsumerTable is slightly more complicated because it " +"supports many types of ops. The logic is written in a separate file (`common/" +"consumer_table_pops.lua`). Interested readers can explore it further:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:82 +msgid "// File: sonic-swss-common - common/consumertable.cpp" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:88 +msgid "\"consumer_table_pops.lua\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:95 +msgid "" +"// Note that here we are processing the messages in bulk with POP_BATCH_SIZE!" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:98 +msgid "\"EVALSHA %s 2 %s %s %d %d\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:107 +msgid "// Parse and pack the messages in bulk" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:112 +msgid "Monitor" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:114 +msgid "" +"To monitor how the `ProducerTable` and `ConsumerTable` work, we can use the " +"`redis-cli monitor` command to see the actual Redis commands that being " +"executed." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:117 +msgid "# Filter to `LPUSH` and `PUBLISH` commands to help us reduce the noise." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:118 +msgid "\"LPUSH|PUBLISH\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:121 +msgid "" +"And here is an example of the output showing a `ProducerTable` enqueue " +"operation:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:123 +msgid "" +"```text\n" +"1735966216.139741 [1 lua] \"LPUSH\" \"ASIC_STATE_KEY_VALUE_OP_QUEUE\" " +"\"SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000\" \"[\\" +"\"SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY\\\",\\\"1\\\"]\" \"Sget" +"\" \n" +"1735966216.139774 [1 lua] \"PUBLISH\" \"ASIC_STATE_CHANNEL@1\" \"G\" \n" +"```" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:134 +#: src\4-2-5-producer-consumer-state-table.md:129 +msgid "[Redis Transactions](https://redis.io/docs/manual/transactions/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:135 +#: src\4-2-5-producer-consumer-state-table.md:130 +msgid "" +"[Redis Atomicity with Lua](https://developer.redis.com/develop/java/spring/" +"rate-limiting/fixed-window/reactive-lua/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:136 +#: src\4-2-5-producer-consumer-state-table.md:131 +msgid "[Redis hashes](https://redis.io/docs/data-types/hashes/)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:1 +msgid "ProducerStateTable / ConsumerStateTable" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:3 +msgid "" +"Although `Producer/ConsumerTable` is straightforward and maintains the order " +"of the messages, each message can only update one table key and requires " +"JSON serialization. However, in many cases, we don't need strict ordering " +"but need higher throughput. To optimize performance, SONiC introduces the " +"fourth, and most frequently used, communication channel: [ProducerStateTable]" +"(https://github.com/sonic-net/sonic-swss-common/blob/master/common/" +"producerstatetable.h) and [ConsumerStateTable](https://github.com/sonic-net/" +"sonic-swss-common/blob/master/common/consumerstatetable.h)." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:5 +msgid "Overview" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:7 +msgid "" +"Unlike `ProducerTable`, `ProducerStateTable` uses a Hash to store messages " +"instead of a List. This means the order of messages will not be guranteed, " +"but it can significantly boosts performance:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:9 +msgid "First, no more JSON serialization, hence its overhead is gone." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:10 +msgid "Second, batch processing:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:11 +msgid "" +"Multiple table updates can be merged into one (single pending update key set " +"per table)." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:12 +msgid "" +"If the same Field under the same Key is changed multiple times, only the " +"latest change is preserved, merging all changes related to that Key into a " +"single message and reducing unnecessary handling." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:14 +msgid "" +"`Producer/ConsumerStateTable` is more complex under the hood than `Producer/" +"ConsumerTable`. The related classes are shown in the diagram below, where " +"`m_shaSet` and `m_shaDel` store the Lua scripts for modifying and sending " +"messages, while `m_shaPop` is used to retrieve messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:16 +msgid "![](assets/chapter-4/producer-consumer-state-table.png)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:18 +msgid "Sending messages" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:20 +msgid "When sending messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:22 +msgid "Each message is stored in two parts:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:23 +msgid "" +"KEY_SET: keeps track of which Keys have been modified (stored as a Set at " +"``)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:24 +msgid "" +"A series of Hash: One Hash for each modified Key (stored at `_`)." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:25 +msgid "" +"After storing a message, if the Producer finds out it's a new Key, it calls " +"`PUBLISH` to notify `_CHANNEL@` that a new Key has " +"appeared." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:28 +msgid "// File: sonic-swss-common - common/producerstatetable.cpp" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:35 +msgid "\"local added = redis.call('SADD', KEYS[2], ARGV[2])\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:36 +msgid "\"for i = 0, #KEYS - 3 do\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:37 +msgid "" +"\" redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i * 2])\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:38 +#: src\4-2-5-producer-consumer-state-table.md:41 +msgid "\"end\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:39 +msgid "\" if added > 0 then \\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:40 +msgid "\" redis.call('PUBLISH', KEYS[1], ARGV[1])\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:47 +msgid "Receiving messages" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:49 +msgid "When receiving messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:51 +msgid "" +"The consumer uses `SUBSCRIBE` to listen on `_CHANNEL@`. " +"Once a new message arrives, it calls a Lua script to run `HGETALL`, fetch " +"all Keys, and write them into the database." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:58 +msgid "\"consumer_state_table_pops.lua\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:67 +msgid "Example" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:69 +msgid "To illustrate, here is an example of enabling Port Ethernet0:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:71 +msgid "" +"First, we call `config interface startup Ethernet0` from the command line to " +"enable Ethernet0. This causes `portmgrd` to send a status update to APP_DB " +"via ProducerStateTable, as shown below:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:73 +msgid "" +"```redis\n" +"EVALSHA \"\" \"6\" \"PORT_TABLE_CHANNEL@0\" " +"\"PORT_TABLE_KEY_SET\" \n" +" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:" +"Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"G\"\n" +" \"Ethernet0\" \"alias\" \"Ethernet5/1\" \"index\" \"5\" \"lanes\" " +"\"9,10,11,12\" \"speed\" \"40000\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:79 +msgid "This command triggers the following creation and broadcast:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:81 +msgid "" +"```redis\n" +"SADD \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" +"PUBLISH \"PORT_TABLE_CHANNEL@0\" \"_PORT_TABLE:Ethernet0\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:90 +msgid "Thus, the message is ultimately stored in APPL_DB as follows:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:103 +msgid "" +"When ConsumerStateTable receives the message, it also calls `EVALSHA` to " +"execute a Lua script, such as:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:105 +msgid "" +"```redis\n" +"EVALSHA \"\" \"3\" \"PORT_TABLE_KEY_SET\" \"PORT_TABLE:\" " +"\"PORT_TABLE_DEL_SET\" \"8192\" \"_\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:109 +msgid "Similar to the Producer side, this script runs:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:111 +msgid "" +"```redis\n" +"SPOP \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" +"HGETALL \"_PORT_TABLE:Ethernet0\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" +"DEL \"_PORT_TABLE:Ethernet0\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:121 +msgid "At this point, the data update is complete." +msgstr "" + +#: src\4-3-zmq-based-channels.md:1 +msgid "ZMQ-based Channels" +msgstr "" + +#: src\4-4-orch-layer.md:1 +msgid "Service Layer - Orch" +msgstr "" + +#: src\4-4-orch-layer.md:3 +msgid "" +"Finally, to make it more convenient for building services, SONiC provides " +"another layer of abstraction on top of the communication layer, offering a " +"base class for services: [Orch](https://github.com/sonic-net/sonic-swss/blob/" +"master/src/orchagent/orch.h)." +msgstr "" + +#: src\4-4-orch-layer.md:5 +msgid "" +"With all the lower layers, adding message communication support in Orch is " +"relatively straightforward. The main class diagram is shown below:" +msgstr "" + +#: src\4-4-orch-layer.md:7 +msgid "![](assets/chapter-4/orch.png)" +msgstr "" + +#: src\4-4-orch-layer.md:13 +msgid "" +"We can see that Orch mainly wraps `SubscriberStateTable` and " +"`ConsumerStateTable` to simplify and unify the message subscription. The " +"core code is very simple and creates different Consumers based on the " +"database type:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:1 +msgid "Event Dispatching and Error Handling" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:3 +msgid "Epoll-based Event Dispatching" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:5 +msgid "" +"Just like many other Linux services, SONiC uses epoll at its core for event " +"dispatching:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:7 +msgid "" +"Any class that supports event dispatching should inherit from `Selectable` " +"and implement two key functions:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:8 +msgid "" +"`int getFd();`: Returns the fd for epoll to listen on. For most services, " +"this fd is the one used for Redis communication, so the call to `getFd()` " +"ultimately delegates to the Redis library." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:9 +msgid "`uint64_t readData()`: Reads data when an event arrives." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:10 +msgid "" +"Any objects that need to participate in event dispatching must register with " +"the `Select` class. This class registers all `Selectable` objects' fds with " +"epoll and calls `Selectable`'s `readData()` when an event arrives." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:12 +msgid "Here's the class diagram:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:14 +msgid "![](assets/chapter-4/event-polling.png)" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:16 +msgid "" +"The core logic lives in the `Select` class, which can be simplified as " +"follows:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:37 +msgid "// error handling here ..." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:47 +msgid "// After update callback ..." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:55 +msgid "" +"However, here comes the question... where is the callback? As mentioned, " +"`readData()` only reads the message and stores it in a pending queue for " +"processing. The real processing needs to call `pops()`. So at which point " +"does every upper-level message handler get called?" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:57 +msgid "" +"Here, let's look back again at `portmgrd`'s `main` function. From the " +"simplified code below, we can see - unlike a typical event loop, SONiC does " +"not handle events with callbacks; the outermost event loop directly calls " +"the actual handlers:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:64 +msgid "// Create PortMgr, which implements Orch interface." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:68 +msgid "// Create Select object for event loop and add PortMgr to it." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:74 +msgid "// Event loop" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:80 +msgid "// When anyone of the selectables gets signaled, select() will call" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:81 +msgid "// into readData() and fetch all events, then return." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:85 +msgid "// Then, we call into execute() explicitly to process all events." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:93 +msgid "Error Handling" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:95 +msgid "" +"Another thing about event loops is error handling. For example, if a Redis " +"command fails, or the connection is broken, or any kind of failure happens, " +"what will happen to our services?" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:97 +msgid "" +"SONiC's error handling is very simple: it just throws exceptions (for " +"example, in the code that fetches command results). Then the event loop " +"catches the exceptions, logs them, and continues:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:105 +msgid "// The only reason of error is REDIS_ERR_OOM (Out of memory)" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:106 +msgid "// ref: https://github.com/redis/hiredis/blob/master/hiredis.c" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:113 +msgid "\"Failed to redisGetReply with \"" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:119 +msgid "" +"There is no specific code here for statistics or telemetry, so monitoring is " +"somewhat weak. We also need to consider data errors (for example, partial " +"writes leading to corrupted data), though simply restarting `*syncd` or " +"`*mgrd` services might fix such issues because many stored data in database " +"will be wiped out, such as APPL_DB, and the services will do a full sync on " +"startup." +msgstr "" + +#: src\5-core-components.md:1 +msgid "Core Components" +msgstr "" + +#: src\5-core-components.md:3 +msgid "" +"In this chapter, we take a deeper look at some of the representative core " +"components in SONiC and their workflows from a code perspective." +msgstr "" + +#: src\5-core-components.md:5 +msgid "" +"```admonish note\n" +"For helping us to read and understand, all the code shown here will be " +"simplified to its core part to illustrate the process. If you would like to " +"read the full code, please refer to [the original code in the repository]" +"(./3-1-code-repos.html).\n" +"\n" +"Additionally, the relevant file path of the code will be shared in the " +"beginning of each code block, which is based on the SONiC's main repository: " +"[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage). If the " +"code is not imported by buildimage repo, the full URL will be provided.\n" +"```" +msgstr "" + +#: src\5-1-syncd-and-sai.md:3 +msgid "" +"[Syncd Container](./2-3-key-containers.html#asic-management-container-syncd) " +"is the container in SONiC dedicated to managing the ASIC. The key process " +"`syncd` is responsible for communicating with the Redis database, loading " +"SAI implementation, and interacting with it to handle ASIC initialization, " +"configuration, status reporting, and so on." +msgstr "" + +#: src\5-1-syncd-and-sai.md:5 +msgid "" +"Since many SONiC workflows ultimately need to interact with the ASIC through " +"Syncd and SAI, this part becomes common to all those workflows. Therefore, " +"before diving into other workflows, let's take a look at how Syncd and SAI " +"work first." +msgstr "" + +#: src\5-1-syncd-and-sai.md:7 +msgid "Syncd Startup Flow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:9 +msgid "" +"The entry point of the `syncd` process is the `syncd_main` function in " +"`syncd_main.cpp`. The startup flow can be roughly divided into two parts." +msgstr "" + +#: src\5-1-syncd-and-sai.md:11 +msgid "The first part creates and initializes various objects:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:29 +msgid "The second part starts the main loop and handles initialization events:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:64 +msgid "Now, let's dive into the code to see how Syncd and SAI are implemented." +msgstr "" + +#: src\5-1-syncd-and-sai.md:66 +msgid "The syncd_main Function" +msgstr "" + +#: src\5-1-syncd-and-sai.md:68 +msgid "" +"The `syncd_main` function itself is straightforward: it creates a `Syncd` " +"object and then calls its `run` method:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:71 +msgid "// File: src/sonic-sairedis/syncd/syncd_main.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:81 +msgid "" +"The Syncd constructor initializes each feature in Syncd, while the run " +"method starts the Syncd main loop." +msgstr "" + +#: src\5-1-syncd-and-sai.md:83 +msgid "The Syncd Constructor" +msgstr "" + +#: src\5-1-syncd-and-sai.md:85 +msgid "" +"The `Syncd` constructor creates or initializes the key components in " +"`Syncd`, such as database connection objects, statistics management, and " +"ASIC notification handler. The key code looks like below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:88 src\5-1-syncd-and-sai.md:229 +#: src\5-1-syncd-and-sai.md:319 src\5-1-syncd-and-sai.md:370 +#: src\5-1-syncd-and-sai.md:466 src\5-1-syncd-and-sai.md:730 +msgid "// File: src/sonic-sairedis/syncd/Syncd.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:98 +msgid "// Load context config" +msgstr "" + +#: src\5-1-syncd-and-sai.md:103 +msgid "// Create FlexCounter manager" +msgstr "" + +#: src\5-1-syncd-and-sai.md:106 +msgid "// Create DB related objects" +msgstr "" + +#: src\5-1-syncd-and-sai.md:111 +msgid "// Create notification processor and handler" +msgstr "" + +#: src\5-1-syncd-and-sai.md:119 +msgid "// Init many other event handlers here" +msgstr "" + +#: src\5-1-syncd-and-sai.md:123 src\5-1-syncd-and-sai.md:143 +msgid "// Initialize SAI" +msgstr "" + +#: src\5-1-syncd-and-sai.md:129 +msgid "SAI Initialization and VendorSai" +msgstr "" + +#: src\5-1-syncd-and-sai.md:131 +msgid "" +"The last and most important step in `Syncd` initialization is to initialize " +"SAI. [In the core component introduction to SAI](./2-4-sai-intro.html), we " +"briefly described how SAI is initialized and implemented, and how it " +"provides support for different platforms in SONiC. And here, we will focus " +"more on how Syncd wraps SAI and uses it." +msgstr "" + +#: src\5-1-syncd-and-sai.md:133 +msgid "" +"`Syncd` uses `VendorSai` to wrap all SAI APIs to simplify upper-level calls. " +"The initialization looks like below, essentially just calling the sai " +"initialize and api query functions, and handling errors:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:136 src\5-1-syncd-and-sai.md:168 +msgid "// File: src/sonic-sairedis/syncd/VendorSai.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:147 +msgid "// If SAI is initialized successfully, query all SAI API methods." +msgstr "" + +#: src\5-1-syncd-and-sai.md:148 +msgid "" +"// sai_metadata_api_query will also update all extern global sai_*_api " +"variables, so we can also use" +msgstr "" + +#: src\5-1-syncd-and-sai.md:149 +msgid "" +"// sai_metadata_get_object_type_info to get methods for a specific SAI " +"object type." +msgstr "" + +#: src\5-1-syncd-and-sai.md:161 +msgid "" +"Once all the SAI APIs have been acquired, we can call into the SAI " +"implementation using the `VendorSai` object." +msgstr "" + +#: src\5-1-syncd-and-sai.md:163 +msgid "" +"Currently, `VendorSai` internally has two different ways to call the SAI " +"APIs:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:165 +msgid "" +"Using `sai_object_type_info_t` from SAI metadata, which essentially acts " +"like a virtual table for all SAI Objects:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:182 +msgid "" +"Using `m_apis` stored in the `VendorSai` object. This approach needs us to " +"check the object type and then call the corresponding APIs, so the code " +"becomes more verbose:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:214 +msgid "\"not implemented, FIXME\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:222 +msgid "The first approach is more succinct." +msgstr "" + +#: src\5-1-syncd-and-sai.md:224 +msgid "Main Event Loop" +msgstr "" + +#: src\5-1-syncd-and-sai.md:226 +msgid "" +"`Syncd`'s main event loop follows SONiC's standard [event dispatching](./4-5-" +"event-polling-and-error-handling.html) pattern. On startup, Syncd registers " +"all Selectable objects handling events with a Select object that waits for " +"events. The main loop calls \"select\" to wait for events:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:236 +msgid "// Start notification processing thread" +msgstr "" + +#: src\5-1-syncd-and-sai.md:239 +msgid "// Start MDIO threads" +msgstr "" + +#: src\5-1-syncd-and-sai.md:243 +msgid "// Registering selectable for event polling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:249 +msgid "// Main event loop" +msgstr "" + +#: src\5-1-syncd-and-sai.md:257 +msgid "// Handling switch restart event and restart switch here." +msgstr "" + +#: src\5-1-syncd-and-sai.md:263 +msgid "// Handle redis updates here." +msgstr "" + +#: src\5-1-syncd-and-sai.md:266 +msgid "\"select failed: %d\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:274 +msgid "" +"Here, `m_selectableChannel` handles Redis database events. It interacts with " +"Redis [ProducerTable / ConsumerTable](./4-2-4-producer-consumer-table.html). " +"Hence, all operations from `orchagent` will be stored in Redis lists, " +"waiting for `Syncd` to consume." +msgstr "" + +#: src\5-1-syncd-and-sai.md:277 +msgid "// File: src/sonic-sairedis/meta/RedisSelectableChannel.h" +msgstr "" + +#: src\5-1-syncd-and-sai.md:288 +msgid "// SelectableChannel overrides" +msgstr "" + +#: src\5-1-syncd-and-sai.md:292 +msgid "// Selectable overrides" +msgstr "" + +#: src\5-1-syncd-and-sai.md:305 +msgid "During the main loop startup, `Syncd` also launches two threads:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:307 +msgid "" +"A notification processing thread for receiving ASIC-reported notifications: " +"`m_processor->startNotificationsProcessingThread()`" +msgstr "" + +#: src\5-1-syncd-and-sai.md:308 +msgid "" +"A thread for handling MDIO communication: `m_mdioIpcServer->startMdioThread()" +"`" +msgstr "" + +#: src\5-1-syncd-and-sai.md:310 +msgid "" +"We'll discuss their details more thoroughly when introducing related " +"workflows." +msgstr "" + +#: src\5-1-syncd-and-sai.md:312 +msgid "Initialize SAI Switch and Notifications" +msgstr "" + +#: src\5-1-syncd-and-sai.md:314 +msgid "" +"Once the main event loop is started, `Syncd` will call into SAI to create " +"the Switch object. There are two main entry points: either a create switch " +"request from ASIC_DB (called by swss) or `Syncd` directlly calls it for the " +"Warm Boot process. Either way, the internal flow is similar." +msgstr "" + +#: src\5-1-syncd-and-sai.md:316 +msgid "" +"A crucial step here is initializing the notification callbacks in the SAI " +"implementation, such as FDB events. These callback functions are passed to " +"SAI as Switch attributes in `create_switch`. The SAI implementation stores " +"them so it can call back into `Syncd` whenever these events occur:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:324 +msgid "// Parse event into SAI object" +msgstr "" + +#: src\5-1-syncd-and-sai.md:332 +msgid "// Update notifications pointers in attribute list" +msgstr "" + +#: src\5-1-syncd-and-sai.md:340 +msgid "" +"// ProcessQuadEventInInitViewMode will eventually call into VendorSai, which " +"calls create_swtich function in SAI." +msgstr "" + +#: src\5-1-syncd-and-sai.md:347 src\5-1-syncd-and-sai.md:702 +msgid "// File: src/sonic-sairedis/syncd/NotificationHandler.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:371 +msgid "// Call stack: processQuadEvent" +msgstr "" + +#: src\5-1-syncd-and-sai.md:372 +msgid "// -> processQuadEventInInitViewMode" +msgstr "" + +#: src\5-1-syncd-and-sai.md:373 +msgid "// -> processQuadInInitViewModeCreate" +msgstr "" + +#: src\5-1-syncd-and-sai.md:374 +msgid "// -> onSwitchCreateInInitViewMode" +msgstr "" + +#: src\5-1-syncd-and-sai.md:392 +msgid "" +"From the open-sourced Mellanox's implementation, we can see how the SAI " +"switch is created and the notification callbacks are set:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:431 +msgid "ASIC Programming Workflow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:433 +msgid "" +"ASIC programming workflow is the most important workflow in `Syncd`. When " +"`orchagent` discovers any configuration changes, it sends ASIC programming " +"request via `ASIC_DB`, which triggers this workflow and uses SAI to update " +"the ASIC. After understanding Syncd's main event loop and the communication " +"channels, the workflow will become easier to follow." +msgstr "" + +#: src\5-1-syncd-and-sai.md:435 +msgid "All steps happen sequentially on the main thread:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:461 +msgid "" +"First, `orchagent` sends operations through Redis, which will be received by " +"the `RedisSelectableChannel.` When the main event loop processes " +"`m_selectableChannel`, it calls `processEvent` to process it, just like what " +"we have discussed in the main event loop section." +msgstr "" + +#: src\5-1-syncd-and-sai.md:463 +msgid "" +"Then, `processEvent` calls the relevant SAI API to update the ASIC. The " +"logic is a giant switch-case statement that dispatches the operations:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:469 +msgid "// Loop all operations in the queue" +msgstr "" + +#: src\5-1-syncd-and-sai.md:496 +msgid "// Parse operation" +msgstr "" + +#: src\5-1-syncd-and-sai.md:511 +msgid "// Process the operation" +msgstr "" + +#: src\5-1-syncd-and-sai.md:519 +msgid "// Send response" +msgstr "" + +#: src\5-1-syncd-and-sai.md:547 +msgid "\"api %s not supported\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:552 +msgid "ASIC State Change Notification Workflow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:554 +msgid "" +"On the other hand, when the ASIC state is changed or needs to report certain " +"status, it notifies us through SAI. `Syncd` listens for these notifications, " +"then reports them back to `orchagent` through our communication channel on " +"top of `ASIC_DB`." +msgstr "" + +#: src\5-1-syncd-and-sai.md:556 +msgid "The workflow shows as below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:587 +msgid "" +"Here, let's look into a real implementation. For better understanding, we " +"still use Mellanox's open-sourced SAI implementation as an example." +msgstr "" + +#: src\5-1-syncd-and-sai.md:589 +msgid "" +"First of all, SAI implementation needs to be able to receive notification " +"from ASIC. This is done by calling into the ASIC SDK. In Mellanox's SAI, it " +"sets up an event thread to hook into ASIC, then use `select` to handle the " +"events from ASIC SDK:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:608 +msgid "// Init SDK API" +msgstr "" + +#: src\5-1-syncd-and-sai.md:621 +msgid "// Register for port and channel notifications" +msgstr "" + +#: src\5-1-syncd-and-sai.md:639 +msgid "// Port state change event" +msgstr "" + +#: src\5-1-syncd-and-sai.md:641 +msgid "// Parse port state event here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:648 +msgid "// Receive notification event." +msgstr "" + +#: src\5-1-syncd-and-sai.md:654 +msgid "// BFD packet event" +msgstr "" + +#: src\5-1-syncd-and-sai.md:657 +msgid "// Parse and check event valid here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:662 +msgid "" +"// Same way to handle BFD timeout event, Bulk counter ready event. Emiited." +msgstr "" + +#: src\5-1-syncd-and-sai.md:664 +msgid "// FDB event and packet event handling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:666 +msgid "\"FDB event\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:672 +msgid "// Parse FDB events here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:681 +msgid "// Packet event handling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:695 +msgid "Using FDB event as an example:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:697 +msgid "" +"When ASIC sends the FDB events, it will be received by the event loop above." +msgstr "" + +#: src\5-1-syncd-and-sai.md:698 +msgid "" +"The callback `g_notification_callbacks.on_fdb_event` stored in SAI " +"implementation will be called to handle this event." +msgstr "" + +#: src\5-1-syncd-and-sai.md:699 +msgid "" +"It then calls `NotificationHandler::onFdbEvent` in Syncd to serialize the " +"event and put it into the notification queue:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:710 +msgid "" +"Then the notification thread is signaled to pick up this event from the " +"queue, then process it under the syncd lock:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:713 src\5-1-syncd-and-sai.md:743 +#: src\5-1-syncd-and-sai.md:763 src\5-1-syncd-and-sai.md:797 +msgid "// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:720 +msgid "// When notification arrives, it will signal this condition variable." +msgstr "" + +#: src\5-1-syncd-and-sai.md:723 +msgid "// Process notifications in the queue." +msgstr "" + +#: src\5-1-syncd-and-sai.md:731 +msgid "// Call from NotificationProcessor::processNotification" +msgstr "" + +#: src\5-1-syncd-and-sai.md:740 +msgid "" +"Now, it goes into the event dispatching and handling logic. " +"`syncProcessNotification` function is essentially a series of `if-else` " +"statements, which calls the corresponding handling function based on the " +"event type:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:755 +msgid "\"unknown notification: %s\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:760 +msgid "" +"For each event, the handling function deserializes the event and processes " +"it, such as `handle_fdb_event` and `process_on_fdb_event`:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:779 +msgid "// Check FDB event notification data here" +msgstr "" + +#: src\5-1-syncd-and-sai.md:788 +msgid "// Send notification" +msgstr "" + +#: src\5-1-syncd-and-sai.md:794 +msgid "" +"Finally, it's written to ASIC_DB via [NotificationProducer](./4-2-3-" +"notification-producer-consumer.md) to notify `orchagent`:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:808 +msgid "// File: src/sonic-sairedis/syncd/RedisNotificationProducer.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:814 +msgid "" +"// The m_notificationProducer is created in the ctor of " +"RedisNotificationProducer as below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:815 +msgid "" +"// m_notificationProducer = std::make_shared" +"(m_db.get(), REDIS_TABLE_NOTIFICATIONS_PER_DB(dbName));" +msgstr "" + +#: src\5-1-syncd-and-sai.md:820 +msgid "That's it! This is basically how things work in high level in `Syncd`!" +msgstr "" + +#: src\5-1-syncd-and-sai.md:825 src\5-2-2-route-update-in-frr.md:742 +#: src\5-2-3-route-update-in-sonic.md:741 +msgid "" +"[Github repo: sonic-sairedis](https://github.com/sonic-net/sonic-sairedis/)" +msgstr "" + +#: src\5-2-bgp.md:3 +msgid "" +"[BGP](https://datatracker.ietf.org/doc/html/rfc4271) might be the most " +"commonly used and important feature in switches. In this section, we take a " +"deeper look at BGP-related workflows." +msgstr "" + +#: src\5-2-bgp.md:5 +msgid "BGP Processes" +msgstr "" + +#: src\5-2-bgp.md:7 +msgid "" +"SONiC uses [FRRouting](https://frrouting.org/) as its BGP implementation, " +"responsible for handling the BGP protocol. FRRouting is an open-source " +"routing software that supports multiple routing protocols, including BGP, " +"OSPF, IS-IS, RIP, PIM, LDP, etc. When a new version of FRR is released, " +"SONiC synchronizes it to the [SONiC FRR repository: sonic-frr](https://" +"github.com/sonic-net/sonic-frr), with each version corresponding to a branch " +"such as `frr/8.2`." +msgstr "" + +#: src\5-2-bgp.md:9 +msgid "" +"FRR mainly consists of two major parts. The first part includes the " +"implementations of each protocol, where processes are named `*d.` When they " +"receive routing update notifications, they inform the second part, the " +"\"zebra\" process. The `zebra` process performs route selection and " +"synchronizes the best routing information to the kernel. Its main structure " +"is shown below:" +msgstr "" + +#: src\5-2-bgp.md:30 +msgid "" +"In SONiC, these FRR processes all run inside the `bgp` container. In " +"addition, to integrate FRR with Redis, SONiC runs a process called " +"`fpmsyncd` (Forwarding Plane Manager syncd) within the `bgp` container. Its " +"main function is to listen to kernel routing updates and synchronize them to " +"the `APPL_DB`. Because it is not part of FRR, its implementation is located " +"in the [sonic-swss](https://github.com/sonic-net/sonic-swss) repository." +msgstr "" + +#: src\5-2-bgp.md:36 src\5-2-1-bgp-cli.md:92 +#: src\5-2-2-route-update-in-frr.md:740 src\5-2-3-route-update-in-sonic.md:739 +msgid "[Github repo: sonic-frr](https://github.com/sonic-net/sonic-frr)" +msgstr "" + +#: src\5-2-bgp.md:37 src\5-2-1-bgp-cli.md:94 +#: src\5-2-2-route-update-in-frr.md:743 src\5-2-3-route-update-in-sonic.md:742 +msgid "" +"[RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://datatracker.ietf.org/" +"doc/html/rfc4271)" +msgstr "" + +#: src\5-2-bgp.md:38 src\5-2-1-bgp-cli.md:95 +#: src\5-2-2-route-update-in-frr.md:744 src\5-2-3-route-update-in-sonic.md:743 +msgid "[FRRouting](https://frrouting.org/)" +msgstr "" + +#: src\5-2-1-bgp-cli.md:3 +msgid "`show` Command" +msgstr "" + +#: src\5-2-1-bgp-cli.md:5 +msgid "" +"Since BGP is implemented using FRR, naturally, the `show` command will " +"forward the direct request to FRR's `vtysh`. The key code is as follows:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:8 +msgid "# file: src/sonic-utilities/show/bgp_frr_v4.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:8 +msgid "# 'summary' subcommand (\"show ip bgp summary\")" +msgstr "" + +#: src\5-2-1-bgp-cli.md:16 +msgid "# file: src/sonic-utilities/utilities_common/bgp_util.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:19 +msgid "# The IPv6 case is omitted here for simplicity" +msgstr "" + +#: src\5-2-1-bgp-cli.md:20 +msgid "\"show ip bgp summary json\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:26 +msgid "'sudo'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:26 +msgid "'-c'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:30 +msgid "We can also verify by running `vtysh` directly:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:59 +msgid "`config` Command" +msgstr "" + +#: src\5-2-1-bgp-cli.md:61 +msgid "" +"Meanwhile, the `config` command directly operates on `CONFIG_DB` to achieve " +"configuration changes." +msgstr "" + +#: src\5-2-1-bgp-cli.md:63 +msgid "Take remove neighbor as an example. The key code is shown as follows:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:66 +msgid "# file: src/sonic-utilities/config/main.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:70 +msgid "\"Remove BGP neighbor configuration from the device\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:73 +msgid "'neighbor'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:74 +msgid "'neighbor_ip_or_hostname'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:74 +msgid "''" +msgstr "" + +#: src\5-2-1-bgp-cli.md:76 +msgid "" +"\"\"\"Removes BGP neighbor configuration (internal or external) from the " +"device\"\"\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:93 src\5-2-2-route-update-in-frr.md:741 +#: src\5-2-3-route-update-in-sonic.md:740 +msgid "" +"[Github repo: sonic-utilities](https://github.com/sonic-net/sonic-utilities)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:3 +msgid "" +"Route update is almost the most important workflow in SONiC. The entire " +"process starts from the `bgpd` process and eventually reaches the ASIC chip " +"through SAI. Many processes are involved in between, and the workflow is " +"quite complex. However, once we understand it, we can understand the design " +"of SONiC and many other configuration workflows much better. Therefore, in " +"this section, we will deeply dive into its overall process." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:5 +msgid "" +"To help us understand the workflow on the code level, we divide this " +"workflow into two major parts: how FRR handles route changes in this " +"chapter, and how the SONiC updates the routes and integrates with FRR in the " +"next chapter." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:7 +msgid "FRR Handling Route Changes" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:38 +msgid "" +"```admonish note\n" +"Regarding the implementation of FRR, this section focuses more on explaining " +"its workflow from the code perspective rather than the details of its BGP " +"implementation. If you want to learn about the details of FRR's BGP " +"implementation, you can refer to the [official documentation](https://docs." +"frrouting.org/en/latest/bgp.html).\n" +"```" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:42 +msgid "`bgpd` Handling Route Changes" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:44 +msgid "" +"`bgpd` is the process in FRR specifically used to handle BGP sessions. It " +"opens TCP port 179 to establish BGP connections with neighbors and handles " +"routing table update requests. When a route changes, FRR also uses this " +"session to notify other neighbors." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:46 +msgid "" +"When a request arrives at `bgpd`, it will land on the io thread first: " +"`bgp_io`. As the name suggests, this thread is responsible for network read " +"and write operations in `bgpd`:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:49 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_io.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:55 +msgid "// Read packets here" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:58 +msgid "" +"// If we have more than 1 complete packet, mark it and process it later." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:73 +msgid "" +"After the packet is read, `bgpd` sends it to the main thread for processing. " +"Here, `bgpd` dispatches the packet based on its type. And the route update " +"requests will be handed over to `bpg_update_receive` for processing:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:76 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_packet.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:86 +msgid "/* read in the packet length and type */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:102 +msgid "// Process BGP UPDATE message for peer." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:111 +msgid "// Parse attributes and NLRI" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:122 +msgid "// More parsing here" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:128 +msgid "/* End-of-RIB received */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:133 +msgid "/* Best path selection */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:147 +msgid "" +"Then, `bgpd` starts checking for better paths and updates its local routing " +"table (RIB, Routing Information Base):" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:150 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_route.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:150 +msgid "/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:159 +msgid "/* Process the route list */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:183 +msgid "/* Best path selection. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:189 +msgid "/* FIB update. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:204 +msgid "/* Withdraw the route from the kernel. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:209 +msgid "/* EVPN route injection and clean up */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:217 +msgid "" +"Finally, `bgp_zebra_announce` notifies `zebra` to update the kernel routing " +"table through `zclient`." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:220 src\5-2-2-route-update-in-frr.md:231 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_zebra.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:228 +msgid "" +"`zclient` communicates with `zebra` using a local socket and provides a " +"series of callback functions to receive notifications from `zebra`. The key " +"code is shown as follows:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:236 +msgid "/* Set default values. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:255 +msgid "/* Connect to zebra. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:264 +msgid "" +"In the `bgpd` container, we can find the socket file used for `zebra` " +"communication in the `/run/frr` directory for simple verification:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:273 +msgid "`zebra` Updating Routing Table" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:275 +msgid "" +"Since FRR supports many routing protocols, if each routing protocol updates " +"kernel independently, conflicts will inevitably arise, because it is " +"difficult to coordinate. Therefore, FRR uses a separate process to " +"communicate with all routing protocol handling processes, merges the " +"information, and then update the kernel routing table. This process is " +"`zebra`." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:277 +msgid "" +"In `zebra`, kernel updates occur in a separate data plane handling thread: " +"`dplane_thread`. All requests are sent to `zebra` through `zclient`, then " +"get processed, and finally get forwarded to `dplane_thread` for handling. In " +"whis way, the route update will always be in order, which avoids any " +"conflicts to happen." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:279 +msgid "" +"When `zebra` starts, it registers all request handlers. When a request " +"arrives, the corresponding handler will be called based on the request type. " +"And here is the key code:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:282 src\5-2-2-route-update-in-frr.md:298 +msgid "// File: src/sonic-frr/frr/zebra/zapi_msg.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:295 +msgid "" +"Take adding a route (`zread_route_add`) as an example to explain the later " +"workflow. From the following code, we can see that when a new route arrives, " +"`zebra` will start checking and updating its internal routing table:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:307 +msgid "// Decode zclient request" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:314 +msgid "// Allocate new route entry." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:320 +msgid "// Init nexthop entry, if we have an id, then add route." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:328 +msgid "// Update stats. IPv6 is omitted here for simplicity." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:332 +msgid "// File: src/sonic-frr/frr/zebra/zebra_rib.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:344 +msgid "/* Find table and nexthop entry */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:349 +msgid "/* Attach the re to the nhe's nexthop group. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:352 +msgid "/* Make it sure prefixlen is applied to the prefix. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:353 +msgid "/* Set default distance by route type. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:356 +msgid "/* Lookup route node.*/" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:360 +msgid "" +"/* If this route is kernel/connected route, notify the dataplane to update " +"kernel route table. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:365 +msgid "/* Link new re to node. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:369 +msgid "/* Clean up */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:375 +msgid "" +"Here, `rib_addnode` will forward this route add request to the rib " +"processing thread, where the requests are being processed sequentially:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:395 +msgid "" +"Then, the request arrives at the RIB processing thread: `rib_process`, which " +"further selects the best route and adds it to `zebra`'s internal routing " +"table (RIB):" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:398 +msgid "/* Core function for processing routing information base. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:415 +msgid "/* Check every route entry and select the best route. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:433 +msgid "/* RNODE_FOREACH_RE */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:436 +msgid "/* Update fib according to selection results */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:444 +msgid "/* Remove all RE entries queued for removal */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:445 +msgid "/* Check if the dest can be deleted now. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:450 +msgid "" +"For new routes, `rib_process_add_fib` is called to add them to `zebra`'s " +"internal routing table and notify the dplane to update the kernel routing " +"table:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:455 +msgid "\"new route selected\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:458 +msgid "/* If labeled-unicast route, install transit LSP. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:474 +msgid "/* Install the resolved nexthop object first. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:477 +msgid "" +"/* If this is a replace to a new RE let the originator of the RE know that " +"they've lost */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:481 +msgid "/* Update fib selection */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:484 +msgid "" +"/* Make sure we update the FPM any time we send new information to the " +"kernel. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:485 +msgid "\"installing in kernel\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:487 +msgid "/* Send add or update */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:494 +msgid "" +"There are two important operations here: one is to call the `dplane_route_*` " +"functions to update the kernel routing table, and the other is the " +"`hook_call` that appears twice here. The FPM hook function is hooked here to " +"receive and forward routing table update notifications." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:496 +msgid "Here, let's look at them one by one:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:498 +msgid "`dplane` Updating Kernel Routing Table" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:500 +msgid "" +"Let's look at the dplane `dplane_route_*` functions first. They are " +"essentially do the same thing: simply pack the request and put it into the " +"`dplane_thread` message queue:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:503 src\5-2-2-route-update-in-frr.md:541 +msgid "// File: src/sonic-frr/frr/zebra/zebra_dplane.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:522 +msgid "/* Create and init context */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:525 +msgid "/* Enqueue context for processing */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:528 +msgid "/* Update counter */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:538 +msgid "" +"Then, on the data plane handling thread `dplane_thread`, in its message " +"loop, it take messages from the queue one by one and call their handling " +"functions:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:549 +msgid "/* Process work here */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:552 +msgid "/* Check for zebra shutdown */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:553 +msgid "/* Dequeue completed work from the provider */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:556 +msgid "/* Locate next provider */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:564 +msgid "" +"By default, `dplane_thread` uses `kernel_dplane_process_func` to process the " +"messages. Inside this function, different kernel operations will be invoked " +"based on the request type:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:578 +msgid "" +"/* A previous provider plugin may have asked to skip the kernel update. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:584 +msgid "/* Dispatch to appropriate kernel-facing apis */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:602 +msgid "/* Call into the synchronous kernel-facing code here */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:608 +msgid "" +"And `kernel_route_update` is the real kernel operation. It notifies the " +"kernel of route updates through netlink:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:611 +msgid "// File: src/sonic-frr/frr/zebra/rt_netlink.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:611 +msgid "" +"// Update or delete a prefix from the kernel, using info from a dataplane " +"context." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:633 +msgid "" +"// Routing table change via netlink interface, using a dataplane context " +"object" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:637 +msgid "// Build netlink request." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:648 +msgid "/* Talk to netlink socket. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:653 +msgid "FPM Route Update Forwarding" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:655 +msgid "" +"FPM (Forwarding Plane Manager) is the protocol in FRR used to notify other " +"processes of route changes. Its main logic code is in `src/sonic-frr/frr/" +"zebra/zebra_fpm.c`. It supports two protocols by default: `protobuf` and " +"`netlink`. The one used in SONiC is the `netlink` protocol." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:657 +msgid "" +"As mentioned earlier, it is implemented through hook functions. By listening " +"for route changes in the RIB, the updates are forwarded to other processes " +"through a local socket. This hook is registered at startup. And the most " +"relevant one to us is the `rib_update` hook, as shown below:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:669 +msgid "\"zebra_fpm\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:670 +msgid "\"zebra FPM (Forwarding Plane Manager) module\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:675 +msgid "" +"When the `rib_update` hook is called, the `zfpm_trigger_update` function " +"will be called, which puts the route update info into the fpm forwarding " +"queue and triggers a write operation:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:683 +msgid "// Queue the update request" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:698 +msgid "" +"The write callback takes the update from the queue, converts it into the FPM " +"message format, and forwards it to other processes through a local socket:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:709 +msgid "// Convert route info to buffer here." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:712 +msgid "" +"// Write to socket until we don' have anything to write or cannot write " +"anymore (partial write)." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:726 +msgid "" +"/* Stop processing the queues if zfpm_g->obuf is full or we do not have more " +"updates to process */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:733 +msgid "At this point, FRR's work is done." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:745 src\5-2-3-route-update-in-sonic.md:744 +msgid "[FRRouting - BGP](https://datatracker.ietf.org/doc/html/rfc4271)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:746 src\5-2-3-route-update-in-sonic.md:745 +msgid "" +"[FRRouting - FPM](https://docs.frrouting.org/projects/dev-guide/en/latest/" +"fpm.html)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:747 src\5-2-3-route-update-in-sonic.md:746 +msgid "" +"[Understanding EVPN Pure Type 5 Routes](https://www.juniper.net/" +"documentation/us/en/software/junos/evpn-vxlan/topics/concept/evpn-route-" +"type5-understanding.html)" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:3 +msgid "" +"After the work of FRR is done, the route update information is forwarded to " +"SONiC, either via Netlink or FPM. This causes a series of operations in " +"SONiC, and eventually updates the route table in the ASIC." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:5 +msgid "The main workflow is shown as below:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:40 +msgid "`fpmsyncd` Updating Route Configuration in Redis" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:42 +msgid "" +"First, let's start from the source. When `fpmsyncd` launches, it starts " +"listening for FPM and Netlink events to receive route change messages and " +"forward to `RouteSync` for processing:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:45 +msgid "// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:54 +msgid "// Register netlink message handler" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:68 +msgid "// Launching FPM server and wait for zebra to connect." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:75 +msgid "" +"// If connection is closed, keep retrying until it succeeds, before handling " +"any other events." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:76 +msgid "\"Connection lost, reconnecting...\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:83 +msgid "" +"In `FpmLink`, the FPM events will be converted into Netlink messages. This " +"unifies the message that being sent to `RouteSync` to Netlink. And " +"`RouteSync::onMsg` will be called for processing them (for how Netlink " +"receives and processes messages, please refer to [4.1.2 Netlink](./4-1-2-" +"netlink.html)):" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:85 +msgid "" +"One small thing to notice is that - EVPN Type 5 messages must be processed " +"in raw message form, so `RouteSync::onMsgRaw` will be called." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:88 +msgid "// File: src/sonic-swss/fpmsyncd/fpmlink.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:88 +msgid "// Called from: FpmLink::readData()" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:96 +msgid "/* Read all netlink messages inside FPM message */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:99 +#: src\5-2-3-route-update-in-sonic.md:341 +msgid "/*" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:112 +msgid "/* EVPN Type5 Add route processing */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:113 +msgid "/* This will call into onRawMsg() */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:116 +msgid "/* This will call into onMsg() */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:130 +msgid "" +"Next, when `RouteSync` receives a route change message, it makes judgments " +"and dispatches in `onMsg` and `onMsgRaw`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:133 +#: src\5-2-3-route-update-in-sonic.md:188 +msgid "// File: src/sonic-swss/fpmsyncd/routesync.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:144 +msgid "// Refill Netlink cache here" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:158 +msgid "" +"/* If the master device name starts with VNET_PREFIX, it is a VNET route." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:165 +msgid "/* Otherwise, it is a regular route (include VRF route). */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:175 +msgid "" +"From the code above, we can see that there are four different route " +"processing entry points. These different routes will be finally written to " +"different tables in `APPL_DB` through their respective [ProducerStateTable]" +"(./4-2-5-producer-consumer-state-table.md):" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Route Type" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Entry Point" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Table" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "MPLS" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "`onLabelRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "LABLE_ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +msgid "Vnet VxLan Tunnel Route" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "`onVnetRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +msgid "VNET_ROUTE_TUNNEL_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "Other Vnet Routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "VNET_ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +msgid "EVPN Type 5" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +msgid "`onEvpnRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "Regular Routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "`onRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:185 +msgid "" +"Here we take regular routes as an example. The implementation of other " +"functions is different, but the basic idea is the same:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:191 +msgid "// Parse route info from nl_object here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:194 +msgid "// Get nexthop lists" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:201 +msgid "" +"// Build route info here, including protocol, interface, next hops, MPLS, " +"weights etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:203 +msgid "\"protocol\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:204 +msgid "\"nexthop\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:211 +msgid "// Push to ROUTE_TABLE via ProducerStateTable." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:213 +msgid "\"RouteTable set msg: %s %s %s %s\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:218 +msgid "`orchagent` Processing Route Configuration Changes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:220 +msgid "" +"Next, these route information will come to `orchagent`. When `orchagent` " +"starts, it creates `VNetRouteOrch` and `RouteOrch` objects, which are used " +"to listen and process Vnet-related routes and EVPN/regular routes " +"respectively:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:223 +#: src\5-2-3-route-update-in-sonic.md:439 +msgid "// File: src/sonic-swss/orchagent/orchdaemon.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:242 +msgid "" +"The entry function that process the incoming messages for all Orch objects " +"is `doTask`. `RouteOrch` and `VNetRouteOrch` are the same. Here we take " +"`RouteOrch` as an example to see how it handles route changes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:248 +msgid "" +"Before we dive into the code, we have a few things to note for `RouteOrch`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:250 +msgid "" +"From the above `init` function, we can see that `RouteOrch` not only manages " +"regular routes but also manages MPLS routes. The logic for handling these " +"two types of routes is different. Therefore, in the following code, to " +"simplify, we only show the logic for handling the regular routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:251 +msgid "" +"Since `ProducerStateTable` transmits and receives messages in batches, " +"`RouteOrch` also processes the route updates in batches. To support batch " +"processing, `RouteOrch` uses `EntityBulker gRouteBulker` to " +"cache the SAI route objects that need to be changed, and then applies these " +"route object changes to SAI at the end of the `doTask()` function." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:252 +msgid "" +"Route operations require a lot of other information, such as the status of " +"each port, the status of each neighbor, the status of each VRF, etc. To " +"obtain this information, `RouteOrch` interacts with other Orch objects, such " +"as `PortOrch`, `NeighOrch`, `VRFOrch`, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:254 +msgid "" +"Let's start with the `RouteOrch::doTask` function. It parses the incoming " +"route operation messages, then calls the `addRoute` or `removeRoute` " +"function to create or delete routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:257 +#: src\5-2-3-route-update-in-sonic.md:381 +msgid "// File: src/sonic-swss/orchagent/routeorch.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:260 +msgid "" +"// Calling PortOrch to make sure all ports are ready before processing route " +"messages." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:263 +msgid "" +"// Call doLabelTask() instead, if the incoming messages are from MPLS " +"messages. Otherwise, move on as regular routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:266 +msgid "/* Default handling is for ROUTE_TABLE (regular routes) */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:269 +msgid "// Add or remove routes with a route bulker" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:274 +msgid "// Parse route operation from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:279 +msgid "// resync application:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:280 +msgid "" +"// - When routeorch receives 'resync' message (key = \"resync\", op = \"SET" +"\"), it marks all current routes as dirty" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:281 +msgid "" +"// and waits for 'resync complete' message. For all newly received routes, " +"if they match current dirty routes," +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:282 +msgid "// it unmarks them dirty." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:283 +msgid "" +"// - After receiving 'resync complete' (key = \"resync\", op != \"SET\") " +"message, it creates all newly added routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:284 +msgid "// and removes all dirty routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:287 +msgid "// Parsing VRF and IP prefix from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:290 +msgid "// Process regular route operations." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:293 +msgid "// Parse and validate route attributes from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:298 +msgid "" +"// If the nexthop_group is empty, create the next hop group key based on the " +"IPs and aliases." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:299 +msgid "" +"// Otherwise, get the key from the NhgOrch. The result will be stored in the " +"\"nhg\" variable below." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:304 +msgid "" +"// Here the nexthop_group is empty, so we create the next hop group key " +"based on the IPs and aliases." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:323 +msgid "// Here we have a nexthop_group, so we get the key from the NhgOrch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:330 +msgid "// Now we start to create the SAI route entry." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:333 +msgid "" +"// Skip certain routes, such as not valid, directly routes to tun0, " +"linklocal or multicast routes, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:336 +msgid "// Create SAI route entry in addRoute function." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:357 +msgid "// Handle other ops, like DEL_COMMAND for route deletion, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:361 +msgid "// Flush the route bulker, so routes will be written to syncd and ASIC" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:364 +msgid "// Go through the bulker results." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:365 +msgid "" +"// Handle SAI failures, update neighbors, counters, send notifications in " +"add/removeRoutePost functions." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:368 +msgid "/* Remove next hop group if the reference count decreases to zero */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:374 +msgid "" +"Here we take `addRoute` as an example. It mainly does a few things below:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:376 +msgid "" +"Get next hop information from `NeighOrch` and check if the next hop is " +"really available." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:377 +msgid "" +"If the route is a new or re-added back while waiting to be deleted, a new " +"SAI route object will be created." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:378 +msgid "If it is an existing route, the existing SAI route object is updated." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:384 +msgid "// Get nexthop information from NeighOrch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:385 +msgid "" +"// We also need to check PortOrch for inband port, IntfsOrch to ensure the " +"related interface is created and etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:388 +msgid "// Start to sync the SAI route entry." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:397 +msgid "// Create a new route entry in this case." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:398 +msgid "//" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:399 +msgid "" +"// In case the entry is already pending removal in the bulk, it would be " +"removed from m_syncdRoutes during the bulk call." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:400 +msgid "" +"// Therefore, such entries need to be re-created rather than set attribute." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:410 +msgid "/* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:418 +msgid "// Update existing route entry in this case." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:420 +msgid "" +"// Set the packet action to forward when there was no next hop (dropped) and " +"not pointing to blackhole." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:429 +msgid "" +"// Only 1 case is listed here as an example. Other cases are handled with " +"similar logic by calling set_entry_attributes as well." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:436 +msgid "" +"After creating and setting up all the routes, `RouteOrch` calls " +"`gRouteBulker.flush()` to write all the routes to `ASIC_DB`. The `flush()` " +"function is straightforward: it processes all requests in batches, with each " +"batch being 1000 by default, defined in `OrchDaemon` and passed through the " +"constructor:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:442 +msgid "// File: src/sonic-swss/orchagent/bulker.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:454 +msgid "// Bulk remove entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:456 +#: src\5-2-3-route-update-in-sonic.md:492 +msgid "" +"// Split into batches of max_bulk_size, then call flush. Similar to " +"creating_entries, so details are omitted." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:463 +msgid "// Bulk create entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:465 +msgid "" +"// Split into batches of max_bulk_size, then call flush_creating_entries to " +"call SAI batch create API to create" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:466 +msgid "// the objects in batch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:479 +msgid "// Batch create here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:490 +msgid "// Bulk update existing entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:509 +msgid "// Call SAI bulk create API" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:515 +msgid "// Set results back to input entries and clean up the batch below." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:528 +msgid "" +"// flush_removing_entries and flush_setting_entries are similar to " +"flush_creating_entries, so we omit them here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:533 +msgid "SAI Object Forwarding in `orchagent`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:535 +msgid "" +"At this point, you might have noticed something strange - The `EntityBulker` " +"seems to be directly calling the SAI API. Shouldn't they be called in " +"`syncd`? If we follow the SAI API objects passed to `EntityBulker`, we will " +"even find that `sai_route_api_t` is indeed the SAI interface, and there is " +"SAI initialization code in `orchagent`, as follows:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:538 +msgid "" +"// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:538 +#: src\5-2-3-route-update-in-sonic.md:700 +msgid "/**" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:554 +msgid "// File: src/sonic-swss/orchagent/saihelper.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:562 +msgid "\"Context config file %s exists\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:585 +msgid "" +"I believe whoever saw this code for the first time will definitely feel " +"confused. But don't worry, this is actually the SAI object forwarding " +"mechanism in `orchagent`." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:587 +msgid "" +"If you are familiar with RPC, the `proxy-stub` pattern might sounds very " +"familar to you - using a unified way to define the interfaces called by both " +"parties in communication, implementing message serialization and sending on " +"the client side, and implementing message receiving, deserialization, and " +"dispatching on the server side. Here, SONiC does something similar: using " +"the SAI API itself as a unified interface, implementing message " +"serialization and sending for `orchagent` to call, and implementing message " +"receiving, deserialization, and dispatch functions in `syncd`." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:589 +msgid "" +"Here, the sending end is called `ClientSai`, implemented in `src/sonic-" +"sairedis/lib/ClientSai.*`. Serialization and deserialization are implemented " +"in SAI metadata: `src/sonic-sairedis/meta/sai_serialize.h`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:592 +msgid "// File: src/sonic-sairedis/lib/ClientSai.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:601 +msgid "// File: src/sonic-sairedis/meta/sai_serialize.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:602 +msgid "// Serialize" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:606 +msgid "// Deserialize" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:612 +msgid "" +"When `orchagent` is compiled, it links to `libsairedis`, which implements " +"the SAI client and handles the serialization and message sending:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:615 +msgid "# File: src/sonic-swss/orchagent/Makefile.am" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:616 +msgid "" +"$(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta -" +"lsaimetadata -lswsscommon -lzmq" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:619 +msgid "" +"Here, we use Bulk Create as an example to see how `ClientSai` serializes and " +"sends the SAI API call:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:622 +#: src\5-2-3-route-update-in-sonic.md:709 +msgid "// File: src/sonic-sairedis/lib/ClientSai.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:638 +msgid "" +"// Server is responsible for generate new OID but for that we need switch ID" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:639 +msgid "" +"// to be sent to server as well, so instead of sending empty oids we will" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:640 +msgid "// send switch IDs" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:646 +msgid "" +"// Since user requested create, OID value was created remotely and it was " +"returned in m_lastCreateOids" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:668 +msgid "// Calling SAI serialize APIs to serialize all objects" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:674 +msgid "\"NULL\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:684 +msgid "// Send to syncd via the communication channel." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:687 +msgid "// Wait for response from syncd." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:692 +msgid "" +"Finally, `ClientSai` calls `m_communicationChannel->set()` to send the " +"serialized SAI objects to `syncd`. This channel, before the 202106 version, " +"was the [ProducerTable based on Redis](https://github.com/sonic-net/sonic-" +"sairedis/blob/202106/lib/inc/RedisChannel.h). Possibly for efficiency " +"reasons, starting from the 202111 version, this channel has been changed to " +"[ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/" +"ZeroMQChannel.h)." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:695 +msgid "" +"// File: https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/" +"RedisChannel.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:728 +msgid "" +"For the inter-process communication, we are going to skip the details here. " +"Please feel free to refer to the [Redis-based channels](./4-2-redis-based-" +"channels.md) described in Chapter 4." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:730 +msgid "`syncd` Updating ASIC" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:732 +msgid "" +"Finally, when the SAI objects are generated and sent to `syncd`, `syncd` " +"will receive, processa and updates `ASIC_DB`, then finally updates the ASIC. " +"We have already described this workflow in detail in the [Syncd-SAI Workflow]" +"(./5-1-syncd-and-sai.md), so we will skip them here. For more details, " +"please refer to the [Syncd-SAI Workflow chapter](./5-1-syncd-and-sai.md)." +msgstr "" + +#: src\6-boot.md:1 +msgid "Boot" +msgstr "" + +#: src\6-1-cold-boot.md:1 +msgid "Cold Boot" +msgstr "" + +#: src\6-2-fast-boot.md:1 +msgid "Fast Boot" +msgstr "" + +#: src\6-3-warm-boot.md:1 +msgid "Warm Boot" +msgstr "" diff --git a/po/en.po b/po/en.po deleted file mode 100644 index 72a8962..0000000 --- a/po/en.po +++ /dev/null @@ -1,12452 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: SONiC入门指南\n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2023-06-25 20:04-0700\n" -"Last-Translator: r12f \n" -"Language-Team: English\n" -"Language: en\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: src/SUMMARY.md:3 -msgid "SONiC入门指南" -msgstr "Getting Started with SONiC" - -#: src/SUMMARY.md:4 -msgid "安装" -msgstr "Installation" - -#: src/SUMMARY.md:5 -msgid "虚拟测试环境" -msgstr "Hello World! Virtually!" - -#: src/SUMMARY.md:6 -msgid "常用命令 (WIP)" -msgstr "Frequently used commands (WIP)" - -#: src/SUMMARY.md:7 -msgid "核心组件简介" -msgstr "Core Components Intro" - -#: src/SUMMARY.md:8 -msgid "Redis数据库" -msgstr "Redis Database" - -#: src/SUMMARY.md:9 -msgid "服务与工作流简介" -msgstr "Service and Workflow Intro" - -#: src/SUMMARY.md:10 -msgid "核心容器" -msgstr "Core Containers" - -#: src/SUMMARY.md:11 -msgid "SAI" -msgstr "SAI" - -#: src/SUMMARY.md:12 -msgid "开发上手指南" -msgstr "Dev Guide" - -#: src/SUMMARY.md:13 -msgid "代码仓库" -msgstr "Code repo" - -#: src/SUMMARY.md:14 -msgid "编译" -msgstr "Compile" - -#: src/SUMMARY.md:15 -msgid "测试 (WIP)" -msgstr "Test (WIP)" - -#: src/SUMMARY.md:16 -msgid "调试 (WIP)" -msgstr "Debugging (WIP)" - -#: src/SUMMARY.md:17 -msgid "SAI调试 (WIP)" -msgstr "SAI debugging (WIP)" - -#: src/SUMMARY.md:18 -msgid "通信机制" -msgstr "Communication" - -#: src/SUMMARY.md:19 -msgid "与内核的通信" -msgstr "Communication with Kernel" - -#: src/SUMMARY.md:20 -msgid "命令行调用" -msgstr "Command line call" - -#: src/SUMMARY.md:21 -msgid "Netlink" -msgstr "Netlink" - -#: src/SUMMARY.md:22 -msgid "基于Redis的通信" -msgstr "Redis-based Communication" - -#: src/SUMMARY.md:23 -msgid "Redis封装" -msgstr "Redis Ops" - -#: src/SUMMARY.md:24 -msgid "通信层" -msgstr "Communication layer" - -#: src/SUMMARY.md:25 -msgid "基于ZMQ的通信 (WIP)" -msgstr "ZMQ-based Communication (WIP)" - -#: src/SUMMARY.md:26 -msgid "服务层封装 - Orch" -msgstr "Service Layer - Orch" - -#: src/SUMMARY.md:27 -msgid "事件分发和错误处理" -msgstr "Event Dispatching and Error Handling" - -#: src/SUMMARY.md:28 -#, fuzzy -msgid "核心组件解析" -msgstr "Core Components" - -#: src/SUMMARY.md:29 -msgid "Syncd与SAI" -msgstr "" - -#: src/SUMMARY.md:30 -msgid "BGP" -msgstr "" - -#: src/SUMMARY.md:31 -msgid "BGP命令实现" -msgstr "BGP CLI Commands" - -#: src/SUMMARY.md:32 -msgid "BGP路由变更下发" -msgstr "BGP route update" - -#: src/SUMMARY.md:33 -msgid "启动流程 (WIP)" -msgstr "Boot (WIP)" - -#: src/SUMMARY.md:34 -msgid "冷启动 (WIP)" -msgstr "Cold boot (WIP)" - -#: src/SUMMARY.md:35 -msgid "快速启动 (WIP)" -msgstr "Fast boot (WIP)" - -#: src/SUMMARY.md:36 -msgid "热启动 (WIP)" -msgstr "Warm boot (WIP)" - -#: src/1-intro.md:1 -msgid "# SONiC入门指南" -msgstr "# Getted Started with SONiC" - -#: src/1-intro.md:3 -msgid "## 为什么要做SONiC" -msgstr "## Why SONiC" - -#: src/1-intro.md:5 -msgid "" -"我们知道交换机内部都有一套可大可小的操作系统,用于配置和查看交换机的状态。但" -"是,从1986年第一台交换机面世开始,虽然各个厂商都在进行着相关的开发,到现在为" -"止种类也相当的多,但是依然存在一些问题,比如:" -msgstr "" -"We know that there is a operating system running inside every switches, no " -"matter if it is complicated or not. It is used for configuring and checking " -"the status of the switch. Since the first switch came out in 1986, with " -"various manufacturers have been doing related development, nowadays, we have " -"a lot of different OSes. However, there are still some problems, for example:" - -#: src/1-intro.md:7 -msgid "" -"1. 生态封闭,不开源,主要是为了支持自家的硬件,无法很好的兼容其他厂商的设备\n" -"2. 支持的场景很有限,难以使用同一套系统去支撑大规模的数据中心中复杂多变的场" -"景\n" -"3. 升级可能会导致网络中断,难以实现无缝升级,这对于云提供商来说有时候是致命" -"的\n" -"4. 设备功能升级缓慢,难以很好的支持快速的产品迭代" -msgstr "" -"1. Closed ecosystem and not open-sourced, primarily used for supporting its " -"own hardware and not very compatible with other manufacturers' devices.\n" -"2. Some OSes only supports limited scenarios, making it challenging to " -"support a variety of complex scenarios in large-scale data centers.\n" -"3. Upgrades may lead to network interruptions, making seamless upgrades " -"difficult, which can often be a deal breaker for cloud providers.\n" -"4. New features or bug fixes are coming out slowly, hard to suppoprt rapid " -"development in production." - -#: src/1-intro.md:12 -msgid "" -"所以,微软在2016年发起了开源项目SONiC,希望能够通过开源的方式,让SONiC能够成" -"为一个通用的网络操作系统,从而解决上面的问题。而且,由于微软在Azure中大范围的" -"使用SONiC,也保证了SONiC的实现确实能够承受大规模的生产环境的考验,这也是SONiC" -"的一个优势。" -msgstr "" -"Therefore, Microsoft initiated an open-source project in 2016 - SONiC, so " -"that we can solve the problems above by building universal network operating " -"system (NOS). Moreover, Microsoft is widely using SONiC in Azure, which " -"ensures SONiC can indeed support large scale cloud environments. This is " -"also an advantage of SONiC." - -#: src/1-intro.md:14 -msgid "## 主体架构" -msgstr "## Architecture" - -#: src/1-intro.md:16 -msgid "" -"SONiC是微软开发的基于debian的开源的网络操作系统,它的设计核心思想有三个:" -msgstr "" -"SONiC is an open-source network operating system (NOS) based on Debian and " -"developed by Microsoft. It is designed with three core principles:" - -#: src/1-intro.md:18 -msgid "" -"1. **硬件和软件解耦**:通过SAI(Switch Abstraction Interface)将硬件的操作抽" -"象出来,从而使得SONiC能够支持多种硬件平台。这一层抽象层由SONiC定义,由各个厂" -"商来实现。\n" -"2. **使用docker容器将软件微服务化**:SONiC上的主要功能都被拆分成了一个个的" -"docker容器,和传统的网络操作系统不同,升级系统可以只对其中的某个容器进行升" -"级,而不需要整体升级和重启,这样就可以很方便的进行升级和维护,支持快速的开发" -"和迭代。\n" -"3. **使用redis作为中心数据库对服务进行解耦**:绝大部分服务的配置和状态最后都" -"被存储到中心的redis数据库中,这样不仅使得所有的服务可以很轻松的进行协作(数据" -"存储和pubsub),也可以让我们很方便的在上面开发工具,使用统一的方法对各个服务" -"进行操作和查询,而不用担心状态丢失和协议兼容问题,最后还可以很方便的进行状态" -"的备份和恢复。" -msgstr "" -"1. **Hardware and software decoupling**: Abstract the hardware layer via SAI " -"(Switch Abstraction Interface), which enables SONiC to support multiple " -"hardware platforms. This layer of abstraction is defined by SONiC and " -"implemented by various manufacturers.\n" -"2. **Containerized service management**: The features of SONiC are broken " -"down into individual Docker containers. Unlike traditional network operating " -"systems, system upgrades can be done on a single container without requiring " -"a full upgrade and restart. This makes upgrades and maintenance convenient, " -"which helps rapid development and iteration.\n" -"3. **Redis as the central database for service decoupling**: The " -"configuration and status of most services are stored in the central Redis " -"database. This not only allows all services to easily collaborate with each " -"other (data storage and communication) but also simplifies the tool " -"development by providing a unified way to query and configure various " -"services without worrying about state loss and protocol compatibility " -"issues. Finally, it is very convenient to back up and restore the configs as " -"well." - -#: src/1-intro.md:22 -msgid "" -"这让SONiC拥有了非常开放的生态([Community][SONiCLanding],[Workgroups]" -"[SONiCWG],[Devices][SONiCDevices]),总体而言,SONiC的架构如下图所示:" -msgstr "" -"This gives SONiC a very open ecosystem ([Community][SONiCLanding], " -"[Workgroups][SONiCWG], [Devices][SONiCDevices]). Overall, the architecture " -"of SONiC is as shown in the following diagram:" - -#: src/1-intro.md:26 src/2-core-components-intro.md:17 -msgid "_(Source: [SONiC Wiki - Architecture][SONiCArch])_" -msgstr "_(Source: [SONiC Wiki - Architecture][SONiCArch])_" - -#: src/1-intro.md:28 -msgid "" -"当然,这样的设计也有一些缺点,比如:对磁盘的占用会变大,不过,现在一点点存储" -"空间并不是什么很大的问题,而且这个问题也都可以通过一些方法来解决。" -msgstr "" -"Of course, such design also has some disadvantages, such as: the disk usage " -"will increase. However, nowadays storage space is usually not a big issue, " -"and it can also be solved or eased by various ways." - -#: src/1-intro.md:30 -msgid "## 发展方向" -msgstr "## Future Direction" - -#: src/1-intro.md:32 -msgid "" -"虽然交换机已经发展很多很多年了,但是随着现在云的发展,对网络的要求也越来越" -"高,不管是直观的需求,比如更大的带宽,更大的容量,还是最新的研究,比如,带内" -"计算,端网融合等等,都对交换机的发展提出了更高的要求和挑战,也促使着各大厂商" -"和研究机构不断的进行创新。SONiC也一样,随着时间的发展,需求一点没有减少。" -msgstr "" -"Although switches have been developed for many years, with the development " -"of the cloud nowadays, the demands of network becomes higher and higher. No " -"matter it's intuitive features, such as larger bandwidth and larger " -"capacity, or the latest research, such as in-band computing, end-network " -"fusion, etc., all result in higher requirements and challenges to the " -"development of switches, as well as continuous innovations from " -"manufacturers and research institutions. The same goes for SONiC. As time " -"goes on, the number of feature request has not decreased at all." - -#: src/1-intro.md:34 -msgid "" -"关于SONiC的发展方向,我们可以在它的[Roadmap][SONiCPlanning]中看到。如果大家对" -"最新的动态感兴趣,也可以关注它的Workshop,比如,最近的[OCP Global Summit " -"2022 - SONiC Workshop][SONiCWorkshop]。这里就不展开了。" -msgstr "" -"Regarding the future direction of SONiC, we can see it in its [Roadmap]" -"[SONiCPlanning]. If you are interested in the latest updates, we can also " -"follow its Workshop, such as the recent [OCP Global Summit 2022 - SONiC " -"Workshop][SONiCWorkshop]." - -#: src/1-intro.md:36 -msgid "## 感谢" -msgstr "## Acknowledgement" - -#: src/1-intro.md:38 -msgid "感谢以下朋友的帮助和贡献,没有你们也就没有这本入门指南!" -msgstr "" -"Huge thanks to the following friends for their help and contributions! " -"Without you there would be no way to get this book done!" - -#: src/1-intro.md:40 -msgid "[@bingwang-ms](https://github.com/bingwang-ms)" -msgstr "[@bingwang-ms](https://github.com/bingwang-ms)" - -#: src/1-intro.md:42 -msgid "# License" -msgstr "# License" - -#: src/1-intro.md:44 -msgid "" -"本书使用 [署名-非商业性使用-相同方式共享(CC BY-NC-SA)4.0 许可协议](https://" -"creativecommons.org/licenses/by-nc-sa/4.0/)。" -msgstr "" -"This book uses the [CC BY-NC-SA 4.0 License Agreement](https://" -"creativecommons.org/licenses/by-nc-sa/4.0/)." - -#: src/1-intro.md:46 src/1-1-install.md:164 -#: src/1-2-hello-world-virtually.md:187 src/1-3-command-cheatsheet.md:184 -#: src/2-core-components-intro.md:19 src/2-1-database.md:74 -#: src/2-2-services-intro.md:74 src/2-3-key-containers.md:179 -#: src/2-4-sai-intro.md:164 src/3-1-code-repos.md:131 src/3-2-compile.md:202 -#: src/4-communications.md:19 src/4-1-1-exec.md:36 src/4-1-2-netlink.md:70 -#: src/4-2-1-redis-wrappers.md:33 src/4-2-2-redis-messaging-layer.md:318 -#: src/4-4-orch-layer.md:34 src/4-5-event-polling-and-error-handling.md:119 -#: src/5-1-syncd-and-sai.md:813 src/5-2-bgp.md:32 -#: src/5-2-1-bgp-command-impl.md:87 src/5-2-2-bgp-route-update-workflow.md:1460 -msgid "# 参考资料" -msgstr "# References" - -#: src/1-intro.md:48 -msgid "" -"1. [SONiC Wiki - Architecture][SONiCArch]\n" -"2. [SONiC Wiki - Roadmap Planning][SONiCPlanning]\n" -"3. [SONiC Landing Page][SONiCLanding]\n" -"4. [SONiC Workgroups][SONiCWG]\n" -"5. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"6. [SONiC User Manual][SONiCManual]\n" -"7. [OCP Global Summit 2022 - SONiC Workshop][SONiCWorkshop]" -msgstr "" -"1. [SONiC Wiki - Architecture][SONiCArch]\n" -"2. [SONiC Wiki - Roadmap Planning][SONiCPlanning]\n" -"3. [SONiC Landing Page][SONiCLanding]\n" -"4. [SONiC Workgroups][SONiCWG]\n" -"5. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"6. [SONiC User Manual][SONiCManual]\n" -"7. [OCP Global Summit 2022 - SONiC Workshop][SONiCWorkshop]" - -#: src/1-1-install.md:1 -msgid "# 安装" -msgstr "# Installation" - -#: src/1-1-install.md:3 -msgid "" -"如果你自己就拥有一台交换机,或者想购买一台交换机,在上面安装SONiC,那么请认真" -"阅读这一小节,否则可以自行跳过。:D" -msgstr "" -"If you own a switch yourself or plan to buy one, then install SONiC on it, " -"please read this section carefully; otherwise, feel free skip it. :D" - -#: src/1-1-install.md:5 -msgid "## 交换机选择和SONiC安装" -msgstr "## Switch Selection and SONiC Installation" - -#: src/1-1-install.md:7 -msgid "" -"首先,请确认你的交换机是否支持SONiC,SONiC目前支持的交换机型号可以在[这里]" -"[SONiCDevices]找到,如果你的交换机型号不在列表中,那么就需要联系厂商,看看是" -"否有支持SONiC的计划。有很多交换机是不支持SONiC的,比如:" -msgstr "" -"First, please confirm whether your switch supports SONiC or not. The switch " -"models currently supported by SONiC can be found [here][SONiCDevices]. If " -"your switch model is not on the list, you will need to contact the " -"manufacturer to see if there is a plan to support SONiC. Many switches do " -"not have SONiC support yet, for example:" - -#: src/1-1-install.md:9 -msgid "" -"1. 普通针对家用的交换机,这些交换机的硬件配置都比较低(即便支持的带宽很高,比" -"如[MikroTik CRS504-4XQ-IN][MikroTik100G],虽然它支持100GbE网络,但是它只有" -"16MB的Flash存储和64MB的RAM,所以基本只能跑它自己的RouterOS了)。\n" -"2. 有些虽然是数据中心用的交换机,但是可能由于型号老旧,厂商并没有计划支持" -"SONiC。" -msgstr "" -"1. Typical home switches: They usually have relatively low hardware specs " -"(even though some of them have high bandwidth support, such as [MikroTik " -"CRS504-4XQ-IN][MikroTik100G], it supports 100GbE network, but it only has " -"16MB of Flash storage and 64MB of RAM, so basically, it can only run its own " -"NOS - RouterOS).\n" -"2. Some data center switches might be outdated models, and manufacturers " -"have no plans to support SONiC." - -#: src/1-1-install.md:12 -msgid "" -"对于安装过程,由于每一家厂商的交换机设计不同,其底层接口各有差别,所以,其安" -"装方法也都有所差别,这些差别主要集中在两个地方:" -msgstr "" -"Regarding the installation process, because different switches from " -"different manufacturers might have very different design, the installation " -"process can also be different. These differences show up in two major areas:" - -#: src/1-1-install.md:14 -msgid "" -"1. 每个厂商都会有自己的[SONiC Build][SONiCDevices],还有的厂商会在SONiC的基础" -"之上进行扩展开发,为自己的交换机支持更多的功能,比如:[Dell Enterprise SONiC]" -"[DellSonic],[EdgeCore Enterprise SONiC][EdgeCoreSONiC],所以需要根据自己的交" -"换机选择对应的版本。\n" -"2. 每个厂商的交换机也会支持不同的安装方式,有一些是直接使用USB对ROM进行" -"Flash,有一些是通过ONIE进行安装,这也需要根据自己的交换机来进行配置。" -msgstr "" -"1. Every manufacturer will have their own [SONiC Builds][SONiCDevices], and " -"some manufacturers will create their own version of SONiC on top of the " -"original one, supporting more functions for their switches. For example:" -"[Dell Enterprise SONiC][DellSonic], [EdgeCore Enterprise SONiC][EdgeCore " -"SONiC]. Therefore, we need to choose the right version carefully according " -"your switch.\n" -"2. Different switch might also have different installation process. Some of " -"them are directly installed from USB by flashing the ROM, and some of them " -"using ONIE. This also needs to be checked according to your switch." - -#: src/1-1-install.md:17 -msgid "" -"所以,虽然安装方法各有差别,但是总体而言,安装的步骤都是差不多的。请联系自己" -"的厂商,获取对应的安装文档,然后按照文档进行安装即可。" -msgstr "" -"So, although the installation process may vary, in general, the installation " -"steps are similar. Please contact your vendor for the detailed installation " -"documentation, and then follow it through." - -#: src/1-1-install.md:19 -msgid "## 配置交换机" -msgstr "## Configure the switch" - -#: src/1-1-install.md:21 -msgid "" -"安装好之后,我们需要进行一些基础设置,部分设置是通用的,我们在这里简单总结一" -"下。" -msgstr "" -"Once SONiC is installed, we need to do some basic settings, some of which " -"are common, no matter which type of switch you are using, and we'll briefly " -"summarize them here." - -#: src/1-1-install.md:23 -msgid "### 设置admin密码" -msgstr "### Set admin password" - -#: src/1-1-install.md:25 -msgid "默认SONiC的账号密码是admin:YourPaSsWoRd,使用默认密码显然不安全:" -msgstr "" -"The default SONiC account and password is admin:YourPaSsWoRd, using the " -"default password is obviously not secure. So, please remember to change it:" - -#: src/1-1-install.md:27 -msgid "" -"```bash\n" -"sudo passwd admin\n" -"```" -msgstr "" -"```bash\n" -"sudo passwd admin\n" -"```" - -#: src/1-1-install.md:31 -msgid "### 设置风扇转速" -msgstr "### Set fan speed" - -#: src/1-1-install.md:33 -msgid "" -"数据中心用的交换机风扇声音都特别的大!比如,我用的交换机是Arista 7050QX-32S," -"上面有4个风扇,最高能到每分钟17000转,放在车库中,高频的啸叫即便是在二楼隔着3" -"面墙还是能听得到,所以如果你是在家使用的话,建议对其进行一些设置,将转速调" -"低。" -msgstr "" -"The switch fans in the data center are exceptionally loud! For example, the " -"switch I use is Arista 7050QX-32S, which has 4 fans on it and can go up to " -"17,000 rpm. Although I put it in my garage, the high frequency whine can " -"still be heard even on the second floor behind 3 walls, so if you are using " -"it at home, it is recommended to change some settings to turn down the speed." - -#: src/1-1-install.md:35 -msgid "" -"可惜,[由于SONiC并没有cli对风扇转速的规则进行控制][SONiCThermal],所以我们需" -"要通过手动修改pmon容器中的配置文件的方式来进行设置。" -msgstr "" -"Unfortunately, [since SONiC does not have a cli to control the rules for fan " -"speed][SONiCThermal], we need to set it by manually modifying the " -"configuration file in the pmon container." - -#: src/1-1-install.md:37 -msgid "" -"```bash\n" -"# Enter pmon container\n" -"sudo docker exec -it pmon bash\n" -"\n" -"# Use pwmconfig to detect all pwm fans and create configuration file. The " -"configuration file will be created at /etc/fancontrol.\n" -"pwmconfig\n" -"\n" -"# Start fancontrol and make sure it works. If it doesn't work, you can run " -"fancontrol directly to see what's wrong.\n" -"VERBOSE=1 /etc/init.d/fancontrol start\n" -"VERBOSE=1 /etc/init.d/fancontrol status\n" -"\n" -"# Exit pmon container\n" -"exit\n" -"\n" -"# Copy the configuration file from the container to the host, so that the " -"configuration will not be lost after reboot.\n" -"# This command needs to know what is the model of your switch, for example, " -"the command I need to run here is as follows. If your switch model is " -"different, please modify it yourself.\n" -"sudo docker cp pmon:/etc/fancontrol /usr/share/sonic/device/x86_64-" -"arista_7050_qx32s/fancontrol\n" -"```" -msgstr "" -"```bash\n" -"# Enter pmon container\n" -"sudo docker exec -it pmon bash\n" -"\n" -"# Use pwmconfig to detect all pwm fans and create configuration file. The " -"configuration file will be created at /etc/fancontrol.\n" -"pwmconfig\n" -"\n" -"# Start fancontrol and make sure it works. If it doesn't work, you can run " -"fancontrol directly to see what's wrong.\n" -"VERBOSE=1 /etc/init.d/fancontrol start\n" -"VERBOSE=1 /etc/init.d/fancontrol status\n" -"\n" -"# Exit pmon container\n" -"exit\n" -"\n" -"# Copy the configuration file from the container to the host, so that the " -"configuration will not be lost after reboot.\n" -"# This command needs to know what is the model of your switch, for example, " -"the command I need to run here is as follows. If your switch model is " -"different, please modify it yourself.\n" -"sudo docker cp pmon:/etc/fancontrol /usr/share/sonic/device/x86_64-" -"arista_7050_qx32s/fancontrol\n" -"```" - -#: src/1-1-install.md:56 -msgid "### 设置交换机Management Port IP" -msgstr "### Set management port IP" - -#: src/1-1-install.md:58 -msgid "" -"一般的数据中心用的交换机都提供了Serial Console连接的方式,但是其速度实在是太" -"慢了,所以我们在安装完成之后,都会尽快的把Management Port给设置好,然后通过" -"SSH的方式来进行管理。" -msgstr "" -"The data center switches usually provides Serial Console connection, but its " -"speed is too slow, so it is better for us to have the Management Port set up " -"as soon as possible, then we can use SSH to manage it, which is way faster." - -#: src/1-1-install.md:60 -msgid "" -"一般来说,management port的设备名是eth0,所以我们可以通过SONiC的配置命令来进" -"行设置:" -msgstr "" -"Generally, the device name of the management port is eth0, so we can set it " -"by using the following SONiC command:" - -#: src/1-1-install.md:62 -msgid "" -"```bash\n" -"# sudo config interface ip add eth0 \n" -"# IPv4\n" -"sudo config interface ip add eth0 192.168.1.2/24 192.168.1.1\n" -"\n" -"# IPv6\n" -"sudo config interface ip add eth0 2001::8/64 2001::1\n" -"```" -msgstr "" -"```bash\n" -"# sudo config interface ip add eth0 \n" -"# IPv4\n" -"sudo config interface ip add eth0 192.168.1.2/24 192.168.1.1\n" -"\n" -"# IPv6\n" -"sudo config interface ip add eth0 2001::8/64 2001::1\n" -"```" - -#: src/1-1-install.md:71 -msgid "### 创建网络配置" -msgstr "### Create network configuration" - -#: src/1-1-install.md:73 -msgid "" -"新安装完的SONiC交换机会有一个默认的网络配置,这个配置有很多问题,比如对于" -"10.0.0.0的IP的使用,如下:" -msgstr "" -"The newly installed SONiC switch will have a default network configuration, " -"which has many problems, such as for the use of the 10.0.0.0 IP, as follows:" - -#: src/1-1-install.md:75 src/1-2-hello-world-virtually.md:118 -msgid "" -"```bash\n" -"admin@sonic:~$ show ip interfaces\n" -"Interface Master IPv4 address/mask Admin/Oper BGP Neighbor " -"Neighbor IP\n" -"----------- -------- ------------------- ------------ -------------- " -"-------------\n" -"Ethernet0 10.0.0.0/31 up/up ARISTA01T2 " -"10.0.0.1\n" -"Ethernet4 10.0.0.2/31 up/up ARISTA02T2 " -"10.0.0.3\n" -"Ethernet8 10.0.0.4/31 up/up ARISTA03T2 " -"10.0.0.5\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ show ip interfaces\n" -"Interface Master IPv4 address/mask Admin/Oper BGP Neighbor " -"Neighbor IP\n" -"----------- -------- ------------------- ------------ -------------- " -"-------------\n" -"Ethernet0 10.0.0.0/31 up/up ARISTA01T2 " -"10.0.0.1\n" -"Ethernet4 10.0.0.2/31 up/up ARISTA02T2 " -"10.0.0.3\n" -"Ethernet8 10.0.0.4/31 up/up ARISTA03T2 " -"10.0.0.5\n" -"```" - -#: src/1-1-install.md:84 -msgid "" -"所以我们需要创建一个新的网络配置,然后将我们使用的Port都放入到这个网络配置" -"中。这里简单的方法就是创建一个VLAN,使用VLAN Routing:" -msgstr "" -"So we need to update the network configuration of the ports we like to use. " -"The easiest way is to create a VLAN, then put all the ports into the VLAN, " -"so we can use VLAN Routing to route the packets:" - -#: src/1-1-install.md:86 -msgid "" -"```bash\n" -"# Create untagged vlan\n" -"sudo config vlan add 2\n" -"\n" -"# Add IP to vlan\n" -"sudo config interface ip add Vlan2 10.2.0.0/24\n" -"\n" -"# Remove all default IP settings\n" -"show ip interfaces | tail -n +3 | grep Ethernet | awk '{print \"sudo config " -"interface ip remove\", $1, $2}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Add all ports to the new vlan\n" -"show interfaces status | tail -n +3 | grep Ethernet | awk '{print \"sudo " -"config vlan member add -u 2\", $1}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Enable proxy arp, so switch can respond to arp requests from hosts\n" -"sudo config vlan proxy_arp 2 enabled\n" -"\n" -"# Save config, so it will be persistent after reboot\n" -"sudo config save -y\n" -"```" -msgstr "" -"```bash\n" -"# Create untagged vlan\n" -"sudo config vlan add 2\n" -"\n" -"# Add IP to vlan\n" -"sudo config interface ip add Vlan2 10.2.0.0/24\n" -"\n" -"# Remove all default IP settings\n" -"show ip interfaces | tail -n +3 | grep Ethernet | awk '{print \"sudo config " -"interface ip remove\", $1, $2}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Add all ports to the new vlan\n" -"show interfaces status | tail -n +3 | grep Ethernet | awk '{print \"sudo " -"config vlan member add -u 2\", $1}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Enable proxy arp, so switch can respond to arp requests from hosts\n" -"sudo config vlan proxy_arp 2 enabled\n" -"\n" -"# Save config, so it will be persistent after reboot\n" -"sudo config save -y\n" -"```" - -#: src/1-1-install.md:106 -msgid "这样就完成了,我们可以通过show vlan brief来查看一下:" -msgstr "That's it! Now, we can take a look at it by running `show vlan brief`:" - -#: src/1-1-install.md:108 -msgid "" -"```\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-------------+----------------+-------------" -"+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | " -"DHCP Helper Address |\n" -"+===========+==============+=============+================+=============+=======================+\n" -"| 2 | 10.2.0.0/24 | Ethernet0 | untagged | enabled " -"| |\n" -"...\n" -"| | | Ethernet124 | untagged | " -"| |\n" -"+-----------+--------------+-------------+----------------+-------------" -"+-----------------------+\n" -"```" -msgstr "" -"```\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-------------+----------------+-------------" -"+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | " -"DHCP Helper Address |\n" -"+===========+==============+=============+================+=============+=======================+\n" -"| 2 | 10.2.0.0/24 | Ethernet0 | untagged | enabled " -"| |\n" -"...\n" -"| | | Ethernet124 | untagged | " -"| |\n" -"+-----------+--------------+-------------+----------------+-------------" -"+-----------------------+\n" -"```" - -#: src/1-1-install.md:119 -msgid "### 配置主机" -msgstr "### Configure the host" - -#: src/1-1-install.md:121 -msgid "" -"如果你家里只有一台主机使用多网口连接交换机进行测试,那么我们还需要在主机上进" -"行一些配置,以保证流量会通过网卡,流经交换机,否则,请跳过这一步。" -msgstr "" -"If you only have one machine and try to connect a dual-port NIC to your " -"switch for testing, then we will also need some changes on the machine to " -"ensure that traffic will go through the NIC and switch, otherwise, feel free " -"to skip this step." - -#: src/1-1-install.md:123 -msgid "" -"这里网上的攻略很多,比如使用iptables中的DNAT和SNAT创建一个虚拟地址,但是过程" -"非常繁琐,经过一些实验,我发现最简单的办法就是将其中一个网口移动到一个新的网" -"络命名空间中,就可以了,即便使用的是同一个网段的IP,也不会有问题。" -msgstr "" -"There are many guidances on the internet here, such as using iptable DNAT " -"and SNAT rules to create a virtual address, but the process is very tedious. " -"After some experiments, I found that the easiest way is to simply move one " -"of the nic into a new network namespace, even if you are using the IP of the " -"same network segment." - -#: src/1-1-install.md:125 -msgid "" -"比如,我家使用的是Netronome Agilio CX 2x40GbE,它会创建两个interface:" -"`enp66s0np0`和`enp66s0np1`,我们这里可以将`enp66s0np1`移动到一个新的网络命名" -"空间中,再配置好ip地址就可以了:" -msgstr "" -"For example, I uses Netronome Agilio CX 2x40GbE, which creates two " -"interfaces: `enp66s0np0` and `enp66s0np1`. With the following commands, we " -"can move `enp66s0np1` to a new network namespace and give it a ip address:" - -#: src/1-1-install.md:127 -msgid "" -"```bash\n" -"# Create a new network namespace\n" -"sudo ip netns add toy-ns-1\n" -"\n" -"# Move the interface to the new namespace\n" -"sudo ip link set enp66s0np1 netns toy-ns-1\n" -"\n" -"# Setting up IP and default routes\n" -"sudo ip netns exec toy-ns-1 ip addr add 10.2.0.11/24 dev enp66s0np1\n" -"sudo ip netns exec toy-ns-1 ip link set enp66s0np1 up\n" -"sudo ip netns exec toy-ns-1 ip route add default via 10.2.0.1\n" -"```" -msgstr "" -"```bash\n" -"# Create a new network namespace\n" -"sudo ip netns add toy-ns-1\n" -"\n" -"# Move the interface to the new namespace\n" -"sudo ip link set enp66s0np1 netns toy-ns-1\n" -"\n" -"# Setting up IP and default routes\n" -"sudo ip netns exec toy-ns-1 ip addr add 10.2.0.11/24 dev enp66s0np1\n" -"sudo ip netns exec toy-ns-1 ip link set enp66s0np1 up\n" -"sudo ip netns exec toy-ns-1 ip route add default via 10.2.0.1\n" -"```" - -#: src/1-1-install.md:140 -msgid "这样就可以了,我们可以通过iperf来测试一下,并在交换机上进行确认:" -msgstr "" -"That's it! Now, we can now test our setup with iperf and confirm the traffic " -"on switch:" - -#: src/1-1-install.md:142 -msgid "" -"```bash\n" -"# On the host (enp66s0np0 has ip 10.2.0.10 assigned)\n" -"$ iperf -s --bind 10.2.0.10\n" -"\n" -"# Test within the new network namespace\n" -"$ sudo ip netns exec toy-ns-1 iperf -c 10.2.0.10 -i 1 -P 16\n" -"------------------------------------------------------------\n" -"Client connecting to 10.2.0.10, TCP port 5001\n" -"TCP window size: 85.0 KByte (default)\n" -"------------------------------------------------------------\n" -"...\n" -"[SUM] 0.0000-10.0301 sec 30.7 GBytes 26.3 Gbits/sec\n" -"[ CT] final connect times (min/avg/max/stdev) = 0.288/0.465/0.647/0.095 ms " -"(tot/err) = 16/0\n" -"\n" -"# Confirm on switch\n" -"admin@sonic:~$ show interfaces counters\n" -" IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR " -"RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP " -"TX_OVR\n" -"----------- ------- ---------- ------------ --------- -------- " -"-------- -------- ---------- ------------ --------- -------- -------- " -"--------\n" -" Ethernet4 U 2,580,140 6190.34 KB/s 0.12% 0 " -"3,783 0 51,263,535 2086.64 MB/s 41.73% 0 " -"0 0\n" -" Ethernet12 U 51,261,888 2086.79 MB/s 41.74% 0 " -"1 0 2,580,317 6191.00 KB/s 0.12% 0 0 " -"0\n" -"```" -msgstr "" -"```bash\n" -"# On the host (enp66s0np0 has ip 10.2.0.10 assigned)\n" -"$ iperf -s --bind 10.2.0.10\n" -"\n" -"# Test within the new network namespace\n" -"$ sudo ip netns exec toy-ns-1 iperf -c 10.2.0.10 -i 1 -P 16\n" -"------------------------------------------------------------\n" -"Client connecting to 10.2.0.10, TCP port 5001\n" -"TCP window size: 85.0 KByte (default)\n" -"------------------------------------------------------------\n" -"...\n" -"[SUM] 0.0000-10.0301 sec 30.7 GBytes 26.3 Gbits/sec\n" -"[ CT] final connect times (min/avg/max/stdev) = 0.288/0.465/0.647/0.095 ms " -"(tot/err) = 16/0\n" -"\n" -"# Confirm on switch\n" -"admin@sonic:~$ show interfaces counters\n" -" IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR " -"RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP " -"TX_OVR\n" -"----------- ------- ---------- ------------ --------- -------- " -"-------- -------- ---------- ------------ --------- -------- -------- " -"--------\n" -" Ethernet4 U 2,580,140 6190.34 KB/s 0.12% 0 " -"3,783 0 51,263,535 2086.64 MB/s 41.73% 0 " -"0 0\n" -" Ethernet12 U 51,261,888 2086.79 MB/s 41.74% 0 " -"1 0 2,580,317 6191.00 KB/s 0.12% 0 0 " -"0\n" -"```" - -#: src/1-1-install.md:166 -msgid "" -"1. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"2. [SONiC Thermal Control Design][SONiCThermal]\n" -"3. [Dell Enterprise SONiC Distribution][DellSONiC]\n" -"4. [Edgecore Enterprise SONiC Distribution][EdgeCoreSONiC]\n" -"5. [Mikrotik CRS504-4XQ-IN][MikroTik100G]" -msgstr "" -"1. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"2. [SONiC Thermal Control Design][SONiCThermal]\n" -"3. [Dell Enterprise SONiC Distribution][DellSONiC]\n" -"4. [Edgecore Enterprise SONiC Distribution][EdgeCoreSONiC]\n" -"5. [Mikrotik CRS504-4XQ-IN][MikroTik100G]" - -#: src/1-2-hello-world-virtually.md:1 -msgid "# 虚拟测试环境" -msgstr "# Virtual test environment" - -#: src/1-2-hello-world-virtually.md:3 -msgid "" -"虽然SONiC功能强大,但是大部分时候一台能够支持SONiC系统的交换机价格并不便宜," -"如果你只是想试一试SONiC,但是又不想花钱买一台SONiC的硬件设备,那么这一章一定" -"不能错过,这一章会总结一下如何通过GNS3在本地搭建一个虚拟的SONiC的Lab,让你可" -"以很快的在本地体验一把SONiC的基本功能。" -msgstr "" -"Although SONiC is powerful, it is usually not cheap to get a switch that " -"supports SONiC. If you would like to give SONiC a try, but don't want to " -"spend too much money on getting a SONiC-supported device, then you are in " -"the right place. This chapter will guide you on how to use GNS3 to build a " -"virtual SONiC's Lab locally, so that you can quickly experience the basic " -"functionalities of SONiC locally." - -#: src/1-2-hello-world-virtually.md:5 -msgid "" -"在本地运行SONiC的方法很好几种,比如docker + vswitch,p4软交换机等等,对于初次" -"使用而言,用GNS3可能是最方便快捷的了,所以本文就以GNS3为例,介绍一下如何在本" -"地搭建一个SONiC的Lab。那么,我们就开始吧!" -msgstr "" -"Despite there are multiple ways to run SONiC locally, such as docker + " -"vswitch or p4 switch, for first time users, using GNS3 is probably the most " -"convenient and fast way. So, we will be using GNS3 as an example in this " -"chapter and introduce how to build your own SONiC lab locally. Now, let's " -"get started!" - -#: src/1-2-hello-world-virtually.md:7 -msgid "## 安装GNS3" -msgstr "## Install GNS3" - -#: src/1-2-hello-world-virtually.md:9 -msgid "" -"首先,为了让我们方便而且直观的建立测试用的虚拟网络,我们需要先来安装一下" -"GNS3。" -msgstr "" -"FIrst, to make it easy and intuitive to set up a virtual network for " -"testing, let's get GNS3 installed." - -#: src/1-2-hello-world-virtually.md:11 -msgid "" -"[GNS3,全称为Graphical Network Simulator 3,是一个图形化的网络仿真软件]" -"[GNS3]。它支持多种不同的虚拟化技术,比如:QEMU、VMware、VirtualBox等等。这" -"样,我们在等会搭建虚拟网络的时候,就不需要手动的运行很多命令,或者写脚本了," -"大部分的工作都可以通过图形界面来完成了。" -msgstr "" -"[GNS3 (Graphical Network Simulator 3), is a graphical network simulation " -"software][GNS3]. It supports many different virtualization technologies, " -"such as: QEMU, VMware, VirtualBox, and so on. With GNS3, we can finish " -"majority of the network creation work through GUI, without worrying about " -"remembering and running any commands or writing any scripts." - -#: src/1-2-hello-world-virtually.md:13 -msgid "### 安装依赖" -msgstr "### Install dependencies" - -#: src/1-2-hello-world-virtually.md:15 -msgid "" -"安装它之前,我们需要先安装几个其他的软件:docker, wireshark, putty, qemu, " -"ubridge, libvirt和bridge-utils,已经装好的小伙伴可以自行跳过。" -msgstr "" -"Before installing GNS3, we need to install several other softwares: docker, " -"wireshark, putty, qemu, ubridge, libvirt and bridge-utils. Please feel free " -"to skip this step, if you have already have them installed." - -#: src/1-2-hello-world-virtually.md:17 -msgid "" -"首先是Docker,它们的安装过程,大家可以自己通过下面的传送门去安装:[https://" -"docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)" -msgstr "" -"First is Docker, we can follow their official doc to get it installed: " -"[https://docs.docker.com/engine/install/](https://docs.docker.com/engine/" -"install/)" - -#: src/1-2-hello-world-virtually.md:19 -msgid "" -"其他的在ubuntu上安装都非常简单,只需要执行下面的命令就可以了。这里安装时要注" -"意,ubridge和Wireshark的安装过程中会询问是不是要创建wireshark用户组来bypass " -"sudo,这里一定要选择Yes。" -msgstr "" -"The rest softwares can be easy installed on ubuntu, by running the following " -"commands. Note that the installation process of ubridge and Wireshark will " -"ask if you want to create a wireshark user group to bypass sudo. Please be " -"sure to select Yes here." - -#: src/1-2-hello-world-virtually.md:21 -msgid "" -"```\n" -"sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-" -"utils wireshark putty ubridge\n" -"```" -msgstr "" -"```\n" -"sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-" -"utils wireshark putty ubridge\n" -"```" - -#: src/1-2-hello-world-virtually.md:25 -msgid "安装好了之后,我们就可以来安装GNS3了。" -msgstr "Once it is done, we can start installing GNS3 now." - -#: src/1-2-hello-world-virtually.md:27 -msgid "### 安装GNS3" -msgstr "### Install GNS3" - -#: src/1-2-hello-world-virtually.md:29 -msgid "在Ubuntu上,GNS3的安装非常简单,只需要执行下面的命令就可以了。" -msgstr "" -"On Ubuntu, the installation of GNS3 is very simple, just execute the " -"following commands:" - -#: src/1-2-hello-world-virtually.md:31 -msgid "" -"```bash\n" -"sudo add-apt-repository ppa:gns3/ppa\n" -"sudo apt update \n" -"sudo apt install gns3-gui gns3-server\n" -"```" -msgstr "" -"```bash\n" -"sudo add-apt-repository ppa:gns3/ppa\n" -"sudo apt update \n" -"sudo apt install gns3-gui gns3-server\n" -"```" - -#: src/1-2-hello-world-virtually.md:37 -msgid "" -"然后把你的用户加入到如下的组中,这样GNS3就可以去访问docker,wireshark等功能而" -"不用sudo了。" -msgstr "" -"Then add your user to the following groups so that GNS3 can go to access " -"docker, wireshark, and other functions without sudo." - -#: src/1-2-hello-world-virtually.md:39 -msgid "" -"```bash\n" -"for g in ubridge libvirt kvm wireshark docker; do\n" -" sudo usermod -aG $g \n" -"done\n" -"```" -msgstr "" -"```bash\n" -"for g in ubridge libvirt kvm wireshark docker; do\n" -" sudo usermod -aG $g \n" -"done\n" -"```" - -#: src/1-2-hello-world-virtually.md:45 -msgid "" -"如果你使用的不是Ubuntu,更详细的安装文档可以参考[他们的官方文档]" -"[GNS3Install]。" -msgstr "" -"If you're not using Ubuntu, please follow the [their official documentation]" -"[GNS3Install] here for more detailed installation process." - -#: src/1-2-hello-world-virtually.md:47 -msgid "## 准备SONiC的镜像" -msgstr "## Prepare SONiC image" - -#: src/1-2-hello-world-virtually.md:49 -msgid "" -"在测试之前,我们还需要一个SONiC的镜像。由于需要支持大量不同的厂商,而每个厂商" -"的底层实现都不一样,所以最后每个厂商都会编译一个自己的镜像。这里因为我们在创" -"建虚拟的环境,所以我们需要使用基于VSwitch的镜像来创建虚拟交换机:sonic-vs." -"img.gz。" -msgstr "" -"Before testing, we still need a SONiC image. Since SONiC needs to support " -"various of platforms, and different platform has different underlying " -"implementation, each platform will have their own image. Here, since we are " -"creating a virtual environment, we need to use the image with VSwitch " -"platform to create the virtual switch: sonic-vs.img.gz." - -#: src/1-2-hello-world-virtually.md:51 -msgid "" -"[SONiC镜像的项目在这里](https://github.com/sonic-net/sonic-buildimage),虽然" -"我们可以自己去编译,但是速度实在有点慢,所以为了节省时间,我们可以直接[去这里" -"下载最新的镜像](https://sonic-build.azurewebsites.net/ui/sonic/pipelines/142/" -"builds?branchName=master)。只要找一个最新的成功的Build就行,在Artifacts中找到" -"sonic-vs.img.gz,下载就可以了。" -msgstr "" -"The [project for building SONiC image is here](https://github.com/sonic-net/" -"sonic-buildimage). Although we can build it ourselves, the speed is really " -"slow. To save time, we can directly [download the latest image from here]" -"(https://sonic- build.azurewebsites.net/ui/sonic/pipelines/142/builds?" -"branchName=master). Simply look for the latest successful Build, find sonic-" -"vs.img.gz in Artifacts, and download it." - -#: src/1-2-hello-world-virtually.md:53 -msgid "然后,我们来准备一下项目:" -msgstr "Then, let's get the project prepared:" - -#: src/1-2-hello-world-virtually.md:55 -msgid "" -"```bash\n" -"git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage." -"git\n" -"cd sonic-buildimage/platform/vs\n" -"\n" -"# 将下载的镜像放在这个目录下,然后运行下面这个命令进行解压缩。\n" -"gzip -d sonic-vs.img.gz\n" -"\n" -"# 下面这个命令会生成GNS3的镜像配置文件\n" -"./sonic-gns3a.sh\n" -"```" -msgstr "" -"```bash\n" -"git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage." -"git\n" -"cd sonic-buildimage/platform/vs\n" -"\n" -"# Download the image under this folder, then unzip the image with following " -"commands。\n" -"gzip -d sonic-vs.img.gz\n" -"\n" -"# Run the following command to generate the GNS3 image configuration file:\n" -"./sonic-gns3a.sh\n" -"```" - -#: src/1-2-hello-world-virtually.md:66 -msgid "执行完成之后,我们运行`ls`命令就可以看到我们需要的镜像文件了。" -msgstr "" -"Once it is done, we can see the image file we need by running `ls` command." - -#: src/1-2-hello-world-virtually.md:68 -msgid "" -"```bash\n" -"r12f@r12f-svr:~/code/sonic/sonic-buildimage/platform/vs\n" -"$ l\n" -"total 2.8G\n" -"...\n" -"-rw-rw-r-- 1 r12f r12f 1.1K Apr 18 16:36 SONiC-latest.gns3a # <= 这个是GNS3" -"的镜像配置文件\n" -"-rw-rw-r-- 1 r12f r12f 2.8G Apr 18 16:32 sonic-vs.img # <= 这个是我们" -"解压出来的镜像\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"r12f@r12f-svr:~/code/sonic/sonic-buildimage/platform/vs\n" -"$ l\n" -"total 2.8G\n" -"...\n" -"-rw-rw-r-- 1 r12f r12f 1.1K Apr 18 16:36 SONiC-latest.gns3a # <= This is " -"the GNS3 image configuration file\n" -"-rw-rw-r-- 1 r12f r12f 2.8G Apr 18 16:32 sonic-vs.img # <= This is " -"the unzipped SONiC image file\n" -"...\n" -"```" - -#: src/1-2-hello-world-virtually.md:78 -msgid "## 导入镜像" -msgstr "## Import image into GNS3" - -#: src/1-2-hello-world-virtually.md:80 -msgid "" -"现在,在命令行里面输入`gns3`,就可以启动GNS3了。如果你是ssh到另外一台机器上," -"可以试着启用X11转发,这样就可以在远程运行GNS3,但是图形界面显示在本地了。我就" -"是这样,将GNS3运行在了远程的服务器上,但是图形界面通过MobaXterm显示在了本地的" -"Windows机器上。" -msgstr "" -"Now, we can run `gns3` in command line to start GNS3! If you are ssh into " -"another machine, try enabling X11 forwarding, so that you can run GNS3 " -"remotely, but with the GUI displayed locally. This is what I am did - " -"running GNS3 on the remote server, but with the GUI displayed locally on the " -"Windows machine via MobaXterm." - -#: src/1-2-hello-world-virtually.md:82 -msgid "" -"运行起来之后,GNS3会让我们创建一个项目,很简单,填个目录地址就好。如果你是使" -"用的X11转发,请注意,这个目录是在你远程服务器上,而不是本地。" -msgstr "" -"Once it's running, GNS3 will ask us to create a project, it's simple, just " -"give it a directory path. If you are using X11 forwarding, please note that " -"this directory is on your remote server, not your local machine." - -#: src/1-2-hello-world-virtually.md:86 -msgid "" -"然后,我们就可以通过`File -> Import appliance`来导入我们刚刚生成的镜像了。" -msgstr "" -"Then, we can import the image we just generated via `File -> Import " -"appliance`." - -#: src/1-2-hello-world-virtually.md:90 -msgid "选择我们刚刚生成的`SONiC-latest.gns3a`镜像配置文件,然后点击`Next`。" -msgstr "" -"Select the `SONiC-latest.gns3a` image configuration file we just generated, " -"and click `Next`." - -#: src/1-2-hello-world-virtually.md:94 -msgid "这个时候就可以看到我们的镜像了,点击`Next`。" -msgstr "Now you can see our image file, click `Next`." - -#: src/1-2-hello-world-virtually.md:98 -msgid "" -"这个时候会开始导入镜像,这个过程可能会比较慢,因为GNS3需要将镜像转换成qcow2格" -"式,放入我们的项目目录中。导入完成之后,我们就可以看到我们的镜像了。" -msgstr "" -"Now, it will start importing the image, this process may be slow because " -"GNS3 needs to convert the image to qcow2 format and put it in our project " -"directory. Once the import is complete, we will be able to see our image." - -#: src/1-2-hello-world-virtually.md:102 -msgid "好的!完成!" -msgstr "Great! Image is now imported!" - -#: src/1-2-hello-world-virtually.md:104 -msgid "## 创建网络" -msgstr "## Create virtual network" - -#: src/1-2-hello-world-virtually.md:106 -msgid "好了!现在一切就绪,我们还是创建一个虚拟的网络吧!" -msgstr "" -"Great! Now we have everything is in place, let's create a virtual network " -"for our testing!" - -#: src/1-2-hello-world-virtually.md:108 -msgid "" -"GNS3的图形界面非常的好用,基本上就是打开侧边栏,把交换机拖进来,把VPC拖进来," -"然后把线连起来就可以了。连接好之后记得点上面的Play按钮开始网络模拟。这里我们" -"就不多说了,直接上图。" -msgstr "" -"The GNS3 GUI are really easy to use, Basically, simply open the sidebar, " -"drag in the switch, drag in the VPC, and connect the wires. After everything " -"is connected, click the Play button on top to start the network simulation. " -"Then we should see the network starts running as below: " - -#: src/1-2-hello-world-virtually.md:112 -msgid "" -"接着,在交换机上点击右键,选择`Custom Console`,再选择Putty,就可以打开我们的" -"上面看到的交换机的Console了。这里,SONiC的默认用户名和密码是`admin`和" -"`YourPaSsWoRd`。登录进去之后,我们就可以运行熟悉的命令,用`show interfaces " -"status`或者`show ip interface`来查看网络的状态了。我们这里也可以看到,前面两" -"个我们连接好了的Interface的状态都是`up`的了。" -msgstr "" -"Next, right click on the switch and select `Custom Console`, then select " -"Putty to open the console for our virtual switch. Here, the default username " -"and password for SONiC are `admin` and `YourPaSsWoRd`. Once we are logged " -"in, we can run any SONiC commands, such as `show interfaces status` or `show " -"ip interface` to see the status of the network. As above shows, we can see " -"the status of the two connected interfaces are both `up`!" - -#: src/1-2-hello-world-virtually.md:114 -msgid "## 配置网络" -msgstr "## Configure the network" - -#: src/1-2-hello-world-virtually.md:116 -msgid "" -"SONiC软交换机下,默认的端口使用的是10.0.0.x的子网(如下),而且都是eth pair:" -msgstr "" -"In the SONiC virtual switch, the default ports are all created as eth pairs " -"and all uses the 10.0.0.x subnet (as follows):" - -#: src/1-2-hello-world-virtually.md:127 -msgid "" -"这里,我们比较方便的做法是创建一个小的vlan,把我们的端口都包在里面(我们这里" -"用的是Ethernet4和Ethernet8):" -msgstr "" -"To make everything work, the most convenient way is still creating a vlan " -"and put all the ports in it (we use Ethernet4 and Ethernet8 here):" - -#: src/1-2-hello-world-virtually.md:129 -msgid "" -"```bash\n" -"# Remove old config\n" -"sudo config interface ip remove Ethernet4 10.0.0.2/31\n" -"sudo config interface ip remove Ethernet8 10.0.0.4/31\n" -"\n" -"# Create VLAN with id 2\n" -"sudo config vlan add 2\n" -"\n" -"# Add ports to VLAN\n" -"sudo config vlan member add -u 2 Ethernet4\n" -"sudo config vlan member add -u 2 Ethernet8\n" -"\n" -"# Add IP address to VLAN\n" -"sudo config interface ip add Vlan2 10.0.0.0/24\n" -"```" -msgstr "" -"```bash\n" -"# Remove old config\n" -"sudo config interface ip remove Ethernet4 10.0.0.2/31\n" -"sudo config interface ip remove Ethernet8 10.0.0.4/31\n" -"\n" -"# Create VLAN with id 2\n" -"sudo config vlan add 2\n" -"\n" -"# Add ports to VLAN\n" -"sudo config vlan member add -u 2 Ethernet4\n" -"sudo config vlan member add -u 2 Ethernet8\n" -"\n" -"# Add IP address to VLAN\n" -"sudo config interface ip add Vlan2 10.0.0.0/24\n" -"```" - -#: src/1-2-hello-world-virtually.md:145 -msgid "这样,我们的vlan就创建好了,我们可以通过`show vlan brief`来查看一下:" -msgstr "" -"There you go! Our vlan is created, and we can check it out by `show vlan " -"brief`:" - -#: src/1-2-hello-world-virtually.md:147 -msgid "" -"```bash\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-----------+----------------+-------------" -"+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | DHCP " -"Helper Address |\n" -"+===========+==============+===========+================+=============+=======================+\n" -"| 2 | 10.0.0.0/24 | Ethernet4 | untagged | disabled " -"| |\n" -"| | | Ethernet8 | untagged | " -"| |\n" -"+-----------+--------------+-----------+----------------+-------------" -"+-----------------------+\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-----------+----------------+-------------" -"+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | DHCP " -"Helper Address |\n" -"+===========+==============+===========+================+=============+=======================+\n" -"| 2 | 10.0.0.0/24 | Ethernet4 | untagged | disabled " -"| |\n" -"| | | Ethernet8 | untagged | " -"| |\n" -"+-----------+--------------+-----------+----------------+-------------" -"+-----------------------+\n" -"```" - -#: src/1-2-hello-world-virtually.md:157 -msgid "然后,我们就可以给所有的主机配置一个10.0.0.x的IP地址了。" -msgstr "Then, we can assign a 10.0.0.x IP address to all the virtual host now:" - -#: src/1-2-hello-world-virtually.md:159 -msgid "" -"```bash\n" -"# VPC1\n" -"ip 10.0.0.2 255.0.0.0 10.0.0.1\n" -"\n" -"# VPC2\n" -"ip 10.0.0.3 255.0.0.0 10.0.0.1\n" -"```" -msgstr "" -"```bash\n" -"# VPC1\n" -"ip 10.0.0.2 255.0.0.0 10.0.0.1\n" -"\n" -"# VPC2\n" -"ip 10.0.0.3 255.0.0.0 10.0.0.1\n" -"```" - -#: src/1-2-hello-world-virtually.md:167 -msgid "好的,现在我们来Ping一下吧!" -msgstr "Okay! Time to ping!" - -#: src/1-2-hello-world-virtually.md:171 -msgid "通了!" -msgstr "Tada!" - -#: src/1-2-hello-world-virtually.md:173 -msgid "## 抓包" -msgstr "## Packet capture" - -#: src/1-2-hello-world-virtually.md:175 -msgid "" -"上面,我们安装GNS3前,我们特意安装了Wireshark,这样我们就可以在GNS3里面抓包" -"了。我们只需要右键点击图中我们想抓包的Link上,然后选择`Start capture`,就可以" -"开始抓包了。" -msgstr "" -"As installation process shows above, before we installed GNS3, we purposely " -"installed Wireshark so that we can directly capture packets inside GNS3. All " -"we need to do is right click on the link we want to capture and select " -"`Start capture`." - -#: src/1-2-hello-world-virtually.md:179 -msgid "稍等一下,Wireshark就会自动打开,实时的显示所有的包,非常的方便:" -msgstr "" -"Very soon, Wireshark will be opened up and display all the network packets " -"in real time, which is very convenient:" - -#: src/1-2-hello-world-virtually.md:183 -msgid "## 更多的网络" -msgstr "## More complicated network, please" - -#: src/1-2-hello-world-virtually.md:185 -msgid "" -"除了上面这种最简单的网络搭建,我们其实可以用GNS3搭建很多非常复杂的网络来进行" -"测试,比如多层ECMP + eBGP等等。XFlow Research发布了一篇非常详细的文档来介绍这" -"些内容,感兴趣的小伙伴可以去传送到这篇文档去看看:[SONiC Deployment and " -"Testing Using GNS3][SONiCWithGNS3]。" -msgstr "" -"Besides the simplest network we built above, we can actually use GNS3 to " -"build very complicated network for testing, such as multi-layer ECMP + eBGP, " -"etc. XFlow Research has published a very detailed document on how to do " -"this. If you are interested, please feel free to check it out: [SONiC " -"Deployment and Testing Using GNS3][SONiCWithGNS3]." - -#: src/1-2-hello-world-virtually.md:189 -msgid "" -"1. [GNS3][GNS3]\n" -"2. [GNS3 Linux Install][GNS3Install]\n" -"3. [SONiC Deployment and Testing Using GNS3][SONiCWithGNS3]" -msgstr "" -"1. [GNS3][GNS3]\n" -"2. [GNS3 Linux Install][GNS3Install]\n" -"3. [SONiC Deployment and Testing Using GNS3][SONiCWithGNS3]" - -#: src/1-3-command-cheatsheet.md:1 -msgid "# 常用命令" -msgstr "# Common commands" - -#: src/1-3-command-cheatsheet.md:3 -msgid "" -"为了帮助我们查看和配置SONiC的状态,SONiC提供了大量的CLI命令供我们调用。这些命" -"令大多分为两类:`show`和`config`,他们的格式基本类似,大多都符合下面的格式:" -msgstr "" -"To help us view and configure the status of SONiC, SONiC provides a large " -"number of CLI commands for us to invoke. Most of these commands fall into " -"two categories: `show` and `config`, and they are basically similar in " -"format, most of them conform to the following format:" - -#: src/1-3-command-cheatsheet.md:5 -msgid "" -"```bash\n" -"show [options]\n" -"config [options]\n" -"```" -msgstr "" -"```bash\n" -"show [options]\n" -"config [options]\n" -"```" - -#: src/1-3-command-cheatsheet.md:10 -msgid "" -"SONiC的文档提供了非常详细的命令列表:[SONiC Command Line Interface Guide]" -"[SONiCCommands],但是由于其命令众多,不便于我们初期的学习和使用,所以列出了一" -"些平时最常用的命令和解释,供大家参考。" -msgstr "" -"SONiC's documentation provides a very detailed list of commands: [SONiC " -"Command Line Interface Guide][SONiCCommands], but since its many commands do " -"not facilitate our initial learning and use, some of the most commonly used " -"commands and explanations are listed for your reference." - -#: src/1-3-command-cheatsheet.md:12 -msgid "" -"```admonish info\n" -"SONiC中的所有命令的子命令都可以只打前三个字母,来帮助我们有效的节约输入命令的" -"时间,比如:\n" -"\n" -" show interface transceiver error-status\n" -" \n" -"和下面这条命令是等价的:\n" -"\n" -" show int tra err\n" -"\n" -"为了帮助大家记忆和查找,下面的命令列表都用的全名,但是大家在实际使用的时候," -"可以大胆的使用缩写来减少工作量。\n" -"```" -msgstr "" -"```admonish info\n" -"SONiC中的所有命令的子命令都可以只打前三个字母,来帮助我们有效的节约输入命令的" -"时间,比如:\n" -"\n" -" show interface transceiver error-status\n" -" \n" -"和下面这条命令是等价的:\n" -"\n" -" show int tra err\n" -"\n" -"为了帮助大家记忆和查找,下面的命令列表都用的全名,但是大家在实际使用的时候," -"可以大胆的使用缩写来减少工作量。\n" -"```" - -#: src/1-3-command-cheatsheet.md:24 -msgid "" -"```admonish info\n" -"如果遇到不熟悉的命令,都可以通过输入`-h`或者`--help`来查看帮助信息,比如:\n" -"\n" -" show -h\n" -" show interface --help\n" -" show interface transceiver --help\n" -"\n" -"```" -msgstr "" -"```admonish info\n" -"如果遇到不熟悉的命令,都可以通过输入`-h`或者`--help`来查看帮助信息,比如:\n" -"\n" -" show -h\n" -" show interface --help\n" -" show interface transceiver --help\n" -"\n" -"```" - -#: src/1-3-command-cheatsheet.md:33 -msgid "## General" -msgstr "## General" - -#: src/1-3-command-cheatsheet.md:35 -msgid "" -"```bash\n" -"show version\n" -"\n" -"show uptime\n" -"\n" -"show platform summary\n" -"```" -msgstr "" -"```bash\n" -"show version\n" -"\n" -"show uptime\n" -"\n" -"show platform summary\n" -"```" - -#: src/1-3-command-cheatsheet.md:43 -msgid "## Config" -msgstr "## Config" - -#: src/1-3-command-cheatsheet.md:45 -msgid "" -"```bash\n" -"sudo config reload\n" -"sudo config load_minigraph\n" -"sudo config save -y\n" -"```" -msgstr "" -"```bash\n" -"sudo config reload\n" -"sudo config load_minigraph\n" -"sudo config save -y\n" -"```" - -#: src/1-3-command-cheatsheet.md:51 -msgid "## Docker相关" -msgstr "## Docker related" - -#: src/1-3-command-cheatsheet.md:53 -msgid "" -"```bash\n" -"docker ps\n" -"```" -msgstr "" -"```bash\n" -"docker ps\n" -"```" - -#: src/1-3-command-cheatsheet.md:57 -msgid "" -"```bash\n" -"docker top |\n" -"```" -msgstr "" -"```bash\n" -"docker top |\n" -"```" - -#: src/1-3-command-cheatsheet.md:61 -msgid "" -"```admonish note\n" -"\n" -"如果我们想对所有的docker container进行某个操作,我们可以通过`docker ps`命令来" -"获取所有的container id,然后pipe到`tail -n +2`来去掉第一行的标题,从而实现批" -"量调用。\n" -"\n" -"比如,我们可以通过如下命令来查看所有container中正在运行的所有线程:\n" -"\n" -" $ for id in `docker ps | tail -n +2 | awk '{print $1}'`; do docker top " -"$id; done\n" -" UID PID PPID " -"C STIME TTY " -"TIME CMD\n" -" root 7126 7103 " -"0 Jun09 pts/0 " -"00:02:24 /usr/bin/python3 /usr/local/bin/supervisord\n" -" root 7390 7126 " -"0 Jun09 pts/0 " -"00:00:24 python3 /usr/bin/supervisor-proc-exit-listener --" -"container-name telemetry\n" -" ...\n" -"```" -msgstr "" -"```admonish note\n" -"\n" -"如果我们想对所有的docker container进行某个操作,我们可以通过`docker ps`命令来" -"获取所有的container id,然后pipe到`tail -n +2`来去掉第一行的标题,从而实现批" -"量调用。\n" -"\n" -"比如,我们可以通过如下命令来查看所有container中正在运行的所有线程:\n" -"\n" -" $ for id in `docker ps | tail -n +2 | awk '{print $1}'`; do docker top " -"$id; done\n" -" UID PID PPID " -"C STIME TTY " -"TIME CMD\n" -" root 7126 7103 " -"0 Jun09 pts/0 " -"00:02:24 /usr/bin/python3 /usr/local/bin/supervisord\n" -" root 7390 7126 " -"0 Jun09 pts/0 " -"00:00:24 python3 /usr/bin/supervisor-proc-exit-listener --" -"container-name telemetry\n" -" ...\n" -"```" - -#: src/1-3-command-cheatsheet.md:74 -msgid "## Interfaces / IPs" -msgstr "## Interfaces / IPs" - -#: src/1-3-command-cheatsheet.md:76 -msgid "" -"```bash\n" -"show interface status\n" -"show interface counters\n" -"show interface portchannel\n" -"show interface transceiver info\n" -"show interface transceiver error-status\n" -"sonic-clear counters\n" -"\n" -"TODO: config\n" -"```" -msgstr "" -"```bash\n" -"show interface status\n" -"show interface counters\n" -"show interface portchannel\n" -"show interface transceiver info\n" -"show interface transceiver error-status\n" -"sonic-clear counters\n" -"\n" -"TODO: config\n" -"```" - -#: src/1-3-command-cheatsheet.md:87 -msgid "## MAC / ARP / NDP" -msgstr "## MAC / ARP / NDP" - -#: src/1-3-command-cheatsheet.md:89 -msgid "" -"```bash\n" -"# Show MAC (FDB) entries\n" -"show mac\n" -"\n" -"# Show IP ARP table\n" -"show arp\n" -"\n" -"# Show IPv6 NDP table\n" -"show ndp\n" -"```" -msgstr "" -"```bash\n" -"# Show MAC (FDB) entries\n" -"show mac\n" -"\n" -"# Show IP ARP table\n" -"show arp\n" -"\n" -"# Show IPv6 NDP table\n" -"show ndp\n" -"```" - -#: src/1-3-command-cheatsheet.md:100 -msgid "## BGP / Routes" -msgstr "## BGP / Routes" - -#: src/1-3-command-cheatsheet.md:102 -msgid "" -"```bash\n" -"show ip/ipv6 bgp summary\n" -"show ip/ipv6 bgp network\n" -"\n" -"show ip/ipv6 bgp neighbors [IP]\n" -"\n" -"show ip/ipv6 route\n" -"\n" -"TODO: add\n" -"config bgp shutdown neighbor \n" -"config bgp shutdown all\n" -"\n" -"TODO: IPv6\n" -"```" -msgstr "" -"```bash\n" -"show ip/ipv6 bgp summary\n" -"show ip/ipv6 bgp network\n" -"\n" -"show ip/ipv6 bgp neighbors [IP]\n" -"\n" -"show ip/ipv6 route\n" -"\n" -"TODO: add\n" -"config bgp shutdown neighbor \n" -"config bgp shutdown all\n" -"\n" -"TODO: IPv6\n" -"```" - -#: src/1-3-command-cheatsheet.md:117 -msgid "## LLDP" -msgstr "## LLDP" - -#: src/1-3-command-cheatsheet.md:119 -msgid "" -"```bash\n" -"# Show LLDP neighbors in table format\n" -"show lldp table\n" -"\n" -"# Show LLDP neighbors details\n" -"show lldp neighbors\n" -"```" -msgstr "" -"```bash\n" -"# Show LLDP neighbors in table format\n" -"show lldp table\n" -"\n" -"# Show LLDP neighbors details\n" -"show lldp neighbors\n" -"```" - -#: src/1-3-command-cheatsheet.md:127 -msgid "## VLAN" -msgstr "## VLAN" - -#: src/1-3-command-cheatsheet.md:129 -msgid "" -"```bash\n" -"show vlan brief\n" -"```" -msgstr "" -"```bash\n" -"show vlan brief\n" -"```" - -#: src/1-3-command-cheatsheet.md:133 -msgid "## QoS相关" -msgstr "## QoS related" - -#: src/1-3-command-cheatsheet.md:135 -msgid "" -"```bash\n" -"# Show PFC watchdog stats\n" -"show pfcwd stats\n" -"show queue counter\n" -"```" -msgstr "" -"```bash\n" -"# Show PFC watchdog stats\n" -"show pfcwd stats\n" -"show queue counter\n" -"```" - -#: src/1-3-command-cheatsheet.md:141 -msgid "## ACL" -msgstr "## ACL" - -#: src/1-3-command-cheatsheet.md:143 -msgid "" -"```bash\n" -"show acl table\n" -"show acl rule\n" -"```" -msgstr "" -"```bash\n" -"show acl table\n" -"show acl rule\n" -"```" - -#: src/1-3-command-cheatsheet.md:148 -msgid "## MUXcable / Dual ToR" -msgstr "## MUXcable / Dual ToR" - -#: src/1-3-command-cheatsheet.md:150 -msgid "### Muxcable mode" -msgstr "### Muxcable mode" - -#: src/1-3-command-cheatsheet.md:152 -msgid "" -"```bash\n" -"config muxcable mode {active} {|all} [--json]\n" -"config muxcable mode active Ethernet4 [--json]\n" -"```" -msgstr "" -"```bash\n" -"config muxcable mode {active} {|all} [--json]\n" -"config muxcable mode active Ethernet4 [--json]\n" -"```" - -#: src/1-3-command-cheatsheet.md:157 -msgid "### Muxcable config" -msgstr "### Muxcable config" - -#: src/1-3-command-cheatsheet.md:159 -msgid "" -"```bash\n" -"show muxcable config [portname] [--json]\n" -"```" -msgstr "" -"```bash\n" -"show muxcable config [portname] [--json]\n" -"```" - -#: src/1-3-command-cheatsheet.md:163 -msgid "### Muxcable status" -msgstr "### Muxcable status" - -#: src/1-3-command-cheatsheet.md:165 -msgid "" -"```bash\n" -"show muxcable status [portname] [--json] \n" -"```" -msgstr "" -"```bash\n" -"show muxcable status [portname] [--json] \n" -"```" - -#: src/1-3-command-cheatsheet.md:169 -msgid "### Muxcable firmware" -msgstr "### Muxcable firmware" - -#: src/1-3-command-cheatsheet.md:171 -msgid "" -"```bash\n" -"# Firmware version:\n" -"show muxcable firmware version \n" -"\n" -"# Firmware download\n" -"# config muxcable firmware download \n" -"sudo config muxcable firmware download AEC_WYOMING_B52Yb0_MS_0.6_20201218." -"bin Ethernet0\n" -"\n" -"# Rollback:\n" -"# config muxcable firmware rollback \n" -"sudo config muxcable firmware rollback Ethernet0\n" -"```" -msgstr "" -"```bash\n" -"# Firmware version:\n" -"show muxcable firmware version \n" -"\n" -"# Firmware download\n" -"# config muxcable firmware download \n" -"sudo config muxcable firmware download AEC_WYOMING_B52Yb0_MS_0.6_20201218." -"bin Ethernet0\n" -"\n" -"# Rollback:\n" -"# config muxcable firmware rollback \n" -"sudo config muxcable firmware rollback Ethernet0\n" -"```" - -#: src/1-3-command-cheatsheet.md:186 -msgid "1. [SONiC Command Line Interface Guide][SONiCCommands]" -msgstr "1. [SONiC Command Line Interface Guide][SONiCCommands]" - -#: src/2-core-components-intro.md:1 -msgid "# 核心组件简介" -msgstr "# Core components" - -#: src/2-core-components-intro.md:3 -msgid "" -"我们也许会觉得交换机是一个很简单的网络设备,但是实际上交换机上的组件非常的" -"多,而且由于SONiC中Redis的解耦,我们很难简单的对代码进行跟踪来理解服务之间的" -"关系,这就需要我们先建立一个比较抽象的整体模型,然后再去深入的学习每个组件的" -"细节。所以在深入其他部分之前,我们这里先对每个组件都做一个点到为止的介绍,帮" -"助大家建立一个大概的整体模型。" -msgstr "" -"We might think that a switch is a simple network device. However, in fact, " -"there are many components in a switch. Furthermore, because SONiC decouples " -"all components using Redis, Redis in SONiC, it is difficult to understand " -"the relationships between services by simply tracing the code. This requires " -"us to first establish a relatively abstract overall model, and then dive " -"into the details of each component. Therefore, before diving into each " -"individual parts, here we will introduce each component briefly to help " -"establish a rough overall model." - -#: src/2-core-components-intro.md:5 -msgid "" -"```admonish info\n" -"在阅读本章之前,有两个名词会经常在本章和SONiC的官方文档中出现:ASIC" -"(Application-Specific Integrated Circuit)和ASIC状态(State)。它们指的是交" -"换机中用来进行包处理的Pipeline的状态,比如,ACL,转发方式等等,这个和其他交换" -"机的硬件状态,比如,端口状态(端口速度,接口类型),IP信息等等硬件状态是非常" -"不同的。\n" -"\n" -"如果大家有兴趣了解更深入的细节,可以先移步阅读两个相关资料:[SAI (Switch " -"Abstraction Interface) API][SAIAPI]和一篇RMT(Reprogrammable Match Table)的" -"相关论文:[Forwarding Metamorphosis: Fast Programmable Match-Action " -"Processing in Hardware for SDN][PISA]。\n" -"\n" -"这些都会对我们阅读SONiC的文档有很大的帮助。\n" -"```" -msgstr "" -"```admonish info\n" -"Before reading this chapter, there are two terms that will frequently appear " -"in this chapter and in official documents of SONiC: ASIC (Application-" -"Specific Integrated Circuit) and ASIC State. They refer to the state of the " -"pipeline used for packet processing in the switch, such as ACL, etc. This is " -"different from other hardware states of switches, such as port states (port " -"speed, interface type), IP address, etc.\n" -"\n" -"If you are interested in more details, please feel free to check out two " -"relevant materials first: [SAI (Switch Abstraction Interface) API][SAIAPI] " -"and a paper on RMT (Reprogrammable Match Table) called \"[Forwarding " -"Metamorphosis: Fast Programmable Match-Action Processing in Hardware for SDN]" -"[PISA]\". \n" -"\n" -"These will be very helpful for us to read SONiC documentation.\n" -"```" - -#: src/2-core-components-intro.md:13 -msgid "" -"另外为了方便我们的理解和阅读,我们也把SONiC架构图在这里放在这一章的开头,作为" -"引用:" -msgstr "" -"In addition, for our convenience of understanding and reading, we also put " -"the SONiC architecture diagram at the beginning of this chapter as a " -"reference." - -#: src/2-core-components-intro.md:21 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]" - -#: src/2-1-database.md:1 -msgid "# Redis数据库" -msgstr "# Redis database" - -#: src/2-1-database.md:3 -msgid "" -"首先,在SONiC里面最核心的服务,自然是当之无愧的中心数据库Redis了!它的主要目" -"的有两个:存储所有服务的配置和状态,并且为各个服务提供通信的媒介。" -msgstr "" -"First of all, the most important service in SONiC is undoubtedly the central " -"database - Redis! It has 2 major purposes: to store the switch configuration " -"and status of all services, as well as to provide a communication channel " -"among all services." - -#: src/2-1-database.md:5 -msgid "" -"为了提供这些功能,SONiC会在Redis中创建一个名为`sonic-db`的数据库实例,其配置" -"和分库信息我们可以在`/var/run/redis/sonic-db/database_config.json`中找到:" -msgstr "" -"In order to provide these features, SONiC will create a instance named " -"`sonic-db` in Redis, and its configuration and sharding information can be " -"found in `/var/run/redis/sonic-db/database_config.json`." - -#: src/2-1-database.md:7 -msgid "" -"```bash\n" -"admin@sonic:~$ cat /var/run/redis/sonic-db/database_config.json\n" -"{\n" -" \"INSTANCES\": {\n" -" \"redis\": {\n" -" \"hostname\": \"127.0.0.1\",\n" -" \"port\": 6379,\n" -" \"unix_socket_path\": \"/var/run/redis/redis.sock\",\n" -" \"persistence_for_warm_boot\": \"yes\"\n" -" }\n" -" },\n" -" \"DATABASES\": {\n" -" \"APPL_DB\": { \"id\": 0, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"ASIC_DB\": { \"id\": 1, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"COUNTERS_DB\": { \"id\": 2, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"LOGLEVEL_DB\": { \"id\": 3, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"CONFIG_DB\": { \"id\": 4, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"PFC_WD_DB\": { \"id\": 5, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"FLEX_COUNTER_DB\": { \"id\": 5, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"STATE_DB\": { \"id\": 6, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"SNMP_OVERLAY_DB\": { \"id\": 7, \"separator\": \"|\", " -"\"instance\": \"redis\" },\n" -" \"RESTAPI_DB\": { \"id\": 8, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"GB_ASIC_DB\": { \"id\": 9, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"GB_COUNTERS_DB\": { \"id\": 10, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"GB_FLEX_COUNTER_DB\": { \"id\": 11, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"APPL_STATE_DB\": { \"id\": 14, \"separator\": \":\", \"instance\": " -"\"redis\" }\n" -" },\n" -" \"VERSION\": \"1.0\"\n" -"}\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ cat /var/run/redis/sonic-db/database_config.json\n" -"{\n" -" \"INSTANCES\": {\n" -" \"redis\": {\n" -" \"hostname\": \"127.0.0.1\",\n" -" \"port\": 6379,\n" -" \"unix_socket_path\": \"/var/run/redis/redis.sock\",\n" -" \"persistence_for_warm_boot\": \"yes\"\n" -" }\n" -" },\n" -" \"DATABASES\": {\n" -" \"APPL_DB\": { \"id\": 0, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"ASIC_DB\": { \"id\": 1, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"COUNTERS_DB\": { \"id\": 2, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"LOGLEVEL_DB\": { \"id\": 3, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"CONFIG_DB\": { \"id\": 4, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"PFC_WD_DB\": { \"id\": 5, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"FLEX_COUNTER_DB\": { \"id\": 5, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"STATE_DB\": { \"id\": 6, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"SNMP_OVERLAY_DB\": { \"id\": 7, \"separator\": \"|\", " -"\"instance\": \"redis\" },\n" -" \"RESTAPI_DB\": { \"id\": 8, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"GB_ASIC_DB\": { \"id\": 9, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"GB_COUNTERS_DB\": { \"id\": 10, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"GB_FLEX_COUNTER_DB\": { \"id\": 11, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"APPL_STATE_DB\": { \"id\": 14, \"separator\": \":\", \"instance\": " -"\"redis\" }\n" -" },\n" -" \"VERSION\": \"1.0\"\n" -"}\n" -"```" - -#: src/2-1-database.md:38 -msgid "" -"虽然我们可以看到SONiC中的数据库有十来个,但是我们大部分时候只需要关注以下几个" -"最重要的数据库就可以了:" -msgstr "" -"Although there are about ten databases in SONiC that we can see, most of the " -"time we only need to focus on the following few most important databases:" - -#: src/2-1-database.md:40 -msgid "" -"- **CONFIG_DB(ID = 4)**:存储所有服务的**配置信息**,比如端口配置,VLAN配置" -"等等。它代表着**用户想要交换机达到的状态**的数据模型,这也是所有CLI和外部应用" -"程序修改配置时的主要操作对象。\n" -"- **APPL_DB(Application DB, ID = 0)**:存储**所有服务的内部状态信息**。这些" -"信息有两种:一种是各个服务在读取了CONFIG_DB的配置信息后,自己计算出来的。我们" -"可以理解为**各个服务想要交换机达到的状态**(Goal State),还有一种是当最终硬" -"件状态发生变化被写回时,有些服务会直接写回到APPL_DB,而不是我们下面马上要介绍" -"的STATE_DB。这些信息我们可以理解为**各个服务认为交换机当前的状态**(Current " -"State)。\n" -"- **STATE_DB(ID = 6)**:存储着交换机**各个部件当前的状态**(Current " -"State)。当SONiC中的服务收到了STATE_DB的状态变化,但是发现和Goal State不一致" -"的时候,SONiC就会重新下发配置,直到两者一致。(当然,对于那些回写到APPL_DB状" -"态,服务就会监听APPL_DB的变化,而不是STATE_DB了。)\n" -"- **ASIC_DB(ID = 1)**:存储着**SONiC想要交换机ASIC达到状态信息**,比如," -"ACL,路由等等。和APPL_DB不同,这个数据库里面的数据模型是面向ASIC设计的,而不" -"是面向服务抽象的。这样做的目的是为了方便各个厂商进行SAI和ASIC驱动的开发。" -msgstr "" -"- **CONFIG_DB (ID = 4)**: It stores **configuration of all services**, such " -"as port configs and VLAN configs. It represents the data model of the " -"**state that the user wants the switch to achieve**, and is also the main " -"target that all CLI and external applications operates when updating the " -"configs.\n" -"- **APPL_DB (Application DB, ID = 0)**: It stores the **internal states of " -"all services**. There are two kinds of information: one is the state that " -"the individual services calculate for themselves after reading the " -"configuration information from CONFIG_DB. We can understand it as the **goal " -"state that each service wants the switch to achieve**. The other type of " -"information is that when the final hardware state changes and is written " -"back, some services will write back directly to APPL_DB instead of the " -"STATE_DB that we will introduce later. We can understand this as the " -"**current state that each service thinks the switch is in**.\n" -"- **STATE_DB (ID = 6)**: It stores the **current state of each component of " -"the switch**. When a service in SONiC receives a state change from STATE_DB " -"which is inconsistent with its goal state, SONiC will redrive the configs " -"until they are consistent. (Of course, for those states that are written " -"back to APPL_DB, the service will listen for changes in APPL_DB instead of " -"STATE_DB.)\n" -"- **ASIC_DB (ID = 1)**: It stores the **state information that SONiC wants " -"the switch ASIC to achieve**, such as ACLs and routes. Unlike APPL_DB, the " -"data model in this database is designed for ASIC, instead of each service. " -"This is to simplify the development of SAI and ASIC drivers by various " -"vendors." - -#: src/2-1-database.md:45 -msgid "" -"这里,我们会发现一个很直观的问题:交换机里面这么多服务,难道所有的配置和状态" -"都放在一个数据库里面没有隔离的么?如果两个服务用了同一个Redis Key怎么办呢?这" -"个问题非常的好,SONiC的解决也很直接,那就是在每个数据库里面继续分表!" -msgstr "" -"Now, we will notice a very intuitive problem: with so many services in the " -"switch, are all configurations and states stored in one database without " -"isolation? What if two services use the same Redis Key? This is a very good " -"question, and SONiC's solution is very direct: continue to partition the " -"tables in each database!" - -#: src/2-1-database.md:47 -msgid "" -"我们知道Redis在每个数据库里面并没有表的概念,而是使用key-value的方式来存储数" -"据。所以,为了进一步分表,SONiC的解决方法是将表的名字放入key中,并且使用分隔" -"符将表和key隔开。上面的配置文件中`separator`字段就是做这个了。比如:`APPL_DB`" -"中的`PORT_TABLE`表中的`Ethernet4`端口的状态,我们可以通过`PORT_TABLE:" -"Ethernet4`来获取,如下:" -msgstr "" -"We know that Redis does not have the concept of table in each database, but " -"directly stores data using key-value pairs. Therefore, in order to further " -"partition tables, SONiC's solution is to put the table name into the key and " -"use a separator to separate the table from the key. The `separator` field in " -"the above configuration file is for this purpose. For example, to retrieve " -"the status of the `Ethernet4` port in the `PORT_TABLE` table in the " -"`APPL_DB`, we can use `PORT_TABLE:Ethernet4` as the key, as follows:" - -#: src/2-1-database.md:49 -msgid "" -"```bash\n" -"127.0.0.1:6379> select 0\n" -"OK\n" -"\n" -"127.0.0.1:6379> hgetall PORT_TABLE:Ethernet4\n" -" 1) \"admin_status\"\n" -" 2) \"up\"\n" -" 3) \"alias\"\n" -" 4) \"Ethernet6/1\"\n" -" 5) \"index\"\n" -" 6) \"6\"\n" -" 7) \"lanes\"\n" -" 8) \"13,14,15,16\"\n" -" 9) \"mtu\"\n" -"10) \"9100\"\n" -"11) \"speed\"\n" -"12) \"40000\"\n" -"13) \"description\"\n" -"14) \"\"\n" -"15) \"oper_status\"\n" -"16) \"up\"\n" -"```" -msgstr "" -"```bash\n" -"127.0.0.1:6379> select 0\n" -"OK\n" -"\n" -"127.0.0.1:6379> hgetall PORT_TABLE:Ethernet4\n" -" 1) \"admin_status\"\n" -" 2) \"up\"\n" -" 3) \"alias\"\n" -" 4) \"Ethernet6/1\"\n" -" 5) \"index\"\n" -" 6) \"6\"\n" -" 7) \"lanes\"\n" -" 8) \"13,14,15,16\"\n" -" 9) \"mtu\"\n" -"10) \"9100\"\n" -"11) \"speed\"\n" -"12) \"40000\"\n" -"13) \"description\"\n" -"14) \"\"\n" -"15) \"oper_status\"\n" -"16) \"up\"\n" -"```" - -#: src/2-1-database.md:72 -msgid "" -"当然在SONiC中,不仅仅是数据模型,包括通信机制,都是使用类似的方法来实现“表”级" -"别的隔离的。" -msgstr "" -"Of course, in SONiC, not only data models but also communication mechanisms " -"are implemented using similar methods to achieve \"table-level\" isolation." - -#: src/2-1-database.md:76 src/2-2-services-intro.md:76 -msgid "1. [SONiC Architecture][SONiCArch]" -msgstr "1. [SONiC Architecture][SONiCArch]" - -#: src/2-2-services-intro.md:1 -msgid "# 服务与工作流简介" -msgstr "# Service and Workflow Intro" - -#: src/2-2-services-intro.md:3 -msgid "" -"SONiC里面的服务(常驻进程)非常的多,有二三十种,它们会在随着交换机启动而启" -"动,并一直保持运行,直到交换机关机。如果我们想快速掌握SONiC,一个一个服务的去" -"了解,会很容易陷入细节的泥潭,所以,我们最好把这些服务和控制流进行一个大的分" -"类,以帮助我们建立一个宏观的概念。" -msgstr "" -"There are very many services (resident processes) inside SONiC, 20 or 30 of " -"them, and they will start as the switch starts up and stay running until the " -"switch is shut down. If we want to quickly grasp SONiC, going through it " -"service by service will easily get bogged down in details, so it is best to " -"make a broad classification of these services and control flows to help us " -"build a macro concept." - -#: src/2-2-services-intro.md:5 -msgid "" -"```admonish note\n" -"我们这里不会深入到某一个具体的服务中去,而是先从整体上来看看SONiC中的服务的结" -"构,帮助我们建立一个整体的认识。关于具体的服务,我们会在工作流一章中,对常用" -"的工作流进行介绍,而关于详细的技术细节,大家也可以查阅每个服务相关的设计文" -"档。\n" -"```" -msgstr "" -"```admonish note\n" -"我们这里不会深入到某一个具体的服务中去,而是先从整体上来看看SONiC中的服务的结" -"构,帮助我们建立一个整体的认识。关于具体的服务,我们会在工作流一章中,对常用" -"的工作流进行介绍,而关于详细的技术细节,大家也可以查阅每个服务相关的设计文" -"档。\n" -"```" - -#: src/2-2-services-intro.md:9 -msgid "## 服务分类" -msgstr "## Service classification" - -#: src/2-2-services-intro.md:11 -msgid "" -"总体而言,SONiC中的服务可以分为以下几类:`*syncd`, `*mgrd`,feature实现," -"`orchagent`和`syncd`。" -msgstr "" -"In general, the services in SONiC can be divided into the following " -"categories: `*syncd`, `*mgrd`, feature implementations, `orchagent` and " -"`syncd`." - -#: src/2-2-services-intro.md:13 -msgid "### `*syncd`服务" -msgstr "### `*syncd` service" - -#: src/2-2-services-intro.md:15 -msgid "" -"这类服务名字中都以`syncd`结尾。它们做的事情都很类似:它们负责将硬件状态同步到" -"Redis中,一般目标都以APPL_DB或者STATE_DB为主。" -msgstr "" -"These services all end with `syncd` in their names. They all do similar " -"things: they are responsible for synchronizing hardware state into Redis, " -"and generally target either APPL_DB or STATE_DB." - -#: src/2-2-services-intro.md:17 -msgid "" -"比如,`portsyncd`就是通过监听netlink的事件,将交换机中所有Port的状态同步到" -"STATE_DB中,而`natsyncd`则是监听netlink的事件,将交换机中所有的NAT状态同步到" -"APPL_DB中。" -msgstr "" -"For example, `portsyncd` is to synchronize the state of all the ports in the " -"switch to STATE_DB by listening to the events of netlink, while `natsyncd` " -"is to listen to the events of netlink and synchronize the state of all the " -"NATs in the switch to APPL_DB." - -#: src/2-2-services-intro.md:19 -msgid "### `*mgrd`服务" -msgstr "### `*mgrd` service" - -#: src/2-2-services-intro.md:21 -msgid "" -"这类服务名字中都以`mgrd`结尾。顾名思义,这些服务是所谓的“Manager”服务,也就是" -"说它们负责各个硬件的配置,和`*syncd`完全相反。它们的逻辑主要有两个部分:" -msgstr "" -"These services have names that end with `mgrd`. As the name suggests, these " -"services are so-called \"Manager\" services, meaning that they are " -"responsible for the configuration of individual hardware, the exact opposite " -"of `*syncd`. Their logic has two main parts:" - -#: src/2-2-services-intro.md:23 -msgid "" -"1. **配置下发**:负责读取配置文件和监听Redis中的配置和状态改变(主要是" -"CONFIG_DB,APPL_DB和STATE_DB),然后将这些修改推送到交换机硬件中去。推送的方" -"法有多种,取决于更新的目标是什么,可以通过更新APPL_DB并发布更新消息,或者是直" -"接调用linux下的命令行,对系统进行修改。比如:`nbrmgr`就是监听CONFIG_DB," -"APPL_DB和STATE_DB中neighbor的变化,并调用netlink和command line来对neighbor和" -"route进行修改,而`intfmgr`除了调用command line还会将一些状态更新到APPL_DB中" -"去。\n" -"2. **状态同步**:对于需要Reconcile的服务,`*mgrd`还会监听STATE_DB中的状态变" -"化,如果发现硬件状态和当前期望状态不一致,就会重新发起配置流程,将硬件状态设" -"置为期望状态。这些STATE_DB中的状态变化一般都是`*syncd`服务推送的。比如:" -"`intfmgr`就会监听STATE_DB中,由`portsyncd`推送的,端口的Up/Down状态和MTU变" -"化,一旦发现和其内存中保存的期望状态不一致,就会重新下发配置。" -msgstr "" -"1. **Configuration downlink**: Responsible for reading configuration files " -"and listening for configuration and state changes in Redis (mainly " -"CONFIG_DB, APPL_DB and STATE_DB), and then pushing these changes to the " -"switch hardware. There are various methods of pushing, depending on what the " -"target of the update is, either by updating APPL_DB and posting an update " -"message, or by directly invoking the command line under linux and making " -"changes to the system. For example: `nbrmgr` is listening to the changes of " -"neighbor in CONFIG_DB, APPL_DB and STATE_DB, and calling netlink and command " -"line to make changes to neighbor and route, while `intfmgr` besides calling " -"command line will also update some state and `intfmgr` will update some " -"state to APPL_DB besides calling command line.\n" -"2. **State synchronization**: For services that require Reconcile, `*mgrd` " -"also listens for state changes in STATE_DB, and if the hardware state is " -"found to be inconsistent with the current desired state, it will re-initiate " -"the configuration process to set the hardware state to the desired state. " -"These state changes in STATE_DB are generally pushed by the `*syncd` " -"service. For example, `intfmgr` listens to the Up/Down status and MTU " -"changes of ports in STATE_DB pushed by `portsyncd`, and re-issues the " -"configuration once it finds that it does not match the desired state stored " -"in its memory." - -#: src/2-2-services-intro.md:26 -msgid "### 功能实现服务" -msgstr "### Function implementation services" - -#: src/2-2-services-intro.md:28 -msgid "" -"有一些功能并不是依靠OS本身来完成的,而是由一些特定的进程来实现的,比如BGP,或" -"者一些外部接口。这些服务名字中经常以`d`结尾,表示deamon,比如:`bgpd`," -"`lldpd`,`snmpd`,`teamd`等,或者干脆就是这个功能的名字,比如:`fancontrol`。" -msgstr "" -"There are some functions that do not rely on the OS itself, but are " -"implemented by some specific processes, such as BGP, or some external " -"interfaces. These services often have names ending in `d` for deamon, e.g.: " -"`bgpd`, `lldpd`, `snmpd`, `teamd`, etc., or simply the name of this " -"function, e.g.: `fancontrol`." - -#: src/2-2-services-intro.md:30 -msgid "### `orchagent`服务" -msgstr "### `orchagent` service" - -#: src/2-2-services-intro.md:32 -msgid "" -"这个是SONiC中最重要的一个服务,不像其他的服务只负责一两个特定的功能," -"`orchagent`作为交换机ASIC状态的编排者(orchestrator),会检查数据库中所有来自" -"`*syncd`服务的状态,整合起来并下发给用于保存交换机ASIC配置的数据库:ASIC_DB。" -"这些状态最后会被`syncd`接收,并调用SAI API经过各个厂商提供的SAI实现和ASIC SDK" -"和ASIC进行交互,最终将配置下发到交换机硬件中。" -msgstr "" -"This is one of the most important services in SONiC, unlike other services " -"that are only responsible for one or two specific functions, `orchagent`, as " -"the orchestrator (orchestrator) of the switch ASIC state, checks all the " -"states in the database from the `*syncd` service, consolidates them and " -"sends them down to the database used to store the switch ASIC configuration: " -"`ASIC_DB: This state is finally received by `syncd` and calls the SAI API to " -"interact with the ASIC SDK and the ASIC through the SAI implementations " -"provided by each vendor, and finally sends the configuration down to the " -"switch hardware." - -#: src/2-2-services-intro.md:34 -msgid "### `syncd`服务" -msgstr "### `syncd` service" - -#: src/2-2-services-intro.md:36 -msgid "" -"`syncd`服务是`orchagent`的下游,它虽然名字叫`syncd`,但是它却同时肩负着ASIC的" -"`*mgrd`和`*syncd`的工作。" -msgstr "" -"The `syncd` service is downstream of `orchagent`, which is named `syncd`, " -"but it is responsible for both `*mgrd` and `*syncd` of ASIC." - -#: src/2-2-services-intro.md:38 -msgid "" -"- 首先,作为`*mgrd`,它会监听ASIC_DB的状态变化,一旦发现,就会获取其新的状态" -"并调用SAI API,将配置下发到交换机硬件中。\n" -"- 然后,作为`*syncd`,如果ASIC发送了任何的通知给SONiC,它也会将这些通知通过消" -"息的方式发送到Redis中,以便`orchagent`和`*mgrd`服务获取到这些变化,并进行处" -"理。这些通知的类型我们可以在[SwitchNotifications.h][SAISwitchNotify]中找到。" -msgstr "" -"- First, as `*mgrd`, it listens for changes in the state of ASIC_DB, and " -"once it finds them, it gets its new state and calls the SAI API to send down " -"the configuration to the switch hardware.\n" -"- Then, as `*syncd`, if ASIC sends any notifications to SONiC, it will also " -"send those notifications to Redis by way of messages so that the `orchagent` " -"and `*mgrd` services can fetch the changes and process them. The types of " -"these notifications we can find in [SwitchNotifications.h][SAISwitchNotify]." - -#: src/2-2-services-intro.md:41 -msgid "## 服务间控制流分类" -msgstr "## Inter-service control flow classification" - -#: src/2-2-services-intro.md:43 -msgid "" -"有了这些分类,我们就可以更加清晰的来理解SONiC中的服务了,而其中非常重要的就是" -"理解服务之间的控制流。有了上面的分类,我们这里也可以把主要的控制流有分为两" -"类:配置下发和状态同步。" -msgstr "" -"With these categories, we can understand the services in SONiC more clearly, " -"and it is very important to understand the control flow between services. " -"With the above classification, we can also divide the main control flow here " -"into two categories: configuration delivery and state synchronization." - -#: src/2-2-services-intro.md:45 -msgid "### 配置下发" -msgstr "### Configuration down" - -#: src/2-2-services-intro.md:47 -msgid "配置下发的流程一般是这样的:" -msgstr "The flow of configuration issuance is generally as follows:" - -#: src/2-2-services-intro.md:49 -msgid "" -"1. **修改配置**:用户可以通过CLI或者REST API修改配置,这些配置会被写入到" -"CONFIG_DB中并通过Redis发送更新通知。或者外部程序可以通过特定的接口,比如BGP的" -"API,来修改配置,这种配置会通过内部的TCP Socket发送给`*mgrd`服务。\n" -"2. **`*mgrd`下发配置**:服务监听到CONFIG_DB中的配置变化,然后将这些配置推送到" -"交换机硬件中。这里由两种主要情况(并且可以同时存在):\n" -" 1. **直接下发**:\n" -" 1. `*mgrd`服务直接调用linux下的命令行,或者是通过netlink来修改系统配" -"置\n" -" 2. `*syncd`服务会通过netlink或者其他方式监听到系统配置的变化,并将这些" -"变化推送到STATE_DB或者APPL_DB中。\n" -" 3. `*mgrd`服务监听到STATE_DB或者APPL_DB中的配置变化,然后将这些配置和其" -"内存中存储的配置进行比较,如果发现不一致,就会重新调用命令行或者netlink来修改" -"系统配置,直到它们一致为止。\n" -" 2. **间接下发**:\n" -" 1. `*mgrd`将状态推送到APPL_DB并通过Redis发送更新通知。\n" -" 2. `orchagent`服务监听到配置变化,然后根据所有相关的状态,计算出此时" -"ASIC应该达到的状态,并下发到ASIC_DB中。\n" -" 3. `syncd`服务监听到ASIC_DB的变化,然后将这些新的配置通过统一的SAI API" -"接口,调用ASIC Driver更新交换机ASIC中的配置。" -msgstr "" -"1. **Modify Configuration**: Users can modify the configuration through the " -"CLI or REST API, which is written to CONFIG_DB and sends update " -"notifications through Redis. Or an external program can modify the " -"configuration through a specific interface, such as the BGP API, and this " -"configuration is sent to the `*mgrd` service via an internal TCP socket.\n" -"2. **`*mgrd` sends down configurations**: the service listens to " -"configuration changes in the CONFIG_DB and then pushes these configurations " -"to the switch hardware. There are two main cases here (and they can both " -"exist):\n" -" 1. **direct downlink**:\n" -" 1. `*mgrd` service directly invokes the command line under linux, or " -"modifies the system configuration via netlink\n" -" 2. `*syncd` service will listen to the system configuration changes " -"via netlink or other means and push these changes to STATE_DB or APPL_DB.\n" -" 3. The `*mgrd` service listens to configuration changes in STATE_DB or " -"APPL_DB, then compares those configurations with the configurations stored " -"in its memory, and if it finds inconsistencies, it will re-invoke the " -"command line or netlink to modify the system configuration until they are " -"consistent.\n" -" 2. **Indirect downlink**:\n" -" 1. `*mgrd` pushes status to APPL_DB and sends update notifications via " -"Redis.\n" -" 2. `orchagent` service listens for configuration changes and then " -"calculates the state ASIC should reach at this time based on all relevant " -"states and downlinks to ASIC_DB.\n" -" 3. The `syncd` service listens to the changes in the ASIC_DB and then " -"takes these new configurations and calls the ASIC Driver to update the " -"configuration in the switch ASIC through the unified SAI API interface." - -#: src/2-2-services-intro.md:60 -msgid "" -"配置初始化和配置下发类似,不过是在服务启动的时候读取配置文件,这里就不展开" -"了。" -msgstr "" -"Configuration initialization is similar to configuration distribution, but " -"the configuration file is read when the service is started, so we won't " -"expand on that here." - -#: src/2-2-services-intro.md:62 -msgid "### 状态同步" -msgstr "### Status synchronization" - -#: src/2-2-services-intro.md:64 -msgid "" -"如果这个时候,出现了一些情况,比如网口坏了,ASIC中的状态变了等等,这个时候我" -"们就需要进行状态更新和同步了。这个流程一般是这样的:" -msgstr "" -"If at this time, something happens, such as a bad network port, the state in " -"the ASIC changes, etc., this time we need to do a state update and " -"synchronization. This process generally looks like this:" - -#: src/2-2-services-intro.md:66 -msgid "" -"1. **检测状态变化**:这个状态变化主要来源于`*syncd`服务(netlink等等)和" -"`syncd`服务([SAI Switch Notification][SAISwitchNotify]),这些服务在检测到变" -"化后,会将它们发送给STATE_DB或者APPL_DB。\n" -"2. **处理状态变化**:`orchagent`和`*mgrd`服务会监听到这些变化,然后开始处理," -"将新的配置重新通过命令行和netlink下发给系统,或者下发到ASIC_DB中,让`syncd`服" -"务再次对ASIC进行更新。" -msgstr "" -"1. **Detecting state changes**: This state change mainly comes from the " -"`*syncd` service (netlink, etc.) and the `syncd` service ([SAI Switch " -"Notification][SAISwitchNotify]), which, after detecting the changes, send " -"them to STATE_DB or APPL_DB.\n" -"2. **Processing state changes**: The `orchagent` and `*mgrd` services will " -"listen to these changes and start processing them, sending the new " -"configuration down to the system again via command line and netlink, or down " -"to ASIC_DB for the `syncd` service to update the ASIC again." - -#: src/2-2-services-intro.md:69 -msgid "### 具体例子" -msgstr "### Specific examples" - -#: src/2-2-services-intro.md:71 -msgid "" -"SONiC的官方文档中给出了几个典型的控制流流转的例子,这里就不过多的展开了,有兴" -"趣的朋友可以去这里看看:[SONiC Subsystem Interactions](https://github.com/" -"sonic-net/SONiC/wiki/Architecture#sonic-subsystems-interactions)。我们在后面" -"工作流一章中,也会选择一些非常常用的工作流进行展开。" -msgstr "" -"The official documentation of SONiC gives several examples of typical " -"control flow, so I won't expand too much here, interested parties can go " -"here: [SONiC Subsystem Interactions](https://github.com/sonic-net/SONiC/" -"wiki/ Architecture#sonic-subsystems-interactions). We will also choose some " -"very common workflows to expand on later in the workflow chapter." - -#: src/2-3-key-containers.md:1 -msgid "# 核心容器" -msgstr "# Core containers" - -#: src/2-3-key-containers.md:3 -msgid "SONiC的设计中最具特色的地方:容器化。" -msgstr "The most distinctive aspect of SONiC's design: containerization." - -#: src/2-3-key-containers.md:5 -msgid "" -"从SONiC的上面的设计图中,我们可以看出来,SONiC中,所有的服务都是以容器的形式" -"存在的。在登录进交换机之后,我们可以通过`docker ps`命令来查看当前运行的容器:" -msgstr "" -"From the above design diagram of SONiC, we can see that in SONiC, all " -"services are in the form of containers. After logging into the switch, we " -"can view the currently running containers by using the `docker ps` command:" - -#: src/2-3-key-containers.md:7 -msgid "" -"```bash\n" -"admin@sonic:~$ docker ps\n" -"CONTAINER ID IMAGE COMMAND " -"CREATED STATUS PORTS NAMES\n" -"ddf09928ec58 docker-snmp:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours snmp\n" -"c480f3cf9dd7 docker-sonic-mgmt-framework:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours mgmt-framework\n" -"3655aff31161 docker-lldp:latest \"/usr/bin/docker-lld…" -"\" 2 days ago Up 32 hours lldp\n" -"78f0b12ed10e docker-platform-monitor:latest \"/usr/bin/docker_ini…" -"\" 2 days ago Up 32 hours pmon\n" -"f9d9bcf6c9a6 docker-router-advertiser:latest \"/usr/bin/docker-ini…" -"\" 2 days ago Up 32 hours radv\n" -"2e5dbee95844 docker-fpm-frr:latest \"/usr/bin/docker_ini…" -"\" 2 days ago Up 32 hours bgp\n" -"bdfa58009226 docker-syncd-brcm:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours syncd\n" -"655e550b7a1b docker-teamd:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours teamd\n" -"1bd55acc181c docker-orchagent:latest \"/usr/bin/docker-ini…" -"\" 2 days ago Up 32 hours swss\n" -"bd20649228c8 docker-eventd:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours eventd\n" -"b2f58447febb docker-database:latest \"/usr/local/bin/dock…" -"\" 2 days ago Up 32 hours database\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker ps\n" -"CONTAINER ID IMAGE COMMAND " -"CREATED STATUS PORTS NAMES\n" -"ddf09928ec58 docker-snmp:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours snmp\n" -"c480f3cf9dd7 docker-sonic-mgmt-framework:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours mgmt-framework\n" -"3655aff31161 docker-lldp:latest \"/usr/bin/docker-lld…" -"\" 2 days ago Up 32 hours lldp\n" -"78f0b12ed10e docker-platform-monitor:latest \"/usr/bin/docker_ini…" -"\" 2 days ago Up 32 hours pmon\n" -"f9d9bcf6c9a6 docker-router-advertiser:latest \"/usr/bin/docker-ini…" -"\" 2 days ago Up 32 hours radv\n" -"2e5dbee95844 docker-fpm-frr:latest \"/usr/bin/docker_ini…" -"\" 2 days ago Up 32 hours bgp\n" -"bdfa58009226 docker-syncd-brcm:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours syncd\n" -"655e550b7a1b docker-teamd:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours teamd\n" -"1bd55acc181c docker-orchagent:latest \"/usr/bin/docker-ini…" -"\" 2 days ago Up 32 hours swss\n" -"bd20649228c8 docker-eventd:latest \"/usr/local/bin/supe…" -"\" 2 days ago Up 32 hours eventd\n" -"b2f58447febb docker-database:latest \"/usr/local/bin/dock…" -"\" 2 days ago Up 32 hours database\n" -"```" - -#: src/2-3-key-containers.md:23 -msgid "这里我们来简单介绍一下这些容器。" -msgstr "Let's briefly introduce these containers here." - -#: src/2-3-key-containers.md:25 -msgid "## 数据库容器:database" -msgstr "## Database container: database" - -#: src/2-3-key-containers.md:27 -msgid "" -"这个容器中运行的就是我们多次提到的SONiC中的中心数据库Redis了,它里面存放着所" -"有交换机的配置和状态信息,SONiC也是主要通过它来向各个服务提供底层的通信机制。" -msgstr "" -"This container runs Redis, the central database in SONiC that we have " -"mentioned many times, which holds the configuration and state information of " -"all switches, and through which SONiC provides the underlying communication " -"mechanism to each service." - -#: src/2-3-key-containers.md:29 -msgid "我们通过docker进入这个容器,就可以看到里面正在运行的redis进程了:" -msgstr "" -"We can see the running redis process inside this container by docker: the" - -#: src/2-3-key-containers.md:31 -msgid "" -"```bash\n" -"admin@sonic:~$ sudo docker exec -it database bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 82 13.7 1.7 130808 71692 pts/0 Sl Apr26 393:27 /usr/bin/" -"redis-server 127.0.0.1:6379\n" -"...\n" -"\n" -"root@sonic:/# cat /var/run/redis/redis.pid\n" -"82\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ sudo docker exec -it database bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 82 13.7 1.7 130808 71692 pts/0 Sl Apr26 393:27 /usr/bin/" -"redis-server 127.0.0.1:6379\n" -"...\n" -"\n" -"root@sonic:/# cat /var/run/redis/redis.pid\n" -"82\n" -"```" - -#: src/2-3-key-containers.md:44 -msgid "" -"那么别的容器是如何来访问这个Redis数据库的呢?答案是通过Unix Socket。我们可以" -"在database容器中看到这个Unix Socket,它将交换机上的`/var/run/redis`目录map进" -"database容器,让database容器可以创建这个socket:" -msgstr "" -"So how does another container access this Redis database? The answer is " -"through a Unix socket, which we can see in the database container, which " -"maps the `/var/run/redis` directory on the switch into the database " -"container, allowing the database container to create the socket: `/var/run/" -"redis`:" - -#: src/2-3-key-containers.md:46 -msgid "" -"```bash\n" -"# In database container\n" -"root@sonic:/# ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"\n" -"# On host\n" -"admin@sonic:~$ ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"```" -msgstr "" -"```bash\n" -"# In database container\n" -"root@sonic:/# ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"\n" -"# On host\n" -"admin@sonic:~$ ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"```" - -#: src/2-3-key-containers.md:56 -msgid "" -"然后再将这个socket给map到其他的容器中,这样所有容器就都可以来访问这个中心数据" -"库啦,比如,swss容器:" -msgstr "" -"Then map this socket to other containers, so that all containers can access " -"the central database, for example, the swss container:" - -#: src/2-3-key-containers.md:58 -msgid "" -"```bash\n" -"admin@sonic:~$ docker inspect swss\n" -"...\n" -" \"HostConfig\": {\n" -" \"Binds\": [\n" -" ...\n" -" \"/var/run/redis:/var/run/redis:rw\",\n" -" ...\n" -" ],\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker inspect swss\n" -"...\n" -" \"HostConfig\": {\n" -" \"Binds\": [\n" -" ...\n" -" \"/var/run/redis:/var/run/redis:rw\",\n" -" ...\n" -" ],\n" -"...\n" -"```" - -#: src/2-3-key-containers.md:70 -msgid "## 交换机状态管理容器:swss(Switch State Service)" -msgstr "## Switch state management container: swss (Switch State Service)" - -#: src/2-3-key-containers.md:72 -msgid "" -"这个容器可以说是SONiC中最关键的容器了,**它是SONiC的大脑**,里面运行着大量的" -"`*syncd`和`*mgrd`服务,用来管理交换机方方面面的配置,比如Port,neighbor," -"ARP,VLAN,Tunnel等等等等。另外里面还运行着上面提到的`orchagent`,用来统一处" -"理和ASIC相关的配置和状态变化。" -msgstr "" -"This container is arguably the most critical container in SONiC, **it is the " -"brain of SONiC**, which runs a large number of `*syncd` and `*mgrd` services " -"to manage all aspects of the switch configuration, such as Port, neighbor, " -"ARP, VLAN, Tunnel, etc., etc. Also running inside is the `orchagent` " -"mentioned above, which is used to handle configuration and status changes " -"related to the ASIC in a unified manner." - -#: src/2-3-key-containers.md:74 -msgid "" -"这些服务大概的功能和流程我们上面已经提过了,所以就不再赘述了。这里我们可以通" -"过`ps`命令来看一下这个容器中运行的服务:" -msgstr "" -"The approximate functions and processes of these services have already been " -"mentioned above, so we won't go over them again. Here we can take a look at " -"the services running in this container by using the `ps` command:" - -#: src/2-3-key-containers.md:76 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it swss bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 43 0.0 0.2 91016 9688 pts/0 Sl Apr26 0:18 /usr/bin/" -"portsyncd\n" -"root 49 0.1 0.6 558420 27592 pts/0 Sl Apr26 4:31 /usr/bin/" -"orchagent -d /var/log/swss -b 8192 -s -m 00:1c:73:f2:bc:b4\n" -"root 74 0.0 0.2 91240 9776 pts/0 Sl Apr26 0:19 /usr/bin/" -"coppmgrd\n" -"root 93 0.0 0.0 4400 3432 pts/0 S Apr26 0:09 /bin/" -"bash /usr/bin/arp_update\n" -"root 94 0.0 0.2 91008 8568 pts/0 Sl Apr26 0:09 /usr/bin/" -"neighsyncd\n" -"root 96 0.0 0.2 91168 9800 pts/0 Sl Apr26 0:19 /usr/bin/" -"vlanmgrd\n" -"root 99 0.0 0.2 91320 9848 pts/0 Sl Apr26 0:20 /usr/bin/" -"intfmgrd\n" -"root 103 0.0 0.2 91136 9708 pts/0 Sl Apr26 0:19 /usr/bin/" -"portmgrd\n" -"root 104 0.0 0.2 91380 9844 pts/0 Sl Apr26 0:20 /usr/bin/" -"buffermgrd -l /usr/share/sonic/hwsku/pg_profile_lookup.ini\n" -"root 107 0.0 0.2 91284 9836 pts/0 Sl Apr26 0:20 /usr/bin/" -"vrfmgrd\n" -"root 109 0.0 0.2 91040 8600 pts/0 Sl Apr26 0:19 /usr/bin/" -"nbrmgrd\n" -"root 110 0.0 0.2 91184 9724 pts/0 Sl Apr26 0:19 /usr/bin/" -"vxlanmgrd\n" -"root 112 0.0 0.2 90940 8804 pts/0 Sl Apr26 0:09 /usr/bin/" -"fdbsyncd\n" -"root 113 0.0 0.2 91140 9656 pts/0 Sl Apr26 0:20 /usr/bin/" -"tunnelmgrd\n" -"root 208 0.0 0.0 5772 1636 pts/0 S Apr26 0:07 /usr/sbin/" -"ndppd\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker exec -it swss bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 43 0.0 0.2 91016 9688 pts/0 Sl Apr26 0:18 /usr/bin/" -"portsyncd\n" -"root 49 0.1 0.6 558420 27592 pts/0 Sl Apr26 4:31 /usr/bin/" -"orchagent -d /var/log/swss -b 8192 -s -m 00:1c:73:f2:bc:b4\n" -"root 74 0.0 0.2 91240 9776 pts/0 Sl Apr26 0:19 /usr/bin/" -"coppmgrd\n" -"root 93 0.0 0.0 4400 3432 pts/0 S Apr26 0:09 /bin/" -"bash /usr/bin/arp_update\n" -"root 94 0.0 0.2 91008 8568 pts/0 Sl Apr26 0:09 /usr/bin/" -"neighsyncd\n" -"root 96 0.0 0.2 91168 9800 pts/0 Sl Apr26 0:19 /usr/bin/" -"vlanmgrd\n" -"root 99 0.0 0.2 91320 9848 pts/0 Sl Apr26 0:20 /usr/bin/" -"intfmgrd\n" -"root 103 0.0 0.2 91136 9708 pts/0 Sl Apr26 0:19 /usr/bin/" -"portmgrd\n" -"root 104 0.0 0.2 91380 9844 pts/0 Sl Apr26 0:20 /usr/bin/" -"buffermgrd -l /usr/share/sonic/hwsku/pg_profile_lookup.ini\n" -"root 107 0.0 0.2 91284 9836 pts/0 Sl Apr26 0:20 /usr/bin/" -"vrfmgrd\n" -"root 109 0.0 0.2 91040 8600 pts/0 Sl Apr26 0:19 /usr/bin/" -"nbrmgrd\n" -"root 110 0.0 0.2 91184 9724 pts/0 Sl Apr26 0:19 /usr/bin/" -"vxlanmgrd\n" -"root 112 0.0 0.2 90940 8804 pts/0 Sl Apr26 0:09 /usr/bin/" -"fdbsyncd\n" -"root 113 0.0 0.2 91140 9656 pts/0 Sl Apr26 0:20 /usr/bin/" -"tunnelmgrd\n" -"root 208 0.0 0.0 5772 1636 pts/0 S Apr26 0:07 /usr/sbin/" -"ndppd\n" -"...\n" -"```" - -#: src/2-3-key-containers.md:99 -msgid "## ASIC管理容器:syncd" -msgstr "## ASIC management container: syncd" - -#: src/2-3-key-containers.md:101 -msgid "" -"这个容器中主要是用于管理交换机上的ASIC的,里面运行着`syncd`服务。我们之前提到" -"的各个厂商提供的SAI(Switch Abstraction Interface)和ASIC Driver都是放在这个" -"容器中的。正是因为这个容器的存在,才使得SONiC可以支持多种不同的ASIC,而不需要" -"修改上层的服务。换句话说,如果没有这个容器,那SONiC就是一个缸中大脑,除了一些" -"基本的配置,其他只能靠想的,什么都干不了。" -msgstr "" -"This container is mainly used to manage the ASICs on the switch, and it runs " -"the `syncd` service. The SAI (Switch Abstraction Interface) and ASIC Driver " -"provided by each vendor we mentioned before are placed in this container. It " -"is the existence of this container that allows SONiC to support many " -"different ASICs without modifying the upper layer services. In other words, " -"without this container, the SONiC is a brain in a tank, except for some " -"basic configuration, other than only by thinking, nothing can be done." - -#: src/2-3-key-containers.md:103 -msgid "" -"在syncd容器中运行的服务并不多,就是syncd,我们可以通过`ps`命令来查看,而在`/" -"usr/lib`目录下,我们也可以找到这个为了支持ASIC而编译出来的巨大无比的SAI文件:" -msgstr "" -"There are not many services running in the syncd container, namely syncd, " -"which we can view with the `ps` command, and in the `/usr/lib` directory, " -"where we can also find this immense and unbelievable SAI file compiled to " -"support ASIC: the" - -#: src/2-3-key-containers.md:105 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 20 0.0 0.0 87708 1544 pts/0 Sl Apr26 0:00 /usr/bin/" -"dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"root 32 10.7 14.9 2724404 599408 pts/0 Sl Apr26 386:49 /usr/bin/" -"syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"...\n" -"\n" -"root@sonic:/# ls -lh /usr/lib\n" -"total 343M\n" -"...\n" -"lrwxrwxrwx 1 root root 13 Apr 25 04:38 libsai.so.1 -> libsai.so.1.0\n" -"-rw-r--r-- 1 root root 343M Feb 1 06:10 libsai.so.1.0\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 20 0.0 0.0 87708 1544 pts/0 Sl Apr26 0:00 /usr/bin/" -"dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"root 32 10.7 14.9 2724404 599408 pts/0 Sl Apr26 386:49 /usr/bin/" -"syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"...\n" -"\n" -"root@sonic:/# ls -lh /usr/lib\n" -"total 343M\n" -"...\n" -"lrwxrwxrwx 1 root root 13 Apr 25 04:38 libsai.so.1 -> libsai.so.1.0\n" -"-rw-r--r-- 1 root root 343M Feb 1 06:10 libsai.so.1.0\n" -"...\n" -"```" - -#: src/2-3-key-containers.md:123 -msgid "## 各种实现特定功能的容器" -msgstr "## Various containers that implement specific functions" - -#: src/2-3-key-containers.md:125 -msgid "" -"SONiC中还有很多的容器是为了实现一些特定功能而存在的。这些容器一般都有着特殊的" -"外部接口(非SONiC CLI和REST API)和实现(非OS或ASIC),比如:" -msgstr "" -"There are many more containers in SONiC that exist to implement some " -"specific functionality. These containers generally have special external " -"interfaces (non-SONiC CLI and REST API) and implementations (non-OS or " -"ASIC), such as" - -#: src/2-3-key-containers.md:127 -msgid "" -"- bgp:用来实现BGP协议(Border Gateway Protocol,边界网关协议)的容器\n" -"- lldp:用来实现LLDP协议(Link Layer Discovery Protocol,链路层发现协议)的容" -"器\n" -"- teamd:用来实现Link Aggregation(链路聚合)的容器\n" -"- snmp:用来实现SNMP协议(Simple Network Management Protocol,简单网络管理协" -"议)的容器" -msgstr "" -"- bgp: the container used to implement the BGP protocol (Border Gateway " -"Protocol)\n" -"- lldp: the container used to implement LLDP (Link Layer Discovery " -"Protocol)\n" -"- teamd: the container used to implement Link Aggregation\n" -"- snmp: the container used to implement SNMP (Simple Network Management " -"Protocol)" - -#: src/2-3-key-containers.md:132 -msgid "" -"和SWSS类似,为了适应SONiC的架构,它们中间也都会运行着上面我们提到的那几种服" -"务:" -msgstr "" -"Similar to SWSS, in order to accommodate the SONiC architecture, they all " -"run the same kinds of services we mentioned above in the middle:" - -#: src/2-3-key-containers.md:134 -msgid "" -"- 配置管理和下发(类似`*mgrd`):`lldpmgrd`,`zebra`(bgp)\n" -"- 状态同步(类似`*syncd`):`lldpsyncd`,`fpmsyncd`(bgp),`teamsyncd`\n" -"- 服务实现或者外部接口(`*d`):`lldpd`,`bgpd`,`teamd`,`snmpd`" -msgstr "" -"- Configuration management and despatch (similar to `*mgrd`): `lldpmgrd`, " -"`zebra` (bgp)\n" -"- Status synchronization (similar to `*syncd`): `lldpsyncd`, `fpmsyncd` " -"(bgp), `teamsyncd`\n" -"- Service implementation or external interface (`*d`): `lldpd`, `bgpd`, " -"`teamd`, `snmpd`" - -#: src/2-3-key-containers.md:138 -msgid "## 管理服务容器:mgmt-framework" -msgstr "## Managed Service Container: mgmt-framework" - -#: src/2-3-key-containers.md:140 -msgid "" -"我们在之前的章节中已经看过如何使用SONiC的CLI来进行一些交换机的配置,但是在实" -"际生产环境中,手动登录交换机使用CLI来配置所有的交换机是不现实的,所以SONiC提" -"供了一个REST API来解决这个问题。这个REST API的实现就是在`mgmt-framework`容器" -"中。我们可以通过`ps`命令来查看:" -msgstr "" -"We have seen in previous sections how to use SONiC's CLI for some switch " -"configuration, but in a real production environment, it is not practical to " -"manually log into the switch to configure all the switches using the CLI, so " -"SONiC provides a REST API to solve this problem. The implementation of this " -"REST API is in the `mgmt-framework` container. We can view it with the `ps` " -"command:" - -#: src/2-3-key-containers.md:142 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it mgmt-framework bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 16 0.3 1.2 1472804 52036 pts/0 Sl 16:20 0:02 /usr/sbin/" -"rest_server -ui /rest_ui -logtostderr -cert /tmp/cert.pem -key /tmp/key.pem\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker exec -it mgmt-framework bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 16 0.3 1.2 1472804 52036 pts/0 Sl 16:20 0:02 /usr/sbin/" -"rest_server -ui /rest_ui -logtostderr -cert /tmp/cert.pem -key /tmp/key.pem\n" -"...\n" -"```" - -#: src/2-3-key-containers.md:151 -msgid "" -"其实除了REST API,SONiC还可以通过其他方式来进行管理,如gNMI,这些也都是运行在" -"这个容器中的。其整体架构如下图所示 [\\[2\\]][SONiCMgmtFramework]:" -msgstr "" -"In fact, besides the REST API, SONiC can also be managed in other ways, such " -"as gNMI, which are also running in this container. Its overall architecture " -"is shown in the following figure [\\[2\\]][SONiCMgmtFramework]:" - -#: src/2-3-key-containers.md:155 -msgid "" -"这里我们也可以发现,其实我们使用的CLI,底层也是通过调用这个REST API来实现的~" -msgstr "" -"Here we can also find that we actually use the CLI, the underlying is also " -"achieved by calling this REST API ~" - -#: src/2-3-key-containers.md:157 -msgid "## 平台监控容器:pmon(Platform Monitor)" -msgstr "## Platform Monitor container: pmon (Platform Monitor)" - -#: src/2-3-key-containers.md:159 -msgid "" -"这个容器里面的服务基本都是用来监控交换机一些基础硬件的运行状态的,比如温度," -"电源,风扇,SFP事件等等。同样,我们可以用`ps`命令来查看这个容器中运行的服务:" -msgstr "" -"The services inside this container are basically used to monitor the " -"operational status of some basic hardware of the switch, such as " -"temperature, power, fans, SFP events, etc. Again, we can use the `ps` " -"command to view the services running in this container:" - -#: src/2-3-key-containers.md:161 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it pmon bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 28 0.0 0.8 49972 33192 pts/0 S Apr26 0:23 python3 /" -"usr/local/bin/ledd\n" -"root 29 0.9 1.0 278492 43816 pts/0 Sl Apr26 34:41 python3 /" -"usr/local/bin/xcvrd\n" -"root 30 0.4 1.0 57660 40412 pts/0 S Apr26 18:41 python3 /" -"usr/local/bin/psud\n" -"root 32 0.0 1.0 57172 40088 pts/0 S Apr26 0:02 python3 /" -"usr/local/bin/syseepromd\n" -"root 33 0.0 1.0 58648 41400 pts/0 S Apr26 0:27 python3 /" -"usr/local/bin/thermalctld\n" -"root 34 0.0 1.3 70044 53496 pts/0 S Apr26 0:46 /usr/bin/" -"python3 /usr/local/bin/pcied\n" -"root 42 0.0 0.0 55320 1136 ? Ss Apr26 0:15 /usr/sbin/" -"sensord -f daemon\n" -"root 45 0.0 0.8 58648 32220 pts/0 S Apr26 2:45 python3 /" -"usr/local/bin/thermalctld\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"admin@sonic:~$ docker exec -it pmon bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 28 0.0 0.8 49972 33192 pts/0 S Apr26 0:23 python3 /" -"usr/local/bin/ledd\n" -"root 29 0.9 1.0 278492 43816 pts/0 Sl Apr26 34:41 python3 /" -"usr/local/bin/xcvrd\n" -"root 30 0.4 1.0 57660 40412 pts/0 S Apr26 18:41 python3 /" -"usr/local/bin/psud\n" -"root 32 0.0 1.0 57172 40088 pts/0 S Apr26 0:02 python3 /" -"usr/local/bin/syseepromd\n" -"root 33 0.0 1.0 58648 41400 pts/0 S Apr26 0:27 python3 /" -"usr/local/bin/thermalctld\n" -"root 34 0.0 1.3 70044 53496 pts/0 S Apr26 0:46 /usr/bin/" -"python3 /usr/local/bin/pcied\n" -"root 42 0.0 0.0 55320 1136 ? Ss Apr26 0:15 /usr/sbin/" -"sensord -f daemon\n" -"root 45 0.0 0.8 58648 32220 pts/0 S Apr26 2:45 python3 /" -"usr/local/bin/thermalctld\n" -"...\n" -"```" - -#: src/2-3-key-containers.md:177 -msgid "" -"其中大部分的服务从名字我们就能猜出来是做什么的了,中间只有xcvrd不是那么明显," -"这里xcvr是transceiver的缩写,它是用来监控交换机的光模块的,比如SFP,QSFP等" -"等。" -msgstr "" -"Most of these services we can guess what they do from the name, only xcvrd " -"in the middle is not so obvious, here xcvr is the abbreviation of " -"transceiver, it is used to monitor the optical modules of the switch, such " -"as SFP, QSFP and so on." - -#: src/2-3-key-containers.md:181 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Management Framework][SONiCMgmtFramework]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Management Framework][SONiCMgmtFramework]" - -#: src/2-4-sai-intro.md:1 -msgid "# SAI" -msgstr "# SAI" - -#: src/2-4-sai-intro.md:3 -msgid "" -"SAI(Switch Abstraction Interface,交换机抽象接口)是SONiC的基石,正因为有了" -"它,SONiC才能支持多种硬件平台。我们在[这个SAI API的文档][SAIAPI]中,可以看到" -"它定义的所有接口。" -msgstr "" -"The SAI (Switch Abstraction Interface) is the cornerstone of SONiC, and it " -"is because of it that SONiC can support multiple hardware platforms. We can " -"see all the interfaces it defines in [this SAI API's documentation][SAIAPI]." - -#: src/2-4-sai-intro.md:5 -msgid "" -"[在核心容器一节中我们提到,SAI运行在`syncd`容器中](./2-3-key-containers." -"html)。不过和其他组件不同,它并不是一个服务,而是一组公共的头文件和动态链接库" -"(.so)。其中,所有的抽象接口都以c语言头文件的方式定义在了[OCP的SAI仓库]" -"[OCPSAI]中,而.so文件则由各个硬件厂商提供,用于实现SAI的接口。" -msgstr "" -"[As we mentioned in the section on core containers, SAI runs in the `syncd` " -"container](. /2-3-key-containers.html). Unlike other components, however, it " -"is not a service, but a set of public header files and dynamic link " -"libraries (.so). Among them, all abstract interfaces are defined in the " -"[OCP's SAI repository][OCPSAI] as c language header files, while .so files " -"are provided by individual hardware vendors for implementing the SAI " -"interfaces." - -#: src/2-4-sai-intro.md:7 -msgid "## SAI接口" -msgstr "## SAI Interface" - -#: src/2-4-sai-intro.md:9 -msgid "" -"为了有一个更加直观的理解,我们拿一小部分代码来展示一下SAI的接口定义和初始化的" -"方法,如下:" -msgstr "" -"To have a more intuitive understanding, let's take a small part of the code " -"to show how the SAI interface is defined and initialized, as follows:" - -#: src/2-4-sai-intro.md:11 -msgid "" -"```cpp\n" -"// File: meta/saimetadata.h\n" -"typedef struct _sai_apis_t {\n" -" sai_switch_api_t* switch_api;\n" -" sai_port_api_t* port_api;\n" -" ...\n" -"} sai_apis_t;\n" -"\n" -"// File: inc/saiswitch.h\n" -"typedef struct _sai_switch_api_t\n" -"{\n" -" sai_create_switch_fn create_switch;\n" -" sai_remove_switch_fn remove_switch;\n" -" sai_set_switch_attribute_fn set_switch_attribute;\n" -" sai_get_switch_attribute_fn get_switch_attribute;\n" -" ...\n" -"} sai_switch_api_t;\n" -"\n" -"// File: inc/saiport.h\n" -"typedef struct _sai_port_api_t\n" -"{\n" -" sai_create_port_fn create_port;\n" -" sai_remove_port_fn remove_port;\n" -" sai_set_port_attribute_fn set_port_attribute;\n" -" sai_get_port_attribute_fn get_port_attribute;\n" -" ...\n" -"} sai_port_api_t;\n" -"```" -msgstr "" -"```cpp\n" -"// File: meta/saimetadata.h\n" -"typedef struct _sai_apis_t {\n" -" sai_switch_api_t* switch_api;\n" -" sai_port_api_t* port_api;\n" -" ...\n" -"} sai_apis_t;\n" -"\n" -"// File: inc/saiswitch.h\n" -"typedef struct _sai_switch_api_t\n" -"{\n" -" sai_create_switch_fn create_switch;\n" -" sai_remove_switch_fn remove_switch;\n" -" sai_set_switch_attribute_fn set_switch_attribute;\n" -" sai_get_switch_attribute_fn get_switch_attribute;\n" -" ...\n" -"} sai_switch_api_t;\n" -"\n" -"// File: inc/saiport.h\n" -"typedef struct _sai_port_api_t\n" -"{\n" -" sai_create_port_fn create_port;\n" -" sai_remove_port_fn remove_port;\n" -" sai_set_port_attribute_fn set_port_attribute;\n" -" sai_get_port_attribute_fn get_port_attribute;\n" -" ...\n" -"} sai_port_api_t;\n" -"```" - -#: src/2-4-sai-intro.md:40 -msgid "" -"其中,`sai_apis_t`结构体是SAI所有模块的接口的集合,其中每个成员都是一个特定模" -"块的接口列表的指针。我们用`sai_switch_api_t`来举例,它定义了SAI Switch模块的" -"所有接口,我们在`inc/saiswitch.h`中可以看到它的定义。同样的,我们在`inc/" -"saiport.h`中可以看到SAI Port模块的接口定义。" -msgstr "" -"where the `sai_apis_t` structure is a collection of interfaces for all SAI " -"modules, where each member is a pointer to a list of interfaces for a " -"particular module. Let's use `sai_switch_api_t` as an example, which defines " -"all the interfaces of the SAI Switch module, which we can see defined in " -"`inc/saiswitch.h`. Similarly, we can see the interface definitions for the " -"SAI Port module in `inc/saiport.h`." - -#: src/2-4-sai-intro.md:42 -msgid "## SAI初始化" -msgstr "## SAI initialization" - -#: src/2-4-sai-intro.md:44 -msgid "" -"SAI的初始化其实就是想办法获取上面这些函数指针,这样我们就可以通过SAI的接口来" -"操作ASIC了。" -msgstr "" -"The initialization of the SAI is actually figuring out how to get these " -"function pointers above so that we can operate the ASIC through the SAI's " -"interface." - -#: src/2-4-sai-intro.md:46 -msgid "参与SAI初始化的主要函数有两个,他们都定义在`inc/sai.h`中:" -msgstr "" -"There are two main functions involved in the initialization of the SAI, both " -"of which are defined in `inc/sai.h`:" - -#: src/2-4-sai-intro.md:48 -msgid "" -"- `sai_api_initialize`:初始化SAI\n" -"- `sai_api_query`:传入SAI的API的类型,获取对应的接口列表" -msgstr "" -"- `sai_api_initialize`: initialize SAI\n" -"- `sai_api_query`: pass in the type of SAI's API and get the list of " -"corresponding interfaces" - -#: src/2-4-sai-intro.md:51 -msgid "" -"虽然大部分厂商的SAI实现是闭源的,但是mellanox却开源了自己的SAI实现,所以这里" -"我们可以借助其更加深入的理解SAI是如何工作的。" -msgstr "" -"While most vendors' SAI implementations are closed source, mellanox has open " -"sourced its own SAI implementation, so here we can use it to understand more " -"deeply how SAI works." - -#: src/2-4-sai-intro.md:53 -msgid "" -"比如,`sai_api_initialize`函数其实就是简单的设置设置两个全局变量,然后返回" -"`SAI_STATUS_SUCCESS`:" -msgstr "" -"For example, the `sai_api_initialize` function actually simply sets sets two " -"global variables and returns `SAI_STATUS_SUCCESS`:" - -#: src/2-4-sai-intro.md:55 -msgid "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const " -"sai_service_method_table_t* services)\n" -"{\n" -" if (g_initialized) {\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -" // Validate parameters here (code omitted)\n" -"\n" -" memcpy(&g_mlnx_services, services, sizeof(g_mlnx_services));\n" -" g_initialized = true;\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const " -"sai_service_method_table_t* services)\n" -"{\n" -" if (g_initialized) {\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -" // Validate parameters here (code omitted)\n" -"\n" -" memcpy(&g_mlnx_services, services, sizeof(g_mlnx_services));\n" -" g_initialized = true;\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" -"```" - -#: src/2-4-sai-intro.md:70 -msgid "" -"初始化完成后,我们就可以使用`sai_api_query`函数,通过传入API的类型来查询对应" -"的接口列表,而每一个接口列表其实都是一个全局变量:" -msgstr "" -"After initialization, we can use the `sai_api_query` function to query the " -"corresponding interface list by passing in the type of the API, and each " -"interface list is actually a global variable: the" - -#: src/2-4-sai-intro.md:72 -msgid "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" if (!g_initialized) {\n" -" return SAI_STATUS_UNINITIALIZED;\n" -" }\n" -" ...\n" -"\n" -" return sai_api_query_eth(sai_api_id, api_method_table);\n" -"}\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery_eth.c\n" -"sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" switch (sai_api_id) {\n" -" case SAI_API_BRIDGE:\n" -" *(const sai_bridge_api_t**)api_method_table = &mlnx_bridge_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" case SAI_API_SWITCH:\n" -" *(const sai_switch_api_t**)api_method_table = &mlnx_switch_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" ...\n" -" default:\n" -" if (sai_api_id >= (sai_api_t)SAI_API_EXTENSIONS_RANGE_END) {\n" -" return SAI_STATUS_INVALID_PARAMETER;\n" -" } else {\n" -" return SAI_STATUS_NOT_IMPLEMENTED;\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_bridge.c\n" -"const sai_bridge_api_t mlnx_bridge_api = {\n" -" mlnx_create_bridge,\n" -" mlnx_remove_bridge,\n" -" mlnx_set_bridge_attribute,\n" -" mlnx_get_bridge_attribute,\n" -" ...\n" -"};\n" -"\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_switch.c\n" -"const sai_switch_api_t mlnx_switch_api = {\n" -" mlnx_create_switch,\n" -" mlnx_remove_switch,\n" -" mlnx_set_switch_attribute,\n" -" mlnx_get_switch_attribute,\n" -" ...\n" -"};\n" -"```" -msgstr "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" if (!g_initialized) {\n" -" return SAI_STATUS_UNINITIALIZED;\n" -" }\n" -" ...\n" -"\n" -" return sai_api_query_eth(sai_api_id, api_method_table);\n" -"}\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_interfacequery_eth.c\n" -"sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" switch (sai_api_id) {\n" -" case SAI_API_BRIDGE:\n" -" *(const sai_bridge_api_t**)api_method_table = &mlnx_bridge_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" case SAI_API_SWITCH:\n" -" *(const sai_switch_api_t**)api_method_table = &mlnx_switch_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" ...\n" -" default:\n" -" if (sai_api_id >= (sai_api_t)SAI_API_EXTENSIONS_RANGE_END) {\n" -" return SAI_STATUS_INVALID_PARAMETER;\n" -" } else {\n" -" return SAI_STATUS_NOT_IMPLEMENTED;\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_bridge.c\n" -"const sai_bridge_api_t mlnx_bridge_api = {\n" -" mlnx_create_bridge,\n" -" mlnx_remove_bridge,\n" -" mlnx_set_bridge_attribute,\n" -" mlnx_get_bridge_attribute,\n" -" ...\n" -"};\n" -"\n" -"\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_switch.c\n" -"const sai_switch_api_t mlnx_switch_api = {\n" -" mlnx_create_switch,\n" -" mlnx_remove_switch,\n" -" mlnx_set_switch_attribute,\n" -" mlnx_get_switch_attribute,\n" -" ...\n" -"};\n" -"```" - -#: src/2-4-sai-intro.md:124 -msgid "## SAI的使用" -msgstr "## Use of SAI" - -#: src/2-4-sai-intro.md:126 -msgid "" -"在`syncd`容器中,SONiC会在启动时启动`syncd`服务,而`syncd`服务会加载当前系统" -"中的SAI组件。这个组件由各个厂商提供,它们会根据自己的硬件平台来实现上面展现的" -"SAI的接口,从而让SONiC使用统一的上层逻辑来控制多种不同的硬件平台。" -msgstr "" -"In the `syncd` container, SONiC will start the `syncd` service at boot time, " -"and the `syncd` service will load the SAI component currently on the system. " -"This component is provided by individual vendors, who will implement the " -"interface to the SAI shown above according to their own hardware platforms, " -"thus allowing SONiC to control many different hardware platforms using a " -"unified upper layer logic." - -#: src/2-4-sai-intro.md:128 -msgid "我们可以通过`ps`, `ls`和`nm`命令来简单的对这个进行验证:" -msgstr "We can verify this simply by using the `ps`, `ls` and `nm` commands:" - -#: src/2-4-sai-intro.md:130 -msgid "" -"```bash\n" -"# Enter into syncd container\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"# List all processes. We will only see syncd process here.\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 21 0.0 0.0 87708 1532 pts/0 Sl 16:20 0:00 /usr/bin/" -"dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"root 33 11.1 15.0 2724396 602532 pts/0 Sl 16:20 36:30 /usr/bin/" -"syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"...\n" -"\n" -"# Find all libsai*.so.* files.\n" -"root@sonic:/# find / -name libsai*.so.*\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0\n" -"/usr/lib/libsai.so.1\n" -"/usr/lib/libsai.so.1.0\n" -"\n" -"# Copy the file out of switch and check libsai.so on your own dev machine.\n" -"# We will see the most important SAI export functions here.\n" -"$ nm -C -D ./libsai.so.1.0 > ./sai-exports.txt\n" -"$ vim sai-exports.txt\n" -"...\n" -"0000000006581ae0 T sai_api_initialize\n" -"0000000006582700 T sai_api_query\n" -"0000000006581da0 T sai_api_uninitialize\n" -"...\n" -"```" -msgstr "" -"```bash\n" -"# Enter into syncd container\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"# List all processes. We will only see syncd process here.\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 21 0.0 0.0 87708 1532 pts/0 Sl 16:20 0:00 /usr/bin/" -"dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"root 33 11.1 15.0 2724396 602532 pts/0 Sl 16:20 36:30 /usr/bin/" -"syncd --diag -u -s -p /etc/sai.d/sai.profile -b /tmp/" -"break_before_make_objects\n" -"...\n" -"\n" -"# Find all libsai*.so.* files.\n" -"root@sonic:/# find / -name libsai*.so.*\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0\n" -"/usr/lib/libsai.so.1\n" -"/usr/lib/libsai.so.1.0\n" -"\n" -"# Copy the file out of switch and check libsai.so on your own dev machine.\n" -"# We will see the most important SAI export functions here.\n" -"$ nm -C -D ./libsai.so.1.0 > ./sai-exports.txt\n" -"$ vim sai-exports.txt\n" -"...\n" -"0000000006581ae0 T sai_api_initialize\n" -"0000000006582700 T sai_api_query\n" -"0000000006581da0 T sai_api_uninitialize\n" -"...\n" -"```" - -#: src/2-4-sai-intro.md:166 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]\n" -"4. [Github: sonic-net/sonic-sairedis][SONiCSAIRedis]\n" -"5. [Github: opencomputeproject/SAI][OCPSAI]\n" -"6. [Arista 7050QX Series 10/40G Data Center Switches Data Sheet]" -"[Arista7050QX]\n" -"7. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]\n" -"4. [Github: sonic-net/sonic-sairedis][SONiCSAIRedis]\n" -"5. [Github: opencomputeproject/SAI][OCPSAI]\n" -"6. [Arista 7050QX Series 10/40G Data Center Switches Data Sheet]" -"[Arista7050QX]\n" -"7. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" - -#: src/3-dev-guide.md:1 -msgid "# 开发上手指南" -msgstr "# Development Tutorial" - -#: src/3-1-code-repos.md:1 -msgid "# 代码仓库" -msgstr "# Code Repository" - -#: src/3-1-code-repos.md:3 -msgid "" -"SONiC的代码都托管在[GitHub的sonic-net账号][SONiCGitHub]上,仓库数量有30几个之" -"多,所以刚开始看SONiC的代码时,肯定是会有点懵的,不过不用担心,我们这里就来一" -"起看看~" -msgstr "" -"SONiC's code is hosted on [GitHub's sonic-net account][SONiCGitHub], and " -"there are more than 30 repositories, so when you first start looking at " -"SONiC's code, you'll definitely be a little confused, but don't worry, let's " -"take a look together here ~" - -#: src/3-1-code-repos.md:5 -msgid "## 核心仓库" -msgstr "## Core Warehouse" - -#: src/3-1-code-repos.md:7 -msgid "首先是SONiC中最重要的两个核心仓库:SONiC和sonic-buildimage。" -msgstr "" -"First are the two most important core repositories in SONiC: SONiC and sonic-" -"buildimage." - -#: src/3-1-code-repos.md:9 -msgid "### Landing仓库:SONiC" -msgstr "### Landing warehouse: SONiC" - -#: src/3-1-code-repos.md:11 -msgid "" -msgstr "" - -#: src/3-1-code-repos.md:13 -msgid "" -"这个仓库里面存储着SONiC的Landing Page和大量的文档,Wiki,教程,以往的Talk的" -"Slides,等等等等。这个仓库可以说是每个新人上手最常用的仓库了,但是注意,这个" -"仓库里面**没有任何的代码**,只有文档。" -msgstr "" -"This repository stores SONiC's Landing Page and a lot of documentation, " -"wiki, tutorials, Slides from previous Talks, etc. etc. This repository can " -"be said to be the most common repository for every newcomer to get started, " -"but note that there is **no code** in this repository, only documentation." - -#: src/3-1-code-repos.md:15 -msgid "### 镜像构建仓库:sonic-buildimage" -msgstr "### Image build repository: sonic-buildimage" - -#: src/3-1-code-repos.md:17 -msgid "" -msgstr "" - -#: src/3-1-code-repos.md:19 -msgid "" -"这个构建仓库为什么对于我们十分重要?和其他项目不同,**SONiC的构建仓库其实才是" -"它的主仓库**!这个仓库里面包含:" -msgstr "" -"Why is this build repository important to us? Unlike other projects, " -"**SONiC's build repository is actually its main repository**! This " -"repository contains:" - -#: src/3-1-code-repos.md:21 -msgid "" -"- 所有的功能实现仓库,它们都以submodule的形式被加入到了这个仓库中(`src`目" -"录)\n" -"- 所有设备厂商的支持文件(`device`目录),比如每个型号的交换机的配置文件,用" -"来访问硬件的支持脚本,等等等等,比如:我的交换机是Arista 7050 QX-32S,那么我" -"就可以在`device/arista/x86_64-arista_7050_qx32s`目录中找到它的支持文件。\n" -"- 所有ASIC芯片厂商提供的支持文件(`platform`目录),比如每个平台的驱动程序," -"BSP,底层支持的脚本等等。这里我们可以看到几乎所有的主流芯片厂商的支持文件,比" -"如:Broadcom,Mellanox,等等,也有用来做模拟软交换机的实现,比如vs和p4。\n" -"- SONiC用来构建所有容器镜像的Dockerfile(`dockers`目录)\n" -"- 各种各样通用的配置文件和脚本(`files`目录)\n" -"- 用来做构建的编译容器的dockerfile(`sonic-slave-*`目录)\n" -"- 等等……" -msgstr "" -"- All the feature implementation repositories, they are added to this " -"repository as submodule (`src` directory)\n" -"- Support files for all device vendors (`device` directory), such as " -"configuration files for each model of switch, support scripts for accessing " -"hardware, etc. etc. For example: my switch is an Arista 7050 QX-32S, so I " -"can find its support files in the `device/arista/x86_64-arista_7050_qx32s ` " -"directory to find its support files.\n" -"- Support files provided by all ASIC chip vendors (`platform` directory), " -"such as drivers for each platform, BSP, underlying support scripts, etc. " -"Here we can see support files from almost all major chip vendors, such as: " -"Broadcom, Mellanox, etc. There are also implementations used to make " -"emulated soft switches, such as vs and p4.\n" -"- The Dockerfile (`dockers` directory) that SONiC uses to build all " -"container images\n" -"- Various generic configuration files and scripts (`files` directory)\n" -"- Dockerfile for compiled containers used to do builds (`sonic-slave-*` " -"directory)\n" -"- etc. ......" - -#: src/3-1-code-repos.md:29 -msgid "" -"正因为这个仓库里面将所有相关的资源全都放在了一起,所以我们学习SONiC的代码时," -"基本只需要下载这一个源码仓库就可以了,不管是搜索还是跳转都非常方便!" -msgstr "" -"Because this repository has all the relevant resources put together, we " -"basically only need to download this one source code repository when " -"learning SONiC's code, no matter it is very convenient to search or jump!" - -#: src/3-1-code-repos.md:31 -msgid "## 功能实现仓库" -msgstr "## Function Implementation Warehouse" - -#: src/3-1-code-repos.md:33 -msgid "" -"除了核心仓库,SONiC下还有很多功能实现仓库,里面都是各个容器和子服务的实现,这" -"些仓库都被以submodule的形式放在了sonic-buildimage的`src`目录下,如果我们想对" -"SONiC进行修改和贡献,我们也需要了解一下。" -msgstr "" -"In addition to the core repository, there are also many feature " -"implementation repositories under SONiC, which are implementations of " -"various containers and subservices. These repositories are placed under the " -"`src` directory of sonic-buildimage in the form of submodule, which we also " -"need to know if we want to make changes and contributions to SONiC." - -#: src/3-1-code-repos.md:35 -msgid "### SWSS(Switch State Service)相关仓库" -msgstr "### SWSS (Switch State Service) related repository" - -#: src/3-1-code-repos.md:37 -msgid "" -"在上一篇中我们介绍过,SWSS容器是SONiC的大脑,在SONiC下,它由两个repo组成:" -"[sonic-swss-common](https://github.com/sonic-net/sonic-swss-common)和[sonic-" -"swss](https://github.com/sonic-net/sonic-swss)。" -msgstr "" -"As we introduced in the previous article, the SWSS container is the brain of " -"SONiC. Under SONiC, it consists of two repo's: [sonic-swss-common](https://" -"github.com/sonic-net/sonic-swss-common) and [sonic-swss]( https://github.com/" -"sonic-net/sonic-swss)." - -#: src/3-1-code-repos.md:39 -msgid "#### SWSS公共库:sonic-swss-common" -msgstr "#### SWSS public library: sonic-swss-common" - -#: src/3-1-code-repos.md:41 -msgid "" -"首先是公共库:sonic-swss-common()。" -msgstr "" -"First is the public library: sonic-swss-common ()." - -#: src/3-1-code-repos.md:43 -msgid "" -"这个仓库里面包含了所有`*mgrd`和`*syncd`服务所需要的公共功能,比如,logger," -"json,netlink的封装,Redis操作和基于Redis的各种服务间通讯机制的封装等等。虽然" -"能看出来这个仓库一开始的目标是专门给swss服务使用的,但是也正因为功能多,很多" -"其他的仓库都有它的引用,比如`swss-sairedis`和`swss-restapi`。" -msgstr "" -"This repository contains all the public functions required by the `*mgrd` " -"and `*syncd` services, such as the encapsulation of logger, json, netlink, " -"Redis operations and various Redis-based inter-service communication " -"mechanisms. Although it can be seen that this repository was initially " -"targeted specifically for use by the swss service, it is also referenced by " -"many other repositories, such as `swss-sairedis` and `swss-restapi`, because " -"of its many features." - -#: src/3-1-code-repos.md:45 -msgid "#### SWSS主仓库:sonic-swss" -msgstr "#### SWSS main repository: sonic-swss" - -#: src/3-1-code-repos.md:47 -msgid "" -"然后就是SWSS的主仓库sonic-swss了:。" -msgstr "" -"Then there is the main SWSS repository, sonic-swss: ." - -#: src/3-1-code-repos.md:49 -msgid "我们可以在这个仓库中找到:" -msgstr "We can find in this repository:" - -#: src/3-1-code-repos.md:51 -msgid "" -"- 绝大部分的`*mgrd`和`*syncd`服务:`orchagent`, `portsyncd/portmgrd/" -"intfmgrd`,`neighsyncd/nbrmgrd`,`natsyncd/natmgrd`,`buffermgrd`," -"`coppmgrd`,`macsecmgrd`,`sflowmgrd`,`tunnelmgrd`,`vlanmgrd`,`vrfmgrd`," -"`vxlanmgrd`,等等。\n" -"- `swssconfig`:在`swssconfig`目录下,用于在快速重启时(fast reboot)恢复FDB" -"和ARP表。\n" -"- `swssplayer`:也在`swssconfig`目录下,用来记录所有通过SWSS进行的配置下发操" -"作,这样我们就可以利用它来做replay,从而对问题进行重现和调试。\n" -"- 甚至一些不在SWSS容器中的服务,比如`fpmsyncd`(bgp容器)和`teamsyncd/" -"teammgrd`(teamd容器)。" -msgstr "" -"- Most of the `*mgrd` and `*syncd` services: `orchagent`, `portsyncd/" -"portmgrd/intfmgrd`, `neighsyncd/nbrmgrd`, `natsyncd/natmgrd`, `buffermgrd`, " -"`coppmgrd` , `macsecmgrd`, `sflowmgrd`, `tunnelmgrd`, `vlanmgrd`, `vrfmgrd`, " -"`vxlanmgrd`, etc.\n" -"- `swssconfig`: in the `swssconfig` directory, used to restore the FDB and " -"ARP tables during fast reboot (fast reboot).\n" -"- `swssplayer`: also in the `swssconfig` directory, used to record all " -"configuration downlink operations performed via SWSS, so we can use it to do " -"replay and thus reproduce and debug the problem.\n" -"- Even some services that are not in the SWSS container, such as `fpmsyncd` " -"(bgp container) and `teamsyncd/teammgrd` (teamd container)." - -#: src/3-1-code-repos.md:56 -msgid "### SAI/平台相关仓库" -msgstr "### SAI/platform related warehouses" - -#: src/3-1-code-repos.md:58 -msgid "" -"接下来就是作为交换机抽象接口的SAI了,[虽然SAI是微软提出来并在2015年3月份发布" -"了0.1版本](https://www.opencompute.org/documents/switch-abstraction-" -"interface-ocp-specification-v0-2-pdf),但是[在2015年9月份,SONiC都还没有发布" -"第一个版本的时候,就已经被OCP接收并作为一个公共的标准了](https://azure." -"microsoft.com/en-us/blog/switch-abstraction-interface-sai-officially-" -"accepted-by-the-open-compute-project-ocp/),这也是SONiC能够在这么短的时间内就" -"得到了这么多厂商的支持的原因之一。而也因为如此,SAI的代码仓库也被分成了两部" -"分:" -msgstr "" -"Next up is SAI as a switch abstraction interface, [although SAI was proposed " -"by Microsoft and released in March 2015 as version 0.1](https://www." -"opencompute.org/documents/switch-abstraction-interface-ocp- specification-" -"v0-2-pdf), [it was accepted by OCP and made a public standard in September " -"2015, before SONiC even released the first version](https://azure.microsoft." -"com/en-us/blog/switch- abstraction-interface-sai-officially-accepted-by-the-" -"open-compute-project-ocp/), which is one of the reasons why SONiC has been " -"able to get support from so many vendors in such a short time. And because " -"of this, SAI's code repository has been divided into two parts:" - -#: src/3-1-code-repos.md:60 -msgid "" -"- OCP下的OpenComputeProject/SAI:。里面包含了有关SAI标准的所有代码,包括SAI的头文件,behavior model,测试" -"用例,文档等等。\n" -"- SONiC下的sonic-sairedis:。里" -"面包含了SONiC中用来和SAI交互的所有代码,比如syncd服务,和各种调试统计,比如用" -"来做replay的`saiplayer`和用来导出asic状态的`saidump`。" -msgstr "" -"- OpenComputeProject/SAI under OCP: . It contains all the code about the SAI standard, including SAI header " -"files, behavior model, test cases, documentation, etc.\n" -"- SONiC under sonic-sairedis: . " -"It contains all the code used in SONiC to interact with SAI, such as syncd " -"service, and various debugging statistics, such as `saiplayer` used to do " -"replay and `saidump` used to export asic state." - -#: src/3-1-code-repos.md:63 -msgid "" -"除了这两个仓库之外,还有一个平台相关的仓库,比如:[sonic-platform-vpp]" -"(https://github.com/sonic-net/sonic-platform-vpp),它的作用是通过SAI的接口," -"利用vpp来实现数据平面的功能,相当于一个高性能的软交换机,个人感觉未来可能会被" -"合并到buildimage仓库中,作为platform目录下的一部分。" -msgstr "" -"In addition to these two repositories, there is also a platform-related " -"repository, for example: [sonic-platform-vpp](https://github.com/sonic-net/" -"sonic-platform-vpp), which serves to implement data plane functions using " -"vpp through SAI's interface, equivalent to a High-performance soft switch. I " -"personally feel that it may be merged into the buildimage repository in the " -"future, as part of the platform directory." - -#: src/3-1-code-repos.md:65 -msgid "### 管理服务(mgmt)相关仓库" -msgstr "### Managed services (mgmt) related warehouses" - -#: src/3-1-code-repos.md:67 -msgid "然后是SONiC中所有和[管理服务][SONiCMgmtFramework]相关的仓库:" -msgstr "" -"Then all the repositories in SONiC related to the [managed services]" -"[SONiCMgmtFramework]:" - -#: src/3-1-code-repos.md:69 -msgid "" -"| 名称 | 说明 |\n" -"| --- | --- |\n" -"| [sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common) | 管理" -"服务的基础库,里面包含着`translib`,yang model相关的代码 |\n" -"| [sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework) " -"| 使用Go来实现的REST Server,是下方架构图中的REST Gateway(进程名:" -"`rest_server`) |\n" -"| [sonic-gnmi](https://github.com/sonic-net/sonic-gnmi) | 和sonic-mgmt-" -"framework类似,是下方架构图中,基于gRPC的gNMI(gRPC Network Management " -"Interface)Server |\n" -"| [sonic-restapi](https://github.com/sonic-net/sonic-restapi) | 这是SONiC使用" -"go来实现的另一个配置管理的REST Server,和mgmt-framework不同,这个server在收到" -"消息后会直接对CONFIG_DB进行操作,而不是走translib(下图中没有,进程名:`go-" -"server-server`) |\n" -"| [sonic-mgmt](https://github.com/sonic-net/sonic-mgmt) | 各种自动化脚本" -"(`ansible`目录),测试(`tests`目录),用来搭建test bed和测试上报" -"(`test_reporting`目录)之类的, |" -msgstr "" -"| Name | Description |\n" -"| --- | --- |\n" -"| [sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common) | The " -"base library for managing services, which contains `translib`, yang model " -"related code |\n" -"| [sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework) " -"| The REST Server, implemented using Go, is the REST Gateway in the " -"architecture diagram below (process name: `rest_server `)\n" -"| [sonic-gnmi](https://github.com/sonic-net/sonic-gnmi) | Similar to sonic-" -"mgmt-framework, this is the gRPC-based gNMI (gRPC Network Management " -"Interface) in the following diagram Server\n" -"| [sonic-restapi](https://github.com/sonic-net/sonic-restapi) | This is " -"another REST Server for configuration management implemented by SONiC using " -"go, unlike the mgmt-framework, this server will directly operate on " -"CONFIG_DB after receiving messages. This server will operate on CONFIG_DB " -"directly after receiving the message, instead of going translib (not in the " -"following figure, process name: `go-server-server`) |\n" -"| [sonic-mgmt](https://github.com/sonic-net/sonic-mgmt) | various automation " -"scripts (`ansible` directory), tests (`tests` directory), used to build test " -"beds and test reporting (`test_reporting` directory) and so on. |" - -#: src/3-1-code-repos.md:77 -msgid "" -"这里还是附上SONiC管理服务的架构图,方便大家配合食用 [\\[4\\]]" -"[SONiCMgmtFramework]:" -msgstr "" -"Here is still attached the architecture diagram of SONiC management service " -"for your convenience with consumption [\\[4\\]][SONiCMgmtFramework]:" - -#: src/3-1-code-repos.md:81 -msgid "### 平台监控相关仓库:sonic-platform-common和sonic-platform-daemons" -msgstr "" -"### Platform monitoring related repositories: sonic-platform-common and " -"sonic-platform-daemons" - -#: src/3-1-code-repos.md:83 -msgid "以下两个仓库都和平台监控和控制相关,比如LED,风扇,电源,温控等等:" -msgstr "" -"The following two warehouses are related to platform monitoring and control, " -"such as LEDs, fans, power supplies, temperature control, etc.:" - -#: src/3-1-code-repos.md:85 -msgid "" -"| 名称 | 说明 |\n" -"| --- | --- |\n" -"| [sonic-platform-common](https://github.com/sonic-net/sonic-platform-" -"common) | 这是给厂商们提供的基础包,用来定义访问风扇,LED,电源管理,温控等等" -"模块的接口定义,这些接口都是用python来实现的 |\n" -"| [sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-" -"daemons) | 这里包含了SONiC中pmon容器中运行的各种监控服务:`chassisd`," -"`ledd`,`pcied`,`psud`,`syseepromd`,`thermalctld`,`xcvrd`,`ycabled`,它" -"们都使用python实现,通过和中心数据库Redis进行连接,和加载并调用各个厂商提供的" -"接口实现来对各个模块进行监控和控制 |" -msgstr "" -"| Name | Description |\n" -"| --- | --- |\n" -"| [sonic-platform-common](https://github.com/sonic-net/sonic-platform-" -"common) | This is a base package for vendors to define interface definitions " -"for accessing modules such as fans, LEDs, power management, temperature " -"control, etc. These interfaces are all implemented using These interfaces " -"are implemented in python |\n" -"| [sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-" -"daemons) | This contains the various monitoring services running in the pmon " -"container in SONiC: `chassisd`, `ledd`, ` pcied`, `psud`, `syseepromd`, " -"`thermalctld`, `xcvrd`, `ycabled`, all of which are implemented in python to " -"monitor and control each module by connecting to the central database Redis " -"and loading and calling the interfaces provided by each vendor." - -#: src/3-1-code-repos.md:90 -msgid "### 其他功能实现仓库" -msgstr "### Other function implementation warehouse" - -#: src/3-1-code-repos.md:92 -msgid "" -"除了上面这些仓库以外,SONiC还有很多实现其方方面面功能的仓库,有些是一个或多个" -"进程,有些是一些库,它们的作用如下表所示:" -msgstr "" -"In addition to these repositories above, SONiC has a number of repositories " -"that implement various aspects of its functionality, some of which are one " -"or more processes, and some of which are libraries that serve the following " -"purposes:" - -#: src/3-1-code-repos.md:94 -msgid "" -"| 仓库 | 介绍 |\n" -"| --- | --- |\n" -"| [sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent) | [AgentX]" -"(https://www.ietf.org/rfc/rfc2741.txt) SNMP subagent的实现" -"(`sonic_ax_impl`),用于连接Redis数据库,给snmpd提供所需要的各种信息,可以把" -"它理解成snmpd的控制面,而snmpd是数据面,用于响应外部SNMP的请求 |\n" -"| [sonic-frr](https://github.com/sonic-net/sonic-frr) | FRRouting,各种路由协" -"议的实现,所以这个仓库中我们可以找到如`bgpd`,`zebra`这类的路由相关的进程实" -"现 |\n" -"| [sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd) | Dual ToR " -"support,检查Link的状态,并且控制ToR的连接 |\n" -"| [sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay) | DHCP " -"relay agent |\n" -"| [sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon) | 监控DHCP的状" -"态,并报告给中心数据库Redis |\n" -"| [sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd) | `lldp_syncd`" -"服务,但是repo的名字没取好,叫做dbsyncd |\n" -"| [sonic-pins](https://github.com/sonic-net/sonic-pins) | Google开发的基于P4" -"的网络栈支持(P4 Integrated Network Stack,PINS),更多信息可以参看[PINS的官" -"网][SONiCPINS]。 |\n" -"| [sonic-stp](https://github.com/sonic-net/sonic-stp) | STP(Spanning Tree " -"Protocol)的支持 |\n" -"| [sonic-ztp](https://github.com/sonic-net/sonic-ztp) | [Zero Touch " -"Provisioning][SONiCZTP] |\n" -"| [DASH](https://github.com/sonic-net/DASH) | [Disaggregated API for SONiC " -"Hosts][SONiCDASH] |\n" -"| [sonic-host-services](https://github.com/sonic-net/sonic-host-services) | " -"运行在host上通过dbus用来为容器中的服务提供支持的服务,比如保存和重新加载配" -"置,保存dump之类的非常有限的功能,类似一个host broker |\n" -"| [sonic-fips](https://github.com/sonic-net/sonic-fips) | FIPS(Federal " -"Information Processing Standards)的支持,里面有很多为了支持FIPS标准而加入的" -"各种补丁文件 |\n" -"| [sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant) " -"| 各种无线网络协议的支持 |" -msgstr "" -"| Warehouse | Introduction |\n" -"| --- | --- |\n" -"| [sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent) | [AgentX]" -"(https://www.ietf.org/rfc/rfc2741.txt) The implementation of the SNMP " -"subagent (` sonic_ax_impl`), which is used to connect to the Redis database " -"and give snmpd the various information it needs, can be interpreted as the " -"control plane of snmpd, which is the data plane and is used to respond to " -"external SNMP requests |\n" -"| [sonic-frr](https://github.com/sonic-net/sonic-frr) | FRRouting, the " -"implementation of various routing protocols, so this repository we can find " -"such routing-related process implementations as `bgpd`, `zebra`, etc. |\n" -"| [sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd) | Dual ToR " -"support, check the status of Link and control the ToR connection |\n" -"| [sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay) | DHCP " -"relay agent |\n" -"| [sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon) | Monitor DHCP " -"status and report to the central database Redis |\n" -"| [sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd) | The " -"`lldp_syncd` service, but the repo is not named properly, it is called " -"dbsyncd |\n" -"| [sonic-pins](https://github.com/sonic-net/sonic-pins) | Google's P4-based " -"network stack support (P4 Integrated Network Stack, PINS), for more " -"information, see [PINS's official website][. SONiCPINS] for more " -"information. |SONiCPINS\n" -"| [sonic-stp](https://github.com/sonic-net/sonic-stp) | STP (Spanning Tree " -"Protocol) support |\n" -"| [sonic-ztp](https://github.com/sonic-net/sonic-ztp) | [Zero Touch " -"Provisioning][SONiCZTP] |\n" -"| [DASH](https://github.com/sonic-net/DASH) | [Disaggregated API for SONiC " -"Hosts][SONiCDASH] |\n" -"| [sonic-host-services](https://github.com/sonic-net/sonic-host-services) | " -"The services running on the host via dbus are used to provide support for " -"services in the container, such as saving and reloading configurations, " -"saving dumps, and other very limited This is similar to a host broker.\n" -"| [sonic-fips](https://github.com/sonic-net/sonic-fips) | FIPS (Federal " -"Information Processing Standards) support, which contains many of the " -"various patch files added to support the FIPS standard |\n" -"| [sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant) " -"| Support for various wireless network protocols |" - -#: src/3-1-code-repos.md:110 -msgid "## 工具仓库:sonic-utilities" -msgstr "## Tools repository: sonic-utilities" - -#: src/3-1-code-repos.md:112 -msgid "" -msgstr "" - -#: src/3-1-code-repos.md:114 -msgid "这个仓库存放着SONiC所有的命令行下的工具:" -msgstr "This repository holds all of SONiC's tools under the command line:" - -#: src/3-1-code-repos.md:116 -msgid "" -"- `config`,`show`,`clear`目录:这是三个SONiC CLI的主命令的实现。需要注意的" -"是,具体的命令实现并不一定在这几个目录里面,大量的命令是通过调用其他命令来实" -"现的,这几个命令只是提供了一个入口。\n" -"- `scripts`,`sfputil`,`psuutil`,`pcieutil`,`fwutil`,`ssdutil`," -"`acl_loader`目录:这些目录下提供了大量的工具命令,但是它们大多并不是直接给用" -"户使用的,而是被`config`,`show`和`clear`目录下的命令调用的,比如:`show " -"platform fan`命令,就是通过调用`scripts`目录下的`fanshow`命令来实现的。\n" -"- `utilities_common`,`flow_counter_util`,`syslog_util`目录:这些目录和上面" -"类似,但是提供的是基础类,可以直接在python中import调用。\n" -"- 另外还有很多其他的命令:`fdbutil`,`pddf_fanutil`,`pddf_ledutil`," -"`pddf_psuutil`,`pddf_thermalutil`,等等,用于查看和控制各个模块的状态。\n" -"- `connect`和`consutil`目录:这两个目录下的命令是用来连接到其他SONiC设备并对" -"其进行管理的。\n" -"- `crm`目录:用来配置和查看SONiC中的[CRM(Critical Resource Monitoring)]" -"[SONiCCRM]。这个命令并没有被包含在`config`和`show`命令中,所以用户可以直接使" -"用。\n" -"- `pfc`目录:用来配置和查看SONiC中的[PFC(Priority-based Flow Control)]" -"[SONiCPFC]。\n" -"- `pfcwd`目录:用来配置和查看SONiC中的[PFC Watch Dog][SONiCPFCWD],比如启动," -"停止,修改polling interval之类的操作。" -msgstr "" -"- The `config`, `show`, and `clear` directories: these are the three main " -"command implementations of the SONiC CLI. Note that the specific command " -"implementations are not necessarily in these directories; a large number of " -"commands are implemented by calling other commands, and these commands just " -"provide an entry point.\n" -"- `scripts`, `sfputil`, `psuutil`, `pcieutil`, `fwutil`, `ssdutil`, " -"`acl_loader` directories: These directories provide a large number of " -"utility commands, but most of them are not directly available to the user, " -"but are called by the `config`, `show` and ` clear` directories. For " -"example, the `show platform fan` command is implemented by calling the " -"`fanshow` command in the `scripts` directory.\n" -"- `utilities_common`, `flow_counter_util`, `syslog_util` directories: these " -"directories are similar to the above, but provide basic classes that can be " -"directly imported in python.\n" -"- There are also many other commands: `fdbutil`, `pddf_fanutil`, " -"`pddf_ledutil`, `pddf_psuutil`, `pddf_thermalutil`, and so on, for viewing " -"and controlling the status of each module.\n" -"- `connect` and `consutil` directories: the commands in these two " -"directories are used to connect to other SONiC devices and manage them.\n" -"- `crm` directory: This is used to configure and view [CRM (Critical " -"Resource Monitoring)][SONiCCRM] in SONiC. This command is not included in " -"the `config` and `show` commands, so users can use it directly.\n" -"- `pfc` directory: used to configure and view [PFC (Priority-based Flow " -"Control)][SONiCPFC] in SONiC.\n" -"- `pfcwd` directory: used to configure and view the [PFC Watch Dog]" -"[SONiCPFCWD] in SONiC, such as start, stop, modify the polling interval and " -"other operations." - -#: src/3-1-code-repos.md:125 -msgid "## 内核补丁:sonic-linux-kernel" -msgstr "## Kernel patch: sonic-linux-kernel" - -#: src/3-1-code-repos.md:127 -msgid "" -msgstr "" - -#: src/3-1-code-repos.md:129 -msgid "" -"虽然SONiC是基于debian的,但是默认的debian内核却不一定能运行SONiC,比如某个模" -"块默认没有启动,或者某些老版本的驱动有问题,所以SONiC需要或多或少有一些修改的" -"Linux内核。而这个仓库就是用来存放所有的内核补丁的。" -msgstr "" -"Although SONiC is based on debian, the default debian kernel is not always " -"able to run SONiC, for example, a module is not started by default, or some " -"old version of the driver has problems, so SONiC needs a more or less " -"modified Linux kernel. And this repository is used to store all the kernel " -"patches." - -#: src/3-1-code-repos.md:133 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Source Repositories][SONiCRepo]\n" -"3. [SONiC Management Framework][SONiCMgmtFramework]\n" -"4. [SAI API][SAIAPI]\n" -"5. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"6. [SONiC Zero Touch Provisioning][SONiCZTP]\n" -"7. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"8. [SONiC P4 Integrated Network Stack][SONiCPINS]\n" -"9. [SONiC Disaggregated API for Switch Hosts][SONiCDash]\n" -"10. [SAI spec for OCP][SAISpec]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Source Repositories][SONiCRepo]\n" -"3. [SONiC Management Framework][SONiCMgmtFramework]\n" -"4. [SAI API][SAIAPI]\n" -"5. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"6. [SONiC Zero Touch Provisioning][SONiCZTP]\n" -"7. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"8. [SONiC P4 Integrated Network Stack][SONiCPINS]\n" -"9. [SONiC Disaggregated API for Switch Hosts][SONiCDash]\n" -"10. [SAI spec for OCP][SAISpec]" - -#: src/3-2-compile.md:1 -msgid "# 编译" -msgstr "# Compilation" - -#: src/3-2-compile.md:3 -msgid "## 编译环境" -msgstr "## Compiler Environment" - -#: src/3-2-compile.md:5 -msgid "" -"由于SONiC是基于debian开发的,为了保证我们无论在什么平台下都可以成功的编译" -"SONiC,并且编译出来的程序能在对应的平台上运行,SONiC使用了容器化的编译环境 " -"—— 它将所有的工具和依赖都安装在对应debian版本的docker容器中,然后将我们的代码" -"挂载进容器,最后在容器内部进行编译工作,这样我们就可以很轻松的在任何平台上编" -"译SONiC,而不用担心依赖不匹配的问题,比如有一些包在debian里的版本比ubuntu更" -"高,这样就可能导致最后的程序在debian上运行的时候出现一些意外的错误。" -msgstr "" -"Since SONiC is based on debian, in order to ensure that we can successfully " -"compile SONiC no matter what platform we are on, and that the compiled " -"program can run on the corresponding platform, SONiC uses a containerized " -"compilation environment -- it installs all the tools and dependencies in the " -"corresponding This allows us to easily compile SONiC on any platform without " -"worrying about dependency mismatches, such as packages that are higher in " -"debian than in ubuntu, which may lead to some problems when the final " -"program runs on This may lead to some unexpected errors when the final " -"program is run on debian." - -#: src/3-2-compile.md:7 -msgid "## 初始化编译环境" -msgstr "## Initialize the compilation environment" - -#: src/3-2-compile.md:9 -msgid "### 安装Docker" -msgstr "### Install Docker" - -#: src/3-2-compile.md:11 -msgid "" -"为了支持容器化的编译环境,第一步,我们需要保证我们的机器上安装了docker。" -msgstr "" -"In order to support a containerized build environment, as a first step, we " -"need to make sure that docker is installed on our machine." - -#: src/3-2-compile.md:13 -msgid "" -"Docker的安装方法可以参考[官方文档][DockerInstall],这里我们以Ubuntu为例,简单" -"介绍一下安装方法。" -msgstr "" -"The installation method of Docker can be found in the [official " -"documentation][DockerInstall], here we take Ubuntu as an example and briefly " -"introduce the installation method." - -#: src/3-2-compile.md:15 -msgid "首先,我们需要把docker的源和证书加入到apt的源列表中:" -msgstr "" -"First, we need to add the docker sources and certificates to the apt source " -"list: the" - -#: src/3-2-compile.md:17 -msgid "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install ca-certificates curl gnupg\n" -"\n" -"sudo install -m 0755 -d /etc/apt/keyrings\n" -"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor " -"-o /etc/apt/keyrings/docker.gpg\n" -"sudo chmod a+r /etc/apt/keyrings/docker.gpg\n" -"\n" -"echo \\\n" -" \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/" -"docker.gpg] https://download.docker.com/linux/ubuntu \\\n" -" \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n" -" sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\n" -"```" -msgstr "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install ca-certificates curl gnupg\n" -"\n" -"sudo install -m 0755 -d /etc/apt/keyrings\n" -"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor " -"-o /etc/apt/keyrings/docker.gpg\n" -"sudo chmod a+r /etc/apt/keyrings/docker.gpg\n" -"\n" -"echo \\\n" -" \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/" -"docker.gpg] https://download.docker.com/linux/ubuntu \\\n" -" \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n" -" sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\n" -"```" - -#: src/3-2-compile.md:31 -msgid "然后,我们就可以通过apt来快速安装啦:" -msgstr "Then, we can quickly install it via apt:" - -#: src/3-2-compile.md:33 -msgid "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-" -"plugin docker-compose-plugin\n" -"```" -msgstr "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-" -"plugin docker-compose-plugin\n" -"```" - -#: src/3-2-compile.md:38 -msgid "" -"安装完docker的程序之后,我们还需要把当前的账户添加到docker的用户组中,然后**" -"退出并重新登录当前用户**,这样我们就可以不用sudo来运行docker命令了!**这一点" -"非常重要**,因为后续SONiC的build是不允许使用sudo的。" -msgstr "" -"After installing docker's program, we also need to add our current account " -"to docker's user group, then **quit and log back in as the current user** so " -"we can run docker commands without sudo! **This is very important** because " -"subsequent builds of SONiC do not allow sudo." - -#: src/3-2-compile.md:40 -msgid "" -"```bash\n" -"sudo gpasswd -a ${USER} docker\n" -"```" -msgstr "" -"```bash\n" -"sudo gpasswd -a ${USER} docker\n" -"```" - -#: src/3-2-compile.md:44 -msgid "" -"安装完成之后,别忘了通过以下命令来验证一下是否安装成功(注意,此处不需要" -"sudo!):" -msgstr "" -"Once the installation is complete, don't forget to verify that it was " -"successful by using the following command (note that sudo is not required " -"here!):" - -#: src/3-2-compile.md:46 -msgid "" -"```bash\n" -"docker run hello-world\n" -"```" -msgstr "" -"```bash\n" -"docker run hello-world\n" -"```" - -#: src/3-2-compile.md:50 -msgid "### 安装其他依赖" -msgstr "### Install additional dependencies" - -#: src/3-2-compile.md:52 -msgid "" -"```bash\n" -"sudo apt install -y python3-pip\n" -"pip3 install --user j2cli\n" -"```" -msgstr "" -"```bash\n" -"sudo apt install -y python3-pip\n" -"pip3 install --user j2cli\n" -"```" - -#: src/3-2-compile.md:57 -msgid "### 拉取代码" -msgstr "### Pull code" - -#: src/3-2-compile.md:59 -msgid "" -"在[3.1 代码仓库](./3-1-code-repos)一章中,我们提到了SONiC的主仓库是[sonic-" -"buildimage][SonicBuildimageRepo]。它也是我们目前为止唯一需要安装关注的repo。" -msgstr "" -"In the [3.1 Code repository](. /3-1-code-repos) chapter, we mentioned that " -"the main SONiC repository is [sonic-buildimage][SonicBuildimageRepo]. It is " -"also the only repo that we need to install and follow so far." - -#: src/3-2-compile.md:61 -msgid "" -"因为这个仓库通过submodule的形式将其他所有和编译相关的仓库包含在内,我们通过" -"git命令拉取代码时需要注意加上`--recuse-submodules`的选项:" -msgstr "" -"Since this repository includes all other build-related repositories in the " -"form of submodules, we need to be careful to add the `-recuse-submodules` " -"option when pulling code via the git command:" - -#: src/3-2-compile.md:63 -msgid "" -"```bash\n" -"git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage." -"git\n" -"```" -msgstr "" -"```bash\n" -"git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage." -"git\n" -"```" - -#: src/3-2-compile.md:67 -msgid "如果在拉取代码的时候忘记拉取submodule,可以通过以下命令来补上:" -msgstr "" -"If you forget to pull the submodule when pulling the code, you can fill it " -"in with the following command:" - -#: src/3-2-compile.md:69 -msgid "" -"```bash\n" -"git submodule update --init --recursive\n" -"```" -msgstr "" -"```bash\n" -"git submodule update --init --recursive\n" -"```" - -#: src/3-2-compile.md:73 -msgid "" -"当代码下载完毕之后,或者对于已有的repo,我们就可以通过以下命令来初始化编译环" -"境了。这个命令更新当前所有的submodule到需要的版本,以帮助我们成功编译:" -msgstr "" -"Once the code has been downloaded, or for existing repo's, we can initialize " -"the compilation environment with the following command. This command updates " -"all current submodules to the required version to help us compile " -"successfully:" - -#: src/3-2-compile.md:75 -msgid "" -"```bash\n" -"sudo modprobe overlay\n" -"make init\n" -"```" -msgstr "" -"```bash\n" -"sudo modprobe overlay\n" -"make init\n" -"```" - -#: src/3-2-compile.md:80 -msgid "## 了解并设置你的目标平台" -msgstr "## Understand and set up your target platform" - -#: src/3-2-compile.md:82 -msgid "" -"[SONiC虽然支持非常多种不同的交换机][SONiCDevices],但是由于不同型号的交换机使" -"用的ASIC不同,所使用的驱动和SDK也会不同。SONiC通过SAI来封装这些变化,为上层提" -"供统一的配置接口,但是在编译的时候,我们需要正确的设置好,这样才能保证我们编" -"译出来的SONiC可以在我们的目标平台上运行。" -msgstr "" -"[Although SONiC supports a very wide variety of different switches]" -"[SONiCDevices], the drivers and SDKs used will be different due to the " -"different ASICs used by different models of switches. SONiC encapsulates " -"these variations through SAI to provide a unified configuration interface " -"for the upper layers, but when compiling, we need to set it up correctly so " -"that the SONiC we compile will work on our target platform." - -#: src/3-2-compile.md:84 -msgid "现在,SONiC主要支持如下几个平台:" -msgstr "Nowadays, SONiC mainly supports the following platforms:" - -#: src/3-2-compile.md:86 -msgid "" -"- barefoot\n" -"- broadcom\n" -"- marvell\n" -"- mellanox\n" -"- cavium\n" -"- centec\n" -"- nephos\n" -"- innovium\n" -"- vs" -msgstr "" -"- barefoot\n" -"- broadcom\n" -"- marvell\n" -"- mellanox\n" -"- cavium\n" -"- centec\n" -"- nephos\n" -"- innovium\n" -"- vs" - -#: src/3-2-compile.md:96 -msgid "在确认好平台之后,我们就可以运行如下命令来配置我们的编译环境了:" -msgstr "" -"After confirming the platform, we can run the following command to configure " -"our compilation environment:" - -#: src/3-2-compile.md:98 -msgid "" -"```bash\n" -"make PLATFORM= configure\n" -"# e.g.: make PLATFORM=mellanox configure\n" -"```" -msgstr "" -"```bash\n" -"make PLATFORM= configure\n" -"# e.g.: make PLATFORM=mellanox configure\n" -"```" - -#: src/3-2-compile.md:103 -msgid "" -"```admonish note\n" -"所有的make命令(除了`make init`)一开始都会检查并创建所有debian版本的" -"docker builder:bullseye,stretch,jessie,buster。每个builder都需要几十分钟" -"的时间才能创建完成,这对于我们平时开发而言实在完全没有必要,一般来说,我们只" -"需要创建最新的版本即可(当前为bullseye,bookwarm暂时还没有支持),具体命令如" -"下:\n" -"\n" -" NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make PLATFORM= configure\n" -"\n" -"当然,为了以后开发更加方便,避免重复输入,我们可以将这个命令写入到`~/.bashrc`" -"中,这样每次打开终端的时候,就会设置好这些环境变量了。\n" -"\n" -" export NOJESSIE=1\n" -" export NOSTRETCH=1\n" -" export NOBUSTER=1\n" -"```" -msgstr "" -"```admonish note\n" -"所有的make命令(除了`make init`)一开始都会检查并创建所有debian版本的" -"docker builder:bullseye,stretch,jessie,buster。每个builder都需要几十分钟" -"的时间才能创建完成,这对于我们平时开发而言实在完全没有必要,一般来说,我们只" -"需要创建最新的版本即可(当前为bullseye,bookwarm暂时还没有支持),具体命令如" -"下:\n" -"\n" -" NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make PLATFORM= configure\n" -"\n" -"当然,为了以后开发更加方便,避免重复输入,我们可以将这个命令写入到`~/.bashrc`" -"中,这样每次打开终端的时候,就会设置好这些环境变量了。\n" -"\n" -" export NOJESSIE=1\n" -" export NOSTRETCH=1\n" -" export NOBUSTER=1\n" -"```" - -#: src/3-2-compile.md:115 -msgid "## 编译代码" -msgstr "## Compile the code" - -#: src/3-2-compile.md:117 -msgid "### 编译全部代码" -msgstr "### Compile the entire code" - -#: src/3-2-compile.md:119 -msgid "设置好平台之后,我们就可以开始编译代码了:" -msgstr "After setting up the platform, we can start compiling the code::" - -#: src/3-2-compile.md:121 -msgid "" -"```bash\n" -"# The number of jobs can be the number of cores on your machine.\n" -"# Say, if you have 16 cores, then feel free to set it to 16 to speed up the " -"build.\n" -"make SONIC_BUILD_JOBS=4 all\n" -"```" -msgstr "" -"```bash\n" -"# The number of jobs can be the number of cores on your machine.\n" -"# Say, if you have 16 cores, then feel free to set it to 16 to speed up the " -"build.\n" -"make SONIC_BUILD_JOBS=4 all\n" -"```" - -#: src/3-2-compile.md:127 -msgid "" -"```admonish note\n" -"当然,对于开发而言,我们可以把SONIC_BUILD_JOBS和上面其他变量一起也加入`~/." -"bashrc`中,减少我们的输入。\n" -"\n" -" export SONIC_BUILD_JOBS=\n" -"```" -msgstr "" -"```admonish note\n" -"当然,对于开发而言,我们可以把SONIC_BUILD_JOBS和上面其他变量一起也加入`~/." -"bashrc`中,减少我们的输入。\n" -"\n" -" export SONIC_BUILD_JOBS=\n" -"```" - -#: src/3-2-compile.md:133 -msgid "### 编译子项目代码" -msgstr "### Compile subproject code" - -#: src/3-2-compile.md:135 -msgid "" -"我们从SONiC的Build Pipeline中就会发现,编译整个项目是非常耗时的,而绝大部分时" -"候,我们的代码改动只会影响很小部分的代码,所以有没有办法减少我们编译的工作量" -"呢?答案是肯定的,我们可以通过指定make target来仅编译我们需要的子项目。" -msgstr "" -"As we can see from SONiC's Build Pipeline, compiling the entire project is " -"very time consuming, and most of the time, our code changes will only affect " -"a small part of the code, so is there any way to reduce our compilation " -"effort? The answer is yes, we can specify make target to compile only the " -"subprojects we need." - -#: src/3-2-compile.md:137 -msgid "SONiC中每个子项目生成的文件都可以在`target`目录中找到,比如:" -msgstr "" -"The files generated by each subproject in SONiC can be found in the `target` " -"directory, e.g:" - -#: src/3-2-compile.md:139 -msgid "" -"- Docker containers: target/.gz,比如:`target/docker-" -"orchagent.gz`\n" -"- Deb packages: target/debs//.deb,比如:`target/" -"debs/bullseye/libswsscommon_1.0.0_amd64.deb`\n" -"- Python wheels: target/python-wheels//.whl,比如:" -"`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl`" -msgstr "" -"- Docker containers: target/.gz,比如:`target/docker-" -"orchagent.gz`\n" -"- Deb packages: target/debs//.deb,比如:`target/" -"debs/bullseye/libswsscommon_1.0.0_amd64.deb`\n" -"- Python wheels: target/python-wheels//.whl,比如:" -"`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl`" - -#: src/3-2-compile.md:143 -msgid "" -"当我们找到了我们需要的子项目之后,我们便可以将其生成的文件删除,然后重新调用" -"make命令,这里我们用`libswsscommon`来举例子,如下:" -msgstr "" -"Once we have found the subproject we need, we can delete its generated files " -"and then re-invoke the make command, here we use `libswsscommon` as an " -"example, as follows:" - -#: src/3-2-compile.md:145 -msgid "" -"```bash\n" -"# Remove the deb package for bullseye\n" -"rm target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"\n" -"# Build the deb package for bullseye\n" -"NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make target/debs/bullseye/" -"libswsscommon_1.0.0_amd64.deb\n" -"```" -msgstr "" -"```bash\n" -"# Remove the deb package for bullseye\n" -"rm target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"\n" -"# Build the deb package for bullseye\n" -"NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make target/debs/bullseye/" -"libswsscommon_1.0.0_amd64.deb\n" -"```" - -#: src/3-2-compile.md:153 -msgid "### 检查和处理编译错误" -msgstr "### Check and handle compilation errors" - -#: src/3-2-compile.md:155 -msgid "" -"如果不巧在编译的时候发生了错误,我们可以通过检查失败项目的日志文件来查看具体" -"的原因。在SONiC中,每一个子编译项目都会生成其相关的日志文件,我们可以很容易的" -"在`target`目录中找到,如下:" -msgstr "" -"If by chance an error occurs while compiling, we can check the exact cause " -"by examining the log file of the failed project. In SONiC, each subcompiled " -"project generates its associated log file, which we can easily find in the " -"`target` directory, as follows:" - -#: src/3-2-compile.md:157 -msgid "" -"```bash\n" -"$ ls -l\n" -"...\n" -"-rw-r--r-- 1 r12f r12f 103M Jun 8 22:35 docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 26K Jun 8 22:35 docker-database.gz.log // Log " -"file for docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 106M Jun 8 22:44 docker-dhcp-relay.gz\n" -"-rw-r--r-- 1 r12f r12f 106K Jun 8 22:44 docker-dhcp-relay.gz.log // Log " -"file for docker-dhcp-relay.gz\n" -"```" -msgstr "" -"```bash\n" -"$ ls -l\n" -"...\n" -"-rw-r--r-- 1 r12f r12f 103M Jun 8 22:35 docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 26K Jun 8 22:35 docker-database.gz.log // Log " -"file for docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 106M Jun 8 22:44 docker-dhcp-relay.gz\n" -"-rw-r--r-- 1 r12f r12f 106K Jun 8 22:44 docker-dhcp-relay.gz.log // Log " -"file for docker-dhcp-relay.gz\n" -"```" - -#: src/3-2-compile.md:166 -msgid "" -"如果我们不想每次在更新代码之后都去代码的根目录下重新编译,然后查看日志文件," -"SONiC还提供了一个更加方便的方法,能让我们在编译完成之后停在docker builder中," -"这样我们就可以直接去对应的目录运行`make`命令来重新编译了:" -msgstr "" -"If we don't want to go to the root of the code and recompile it every time " -"we update it and then check the log files, SONiC also provides a more " -"convenient way to stop the build in the docker builder after it's done, so " -"we can go directly to the corresponding directory and run the `make` command " -"to recompile it:" - -#: src/3-2-compile.md:168 -msgid "" -"```bash\n" -"# KEEP_SLAVE_ON=yes make \n" -"KEEP_SLAVE_ON=yes make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"KEEP_SLAVE_ON=yes make all\n" -"```" -msgstr "" -"```bash\n" -"# KEEP_SLAVE_ON=yes make \n" -"KEEP_SLAVE_ON=yes make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"KEEP_SLAVE_ON=yes make all\n" -"```" - -#: src/3-2-compile.md:174 -msgid "" -"```admonish note\n" -"有些仓库中的部分代码在全量编译的时候是不会编译的,比如,`sonic-swss-common`中" -"的gtest,所以使用这种方法重编译的时候,请一定注意查看原仓库的编译指南,以避免" -"出错,如:。\n" -"```" -msgstr "" -"```admonish note\n" -"有些仓库中的部分代码在全量编译的时候是不会编译的,比如,`sonic-swss-common`中" -"的gtest,所以使用这种方法重编译的时候,请一定注意查看原仓库的编译指南,以避免" -"出错,如:。\n" -"```" - -#: src/3-2-compile.md:178 -msgid "## 获取正确的镜像文件" -msgstr "## Get the correct image file" - -#: src/3-2-compile.md:180 -msgid "" -"编译完成之后,我们就可以在`target`目录中找到我们需要的镜像文件了,但是这里有" -"一个问题:我们到底要用哪一种镜像来把SONiC安装到我们的交换机上呢?这里主要取决" -"于交换机使用什么样的BootLoader或者安装程序,其映射关系如下:" -msgstr "" -"Once compiled, we can find the image file we need in the `target` directory, " -"but here's a question: which image do we use to install SONiC on our switch? " -"Here it depends on what BootLoader or installer the switch is using, and the " -"mapping is as follows:" - -#: src/3-2-compile.md:182 -msgid "" -"| Bootloader | 后缀 |\n" -"| --- | --- |\n" -"| Aboot | .swi |\n" -"| ONIE | .bin |\n" -"| Grub | .img.gz |" -msgstr "" -"| Bootloader | 后缀 |\n" -"| --- | --- |\n" -"| Aboot | .swi |\n" -"| ONIE | .bin\n" -"| Grub | .img.gz |." - -#: src/3-2-compile.md:188 -msgid "## 部分升级" -msgstr "## Partial upgrade" - -#: src/3-2-compile.md:190 -msgid "" -"显然,在开发的时候,每次都编译安装镜像然后进行全量安装的效率是相当低下的,所" -"以我们可以选择不安装镜像而使用直接升级deb包的方式来进行部分升级,从而提高我们" -"的开发效率。" -msgstr "" -"Obviously, when developing, it is quite inefficient to compile and install " -"the image each time and then do a full install, so we can choose not to " -"install the image and use the direct upgrade deb package to do a partial " -"upgrade, thus improving our development efficiency." - -#: src/3-2-compile.md:192 -msgid "" -"我们可以将deb包上传到交换机的`/etc/sonic`目录下,这个目录下的文件会被map到所" -"有容器的`/etc/sonic`目录下,接着我们可以进入到容器中,然后使用`dpkg`命令来安" -"装deb包,如下:" -msgstr "" -"We can upload the deb package to the `/etc/sonic` directory of the switch, " -"the files in this directory will be mapped to the `/etc/sonic` directory of " -"all containers, then we can enter the container and use the `dpkg` command " -"to install the deb package as follows:" - -#: src/3-2-compile.md:194 -msgid "" -"```bash\n" -"# Enter the docker container\n" -"docker exec -it bash\n" -"\n" -"# Install deb package\n" -"dpkg -i \n" -"```" -msgstr "" -"```bash\n" -"# Enter the docker container\n" -"docker exec -it bash\n" -"\n" -"# Install deb package\n" -"dpkg -i \n" -"```" - -#: src/3-2-compile.md:204 -msgid "" -"1. [SONiC Build Guide][SONiCBuild]\n" -"2. [Install Docker Engine][DockerInstall]\n" -"3. [Github repo: sonic-buildimage][SonicBuildimageRepo]\n" -"4. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"5. [Wrapper for starting make inside sonic-slave container]" -"[SONiCBuildImageMakeFile]" -msgstr "" -"1. [SONiC Build Guide][SONiCBuild]\n" -"2. [Install Docker Engine][DockerInstall]\n" -"3. [Github repo: sonic-buildimage][SonicBuildimageRepo]\n" -"4. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"5. [Wrapper for starting make inside sonic-slave container]" -"[SONiCBuildImageMakeFile]" - -#: src/3-3-testing.md:1 -msgid "# 测试" -msgstr "# Testing" - -#: src/3-4-debugging.md:1 -msgid "# 调试" -msgstr "# Debugging" - -#: src/3-4-sai-debugging.md:1 -msgid "# SAI调试" -msgstr "# SAI debugging" - -#: src/4-communications.md:1 -msgid "# 通信机制" -msgstr "# Communication mechanisms" - -#: src/4-communications.md:3 -msgid "" -"SONiC中主要的通信机制有三种:与内核的通信,基于Redis和基于ZMQ的服务间的通信。" -msgstr "" -"There are three main communication mechanisms in SONiC: communication with " -"the kernel, Redis-based and ZMQ-based inter-service communication." - -#: src/4-communications.md:5 -msgid "" -"- 与内核通信主要有两种方法:命令行调用和Netlink消息。\n" -"- 基于Redis的服务间通信主要有四种方法:SubscriberStateTable," -"NotificationProducer/Consumer,Producer/ConsumerTable,Producer/" -"ConsumerStateTable。虽然它们都是基于Redis的,但是它们解决的问题和方法却非常不" -"同。\n" -"- 基于ZMQ的服务间通信:现在只在`orchagent`和`syncd`的通信中使用了这种通信机" -"制。" -msgstr "" -"- There are two main methods for communicating with the kernel: command line " -"calls and Netlink messages.\n" -"- There are four main methods of Redis-based inter-service communication: " -"SubscriberStateTable, NotificationProducer/Consumer, Producer/ConsumerTable, " -"and Producer/ConsumerStateTable. Although they are both Redis-based, they " -"solve very different problems and approaches.\n" -"- ZMQ-based inter-service communication: This communication mechanism is now " -"used only for `orchagent` and `syncd` communication." - -#: src/4-communications.md:9 -msgid "" -"```admonish note\n" -"虽然大部分的通信机制都支持多消费者的PubSub的模式,但是请特别注意:在SONiC中," -"所有的通信都是点对点的,即一个生产者对应一个消费者,绝对不会出现一个生产者对" -"应多个消费者的情况!\n" -"\n" -"一旦多消费者出现,那么一个消息的处理逻辑将可能发生在多个进程中,这将导致很大" -"的问题,因为对于任何一种特定的消息,SONiC中只有一个地方来处理,所以这会导致部" -"分消息不可避免的出错或者丢失。\n" -"```" -msgstr "" -"```admonish note\n" -"虽然大部分的通信机制都支持多消费者的PubSub的模式,但是请特别注意:在SONiC中," -"所有的通信都是点对点的,即一个生产者对应一个消费者,绝对不会出现一个生产者对" -"应多个消费者的情况!\n" -"\n" -"一旦多消费者出现,那么一个消息的处理逻辑将可能发生在多个进程中,这将导致很大" -"的问题,因为对于任何一种特定的消息,SONiC中只有一个地方来处理,所以这会导致部" -"分消息不可避免的出错或者丢失。\n" -"```" - -#: src/4-communications.md:15 -msgid "" -"所有这些基础的通信机制的实现都在[sonic-swss-common][SONiCSWSSCommon]这个repo" -"中的`common`目录下。另外在其之上,为了方便各个服务使用,SONiC还在[sonic-swss]" -"[SONiCSWSS]中封装了一层Orch,将常用的表放在其中。" -msgstr "" -"The implementation of all these basic communication mechanisms is in the " -"`common` directory in the repo [sonic-swss-common][SONiCSWSSCommon]. Also on " -"top of it, for the convenience of each service, SONiC has wrapped a layer of " -"Orch in [sonic-swss][SONiCSWSS], where the commonly used tables are placed." - -#: src/4-communications.md:17 -msgid "这一章,我们就主要来看看这些通信机制的实现吧!" -msgstr "" -"In this chapter, let's focus on the implementation of these communication " -"mechanisms!" - -#: src/4-communications.md:21 src/4-2-1-redis-wrappers.md:35 -#: src/4-4-orch-layer.md:36 src/4-5-event-polling-and-error-handling.md:121 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]" - -#: src/4-1-1-exec.md:1 -msgid "# 命令行调用" -msgstr "# Command line calls" - -#: src/4-1-1-exec.md:3 -msgid "" -"SONiC中的与内核通信最简单的方式就是命令行调用了,其实现放在[common/exec.h]" -"(https://github.com/sonic-net/sonic-swss-common/blob/master/common/exec.h)文" -"件下,且十分简单,接口如下:" -msgstr "" -"The easiest way to communicate with the kernel in SONiC is to make a command " -"line call, which is placed under the [common/exec.h](https://github.com/" -"sonic-net/sonic-swss-common/blob/master/common/exec.h) file and is very " -"simple to implement The interface is as follows:" - -#: src/4-1-1-exec.md:5 -msgid "" -"```cpp\n" -"// File: common/exec.h\n" -"// Namespace: swss\n" -"int exec(const std::string &cmd, std::string &stdout);\n" -"```" -msgstr "" -"```cpp\n" -"// File: common/exec.h\n" -"// Namespace: swss\n" -"int exec(const std::string &cmd, std::string &stdout);\n" -"```" - -#: src/4-1-1-exec.md:11 -msgid "" -"其中,`cmd`是要执行的命令,`stdout`是命令执行的输出。这里的`exec`函数是一个同" -"步调用,调用者会一直阻塞,直到命令执行完毕。其内部通过调用`popen`函数来创建子" -"进程,并且通过`fgets`函数来获取输出。不过,**虽然这个函数返回了输出,但是基本" -"上并没有人使用**,而只是通过返回值来判断是否成功,甚至连错误log中都不会写入输" -"出的结果。" -msgstr "" -"Where `cmd` is the command to be executed and `stdout` is the output of the " -"command execution. The `exec` function here is a synchronous call, and the " -"caller blocks until the command is executed. Internally, it creates a child " -"process by calling the `popen` function and gets the output by using the " -"`fgets` function. However, **although this function returns output, " -"basically no one uses it**, but only by the return value to determine " -"whether it succeeded or not, and even the output is not written in the error " -"log." - -#: src/4-1-1-exec.md:13 -msgid "" -"这个函数虽然粗暴,但是使用广泛,特别是在各个`*mgrd`服务中,比如`portmgrd`中就" -"用它来设置每一个Port的状态等等。" -msgstr "" -"This function is crude but widely used, especially in the various `*mgrd` " -"services, such as `portmgrd` where it is used to set the status of each " -"port, etc." - -#: src/4-1-1-exec.md:15 -msgid "" -"```cpp\n" -"// File: sonic-swss - cfgmgr/portmgr.cpp\n" -"bool PortMgr::setPortAdminStatus(const string &alias, const bool up)\n" -"{\n" -" stringstream cmd;\n" -" string res, cmd_str;\n" -"\n" -" // ip link set dev [up|down]\n" -" cmd << IP_CMD << \" link set dev \" << shellquote(alias) << (up ? \" " -"up\" : \" down\");\n" -" cmd_str = cmd.str();\n" -" int ret = swss::exec(cmd_str, res);\n" -"\n" -" // ...\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss - cfgmgr/portmgr.cpp\n" -"bool PortMgr::setPortAdminStatus(const string &alias, const bool up)\n" -"{\n" -" stringstream cmd;\n" -" string res, cmd_str;\n" -"\n" -" // ip link set dev [up|down]\n" -" cmd << IP_CMD << \" link set dev \" << shellquote(alias) << (up ? \" " -"up\" : \" down\");\n" -" cmd_str = cmd.str();\n" -" int ret = swss::exec(cmd_str, res);\n" -"\n" -" // ...\n" -"```" - -#: src/4-1-1-exec.md:30 -msgid "" -"```admonish note\n" -"**为什么说命令行调用是一种通信机制呢**?\n" -"\n" -"原因是当`*mgrd`服务调用`exec`函数对系统进行的修改,会触发下面马上会提到的" -"netlink事件,从而通知其他服务进行相应的修改,比如`*syncd`,这样就间接的构成了" -"一种通信。所以这里我们把命令行调用看作一种通信机制能帮助我们以后更好的理解" -"SONiC的各种工作流。\n" -"```" -msgstr "" -"```admonish note\n" -"**为什么说命令行调用是一种通信机制呢**?\n" -"\n" -"原因是当`*mgrd`服务调用`exec`函数对系统进行的修改,会触发下面马上会提到的" -"netlink事件,从而通知其他服务进行相应的修改,比如`*syncd`,这样就间接的构成了" -"一种通信。所以这里我们把命令行调用看作一种通信机制能帮助我们以后更好的理解" -"SONiC的各种工作流。\n" -"```" - -#: src/4-1-1-exec.md:38 src/4-1-2-netlink.md:72 -msgid "1. [Github repo: sonic-swss-common][SONiCSWSSCommon]" -msgstr "1. [Github repo: sonic-swss-common][SONiCSWSSCommon]" - -#: src/4-1-2-netlink.md:1 -msgid "# Netlink" -msgstr "# Netlink" - -#: src/4-1-2-netlink.md:3 -msgid "" -"Netlink是Linux内核中用于内核与用户空间进程之间的一种基于消息的通信机制。它通" -"过套接字接口和自定义的协议族来实现,可以用来传递各种类型的内核消息,包括网络" -"设备状态、路由表更新、防火墙规则变化、系统资源使用情况等等。而SONiC的`*sync`" -"服务就大量使用了Netlink的机制来监听系统中网络设备的变化,并将最新的状态同步到" -"Redis中,并通知其他服务进行相应的修改。" -msgstr "" -"Netlink is a message-based communication mechanism used in the Linux kernel " -"between the kernel and user-space processes. It is implemented through a " -"socket interface and a custom protocol family, and can be used to deliver " -"various types of kernel messages, including network device status, routing " -"table updates, firewall rule changes, system resource usage, and so on. " -"SONiC's `*sync` service makes extensive use of Netlink's mechanism to listen " -"for changes to network devices in the system, synchronize the latest state " -"to Redis, and notify other services of the corresponding changes." - -#: src/4-1-2-netlink.md:5 -msgid "" -"Netlink的实现主要在这几个文件中:[common/netmsg.*](https://github.com/sonic-" -"net/sonic-swss-common/blob/master/common/netmsg.h)、[common/netlink.*]" -"(https://github.com/sonic-net/sonic-swss-common/blob/master/common/netlink." -"h) 和 [common/netdispatcher.*](https://github.com/sonic-net/sonic-swss-" -"common/blob/master/common/netdispatcher.h),具体类图如下:" -msgstr "" -"The Netlink implementation is mainly in these files: [common/netmsg.*]" -"(https://github.com/sonic-net/sonic-swss-common/blob/master/common/netmsg." -"h), [common/netlink. *](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/netlink.h) and [common/netdispatcher.*](https://github.com/" -"sonic-) net/sonic-swss-common/blob/master/common/netdispatcher.h), with the " -"following class diagram:" - -#: src/4-1-2-netlink.md:9 src/4-2-1-redis-wrappers.md:9 -msgid "其中:" -msgstr "Among them:" - -#: src/4-1-2-netlink.md:11 -msgid "" -"- **Netlink**:封装了Netlink的套接字接口,提供了Netlink消息的接口和接收消息的" -"回调。\n" -"- **NetDispatcher**:它是一个单例,提供了Handler注册的接口。当Netlink类接收到" -"原始的消息后,就会调用NetDispatcher将其解析成nl_onject,并根据消息的类型调用" -"相应的Handler。\n" -"- **NetMsg**:Netlink消息Handler的基类,仅提供了onMsg的接口,其中没有实现。" -msgstr "" -"- **Netlink**: It encapsulates the socket interface of Netlink and provides " -"the interface of Netlink messages and callbacks for receiving messages.\n" -"- **NetDispatcher**: It is a single instance that provides the interface for " -"Handler registration. When the Netlink class receives a raw message, it " -"calls NetDispatcher to parse it into nl_onject and calls the corresponding " -"Handler according to the type of the message.\n" -"- **NetMsg**: The base class of Netlink message Handler, which only provides " -"the interface of onMsg, of which there is no implementation." - -#: src/4-1-2-netlink.md:15 -msgid "" -"举一个例子,当`portsyncd`启动的时候,它会创建一个Netlink对象,用来监听Link相" -"关的状态变化,并且会实现NetMsg的接口,对Link相关的消息进行处理。具体的实现如" -"下:" -msgstr "" -"As an example, when `portsyncd` starts, it will create a Netlink object to " -"listen for Link-related state changes and will implement the NetMsg " -"interface to handle Link-related messages. The concrete implementation is as " -"follows:" - -#: src/4-1-2-netlink.md:17 -msgid "" -"```cpp\n" -"// File: sonic-swss - portsyncd/portsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create Netlink object to listen to link messages\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" // Here SONiC request a fulldump of current state, so that it can get " -"the current state of all links\n" -" netlink.dumpRequest(RTM_GETLINK); \n" -" cout << \"Listen to link messages...\" << endl;\n" -" // ...\n" -"\n" -" // Register handler for link messages\n" -" LinkSync sync(&appl_db, &state_db);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" // ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss - portsyncd/portsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create Netlink object to listen to link messages\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" // Here SONiC request a fulldump of current state, so that it can get " -"the current state of all links\n" -" netlink.dumpRequest(RTM_GETLINK); \n" -" cout << \"Listen to link messages...\" << endl;\n" -" // ...\n" -"\n" -" // Register handler for link messages\n" -" LinkSync sync(&appl_db, &state_db);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" // ...\n" -"}\n" -"```" - -#: src/4-1-2-netlink.md:41 -msgid "" -"上面的LinkSync,就是一个NetMsg的实现,它实现了onMsg接口,用来处理Link相关的消" -"息:" -msgstr "" -"The above LinkSync, which is an implementation of NetMsg, implements the " -"onMsg interface to handle Link-related messages::" - -#: src/4-1-2-netlink.md:43 -msgid "" -"```cpp\n" -"// File: sonic-swss - portsyncd/linksync.h\n" -"class LinkSync : public NetMsg\n" -"{\n" -"public:\n" -" LinkSync(DBConnector *appl_db, DBConnector *state_db);\n" -"\n" -" // NetMsg interface\n" -" virtual void onMsg(int nlmsg_type, struct nl_object *obj);\n" -"\n" -" // ...\n" -"};\n" -"\n" -"// File: sonic-swss - portsyncd/linksync.cpp\n" -"void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // ...\n" -"\n" -" // Write link state to Redis DB\n" -" FieldValueTuple fv(\"oper_status\", oper ? \"up\" : \"down\");\n" -" vector fvs;\n" -" fvs.push_back(fv);\n" -" m_stateMgmtPortTable.set(key, fvs);\n" -" // ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss - portsyncd/linksync.h\n" -"class LinkSync : public NetMsg\n" -"{\n" -"public:\n" -" LinkSync(DBConnector *appl_db, DBConnector *state_db);\n" -"\n" -" // NetMsg interface\n" -" virtual void onMsg(int nlmsg_type, struct nl_object *obj);\n" -"\n" -" // ...\n" -"};\n" -"\n" -"// File: sonic-swss - portsyncd/linksync.cpp\n" -"void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // ...\n" -"\n" -" // Write link state to Redis DB\n" -" FieldValueTuple fv(\"oper_status\", oper ? \"up\" : \"down\");\n" -" vector fvs;\n" -" fvs.push_back(fv);\n" -" m_stateMgmtPortTable.set(key, fvs);\n" -" // ...\n" -"}\n" -"```" - -#: src/4-2-1-redis-wrappers.md:1 -msgid "# Redis封装" -msgstr "# Redis Wrapper" - -#: src/4-2-1-redis-wrappers.md:3 -msgid "## Redis数据库操作层" -msgstr "## Redis Database Operations Layer" - -#: src/4-2-1-redis-wrappers.md:5 -msgid "" -"第一层,也是最底层,是Redis的数据库操作层,封装了各种基本命令,比如,DB的连" -"接,命令的执行,事件通知的回调接口等等。具体的类图如下:" -msgstr "" -"The first and lowest layer is the database operations layer of Redis, which " -"encapsulates various basic commands, such as DB connection, command " -"execution, callback interface for event notification, etc. The specific " -"class diagram is as follows:" - -#: src/4-2-1-redis-wrappers.md:11 -msgid "" -"- **[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/dbconnector.h)**:封装并保持着与Redis的连接,当其销毁时会将其连" -"接关闭。\n" -"- **[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/dbconnector.h)**:封装了所有的底层使用到的Redis的命令,比如`SET`、" -"`GET`、`DEL`等等。\n" -"- **[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/redistran.h)**:封装了Redis的事务操作,用于在一个事务中执行多个" -"命令,比如`MULTI`、`EXEC`等等。\n" -"- **[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/redispipeline.h)**:封装了hiredis的redisAppendFormattedCommand " -"API,提供了一个类似队列的异步的执行Redis命令的接口(虽然大部分使用方法依然是" -"同步的)。它也是少有的对`SCRIPT LOAD`命令进行了封装的类,用于在Redis中加载Lua" -"脚本实现存储过程。SONiC中绝大部分需要执行Lua脚本的类,都会使用这个类来进行加" -"载和调用。\n" -"- **[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/redisselect.h)**:它实现了Selectable的接口,用来支持基于epoll的事件通" -"知机制(Event Polling)。主要是在我们收到了Redis的回复,用来触发epoll进行回调" -"(我们最后会更详细的介绍)。\n" -"- **[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/dbconnector.h)**:这个类是一个“静态类”,它主要实现了SONiC DB的" -"配置文件的读取和解析。其他的数据库操作类,如果需要任何的配置信息,都会通过这" -"个类来获取。" -msgstr "" -"- **[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/dbconnector.h)**: encapsulates and maintains the connection to " -"Redis, and will close it when it is destroyed.\n" -"- **[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/dbconnector.h)**: encapsulates all the underlying Redis commands that " -"are used, such as `SET`, ` GET`, `DEL`, and so on.\n" -"- **[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/redistran.h)**: encapsulates the Redis transaction operations " -"used to execute multiple commands, such as `MULTI`, `EXEC`, and so on.\n" -"- **[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/redispipeline.h)**: encapsulates the hiredis " -"redisAppendFormattedCommand API, providing a queue-like interface for " -"executing Redis commands asynchronously (although most of the usage is still " -"synchronous). It is also one of the few classes that wraps the `SCRIPT LOAD` " -"command for loading Lua scripts to implement stored procedures in Redis.\n" -"- **[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/redisselect.h)**: It implements the Selectable interface, which is " -"used to support the epoll-based Event Polling. It is mainly used to trigger " -"epoll for callbacks when we receive a reply from Redis (we will cover this " -"in more detail at the end).\n" -"- **[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/dbconnector.h)**: This class is a \"static class \", it mainly " -"implements the reading and parsing of the configuration file of SONiC DB. " -"Other database operation classes, if they need any configuration " -"information, will get it through this class." - -#: src/4-2-1-redis-wrappers.md:19 -msgid "## 表(Table)抽象层" -msgstr "## Table abstraction layer" - -#: src/4-2-1-redis-wrappers.md:21 -msgid "" -"在Redis数据库操作层之上,便是SONiC自己利用Redis中间的Key建立的表(Table)的抽" -"象了,因为每一个Redis的Key的格式都是``,所以" -"SONiC在访问数据库时需要对其进行一次转换(没有印象的小伙伴可以移步[我之前的博" -"客了解更多的信息](/posts/sonic-2-key-components/#数据库))。" -msgstr "" -"On top of the Redis database operation layer is the abstraction of the Table " -"that SONiC itself builds using the Redis intermediate Key, because the " -"format of each Redis Key is ``, so SONiC " -"needs to perform a conversion (for those who don't remember, you can move to " -"[my previous blog for more information](/posts/sonic-2-key-components/" -"#database))." - -#: src/4-2-1-redis-wrappers.md:23 -msgid "相关类的主要类图如下:" -msgstr "The main class diagram of the related classes is as follows:" - -#: src/4-2-1-redis-wrappers.md:27 -msgid "其中关键的类有三个:" -msgstr "Three of the key classes are:" - -#: src/4-2-1-redis-wrappers.md:29 -msgid "" -"- **[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/table.h)**:这个类是所有表的基类,它主要封装了表的基本信息,如表的名" -"字,Redis Key的打包,每个表发生修改时用于通信的Channel的名字,等等。\n" -"- **[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/table.h)**:这个类就是对于每个表增删改查的封装了,里面包含了表的名称和" -"分隔符,这样就可以在调用时构造最终的key了。\n" -"- **[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/consumertablebase.h)**:这个类是各种SubscriptionTable的基类,里" -"面主要是封装了一个简单的队列和其pop操作(对,只有pop,没有push),用来给上层" -"调用。" -msgstr "" -"- **[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/table.h)**: This class is the base class for all tables, it mainly " -"encapsulates the basic information about the table, such as the name of the " -"table, the Redis Key It encapsulates basic information about the table, such " -"as the name of the table, the packing of the Redis Key, the name of the " -"Channel used to communicate with each table when a modification occurs, and " -"so on.\n" -"- **[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/table.h)**: This class is the encapsulation for each table add, " -"delete, and modify, and contains the table name and separator so that the " -"final key can be constructed when it is called. constructs the final key.\n" -"- **[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/consumertablebase.h)**: This class is the base class for all " -"kinds of SubscriptionTable base class, which mainly encapsulates a simple " -"queue and its pop operation (yes, only pop, not push), used for upper-level " -"calls." - -#: src/4-2-2-redis-messaging-layer.md:1 -msgid "# 通信层" -msgstr "# Communication层" - -#: src/4-2-2-redis-messaging-layer.md:3 -msgid "" -"在Redis的封装和表抽象之上,便是SONiC的通信层了,由于需求的不同,这一层中提供" -"了四种不同的PubSub的封装,用于服务间的通信。" -msgstr "" -"On top of the Redis encapsulation and table abstraction is the SONiC " -"communication layer, which provides four different PubSub encapsulations for " -"inter-service communication, depending on the requirements." - -#: src/4-2-2-redis-messaging-layer.md:5 -msgid "## SubscribeStateTable" -msgstr "## SubscribeStateTable" - -#: src/4-2-2-redis-messaging-layer.md:7 -msgid "" -"最直接的就是[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-" -"common/blob/master/common/subscriberstatetable.h)了。" -msgstr "" -"最直接的就是[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-" -"common/blob/master/common/subscriberstatetable.h)了。" - -#: src/4-2-2-redis-messaging-layer.md:9 -msgid "" -"它的原理是利用Redis数据库中自带的keyspace消息通知机制 [\\[4\\]]" -"[RedisKeyspace] —— 当数据库中的任何一个key对应的值发生了变化,就会触发Redis发" -"送两个keyspace的事件通知,一个是`__keyspace@__:`下的``事件," -"一个是`__keyspace@__:`下的`>`事件,比如,在数据库0中删除了一" -"个key,那么就会触发两个事件通知:" -msgstr "" -"It works by using the keyspace message notification mechanism that comes " -"with the Redis database [\\[4\\]][RedisKeyspace] -- when the value " -"corresponding to any key in the database changes, it triggers Redis to send " -"two keyspace event notifications , one is the `` event under " -"`__keyspace@__:`, and one is the `>` event under " -"`__keyspace@__:`, for example, if a key is deleted in database 0, " -"then two event notifications are triggered:" - -#: src/4-2-2-redis-messaging-layer.md:11 -msgid "" -"```redis\n" -"PUBLISH __keyspace@0__:foo del\n" -"PUBLISH __keyevent@0__:del foo\n" -"```" -msgstr "" -"```redis\n" -"PUBLISH __keyspace@0__:foo del\n" -"PUBLISH __keyevent@0__:del foo\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:16 -msgid "" -"而SubscriberStateTable就是监听了第一个事件通知,然后调用相应的回调函数。和其" -"直接相关的主要的类的类图如下,这里可以看到它继承了ConsumerTableBase,因为它是" -"Redis的消息的Consumer:" -msgstr "" -"The SubscriberStateTable listens for the first event notification and then " -"calls the corresponding callback function. The class diagram of the main " -"class directly related to it is as follows, where you can see that it " -"inherits from ConsumerTableBase, since it is the Consumer of Redis messages:" - -#: src/4-2-2-redis-messaging-layer.md:20 -msgid "在初始化时,我们可以看到它是如何订阅Redis的事件通知的:" -msgstr "" -"At initialization time, we can see how it subscribes to Redis event " -"notifications: the" - -#: src/4-2-2-redis-messaging-layer.md:22 -msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"SubscriberStateTable::SubscriberStateTable(DBConnector *db, const string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri), m_table(db, " -"tableName)\n" -"{\n" -" m_keyspace = \"__keyspace@\";\n" -" m_keyspace += to_string(db->getDbId()) + \"__:\" + tableName + m_table." -"getTableNameSeparator() + \"*\";\n" -" psubscribe(m_db, m_keyspace);\n" -" // ...\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"SubscriberStateTable::SubscriberStateTable(DBConnector *db, const string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri), m_table(db, " -"tableName)\n" -"{\n" -" m_keyspace = \"__keyspace@\";\n" -" m_keyspace += to_string(db->getDbId()) + \"__:\" + tableName + m_table." -"getTableNameSeparator() + \"*\";\n" -" psubscribe(m_db, m_keyspace);\n" -" // ...\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:33 -msgid "其事件接收和分发主要由两个函数负责:" -msgstr "" -"Its event reception and distribution are mainly handled by two functions:" - -#: src/4-2-2-redis-messaging-layer.md:35 -msgid "" -"- `readData()`负责将redis中待读取的事件读取出来,并放入ConsumerTableBase中的" -"队列中\n" -"- `pops()`:负责将队列中的原始事件取出来,并且进行解析,然后通过函数参数传递" -"给调用方" -msgstr "" -"- `readData()` is responsible for reading out the events to be read from " -"redis and putting them into the queue in ConsumerTableBase\n" -"- `pops()`: responsible for taking out the original events from the queue, " -"parsing them, and passing them to the caller via function parameters" - -#: src/4-2-2-redis-messaging-layer.md:38 -msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"uint64_t SubscriberStateTable::readData()\n" -"{\n" -" // ...\n" -" reply = nullptr;\n" -" int status;\n" -" do {\n" -" status = redisGetReplyFromReader(m_subscribe->getContext(), " -"reinterpret_cast(&reply));\n" -" if(reply != nullptr && status == REDIS_OK) {\n" -" m_keyspace_event_buffer." -"emplace_back(make_shared(reply));\n" -" }\n" -" } while(reply != nullptr && status == REDIS_OK);\n" -" // ...\n" -" return 0;\n" -"}\n" -"\n" -"void SubscriberStateTable::pops(deque &vkco, const " -"string& /*prefix*/)\n" -"{\n" -" vkco.clear();\n" -" // ...\n" -"\n" -" // Pop from m_keyspace_event_buffer, which is filled by readData()\n" -" while (auto event = popEventBuffer()) {\n" -" KeyOpFieldsValuesTuple kco;\n" -" // Parsing here ...\n" -" vkco.push_back(kco);\n" -" }\n" -"\n" -" m_keyspace_event_buffer.clear();\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"uint64_t SubscriberStateTable::readData()\n" -"{\n" -" // ...\n" -" reply = nullptr;\n" -" int status;\n" -" do {\n" -" status = redisGetReplyFromReader(m_subscribe->getContext(), " -"reinterpret_cast(&reply));\n" -" if(reply != nullptr && status == REDIS_OK) {\n" -" m_keyspace_event_buffer." -"emplace_back(make_shared(reply));\n" -" }\n" -" } while(reply != nullptr && status == REDIS_OK);\n" -" // ...\n" -" return 0;\n" -"}\n" -"\n" -"void SubscriberStateTable::pops(deque &vkco, const " -"string& /*prefix*/)\n" -"{\n" -" vkco.clear();\n" -" // ...\n" -"\n" -" // Pop from m_keyspace_event_buffer, which is filled by readData()\n" -" while (auto event = popEventBuffer()) {\n" -" KeyOpFieldsValuesTuple kco;\n" -" // Parsing here ...\n" -" vkco.push_back(kco);\n" -" }\n" -"\n" -" m_keyspace_event_buffer.clear();\n" -"}\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:71 -msgid "## NotificationProducer / NotificationConsumer" -msgstr "## NotificationProducer / NotificationConsumer" - -#: src/4-2-2-redis-messaging-layer.md:73 -msgid "" -"说到消息通信,我们很容易就会联想到消息队列,这就是我们的第二种通信方式 —— " -"[NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/notificationproducer.h)和[NotificationConsumer](https://github." -"com/sonic-net/sonic-swss-common/blob/master/common/notificationconsumer.h)。" -msgstr "" -"When it comes to message communication, it's easy to associate it with " -"message queues, which is our second form of communication -- " -"[NotificationProducer](https://github.com/sonic-net/sonic-swss-common /blob/" -"master/common/notificationproducer.h) and [NotificationConsumer](https://" -"github.com/sonic-net/sonic-swss-common/blob/master/ common/" -"notificationconsumer.h)." - -#: src/4-2-2-redis-messaging-layer.md:75 -msgid "" -"这种通信方式通过Redis的自带的PubSub来实现,主要是对`PUBLISH`和`SUBSCRIBE`命令" -"的包装,很有限的应用在最简单的通知型的场景中,比如orchagent中的timeout " -"check, restart check之类,非传递用户配置和数据的场景:" -msgstr "" -"This communication method is implemented through Redis' own PubSub, which is " -"mainly a wrapper around the `PUBLISH` and `SUBSCRIBE` commands, and is very " -"limited in the simplest notification scenarios, such as timeout check, " -"restart check, etc. in orchagent, not for passing user configuration and " -"data. Scenarios:" - -#: src/4-2-2-redis-messaging-layer.md:79 -msgid "" -"这种通信模式下,消息的发送方Producer,主要会做两件事情:一是将消息打包成JSON" -"格式,二是调用Redis的`PUBLISH`命令将消息发送出去。而且由于`PUBLISH`命令只能携" -"带一个消息,所以请求中的`op`和`data`字段会被放在`values`的最前面,然后再调用" -"`buildJson`函数将其打包成一个JSON数组的格式:" -msgstr "" -"In this communication mode, the Producer, the sender of the message, does " -"two main things: first, it packages the message into JSON format, and " -"second, it calls Redis' `PUBLISH` command to send the message out. And since " -"the `PUBLISH` command can only carry one message, the `op` and `data` fields " -"in the request are placed at the top of the `values`, and then the " -"`buildJson` function is called to package it into a JSON array format:" - -#: src/4-2-2-redis-messaging-layer.md:81 -msgid "" -"```cpp\n" -"int64_t swss::NotificationProducer::send(const std::string &op, const std::" -"string &data, std::vector &values)\n" -"{\n" -" // Pack the op and data into values array, then pack everything into a " -"JSON string as the message\n" -" FieldValueTuple opdata(op, data);\n" -" values.insert(values.begin(), opdata);\n" -" std::string msg = JSon::buildJson(values);\n" -" values.erase(values.begin());\n" -"\n" -" // Publish message to Redis channel\n" -" RedisCommand command;\n" -" command.format(\"PUBLISH %s %s\", m_channel.c_str(), msg.c_str());\n" -" // ...\n" -" RedisReply reply = m_pipe->push(command);\n" -" reply.checkReplyType(REDIS_REPLY_INTEGER);\n" -" return reply.getReply();\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"int64_t swss::NotificationProducer::send(const std::string &op, const std::" -"string &data, std::vector &values)\n" -"{\n" -" // Pack the op and data into values array, then pack everything into a " -"JSON string as the message\n" -" FieldValueTuple opdata(op, data);\n" -" values.insert(values.begin(), opdata);\n" -" std::string msg = JSon::buildJson(values);\n" -" values.erase(values.begin());\n" -"\n" -" // Publish message to Redis channel\n" -" RedisCommand command;\n" -" command.format(\"PUBLISH %s %s\", m_channel.c_str(), msg.c_str());\n" -" // ...\n" -" RedisReply reply = m_pipe->push(command);\n" -" reply.checkReplyType(REDIS_REPLY_INTEGER);\n" -" return reply.getReply();\n" -"}\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:100 -msgid "接收方则是利用`SUBSCRIBE`命令来接收所有的通知:" -msgstr "The receiver receives all notifications using the `SUBSCRIBE` command:" - -#: src/4-2-2-redis-messaging-layer.md:102 -msgid "" -"```cpp\n" -"void swss::NotificationConsumer::subscribe()\n" -"{\n" -" // ...\n" -" m_subscribe = new DBConnector(m_db->getDbId(),\n" -" m_db->getContext()->unix_sock.path,\n" -" NOTIFICATION_SUBSCRIBE_TIMEOUT);\n" -" // ...\n" -"\n" -" // Subscribe to Redis channel\n" -" std::string s = \"SUBSCRIBE \" + m_channel;\n" -" RedisReply r(m_subscribe, s, REDIS_REPLY_ARRAY);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"void swss::NotificationConsumer::subscribe()\n" -"{\n" -" // ...\n" -" m_subscribe = new DBConnector(m_db->getDbId(),\n" -" m_db->getContext()->unix_sock.path,\n" -" NOTIFICATION_SUBSCRIBE_TIMEOUT);\n" -" // ...\n" -"\n" -" // Subscribe to Redis channel\n" -" std::string s = \"SUBSCRIBE \" + m_channel;\n" -" RedisReply r(m_subscribe, s, REDIS_REPLY_ARRAY);\n" -"}\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:117 -msgid "## ProducerTable / ConsumerTable" -msgstr "## ProducerTable / ConsumerTable" - -#: src/4-2-2-redis-messaging-layer.md:119 -msgid "" -"我们可以看到NotificationProducer/Consumer实现简单粗暴,但是由于API的限制 " -"[\\[8\\]][RedisClientHandling],它并不适合用来传递数据,所以,SONiC中提供了一" -"种和它非常接近的另外一种基于消息队列的通信机制 —— [ProducerTable](https://" -"github.com/sonic-net/sonic-swss-common/blob/master/common/producertable.h)和" -"[ConsumerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/" -"common/consumertable.h)。" -msgstr "" -"We can see that the NotificationProducer/Consumer implementation is simple " -"and brutal, but due to the limitations of the API [\\[8\\]]" -"[RedisClientHandling], it is not suitable for passing data, so an " -"alternative message queue-based communication mechanism is provided in SONiC " -"that is very close to it -- [ProducerTable](https://github.com/sonic-net/" -"sonic-swss-common/blob/master/common/producertable.h) and [ ConsumerTable]" -"(https://github.com/sonic-net/sonic-swss-common/blob/master/common/" -"consumertable.h)." - -#: src/4-2-2-redis-messaging-layer.md:121 -msgid "" -"这种通信方式通过Redis的List来实现,和Notification不同的地方在于,发布给" -"Channel中的消息非常的简单(单字符\"G\"),所有的数据都存储在List中,从而解决" -"了Notification中消息大小限制的问题。在SONiC中,它主要用在FlexCounter,`syncd`" -"服务和`ASIC_DB`中:" -msgstr "" -"The difference between this communication method and Notification is that " -"the message published to the Channel is very simple (single character \"G\") " -"and all the data is stored in the List, thus solving the problem of message " -"size limitation in Notification. In SONiC, it is mainly used in the " -"FlexCounter, `syncd` service and `ASIC_DB`:" - -#: src/4-2-2-redis-messaging-layer.md:123 -msgid "" -"1. **消息格式**:每条消息都是一个(Key, FieldValuePairs, Op)的三元组,如果用" -"JSON来表达这个消息,那么它的格式如下:(这里的Key是Table中数据的Key,被操作的" -"数据是[Hash][RedisHash],所以Field就是Hash中的Field,Value就是Hash中的Value" -"了,也就是说一个消息可以对很多个Field进行操作)\n" -"\n" -" ```json\n" -" [ \"Key\", \"[\\\"Field1\\\", \\\"Value1\\\", \\\"Field2\", \\\"Value2\\" -"\", ...]\", \"Op\" ]\n" -" ```\n" -"\n" -"2. **Enqueue**:ProducerTable通过Lua脚本将消息三元组原子的写入消息队列中" -"(Key = `_KEY_VALUE_OP_QUEUE`,并且发布更新通知到特定的Channel" -"(Key = `_CHANNEL`)中。\n" -"3. **Pop**:ConsumerTable也通过Lua脚本从消息队列中原子的读取消息三元组,并**" -"在读取过程中**将其中请求的改动真正的写入到数据库中。" -msgstr "" -"1. **Message format**: Each message is a triple of (Key, FieldValuePairs, " -"Op), if the message is expressed in JSON, then it has the following format: " -"(Here the Key is the Key of the data in the Table, the data being operated " -"on is [Hash][RedisHash], so the Field is the The Field is the Field in the " -"Hash, and the Value is the Value in the Hash, which means that a message can " -"operate on many Fields)\n" -"\n" -" ```json\n" -" [ \"Key\", \"[\\\"Field1\\\", \\\"Value1\\\", \\\"Field2\\\", \\\"Value2\\" -"\", ...]\" , \"Op\" ]\n" -" ```\n" -"\n" -"2. **Enqueue**: ProducerTable writes the message triplet atomically to the " -"message queue (Key = `_KEY_VALUE_OP_QUEUE`) via Lua script and " -"issues update notifications to a specific Channel (Key = `_ " -"CHANNEL`).\n" -"3. **Pop**: ConsumerTable also reads the message triplet atomically from the " -"message queue via Lua script and **during the read** actually writes the " -"requested changes therein to the database." - -#: src/4-2-2-redis-messaging-layer.md:132 -msgid "" -"```admonish note\n" -"**注意**:Redis中Lua脚本和MULTI/EXEC的原子性和通常说的数据库ACID中的原子性" -"(Atomicity)不同,Redis中的原子性其实更接近于ACID中的隔离性(Isolation),他" -"保证Lua脚本中所有的命令在执行的时候不会有其他的命令执行,但是并不保证Lua脚本" -"中的所有命令都会执行成功,比如,如果Lua脚本中的第二个命令执行失败了,那么第一" -"个命令依然会被提交,只是后面的命令就不会继续执行了。更多的细节可以参考Redis官" -"方文档 [\\[5\\]][RedisTx] [\\[6\\]][RedisLuaAtomicity]。\n" -"```" -msgstr "" -"```admonish note\n" -"**注意**:Redis中Lua脚本和MULTI/EXEC的原子性和通常说的数据库ACID中的原子性" -"(Atomicity)不同,Redis中的原子性其实更接近于ACID中的隔离性(Isolation),他" -"保证Lua脚本中所有的命令在执行的时候不会有其他的命令执行,但是并不保证Lua脚本" -"中的所有命令都会执行成功,比如,如果Lua脚本中的第二个命令执行失败了,那么第一" -"个命令依然会被提交,只是后面的命令就不会继续执行了。更多的细节可以参考Redis官" -"方文档 [\\[5\\]][RedisTx] [\\[6\\]][RedisLuaAtomicity]。\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:136 -msgid "" -"其主要类图如下,这里我们可以看到在ProducerTable中的`m_shaEnqueue`和" -"ConsumerTable中的`m_shaPop`,它们就是上面我们提到的这两个Lua脚本在加载时获得" -"的SHA了,而之后我们就可以使用Redis的`EVALSHA`命令对他们进行原子的调用了:" -msgstr "" -"The main class diagram is as follows. Here we can see `m_shaEnqueue` in the " -"ProducerTable and `m_shaPop` in the ConsumerTable, which are the SHAs " -"obtained by the two Lua scripts we mentioned above at load time, and we can " -"then use Redis's `EVALSHA` command to make atomic calls to them: the" - -#: src/4-2-2-redis-messaging-layer.md:140 -msgid "" -"ProducerTable的核心逻辑如下,我们可以看到对Values的JSON打包,和使用`EVALSHA`" -"来进行Lua脚本的调用:" -msgstr "" -"The core logic of the ProducerTable is as follows, we can see the JSON " -"packaging of Values and the use of `EVALSHA` to make Lua script calls:" - -#: src/4-2-2-redis-messaging-layer.md:142 -msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/producertable.cpp\n" -"ProducerTable::ProducerTable(RedisPipeline *pipeline, const string " -"&tableName, bool buffered)\n" -" // ...\n" -"{\n" -" string luaEnque =\n" -" \"redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);\"\n" -" \"redis.call('PUBLISH', KEYS[2], ARGV[4]);\";\n" -"\n" -" m_shaEnque = m_pipe->loadRedisScript(luaEnque);\n" -"}\n" -"\n" -"void ProducerTable::set(const string &key, const vector " -"&values, const string &op, const string &prefix)\n" -"{\n" -" enqueueDbChange(key, JSon::buildJson(values), \"S\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::del(const string &key, const string &op, const string " -"&prefix)\n" -"{\n" -" enqueueDbChange(key, \"{}\", \"D\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::enqueueDbChange(const string &key, const string &value, " -"const string &op, const string& /* prefix */)\n" -"{\n" -" RedisCommand command;\n" -"\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %s %s %s %s\",\n" -" m_shaEnque.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" getChannelName(m_pipe->getDbId()).c_str(),\n" -" key.c_str(),\n" -" value.c_str(),\n" -" op.c_str(),\n" -" \"G\");\n" -"\n" -" m_pipe->push(command, REDIS_REPLY_NIL);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss-common - common/producertable.cpp\n" -"ProducerTable::ProducerTable(RedisPipeline *pipeline, const string " -"&tableName, bool buffered)\n" -" // ...\n" -"{\n" -" string luaEnque =\n" -" \"redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);\"\n" -" \"redis.call('PUBLISH', KEYS[2], ARGV[4]);\";\n" -"\n" -" m_shaEnque = m_pipe->loadRedisScript(luaEnque);\n" -"}\n" -"\n" -"void ProducerTable::set(const string &key, const vector " -"&values, const string &op, const string &prefix)\n" -"{\n" -" enqueueDbChange(key, JSon::buildJson(values), \"S\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::del(const string &key, const string &op, const string " -"&prefix)\n" -"{\n" -" enqueueDbChange(key, \"{}\", \"D\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::enqueueDbChange(const string &key, const string &value, " -"const string &op, const string& /* prefix */)\n" -"{\n" -" RedisCommand command;\n" -"\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %s %s %s %s\",\n" -" m_shaEnque.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" getChannelName(m_pipe->getDbId()).c_str(),\n" -" key.c_str(),\n" -" value.c_str(),\n" -" op.c_str(),\n" -" \"G\");\n" -"\n" -" m_pipe->push(command, REDIS_REPLY_NIL);\n" -"}\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:182 -msgid "" -"而另一侧的ConsumerTable就稍稍复杂一点,因为其支持的op类型很多,所以逻辑都写在" -"了一个单独的文件中(`common/consumer_table_pops.lua`),我们这里就不贴代码" -"了,有兴趣的同学可以自己去看看。" -msgstr "" -"The other side of the ConsumerTable is a little more complicated, because it " -"supports many op types, so the logic is written in a separate file (`common/" -"consumer_table_pops.lua`), we won't post the code here, interested students " -"can see for themselves." - -#: src/4-2-2-redis-messaging-layer.md:184 -msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/consumertable.cpp\n" -"ConsumerTable::ConsumerTable(DBConnector *db, const string &tableName, int " -"popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeyValueOpQueues(tableName)\n" -" , m_modifyRedis(true)\n" -"{\n" -" std::string luaScript = loadLuaScript(\"consumer_table_pops.lua\");\n" -" m_shaPop = loadRedisScript(db, luaScript);\n" -" // ...\n" -"}\n" -"\n" -"void ConsumerTable::pops(deque &vkco, const string " -"&prefix)\n" -"{\n" -" // Note that here we are processing the messages in bulk with " -"POP_BATCH_SIZE!\n" -" RedisCommand command;\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %d %d\",\n" -" m_shaPop.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" (prefix+getTableName()).c_str(),\n" -" POP_BATCH_SIZE,\n" -"\n" -" RedisReply r(m_db, command, REDIS_REPLY_ARRAY);\n" -" vkco.clear();\n" -"\n" -" // Parse and pack the messages in bulk\n" -" // ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: sonic-swss-common - common/consumertable.cpp\n" -"ConsumerTable::ConsumerTable(DBConnector *db, const string &tableName, int " -"popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeyValueOpQueues(tableName)\n" -" , m_modifyRedis(true)\n" -"{\n" -" std::string luaScript = loadLuaScript(\"consumer_table_pops.lua\");\n" -" m_shaPop = loadRedisScript(db, luaScript);\n" -" // ...\n" -"}\n" -"\n" -"void ConsumerTable::pops(deque &vkco, const string " -"&prefix)\n" -"{\n" -" // Note that here we are processing the messages in bulk with " -"POP_BATCH_SIZE!\n" -" RedisCommand command;\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %d %d\",\n" -" m_shaPop.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" (prefix+getTableName()).c_str(),\n" -" POP_BATCH_SIZE,\n" -"\n" -" RedisReply r(m_db, command, REDIS_REPLY_ARRAY);\n" -" vkco.clear();\n" -"\n" -" // Parse and pack the messages in bulk\n" -" // ...\n" -"}\n" -"```" - -#: src/4-2-2-redis-messaging-layer.md:215 -msgid "## ProducerStateTable / ConsumerStateTable" -msgstr "## ProducerStateTable / ConsumerStateTable" - -#: src/4-2-2-redis-messaging-layer.md:217 -msgid "" -"Producer/ConsumerTable虽然直观,而且保序,但是它一个消息只能处理一个Key,并且" -"还需要JSON的序列化,然而很多时候我们并用不到保序的功能,反而更需要更大的吞吐" -"量,所以为了优化性能,SONiC就引入了第四种通信方式,也是最常用的通信方式:" -"[ProducerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/producerstatetable.h)和[ConsumerStateTable](https://github.com/" -"sonic-net/sonic-swss-common/blob/master/common/consumertatetable.h)。" -msgstr "" -"Although Producer/ConsumerTable is intuitive and order-preserving, it can " -"only handle one Key for one message and also requires JSON serialization, " -"however, many times we do not use the order-preserving function, but rather " -"need more throughput, so in order to optimize performance, SONiC introduces " -"the fourth communication method, which is also the most commonly used " -"communication method: the [ProducerStateTable](https://github.com/sonic-net/" -"sonic-swss-common/blob/master/common/producerstatetable.h) and " -"[ConsumerStateTable]( https://github.com/sonic-net/sonic-swss-common/blob/" -"master/common/consumertatetable.h)." - -#: src/4-2-2-redis-messaging-layer.md:219 -msgid "" -"与ProducerTable不同,ProducerStateTable使用Hash的方式来存储消息,而不是List。" -"这样虽然不能保证消息的顺序,但是却可以很好的提升性能!首先,我们省下了JSON的" -"序列化的开销,其次,对于同一个Key下的相同的Field如果被变更多次,那么只需要保" -"留最后一次的变更,这样就将关于这个Key的所有变更消息就合并成了一条,减少了很多" -"不必要的消息处理。" -msgstr "" -"Unlike ProducerTable, ProducerStateTable uses Hash to store messages instead " -"of List, which doesn't guarantee the order of messages, but is a great " -"performance boost! First, we save the overhead of JSON serialization, and " -"second, for the same Field under the same Key if it is changed several " -"times, then only the last change needs to be kept, so that all the change " -"messages about the Key are combined into one, reducing a lot of unnecessary " -"message processing." - -#: src/4-2-2-redis-messaging-layer.md:221 -msgid "" -"Producer/ConsumerStateTable的底层实现相比于Producer/ConsumerTable也更加复杂一" -"些。其相关联的类的主要类图如下,这里我们依然可以看到它的实现是通过`EVALSHA`调" -"用Lua脚本来实现的,`m_shaSet`和`m_shaDel`就是用来存放修改和发送消息的,而另一" -"边`m_shaPop`就是用来获取消息的:" -msgstr "" -"The underlying implementation of Producer/ConsumerStateTable is also a bit " -"more complex than Producer/ConsumerTable. The main class diagram of its " -"associated classes is as follows. Here we can still see that it is " -"implemented by calling Lua scripts through `EVALSHA`, `m_shaSet` and " -"`m_shaDel` are used to store modified and sent messages, while `m_shaPop` is " -"used to get messages on the other side:" - -#: src/4-2-2-redis-messaging-layer.md:225 -msgid "在传递消息时:" -msgstr "When delivering messages:" - -#: src/4-2-2-redis-messaging-layer.md:227 -msgid "" -"- 首先,每个消息会被存放成两个部分:一个是KEY_SET,用来保存当前有哪些Key发生" -"了修改,它以Set的形式存放在``的key下,另一个是所有被修改" -"的Key的内容,它以Hash的形式存放在`_`的key下。\n" -"- 然后,消息存放之后Producer如果发现是新的Key,那么就是调用`PUBLISH`命令,来" -"通知`_CHANNEL@`Channel,有新的Key出现了。\n" -"\n" -" ```cpp\n" -" // File: sonic-swss-common - common/producerstatetable.cpp\n" -" ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const " -"string &tableName, bool buffered)\n" -" : TableBase(tableName, SonicDBConfig::getSeparator(pipeline-" -">getDBConnector()))\n" -" , TableName_KeySet(tableName)\n" -" // ...\n" -" {\n" -" string luaSet =\n" -" \"local added = redis.call('SADD', KEYS[2], ARGV[2])\\n\"\n" -" \"for i = 0, #KEYS - 3 do\\n\"\n" -" \" redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i " -"* 2])\\n\"\n" -" \"end\\n\"\n" -" \" if added > 0 then \\n\"\n" -" \" redis.call('PUBLISH', KEYS[1], ARGV[1])\\n\"\n" -" \"end\\n\";\n" -"\n" -" m_shaSet = m_pipe->loadRedisScript(luaSet);\n" -" ```\n" -"\n" -"- 最后,Consumer会通过`SUBSCRIBE`命令来订阅`_CHANNEL@`Channel,一旦有新的消息到来,就会使用Lua脚本调用`HGETALL`命令来获取所有的" -"Key,并将其中的值读取出来并真正的写入到数据库中去。\n" -"\n" -" ```cpp\n" -" ConsumerStateTable::ConsumerStateTable(DBConnector *db, const std::string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeySet(tableName)\n" -" {\n" -" std::string luaScript = loadLuaScript(\"consumer_state_table_pops." -"lua\");\n" -" m_shaPop = loadRedisScript(db, luaScript);\n" -" // ...\n" -" \n" -" subscribe(m_db, getChannelName(m_db->getDbId()));\n" -" // ...\n" -" ```" -msgstr "" -"- First, each message is stored into two parts: one is KEY_SET, which is " -"used to store which Keys are currently modified, and it is stored as a Set " -"under the key of ``, and the other is the content of all " -"modified Keys, and it is stored as a Hash under the key of `_` under the key.\n" -"- Then, after the message is stored the Producer, if it finds that it is a " -"new Key, then it is calling the `PUBLISH` command to notify the `_CHANNEL@`Channel that a new Key is available.\n" -"\n" -" ```cpp\n" -" // File: sonic-swss-common - common/producerstatetable.cpp\n" -" ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const " -"string &tableName, bool buffered)\n" -" : TableBase(tableName, SonicDBConfig::getSeparator(pipeline -> " -"getDBConnector()))\n" -" , TableName_KeySet(tableName)\n" -" // ...\n" -" {\n" -" string luaSet =\n" -" \"local added = redis.call('SADD', KEYS[2], ARGV[2])\\n\"\n" -" \"for i = 0, #KEYS - 3 do\\n\"\n" -" \" redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i * " -"2])\\n\"\n" -" \"end\\n\"\n" -" \" if added > 0 then \\n\"\n" -" \" redis.call('PUBLISH', KEYS[1], ARGV[1])\\n\"\n" -" \"end\\n\".\n" -"\n" -" m_shaSet = m_pipe->loadRedisScript(luaSet).\n" -" ```\n" -"\n" -"- Finally, the Consumer will subscribe to the `_CHANNEL@`Channel with the `SUBSCRIBE` command, and once a new message arrives, it " -"will use the LuaScript to call the `HGETALL` command to get all the Keys and " -"read the values from them and actually write them to the the database.\n" -"\n" -" ```cpp\n" -" ConsumerStateTable::ConsumerStateTable(DBConnector *db, const std::string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeySet(tableName)\n" -" {\n" -" std::string luaScript = loadLuaScript(\"consumer_state_table_pops." -"lua\").\n" -" m_shaPop = loadRedisScript(db, luaScript).\n" -" // ...\n" -"\n" -" subscribe(m_db, getChannelName(m_db->getDbId())).\n" -" // ...\n" -" ```" - -#: src/4-2-2-redis-messaging-layer.md:264 -msgid "为了方便理解,我们这里举一个例子:启用Port Ethernet0:" -msgstr "" -"For ease of understanding, let's take an example here: Enabling Port " -"Ethernet0:" - -#: src/4-2-2-redis-messaging-layer.md:266 -msgid "" -"- 首先,我们在命令行下调用`config interface startup Ethernet0`来启用" -"Ethernet0,这会导致`portmgrd`通过ProducerStateTable向APP_DB发送状态更新消息," -"如下:\n" -"\n" -" ```redis\n" -" EVALSHA \"\" \"6\" \"PORT_TABLE_CHANNEL@0\" " -"\"PORT_TABLE_KEY_SET\" \n" -" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:" -"Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"G\"\n" -" \"Ethernet0\" \"alias\" \"Ethernet5/1\" \"index\" \"5\" \"lanes\" " -"\"9,10,11,12\" \"speed\" \"40000\"\n" -" ```\n" -"\n" -" 这个命令会在其中调用如下的命令来创建和发布消息:\n" -"\n" -" ```redis\n" -" SADD \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" PUBLISH \"PORT_TABLE_CHANNEL@0\" \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" 所以最终这个消息会在APPL_DB中被存放成如下的形式:\n" -"\n" -" ```redis\n" -" PORT_TABLE_KEY_SET:\n" -" _PORT_TABLE:Ethernet0\n" -"\n" -" _PORT_TABLE:Ethernet0:\n" -" alias: Ethernet5/1\n" -" index: 5\n" -" lanes: 9,10,11,12\n" -" speed: 40000\n" -" ```\n" -"\n" -"- 当ConsumerStateTable收到消息后,也会调用`EVALSHA`命令来执行Lua脚本,如" -"下:\n" -"\n" -" ```redis\n" -" EVALSHA \"\" \"3\" \"PORT_TABLE_KEY_SET\" \"PORT_TABLE:\" " -"\"PORT_TABLE_DEL_SET\" \"8192\" \"_\"\n" -" ```\n" -"\n" -" 和Producer类似,这个脚本会执行如下命令,将`PORT_TABLE_KEY_SET`中的key,也就" -"是`_PORT_TABLE:Ethernet0`读取出来,然后再将其对应的Hash读取出来,并更新到" -"`PORT_TABLE:Ethernet0`去,同时将`_PORT_TABLE:Ethernet0`从数据库和" -"`PORT_TABLE_KEY_SET`中删除。\n" -"\n" -" ```redis\n" -" SPOP \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HGETALL \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" DEL \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" 到这里,数据的更新才算是完成了。" -msgstr "" -"- First, we enable Ethernet0 by calling `config interface startup Ethernet0` " -"at the command line, which causes `portmgrd` to send a state update message " -"to APP_DB via the ProducerStateTable as follows:\n" -"\n" -" ``redis\n" -" EVALSHA \"\" \"6\" \"PORT_TABLE_CHANNEL@0\" " -"\"PORT_TABLE_KEY_SET\"\n" -" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:" -"Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"G\"\n" -" \"Ethernet0\" \"alias\" \"Ethernet5/1\" \"index\" \"5\" \"lanes\" " -"\"9,10,11,12\" \"speed\" \"40000\"\n" -" ```\n" -"\n" -" This command will create and publish messages by calling the following " -"commands in it:\n" -"\n" -" ```redis\n" -" SADD \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" PUBLISH \"PORT_TABLE_CHANNEL@0\" \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" So eventually this message will be stored in APPL_DB in the following " -"form:\n" -"\n" -" ```redis\n" -" PORT_TABLE_KEY_SET.\n" -" _PORT_TABLE:Ethernet0\n" -"\n" -" _PORT_TABLE:Ethernet0.\n" -" alias: Ethernet5/1\n" -" index: 5\n" -" lanes: 9,10,11,12\n" -" speed: 40000\n" -" ```\n" -"\n" -"- When the ConsumerStateTable receives the message, it also calls the " -"`EVALSHA` command to execute the Lua script, as follows:\n" -"\n" -" ```redis\n" -" EVALSHA \"\" \"3\" \"PORT_TABLE_KEY_SET\" \"PORT_TABLE:\" " -"\"PORT_TABLE_DEL_SET\" \"8192\" \"_\"\n" -" ```\n" -"\n" -" Similar to Producer, this script will execute the following command to " -"read out the key in `PORT_TABLE_KEY_SET`, which is `_PORT_TABLE:Ethernet0`, " -"and then read out its corresponding hash and update it to `PORT_TABLE:" -"Ethernet0`, and at the same time, read out the `_PORT_TABLE:Ethernet0` is " -"removed from the database and `PORT_TABLE_KEY_SET`.\n" -"\n" -" ``redis\n" -" SPOP \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HGETALL \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" DEL \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" Here, the data update is considered complete." - -#: src/4-2-2-redis-messaging-layer.md:320 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Redis keyspace notifications][RedisKeyspace]\n" -"5. [Redis Transactions][RedisTx]\n" -"6. [Redis Atomicity with Lua][RedisLuaAtomicity]\n" -"7. [Redis hashes][RedisHash]\n" -"8. [Redis client handling][RedisClientHandling]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Redis keyspace notifications][RedisKeyspace]\n" -"5. [Redis Transactions][RedisTx]\n" -"6. [Redis Atomicity with Lua][RedisLuaAtomicity]\n" -"7. [Redis hashes][RedisHash]\n" -"8. [Redis client handling][RedisClientHandling]" - -#: src/4-3-zmq-messaging.md:1 -msgid "# 基于ZMQ的通信" -msgstr "# ZMQ-based communication" - -#: src/4-4-orch-layer.md:1 -msgid "# 服务层 - Orch" -msgstr "# Service Layer - Orch" - -#: src/4-4-orch-layer.md:3 -msgid "" -"最后,为了方便各个服务使用,SONiC还在通信层上进行了更进一步的封装,为各个服务" -"提供了一个基类:[Orch](https://github.com/sonic-net/sonic-swss/blob/master/" -"src/orchagent/orch.hcommon/consumertatetable.h)。" -msgstr "" -"Finally, to facilitate the use of the individual services, SONiC has further " -"encapsulated the communication layer by providing a base class for each " -"service: [Orch](https://github.com/sonic-net/sonic-swss/blob/master/src/" -"orchagent/orch. hcommon/consumertatetable.h)." - -#: src/4-4-orch-layer.md:5 -msgid "" -"由于有了上面这些封装,Orch中关于消息通信的封装就相对简单了,主要的类图如下:" -msgstr "" -"Thanks to these above encapsulations, the encapsulation of message " -"communication in Orch is relatively simple, and the main class diagram is as " -"follows:" - -#: src/4-4-orch-layer.md:9 -msgid "" -"```admonish note\n" -"注意:由于这一层是服务层,所以其代码是在`sonic-swss`的仓库中,而不是`sonic-" -"swss`。这个类中除了消息通信的封装以外,还提供了很多和服务实现相关的公共函数," -"比如,日志文件等等。\n" -"```" -msgstr "" -"```admonish note\n" -"注意:由于这一层是服务层,所以其代码是在`sonic-swss`的仓库中,而不是`sonic-" -"swss`。这个类中除了消息通信的封装以外,还提供了很多和服务实现相关的公共函数," -"比如,日志文件等等。\n" -"```" - -#: src/4-4-orch-layer.md:13 -msgid "" -"可以看到,Orch主要是封装了`SubscriberStateTable`和`ConsumerStateTable`来简化" -"和统一消息的订阅,核心代码非常简单,就是根据不同的数据库类型来创建不同的" -"Consumer,如下:" -msgstr "" -"As you can see, Orch mainly encapsulates `SubscriberStateTable` and " -"`ConsumerStateTable` to simplify and unify the subscription of messages. The " -"core code is very simple, which is to create different Consumers according " -"to different database types, as follows:" - -#: src/4-4-orch-layer.md:15 -msgid "" -"```cpp\n" -"void Orch::addConsumer(DBConnector *db, string tableName, int pri)\n" -"{\n" -" if (db->getDbId() == CONFIG_DB || db->getDbId() == STATE_DB || db-" -">getDbId() == CHASSIS_APP_DB) {\n" -" addExecutor(\n" -" new Consumer(\n" -" new SubscriberStateTable(db, tableName, TableConsumable::" -"DEFAULT_POP_BATCH_SIZE, pri),\n" -" this,\n" -" tableName));\n" -" } else {\n" -" addExecutor(\n" -" new Consumer(\n" -" new ConsumerStateTable(db, tableName, gBatchSize, pri),\n" -" this,\n" -" tableName));\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"void Orch::addConsumer(DBConnector *db, string tableName, int pri)\n" -"{\n" -" if (db->getDbId() == CONFIG_DB || db->getDbId() == STATE_DB || db-" -">getDbId() == CHASSIS_APP_DB) {\n" -" addExecutor(\n" -" new Consumer(\n" -" new SubscriberStateTable(db, tableName, TableConsumable::" -"DEFAULT_POP_BATCH_SIZE, pri),\n" -" this,\n" -" tableName));\n" -" } else {\n" -" addExecutor(\n" -" new Consumer(\n" -" new ConsumerStateTable(db, tableName, gBatchSize, pri),\n" -" this,\n" -" tableName));\n" -" }\n" -"}\n" -"```" - -#: src/4-5-event-polling-and-error-handling.md:1 -msgid "# 事件分发和错误处理" -msgstr "# Event distribution and error handling" - -#: src/4-5-event-polling-and-error-handling.md:3 -msgid "## 基于epoll的事件分发机制" -msgstr "## epoll-based event distribution mechanism" - -#: src/4-5-event-polling-and-error-handling.md:5 -msgid "和很多的Linux服务一样,SONiC底层使用了epoll作为事件分发机制:" -msgstr "" -"As with many Linux services, SONiC uses epoll as the event distribution " -"mechanism at the bottom:" - -#: src/4-5-event-polling-and-error-handling.md:7 -msgid "" -"- 所有需要支持事件分发的类都需要继承`Selectable`类,并实现两个最核心的函数:" -"`int getFd();`(用于返回epoll能用来监听事件的fd)和`uint64_t readData()`(用" -"于在监听到事件到来之后进行读取)。而对于一般服务而言,这个fd就是redis通信使用" -"的fd,所以`getFd()`函数的调用,都会被最终转发到Redis的库中。\n" -"- 所有需要参与事件分发的对象,都需要注册到`Select`类中,这个类会将所有的" -"`Selectable`对象的fd注册到epoll中,并在事件到来时调用`Selectable`的" -"`readData()`函数。" -msgstr "" -"- All classes that need to support event distribution need to inherit the " -"`Selectable` class and implement the two core functions: `int getFd();` " -"(used to return the fd that epoll can use to listen for events) and " -"`uint64_t readData()` (used to read events as they come in). For general " -"services, this fd is the fd used for redis communication, so calls to the " -"`getFd()` function are eventually forwarded to the Redis library.\n" -"- All objects that need to participate in event distribution need to be " -"registered to the `Select` class, which registers the fd of all `Selectable` " -"objects to epoll and calls the `readData()` function of `Selectable` when " -"the event arrives." - -#: src/4-5-event-polling-and-error-handling.md:10 -msgid "其类图如下:" -msgstr "The class diagram is as follows:" - -#: src/4-5-event-polling-and-error-handling.md:14 -msgid "在Select类中,我们可以很容易的找到其最核心的代码,实现也非常的简单:" -msgstr "" -"In the Select class, we can easily find its most core code, and the " -"implementation is very simple:" - -#: src/4-5-event-polling-and-error-handling.md:16 -msgid "" -"```cpp\n" -"int Select::poll_descriptors(Selectable **c, unsigned int timeout, bool " -"interrupt_on_signal = false)\n" -"{\n" -" int sz_selectables = static_cast(m_objects.size());\n" -" std::vector events(sz_selectables);\n" -" int ret;\n" -"\n" -" while(true) {\n" -" ret = ::epoll_wait(m_epoll_fd, events.data(), sz_selectables, " -"timeout);\n" -" // ...\n" -" }\n" -" // ...\n" -"\n" -" for (int i = 0; i < ret; ++i)\n" -" {\n" -" int fd = events[i].data.fd;\n" -" Selectable* sel = m_objects[fd];\n" -"\n" -" sel->readData();\n" -" // error handling here ...\n" -"\n" -" m_ready.insert(sel);\n" -" }\n" -"\n" -" while (!m_ready.empty())\n" -" {\n" -" auto sel = *m_ready.begin();\n" -" m_ready.erase(sel);\n" -" \n" -" // After update callback ...\n" -" return Select::OBJECT;\n" -" }\n" -"\n" -" return Select::TIMEOUT;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"int Select::poll_descriptors(Selectable **c, unsigned int timeout, bool " -"interrupt_on_signal = false)\n" -"{\n" -" int sz_selectables = static_cast(m_objects.size());\n" -" std::vector events(sz_selectables);\n" -" int ret;\n" -"\n" -" while(true) {\n" -" ret = ::epoll_wait(m_epoll_fd, events.data(), sz_selectables, " -"timeout);\n" -" // ...\n" -" }\n" -" // ...\n" -"\n" -" for (int i = 0; i < ret; ++i)\n" -" {\n" -" int fd = events[i].data.fd;\n" -" Selectable* sel = m_objects[fd];\n" -"\n" -" sel->readData();\n" -" // error handling here ...\n" -"\n" -" m_ready.insert(sel);\n" -" }\n" -"\n" -" while (!m_ready.empty())\n" -" {\n" -" auto sel = *m_ready.begin();\n" -" m_ready.erase(sel);\n" -" \n" -" // After update callback ...\n" -" return Select::OBJECT;\n" -" }\n" -"\n" -" return Select::TIMEOUT;\n" -"}\n" -"```" - -#: src/4-5-event-polling-and-error-handling.md:53 -msgid "" -"然而,问题来了…… 回调呢?我们上面提过,`readData()`只是把消息读出来放在一个待" -"处理队列中,并不会真正的处理消息,真正的消息处理需要调用`pops()`函数,将消息" -"拿出来处理,所以什么地方会调用每一个上层封装的消息处理呢?" -msgstr "" -"However, the question arises ...... What about callbacks? As we mentioned " -"above, `readData()` just reads the message out and puts it in a pending " -"queue, it doesn't really process the message, the real message processing " -"needs to call the `pops()` function to take the message out and process it, " -"so where does it call each of the upper level wrapped message processing?" - -#: src/4-5-event-polling-and-error-handling.md:55 -msgid "" -"这里我们还是找到我们的老朋友`portmgrd`的`main`函数,从下面简化的代码中,我们" -"可以看到和一般的Event Loop实现不同,SONiC中,最后的事件处理不是通过回调来实现" -"的,而是需要最外层的Event Loop来主动调用完成:" -msgstr "" -"Here we still find the `main` function of our old friend `portmgrd`. From " -"the following simplified code, we can see that unlike the general Event Loop " -"implementation, the final event handling in SONiC is not achieved through " -"callbacks, but requires the outermost Event Loop to be actively called to " -"complete:" - -#: src/4-5-event-polling-and-error-handling.md:57 -msgid "" -"```cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create PortMgr, which implements Orch interface.\n" -" PortMgr portmgr(&cfgDb, &appDb, &stateDb, cfg_port_tables);\n" -" vector cfgOrchList = {&portmgr};\n" -"\n" -" // Create Select object for event loop and add PortMgr to it.\n" -" swss::Select s;\n" -" for (Orch *o : cfgOrchList) {\n" -" s.addSelectables(o->getSelectables());\n" -" }\n" -"\n" -" // Event loop\n" -" while (true)\n" -" {\n" -" Selectable *sel;\n" -" int ret;\n" -"\n" -" // When anyone of the selectables gets signaled, select() will call\n" -" // into readData() and fetch all events, then return.\n" -" ret = s.select(&sel, SELECT_TIMEOUT);\n" -" // ...\n" -"\n" -" // Then, we call into execute() explicitly to process all events.\n" -" auto *c = (Executor *)sel;\n" -" c->execute();\n" -" }\n" -" return -1;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create PortMgr, which implements Orch interface.\n" -" PortMgr portmgr(&cfgDb, &appDb, &stateDb, cfg_port_tables);\n" -" vector cfgOrchList = {&portmgr};\n" -"\n" -" // Create Select object for event loop and add PortMgr to it.\n" -" swss::Select s;\n" -" for (Orch *o : cfgOrchList) {\n" -" s.addSelectables(o->getSelectables());\n" -" }\n" -"\n" -" // Event loop\n" -" while (true)\n" -" {\n" -" Selectable *sel;\n" -" int ret;\n" -"\n" -" // When anyone of the selectables gets signaled, select() will call\n" -" // into readData() and fetch all events, then return.\n" -" ret = s.select(&sel, SELECT_TIMEOUT);\n" -" // ...\n" -"\n" -" // Then, we call into execute() explicitly to process all events.\n" -" auto *c = (Executor *)sel;\n" -" c->execute();\n" -" }\n" -" return -1;\n" -"}\n" -"```" - -#: src/4-5-event-polling-and-error-handling.md:91 -msgid "## 错误处理" -msgstr "## Error Handling" - -#: src/4-5-event-polling-and-error-handling.md:93 -msgid "" -"关于Event Loop我们还有一个问题,那就是错误处理,比如,如果Redis的命令执行出错" -"了,连接断开了,故障了等等的情况下,我们的服务会发生什么呢?" -msgstr "" -"Another issue we have with Event Loop is error handling. For example, what " -"happens to our service if a Redis command is executed incorrectly, a " -"connection is broken, a fault occurs, etc.?" - -#: src/4-5-event-polling-and-error-handling.md:95 -msgid "" -"从代码上来看,SONiC中的错误处理是非常简单的,就是直接抛出异常(比如,获取命令" -"执行结果的代码,如下),然后在Event Loop中捕获异常,打印日志,接着继续执行。" -msgstr "" -"From the code point of view, the error handling in SONiC is very simple, " -"that is, it just throws an exception (for example, the code to get the " -"result of command execution, as follows), then catches the exception in the " -"Event Loop, prints the log, and then continues the execution." - -#: src/4-5-event-polling-and-error-handling.md:97 -msgid "" -"```cpp\n" -"RedisReply::RedisReply(RedisContext *ctx, const RedisCommand& command)\n" -"{\n" -" int rc = redisAppendFormattedCommand(ctx->getContext(), command.c_str(), " -"command.length());\n" -" if (rc != REDIS_OK)\n" -" {\n" -" // The only reason of error is REDIS_ERR_OOM (Out of memory)\n" -" // ref: https://github.com/redis/hiredis/blob/master/hiredis.c\n" -" throw bad_alloc();\n" -" }\n" -"\n" -" rc = redisGetReply(ctx->getContext(), (void**)&m_reply);\n" -" if (rc != REDIS_OK)\n" -" {\n" -" throw RedisError(\"Failed to redisGetReply with \" + string(command." -"c_str()), ctx->getContext());\n" -" }\n" -" guard([&]{checkReply();}, command.c_str());\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"RedisReply::RedisReply(RedisContext *ctx, const RedisCommand& command)\n" -"{\n" -" int rc = redisAppendFormattedCommand(ctx->getContext(), command.c_str(), " -"command.length());\n" -" if (rc != REDIS_OK)\n" -" {\n" -" // The only reason of error is REDIS_ERR_OOM (Out of memory)\n" -" // ref: https://github.com/redis/hiredis/blob/master/hiredis.c\n" -" throw bad_alloc();\n" -" }\n" -"\n" -" rc = redisGetReply(ctx->getContext(), (void**)&m_reply);\n" -" if (rc != REDIS_OK)\n" -" {\n" -" throw RedisError(\"Failed to redisGetReply with \" + string(command." -"c_str()), ctx->getContext());\n" -" }\n" -" guard([&]{checkReply();}, command.c_str());\n" -"}\n" -"```" - -#: src/4-5-event-polling-and-error-handling.md:117 -msgid "" -"关于异常和错误的种类及其原因,在代码里面并没有看到用于统计和Telemetry的代码," -"所以监控上说是比较薄弱的。另外还需要考虑数据出错的场景,比如数据库写到一半突" -"然断开导致的脏数据,不过简单的重启相关的`*syncd`和`*mgrd`服务可能可以解决此类" -"问题,因为启动时会进行全量同步。" -msgstr "" -"Regarding the types of exceptions and errors and their causes, there is no " -"code seen inside the code for statistics and Telemetry, so monitoring is " -"said to be weak. Also need to consider data error scenarios, such as dirty " -"data due to sudden disconnection halfway through writing the database, but a " -"simple restart of the related `*syncd` and `*mgrd` services may solve such " -"problems, as the full amount of synchronization will be performed at startup." - -#: src/5-core-components.md:1 -msgid "# 核心组件解析" -msgstr "Core Components" - -#: src/5-core-components.md:3 -msgid "" -"这一章,我们会从代码的层面上来深入的分析一下SONiC中一些比较有代表性的核心组件" -"和它们的工作流。" -msgstr "" -"In this chapter, we will take a deeper look at some of the more " -"representative workflows in SONiC from the code level." - -#: src/5-core-components.md:5 -msgid "" -"```admonish note\n" -"为了方便阅读和理解,所有的代码都只是列出了最核心的代码来展现流程,并不是完整" -"的代码,如果需要查看完整代码,请参考[仓库中的原始代码](./3-1-code-repos." -"html)。\n" -"\n" -"另外,每个代码块的开头都给出了相关文件的路径,其使用的是仓库均为SONiC的主仓" -"库:[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage)。\n" -"```" -msgstr "" -"```admonish note\n" -"为了方便阅读和理解,所有的代码都只是列出了最核心的代码来展现流程,并不是完整" -"的代码,如果需要查看完整代码,请参考[仓库中的原始代码](./3-1-code-repos." -"html)。\n" -"\n" -"另外,每个代码块的开头都给出了相关文件的路径,其使用的是仓库均为SONiC的主仓" -"库:[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage)。\n" -"```" - -#: src/5-1-syncd-and-sai.md:1 -msgid "# Syncd和SAI" -msgstr "# Syncd and SAI" - -#: src/5-1-syncd-and-sai.md:3 -msgid "" -"[Syncd容器](./2-3-key-containers.html#asic管理容器syncd)是SONiC中专门负责管理" -"ASIC的容器,其中核心进程`syncd`负责与Redis数据库沟通,加载SAI并与其交互,以完" -"成ASIC的初始化,配置和状态上报的处理等等。" -msgstr "" -"The [Syncd container](. /2-3-key-containers.html#asic management container " -"syncd) is a container in SONiC dedicated to managing ASICs, where the core " -"process `syncd` is responsible for communicating with the Redis database, " -"loading SAIs and interacting with them to complete ASIC initialization, " -"configuration and status reporting processing, and so on." - -#: src/5-1-syncd-and-sai.md:5 -msgid "" -"由于SONiC中大量的工作流最后都需要通过Syncd和SAI来和ASIC进行交互,所以这一部分" -"也就成为了这些工作流的公共部分,所以,在展开其他工作流之前,我们先来看一下" -"Syncd和SAI是如何工作的。" -msgstr "" -"Since a large number of workflows in SONiC end up needing to interact with " -"ASIC through Syncd and SAI, this part becomes a common part of these " -"workflows, so let's take a look at how Syncd and SAI work before we expand " -"on other workflows." - -#: src/5-1-syncd-and-sai.md:7 -msgid "## Syncd启动流程" -msgstr "## Syncd startup process" - -#: src/5-1-syncd-and-sai.md:9 -msgid "" -"`syncd`进程的入口在`syncd_main.cpp`中的`syncd_main`函数,其启动的整体流程大致" -"分为两部分。" -msgstr "" -"The entry point of the `syncd` process is in the `syncd_main.cpp` function, " -"and the overall process of its startup is roughly divided into two parts." - -#: src/5-1-syncd-and-sai.md:11 -msgid "第一部分是创建各个对象,并进行初始化:" -msgstr "" -"The first part is the creation of individual objects and their " -"initialization:" - -#: src/5-1-syncd-and-sai.md:13 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -"\n" -" SDM->>+SD: 调用构造函数\n" -" SD->>SD: 加载和解析命令行参数和配置文件\n" -" SD->>SD: 创建数据库相关对象,如:
ASIC_DB Connector和" -"FlexCounterManager\n" -" SD->>SD: 创建MDIO IPC服务器\n" -" SD->>SD: 创建SAI上报处理逻辑\n" -" SD->>SD: 创建RedisSelectableChannel用于接收Redis通知\n" -" SD->>-SAI: 初始化SAI\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -"\n" -" SDM->>+SD: 调用构造函数\n" -" SD->>SD: 加载和解析命令行参数和配置文件\n" -" SD->>SD: 创建数据库相关对象,如:
ASIC_DB Connector和" -"FlexCounterManager\n" -" SD->>SD: 创建MDIO IPC服务器\n" -" SD->>SD: 创建SAI上报处理逻辑\n" -" SD->>SD: 创建RedisSelectableChannel用于接收Redis通知\n" -" SD->>-SAI: 初始化SAI\n" -"```" - -#: src/5-1-syncd-and-sai.md:29 -msgid "第二个部分是启动主循环,并且处理初始化事件:" -msgstr "" -"The second part starts the main loop and handles the initialization events:" - -#: src/5-1-syncd-and-sai.md:31 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" box purple 主线程\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" end\n" -" box darkgreen MDIO IPC服务器线程\n" -" participant MIS as MdioIpcServer\n" -" end\n" -"\n" -" SDM->>+SD: 启动主线程循环\n" -" SD->>NP: 启动SAI上报处理线程\n" -" NP->>NP: 开始通知处理循环\n" -" SD->>MIS: 启动MDIO IPC服务器线程\n" -" MIS->>MIS: 开始MDIO IPC服务器事件循环\n" -" SD->>SD: 初始化并启动事件分发机制,开始主循环\n" -"\n" -" loop 处理事件\n" -" alt 如果是创建Switch的事件或者是WarmBoot\n" -" SD->>SAI: 创建Switch对象,设置通知回调\n" -" else 如果是其他事件\n" -" SD->>SD: 处理事件\n" -" end\n" -" end\n" -"\n" -" SD->>-SDM: 退出主循环返回\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" box purple 主线程\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" end\n" -" box darkgreen MDIO IPC服务器线程\n" -" participant MIS as MdioIpcServer\n" -" end\n" -"\n" -" SDM->>+SD: 启动主线程循环\n" -" SD->>NP: 启动SAI上报处理线程\n" -" NP->>NP: 开始通知处理循环\n" -" SD->>MIS: 启动MDIO IPC服务器线程\n" -" MIS->>MIS: 开始MDIO IPC服务器事件循环\n" -" SD->>SD: 初始化并启动事件分发机制,开始主循环\n" -"\n" -" loop 处理事件\n" -" alt 如果是创建Switch的事件或者是WarmBoot\n" -" SD->>SAI: 创建Switch对象,设置通知回调\n" -" else 如果是其他事件\n" -" SD->>SD: 处理事件\n" -" end\n" -" end\n" -"\n" -" SD->>-SDM: 退出主循环返回\n" -"```" - -#: src/5-1-syncd-and-sai.md:64 -msgid "然后我们再从代码的角度来更加仔细的看一下这个流程。" -msgstr "Then let's take a closer look at the process from a code perspective." - -#: src/5-1-syncd-and-sai.md:66 -msgid "### syncd_main函数" -msgstr "### syncd_main function" - -#: src/5-1-syncd-and-sai.md:68 -msgid "" -"`syncd_main`函数本身非常简单,主要逻辑就是创建Syncd对象,然后调用其`run`方" -"法:" -msgstr "" -"The `syncd_main` function itself is very simple, the main logic is to create " -"the Syncd object and then call its `run` method:" - -#: src/5-1-syncd-and-sai.md:70 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/syncd_main.cpp\n" -"int syncd_main(int argc, char **argv)\n" -"{\n" -" auto vendorSai = std::make_shared();\n" -" auto syncd = std::make_shared(vendorSai, commandLineOptions, " -"isWarmStart);\n" -" syncd->run();\n" -" return EXIT_SUCCESS;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/syncd_main.cpp\n" -"int syncd_main(int argc, char **argv)\n" -"{\n" -" auto vendorSai = std::make_shared();\n" -" auto syncd = std::make_shared(vendorSai, commandLineOptions, " -"isWarmStart);\n" -" syncd->run();\n" -" return EXIT_SUCCESS;\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:81 -msgid "" -"其中,`Syncd`对象的构造函数负责初始化`Syncd`中的各个功能,而`run`方法则负责启" -"动Syncd的主循环。" -msgstr "" -"The constructor of the `Syncd` object is responsible for initializing the " -"various functions in `Syncd`, while the `run` method is responsible for " -"starting the main loop of Syncd." - -#: src/5-1-syncd-and-sai.md:83 -msgid "### Syncd构造函数" -msgstr "### Syncd constructor" - -#: src/5-1-syncd-and-sai.md:85 -msgid "" -"`Syncd`对象的构造函数负责创建或初始化`Syncd`中的各个功能,比如用于连接数据库" -"的对象,统计管理,和ASIC通知的处理逻辑等等,其主要代码如下:" -msgstr "" -"The constructor of the `Syncd` object is responsible for creating or " -"initializing various functions in `Syncd`, such as objects for connecting to " -"the database, statistics management, and ASIC notification processing logic, " -"etc. The main code is as follows:" - -#: src/5-1-syncd-and-sai.md:87 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"Syncd::Syncd(\n" -" _In_ std::shared_ptr vendorSai,\n" -" _In_ std::shared_ptr cmd,\n" -" _In_ bool isWarmStart):\n" -" m_vendorSai(vendorSai),\n" -" ...\n" -"{\n" -" ...\n" -"\n" -" // Load context config\n" -" auto ccc = sairedis::ContextConfigContainer::" -"loadFromFile(m_commandLineOptions->m_contextConfig.c_str());\n" -" m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext);\n" -" ...\n" -"\n" -" // Create FlexCounter manager\n" -" m_manager = std::make_shared(m_vendorSai, " -"m_contextConfig->m_dbCounters);\n" -"\n" -" // Create DB related objects\n" -" m_dbAsic = std::make_shared(m_contextConfig-" -">m_dbAsic, 0);\n" -" m_mdioIpcServer = std::make_shared(m_vendorSai, " -"m_commandLineOptions->m_globalContext);\n" -" m_selectableChannel = std::make_shared(m_dbAsic, ASIC_STATE_TABLE, REDIS_TABLE_GETRESPONSE, " -"TEMP_PREFIX, modifyRedis);\n" -"\n" -" // Create notification processor and handler\n" -" m_notifications = std::" -"make_shared(m_contextConfig->m_dbAsic);\n" -" m_client = std::make_shared(m_dbAsic);\n" -" m_processor = std::make_shared(m_notifications, " -"m_client, std::bind(&Syncd::syncProcessNotification, this, _1));\n" -"\n" -" m_handler = std::make_shared(m_processor);\n" -" m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler." -"get(), _1, _2);\n" -" m_sn.onNatEvent = std::bind(&NotificationHandler::onNatEvent, m_handler." -"get(), _1, _2);\n" -" // Init many other event handlers here\n" -" m_handler->setSwitchNotifications(m_sn.getSwitchNotifications());\n" -" ...\n" -"\n" -" // Initialize SAI\n" -" sai_status_t status = vendorSai->initialize(0, &m_test_services);\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"Syncd::Syncd(\n" -" _In_ std::shared_ptr vendorSai,\n" -" _In_ std::shared_ptr cmd,\n" -" _In_ bool isWarmStart):\n" -" m_vendorSai(vendorSai),\n" -" ...\n" -"{\n" -" ...\n" -"\n" -" // Load context config\n" -" auto ccc = sairedis::ContextConfigContainer::" -"loadFromFile(m_commandLineOptions->m_contextConfig.c_str());\n" -" m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext);\n" -" ...\n" -"\n" -" // Create FlexCounter manager\n" -" m_manager = std::make_shared(m_vendorSai, " -"m_contextConfig->m_dbCounters);\n" -"\n" -" // Create DB related objects\n" -" m_dbAsic = std::make_shared(m_contextConfig-" -">m_dbAsic, 0);\n" -" m_mdioIpcServer = std::make_shared(m_vendorSai, " -"m_commandLineOptions->m_globalContext);\n" -" m_selectableChannel = std::make_shared(m_dbAsic, ASIC_STATE_TABLE, REDIS_TABLE_GETRESPONSE, " -"TEMP_PREFIX, modifyRedis);\n" -"\n" -" // Create notification processor and handler\n" -" m_notifications = std::" -"make_shared(m_contextConfig->m_dbAsic);\n" -" m_client = std::make_shared(m_dbAsic);\n" -" m_processor = std::make_shared(m_notifications, " -"m_client, std::bind(&Syncd::syncProcessNotification, this, _1));\n" -"\n" -" m_handler = std::make_shared(m_processor);\n" -" m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, m_handler." -"get(), _1, _2);\n" -" m_sn.onNatEvent = std::bind(&NotificationHandler::onNatEvent, m_handler." -"get(), _1, _2);\n" -" // Init many other event handlers here\n" -" m_handler->setSwitchNotifications(m_sn.getSwitchNotifications());\n" -" ...\n" -"\n" -" // Initialize SAI\n" -" sai_status_t status = vendorSai->initialize(0, &m_test_services);\n" -" ...\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:129 -msgid "### SAI的初始化与VendorSai" -msgstr "### SAI initialization with VendorSai" - -#: src/5-1-syncd-and-sai.md:131 -msgid "" -"`Syncd`初始化的最后也是最重要的一步,就是对SAI进行初始化。[在核心组件的SAI介" -"绍中,我们简单的展示了SAI的初始化,实现,以及它是如何为SONiC提供不同平台的支" -"持](./2-4-sai-intro.html),所以这里我们主要来看看`Syncd`是如何对SAI进行封装和" -"调用的。" -msgstr "" -"The last and most important step in the initialization of `Syncd` is the " -"initialization of the SAI. [In the SAI introduction for the core component, " -"we briefly showed the initialization of SAI, the implementation, and how it " -"provides support for different platforms for SONiC](. /2-4-sai-intro.html), " -"so here we will mainly look at how `Syncd` wraps and calls SAI." - -#: src/5-1-syncd-and-sai.md:133 -msgid "" -"`Syncd`使用`VendorSai`来对SAI的所有API进行封装,方便上层调用。其初始化过程也" -"非常直接,基本就是对上面两个函数的直接调用和错误处理,如下:" -msgstr "" -"`Syncd` uses `VendorSai` to encapsulate all the APIs of SAI, making it easy " -"for the upper layers to call. Its initialization process is also very " -"straightforward, basically a direct call to the above two functions and " -"error handling, as follows:" - -#: src/5-1-syncd-and-sai.md:135 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" // Initialize SAI\n" -" memcpy(&m_service_method_table, service_method_table, " -"sizeof(m_service_method_table));\n" -" auto status = sai_api_initialize(flags, service_method_table);\n" -"\n" -" // If SAI is initialized successfully, query all SAI API methods.\n" -" // sai_metadata_api_query will also update all extern global sai_*_api " -"variables, so we can also use\n" -" // sai_metadata_get_object_type_info to get methods for a specific SAI " -"object type.\n" -" if (status == SAI_STATUS_SUCCESS) {\n" -" memset(&m_apis, 0, sizeof(m_apis));\n" -" int failed = sai_metadata_apis_query(sai_api_query, &m_apis);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return status;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" // Initialize SAI\n" -" memcpy(&m_service_method_table, service_method_table, " -"sizeof(m_service_method_table));\n" -" auto status = sai_api_initialize(flags, service_method_table);\n" -"\n" -" // If SAI is initialized successfully, query all SAI API methods.\n" -" // sai_metadata_api_query will also update all extern global sai_*_api " -"variables, so we can also use\n" -" // sai_metadata_get_object_type_info to get methods for a specific SAI " -"object type.\n" -" if (status == SAI_STATUS_SUCCESS) {\n" -" memset(&m_apis, 0, sizeof(m_apis));\n" -" int failed = sai_metadata_apis_query(sai_api_query, &m_apis);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return status;\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:161 -msgid "" -"当获取好所有的SAI API之后,我们就可以通过`VendorSai`对象来调用SAI的API了。当" -"前调用SAI的API方式主要有两种。" -msgstr "" -"Once all the SAI APIs are obtained, we can call the SAI APIs through the " -"`VendorSai` object. Currently there are two main ways to call the SAI API." - -#: src/5-1-syncd-and-sai.md:163 -msgid "" -"第一种是通过`sai_object_type_into_t`来调用,它类似于为所有的SAI Object实现了" -"一个虚表,如下:" -msgstr "" -"The first one is called by `sai_object_type_into_t`, which is similar to " -"implementing a dummy table for all SAI Objects, as follows:" - -#: src/5-1-syncd-and-sai.md:165 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::set(\n" -" _In_ sai_object_type_t objectType,\n" -" _In_ sai_object_id_t objectId,\n" -" _In_ const sai_attribute_t *attr)\n" -"{\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(objectType);\n" -" sai_object_meta_key_t mk = { .objecttype = objectType, .objectkey = { ." -"key = { .object_id = objectId } } };\n" -" return info->set(&mk, attr);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::set(\n" -" _In_ sai_object_type_t objectType,\n" -" _In_ sai_object_id_t objectId,\n" -" _In_ const sai_attribute_t *attr)\n" -"{\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(objectType);\n" -" sai_object_meta_key_t mk = { .objecttype = objectType, .objectkey = { ." -"key = { .object_id = objectId } } };\n" -" return info->set(&mk, attr);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:180 -msgid "" -"另外一种是通过保存在`VendorSai`对象中的`m_apis`来调用,这种方式更加直接,但是" -"调用前需要先根据SAI Object的类型来调用不同的API。" -msgstr "" -"The other way is to call through `m_apis` saved in the `VendorSai` object. " -"This way is more direct, but before calling it, you need to call different " -"APIs according to the type of SAI Object." - -#: src/5-1-syncd-and-sai.md:182 -msgid "" -"```cpp\n" -"sai_status_t VendorSai::getStatsExt(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t object_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters)\n" -"{\n" -" sai_status_t (*ptr)(\n" -" _In_ sai_object_id_t port_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters);\n" -"\n" -" switch ((int)object_type)\n" -" {\n" -" case SAI_OBJECT_TYPE_PORT:\n" -" ptr = m_apis.port_api->get_port_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_ROUTER_INTERFACE:\n" -" ptr = m_apis.router_interface_api-" -">get_router_interface_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_POLICER:\n" -" ptr = m_apis.policer_api->get_policer_stats_ext;\n" -" break;\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_ERROR(\"not implemented, FIXME\");\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -"\n" -" return ptr(object_id, number_of_counters, counter_ids, mode, counters);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"sai_status_t VendorSai::getStatsExt(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t object_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters)\n" -"{\n" -" sai_status_t (*ptr)(\n" -" _In_ sai_object_id_t port_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters);\n" -"\n" -" switch ((int)object_type)\n" -" {\n" -" case SAI_OBJECT_TYPE_PORT:\n" -" ptr = m_apis.port_api->get_port_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_ROUTER_INTERFACE:\n" -" ptr = m_apis.router_interface_api-" -">get_router_interface_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_POLICER:\n" -" ptr = m_apis.policer_api->get_policer_stats_ext;\n" -" break;\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_ERROR(\"not implemented, FIXME\");\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -"\n" -" return ptr(object_id, number_of_counters, counter_ids, mode, counters);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:220 -msgid "可以明显看出,第一种调用方式代码要精炼和直观许多。" -msgstr "" -"As you can clearly see, the code of the first call is much more concise and " -"intuitive." - -#: src/5-1-syncd-and-sai.md:222 -msgid "### Syncd主循环" -msgstr "### Syncd main loop" - -#: src/5-1-syncd-and-sai.md:224 -msgid "" -"`Syncd`的主循环也是使用的SONiC中标准的[事件分发](./4-3-event-polling-and-" -"error-handling.html)机制:在启动时,`Syncd`会将所有用于事件处理的`Selectable`" -"对象注册到用于获取事件的`Select`对象中,然后在主循环中调用`Select`的`select`" -"方法,等待事件的发生。核心代码如下:" -msgstr "" -"The main loop of `Syncd` is also using the standard [event distribution] in " -"SONiC (. /4-3-event-polling-and-error-handling.html) mechanism: at startup, " -"`Syncd` registers all `Selectable` objects used for event handling into the " -"`Select` object used to fetch events, and then calls `Select`'s `select ` " -"method in the main loop and wait for the event to occur. The core code is as " -"follows:" - -#: src/5-1-syncd-and-sai.md:226 -msgid "" -"```c\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::run()\n" -"{\n" -" volatile bool runMainLoop = true;\n" -" std::shared_ptr s = std::make_shared();\n" -" onSyncdStart(m_commandLineOptions->m_startType == " -"SAI_START_TYPE_WARM_BOOT);\n" -"\n" -" // Start notification processing thread\n" -" m_processor->startNotificationsProcessingThread();\n" -"\n" -" // Start MDIO threads\n" -" for (auto& sw: m_switches) { m_mdioIpcServer->setSwitchId(sw.second-" -">getRid()); }\n" -" m_mdioIpcServer->startMdioThread();\n" -"\n" -" // Registering selectable for event polling\n" -" s->addSelectable(m_selectableChannel.get());\n" -" s->addSelectable(m_restartQuery.get());\n" -" s->addSelectable(m_flexCounter.get());\n" -" s->addSelectable(m_flexCounterGroup.get());\n" -"\n" -" // Main event loop\n" -" while (runMainLoop)\n" -" {\n" -" swss::Selectable *sel = NULL;\n" -" int result = s->select(&sel);\n" -"\n" -" ...\n" -" if (sel == m_restartQuery.get()) {\n" -" // Handling switch restart event and restart switch here.\n" -" } else if (sel == m_flexCounter.get()) {\n" -" processFlexCounterEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_flexCounterGroup.get()) {\n" -" processFlexCounterGroupEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_selectableChannel.get()) {\n" -" // Handle redis updates here.\n" -" processEvent(*m_selectableChannel.get());\n" -" } else {\n" -" SWSS_LOG_ERROR(\"select failed: %d\", result);\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::run()\n" -"{\n" -" volatile bool runMainLoop = true;\n" -" std::shared_ptr s = std::make_shared();\n" -" onSyncdStart(m_commandLineOptions->m_startType == " -"SAI_START_TYPE_WARM_BOOT);\n" -"\n" -" // Start notification processing thread\n" -" m_processor->startNotificationsProcessingThread();\n" -"\n" -" // Start MDIO threads\n" -" for (auto& sw: m_switches) { m_mdioIpcServer->setSwitchId(sw.second-" -">getRid()); }\n" -" m_mdioIpcServer->startMdioThread();\n" -"\n" -" // Registering selectable for event polling\n" -" s->addSelectable(m_selectableChannel.get());\n" -" s->addSelectable(m_restartQuery.get());\n" -" s->addSelectable(m_flexCounter.get());\n" -" s->addSelectable(m_flexCounterGroup.get());\n" -"\n" -" // Main event loop\n" -" while (runMainLoop)\n" -" {\n" -" swss::Selectable *sel = NULL;\n" -" int result = s->select(&sel);\n" -"\n" -" ...\n" -" if (sel == m_restartQuery.get()) {\n" -" // Handling switch restart event and restart switch here.\n" -" } else if (sel == m_flexCounter.get()) {\n" -" processFlexCounterEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_flexCounterGroup.get()) {\n" -" processFlexCounterGroupEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_selectableChannel.get()) {\n" -" // Handle redis updates here.\n" -" processEvent(*m_selectableChannel.get());\n" -" } else {\n" -" SWSS_LOG_ERROR(\"select failed: %d\", result);\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:272 -msgid "" -"其中,`m_selectableChannel`就是主要负责处理Redis数据库中的事件的对象。它使用" -"[ProducerTable / ConsumerTable](./4-2-2-redis-messaging-layer." -"md#producertable--consumertable)的方式与Redis数据库进行交互,所以,所有" -"`orchagent`发送过来的操作都会以三元组的形式保存在Redis中的list中,等待`Syncd`" -"的处理。其核心定义如下:" -msgstr "" -"One of them, `m_selectableChannel`, is the object that is primarily " -"responsible for handling events in the Redis database. It uses the " -"[ProducerTable / ConsumerTable](. /4-2-2-redis-messaging-layer." -"md#producertable--consumertable) to interact with the Redis database, so all " -"operations sent by the `orchagent` are stored in a list in Redis in the form " -"of a triple, waiting for ` Syncd` for processing. The core definition is as " -"follows:" - -#: src/5-1-syncd-and-sai.md:274 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/meta/RedisSelectableChannel.h\n" -"class RedisSelectableChannel: public SelectableChannel\n" -"{\n" -" public:\n" -" RedisSelectableChannel(\n" -" _In_ std::shared_ptr dbAsic,\n" -" _In_ const std::string& asicStateTable,\n" -" _In_ const std::string& getResponseTable,\n" -" _In_ const std::string& tempPrefix,\n" -" _In_ bool modifyRedis);\n" -"\n" -" public: // SelectableChannel overrides\n" -" virtual bool empty() override;\n" -" ...\n" -"\n" -" public: // Selectable overrides\n" -" virtual int getFd() override;\n" -" virtual uint64_t readData() override;\n" -" ...\n" -"\n" -" private:\n" -" std::shared_ptr m_dbAsic;\n" -" std::shared_ptr m_asicState;\n" -" std::shared_ptr m_getResponse;\n" -" ...\n" -"};\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/meta/RedisSelectableChannel.h\n" -"class RedisSelectableChannel: public SelectableChannel\n" -"{\n" -" public:\n" -" RedisSelectableChannel(\n" -" _In_ std::shared_ptr dbAsic,\n" -" _In_ const std::string& asicStateTable,\n" -" _In_ const std::string& getResponseTable,\n" -" _In_ const std::string& tempPrefix,\n" -" _In_ bool modifyRedis);\n" -"\n" -" public: // SelectableChannel overrides\n" -" virtual bool empty() override;\n" -" ...\n" -"\n" -" public: // Selectable overrides\n" -" virtual int getFd() override;\n" -" virtual uint64_t readData() override;\n" -" ...\n" -"\n" -" private:\n" -" std::shared_ptr m_dbAsic;\n" -" std::shared_ptr m_asicState;\n" -" std::shared_ptr m_getResponse;\n" -" ...\n" -"};\n" -"```" - -#: src/5-1-syncd-and-sai.md:303 -msgid "另外,在主循环启动时,`Syncd`还会额外启动两个线程:" -msgstr "" -"In addition, when the main loop is started, `Syncd` starts two additional " -"threads:" - -#: src/5-1-syncd-and-sai.md:305 -msgid "" -"- 用于接收ASIC上报通知的通知处理线程:`m_processor-" -">startNotificationsProcessingThread();`\n" -"- 用于处理MDIO通信的MDIO IPC处理线程:`m_mdioIpcServer->startMdioThread();`" -msgstr "" -"- Notifications processing thread for receiving ASIC upload notifications: " -"`m_processor->startNotificationsProcessingThread();`\n" -"- MDIO IPC processing thread for handling MDIO communication: " -"`m_mdioIpcServer->startMdioThread();`" - -#: src/5-1-syncd-and-sai.md:308 -msgid "" -"它们的细节我们在初始化的部分不做过多展开,等后面介绍相关工作流时再来详细介" -"绍。" -msgstr "" -"We won't expand too much on their details in the initialization section, but " -"will come back to them later when we introduce the relevant workflows." - -#: src/5-1-syncd-and-sai.md:310 -msgid "### 创建Switch对象,初始化通知机制" -msgstr "### Create Switch object, initialize notification mechanism" - -#: src/5-1-syncd-and-sai.md:312 -msgid "" -"在主循环启动后,`Syncd`就会开始调用SAI的API来创建Switch对象,这里的入口有两" -"个,一个是ASIC_DB收到创建Switch的通知,另外一个是Warm Boot时,`Syncd`来主动调" -"用,但是创建Switch这一步的内部流程都类似。" -msgstr "" -"After the main loop starts, `Syncd` will start calling the SAI API to create " -"Switch objects. There are two entry points here, one is when ASIC_DB " -"receives a notification to create a Switch, and the other is when Warm Boot, " -"`Syncd` comes to initiate the call, but the internal flow of this step of " -"creating a Switch is similar." - -#: src/5-1-syncd-and-sai.md:314 -msgid "" -"在这一步中间,有一个很重要的步骤,就是初始化SAI内部实现中的通知回调,将我们之" -"前已经创建好的通知处理逻辑传递给SAI的实现,比如FDB的事件等等。这些回调函数会" -"被当做Switch的属性(Attributes)通过参数的形式传给SAI的`create_switch`方法," -"SAI的实现会将其保存起来,这样就可以在事件发生时调用回调函数,来通知`Syncd`" -"了。这里的核心代码如下:" -msgstr "" -"In the middle of this step, there is an important step to initialize the " -"notification callbacks in the SAI's internal implementation to pass the " -"notification handling logic we have created before to the SAI's " -"implementation, such as FDB's events and so on. These callbacks will be " -"passed as Attributes of the Switch to the SAI's `create_switch` method as " -"parameters, and the SAI implementation will save them so that the callbacks " -"can be called to notify `Syncd` when an event occurs. The core code here is " -"as follows:" - -#: src/5-1-syncd-and-sai.md:316 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse event into SAI object\n" -" sai_object_meta_key_t metaKey;\n" -" ...\n" -"\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -"\n" -" // Update notifications pointers in attribute list\n" -" if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == " -"SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET))\n" -" {\n" -" m_handler->updateNotificationsPointers(attr_count, attr_list);\n" -" }\n" -"\n" -" if (isInitViewMode())\n" -" {\n" -" // ProcessQuadEventInInitViewMode will eventually call into " -"VendorSai, which calls create_swtich function in SAI.\n" -" sai_status_t status = processQuadEventInInitViewMode(metaKey." -"objecttype, strObjectId, api, attr_count, attr_list);\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -" }\n" -" ...\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::updateNotificationsPointers(_In_ uint32_t " -"attr_count, _In_ sai_attribute_t *attr_list) const\n" -"{\n" -" for (uint32_t index = 0; index < attr_count; ++index) {\n" -" ...\n" -"\n" -" sai_attribute_t &attr = attr_list[index];\n" -" switch (attr.id) {\n" -" ...\n" -"\n" -" case SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY:\n" -" attr.value.ptr = (void*)m_switchNotifications." -"on_switch_shutdown_request;\n" -" break;\n" -"\n" -" case SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY:\n" -" attr.value.ptr = (void*)m_switchNotifications.on_fdb_event;\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call stack: processQuadEvent\n" -"// -> processQuadEventInInitViewMode\n" -"// -> processQuadInInitViewModeCreate\n" -"// -> onSwitchCreateInInitViewMode\n" -"void Syncd::onSwitchCreateInInitViewMode(_In_ sai_object_id_t switchVid, " -"_In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list)\n" -"{\n" -" if (m_switches.find(switchVid) == m_switches.end()) {\n" -" sai_object_id_t switchRid;\n" -" sai_status_t status;\n" -" status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, " -"attr_count, attr_list);\n" -" ...\n" -"\n" -" m_switches[switchVid] = std::make_shared(switchVid, " -"switchRid, m_client, m_translator, m_vendorSai);\n" -" m_mdioIpcServer->setSwitchId(switchRid);\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse event into SAI object\n" -" sai_object_meta_key_t metaKey;\n" -" ...\n" -"\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -"\n" -" // Update notifications pointers in attribute list\n" -" if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == " -"SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET))\n" -" {\n" -" m_handler->updateNotificationsPointers(attr_count, attr_list);\n" -" }\n" -"\n" -" if (isInitViewMode())\n" -" {\n" -" // ProcessQuadEventInInitViewMode will eventually call into " -"VendorSai, which calls create_swtich function in SAI.\n" -" sai_status_t status = processQuadEventInInitViewMode(metaKey." -"objecttype, strObjectId, api, attr_count, attr_list);\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -" }\n" -" ...\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::updateNotificationsPointers(_In_ uint32_t " -"attr_count, _In_ sai_attribute_t *attr_list) const\n" -"{\n" -" for (uint32_t index = 0; index < attr_count; ++index) {\n" -" ...\n" -"\n" -" sai_attribute_t &attr = attr_list[index];\n" -" switch (attr.id) {\n" -" ...\n" -"\n" -" case SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY:\n" -" attr.value.ptr = (void*)m_switchNotifications." -"on_switch_shutdown_request;\n" -" break;\n" -"\n" -" case SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY:\n" -" attr.value.ptr = (void*)m_switchNotifications.on_fdb_event;\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call stack: processQuadEvent\n" -"// -> processQuadEventInInitViewMode\n" -"// -> processQuadInInitViewModeCreate\n" -"// -> onSwitchCreateInInitViewMode\n" -"void Syncd::onSwitchCreateInInitViewMode(_In_ sai_object_id_t switchVid, " -"_In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list)\n" -"{\n" -" if (m_switches.find(switchVid) == m_switches.end()) {\n" -" sai_object_id_t switchRid;\n" -" sai_status_t status;\n" -" status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, " -"attr_count, attr_list);\n" -" ...\n" -"\n" -" m_switches[switchVid] = std::make_shared(switchVid, " -"switchRid, m_client, m_translator, m_vendorSai);\n" -" m_mdioIpcServer->setSwitchId(switchRid);\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:390 -msgid "从Mellanox的SAI实现,我们可以看到其具体的保存的方法:" -msgstr "" -"From Mellanox's SAI implementation, we can see its specific approach to " -"preservation:" - -#: src/5-1-syncd-and-sai.md:392 -msgid "" -"```cpp\n" -"static sai_status_t mlnx_create_switch(_Out_ sai_object_id_t * " -"switch_id,\n" -" _In_ uint32_t " -"attr_count,\n" -" _In_ const sai_attribute_t " -"*attr_list)\n" -"{\n" -" ...\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SWITCH_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_state_change = " -"(sai_switch_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_shutdown_request =\n" -" (sai_switch_shutdown_request_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_fdb_event = " -"(sai_fdb_event_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PORT_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_port_state_change = " -"(sai_port_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PACKET_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_packet_event = " -"(sai_packet_event_notification_fn)attr_val->ptr;\n" -" }\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"static sai_status_t mlnx_create_switch(_Out_ sai_object_id_t * " -"switch_id,\n" -" _In_ uint32_t " -"attr_count,\n" -" _In_ const sai_attribute_t " -"*attr_list)\n" -"{\n" -" ...\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SWITCH_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_state_change = " -"(sai_switch_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_shutdown_request =\n" -" (sai_switch_shutdown_request_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_fdb_event = " -"(sai_fdb_event_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PORT_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_port_state_change = " -"(sai_port_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PACKET_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_packet_event = " -"(sai_packet_event_notification_fn)attr_val->ptr;\n" -" }\n" -" ...\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:428 -msgid "## ASIC状态更新" -msgstr "## ASIC Status Update" - -#: src/5-1-syncd-and-sai.md:430 -msgid "" -"ASIC状态更新是`Syncd`中最重要的工作流之一,当`orchagent`发现任何变化并开始修" -"改ASIC_DB时,就会触发该工作流,通过SAI来对ASIC进行更新。在了解了`Syncd`的主循" -"环之后,理解ASIC状态更新的工作流就很简单了。" -msgstr "" -"ASIC status update is one of the most important workflows in `Syncd`, which " -"is triggered when `orchagent` finds any change and starts modifying ASIC_DB " -"to update ASIC via SAI. After understanding the main loop of `Syncd`, it is " -"easy to understand the workflow of ASIC state update." - -#: src/5-1-syncd-and-sai.md:432 -msgid "所有的步骤都发生在主线程一个线程中,顺序执行,总结成时序图如下:" -msgstr "" -"All steps occur in one thread in the main thread and are executed " -"sequentially, summarized in a timing diagram as follows:" - -#: src/5-1-syncd-and-sai.md:434 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SD as Syncd\n" -" participant RSC as RedisSelectableChannel\n" -" participant SAI as VendorSai\n" -" participant R as Redis\n" -"\n" -" loop 主线程循环\n" -" SD->>RSC: 收到epoll通知,通知获取所有到来的消息\n" -" RSC->>R: 通过ConsumerTable获取所有到来的消息\n" -"\n" -" critical 给Syncd加锁\n" -" loop 所有收到的消息\n" -" SD->>RSC: 获取一个消息\n" -" SD->>SD: 解析消息,获取操作类型和操作对象\n" -" SD->>SAI: 调用对应的SAI API,更新ASIC\n" -" SD->>RSC: 发送调用结果给Redis\n" -" RSC->>R: 将调用结果写入Redis\n" -" end\n" -" end\n" -" end\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SD as Syncd\n" -" participant RSC as RedisSelectableChannel\n" -" participant SAI as VendorSai\n" -" participant R as Redis\n" -"\n" -" loop 主线程循环\n" -" SD->>RSC: 收到epoll通知,通知获取所有到来的消息\n" -" RSC->>R: 通过ConsumerTable获取所有到来的消息\n" -"\n" -" critical 给Syncd加锁\n" -" loop 所有收到的消息\n" -" SD->>RSC: 获取一个消息\n" -" SD->>SD: 解析消息,获取操作类型和操作对象\n" -" SD->>SAI: 调用对应的SAI API,更新ASIC\n" -" SD->>RSC: 发送调用结果给Redis\n" -" RSC->>R: 将调用结果写入Redis\n" -" end\n" -" end\n" -" end\n" -"```" - -#: src/5-1-syncd-and-sai.md:458 -msgid "" -"首先,`orchagent`通过Redis发送过来的操作会被`RedisSelectableChannel`对象接" -"收,然后在主循环中被处理。当`Syncd`处理到`m_selectableChannel`时,就会调用" -"`processEvent`方法来处理该操作。这几步的核心代码我们上面介绍主循环时已经介绍" -"过了,这里就不再赘述。" -msgstr "" -"First, the operation sent by `orchagent` through Redis is received by the " -"`RedisSelectableChannel` object and then processed in the main loop. When " -"`Syncd` processes to `m_selectableChannel`, it calls the `processEvent` " -"method to process the operation. The core code for these steps has been " -"described above when we introduced the main loop, so we won't go over it " -"here." - -#: src/5-1-syncd-and-sai.md:460 -msgid "" -"然后,`processEvent`会根据其中的操作类型,调用对应的SAI的API来对ASIC进行更" -"新。其逻辑是一个巨大的switch-case语句,如下:" -msgstr "" -"Then, `processEvent` will call the corresponding SAI's API to update the " -"ASIC according to the type of operation in it. The logic is a giant switch-" -"case statement, as follows:" - -#: src/5-1-syncd-and-sai.md:462 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::processEvent(_In_ sairedis::SelectableChannel& consumer)\n" -"{\n" -" // Loop all operations in the queue\n" -" std::lock_guard lock(m_mutex);\n" -" do {\n" -" swss::KeyOpFieldsValuesTuple kco;\n" -" consumer.pop(kco, isInitViewMode());\n" -" processSingleEvent(kco);\n" -" } while (!consumer.empty());\n" -"}\n" -"\n" -"sai_status_t Syncd::processSingleEvent(_In_ const swss::" -"KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" auto& op = kfvOp(kco);\n" -" ...\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_CREATE)\n" -" return processQuadEvent(SAI_COMMON_API_CREATE, kco);\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_REMOVE)\n" -" return processQuadEvent(SAI_COMMON_API_REMOVE, kco);\n" -" \n" -" ...\n" -"}\n" -"\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse operation\n" -" const std::string& key = kfvKey(kco);\n" -" const std::string& strObjectId = key.substr(key.find(\":\") + 1);\n" -"\n" -" sai_object_meta_key_t metaKey;\n" -" sai_deserialize_object_meta_key(key, metaKey);\n" -"\n" -" auto& values = kfvFieldsValues(kco);\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(metaKey.objecttype);\n" -"\n" -" // Process the operation\n" -" sai_status_t status;\n" -" if (info->isnonobjectid) {\n" -" status = processEntry(metaKey, api, attr_count, attr_list);\n" -" } else {\n" -" status = processOid(metaKey.objecttype, strObjectId, api, " -"attr_count, attr_list);\n" -" }\n" -"\n" -" // Send response\n" -" if (api == SAI_COMMON_API_GET) {\n" -" sai_object_id_t switchVid = VidManager::switchIdQuery(metaKey." -"objectkey.key.object_id);\n" -" sendGetResponse(metaKey.objecttype, strObjectId, switchVid, status, " -"attr_count, attr_list);\n" -" ...\n" -" } else {\n" -" sendApiResponse(api, status);\n" -" }\n" -"\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t Syncd::processEntry(_In_ sai_object_meta_key_t metaKey, _In_ " -"sai_common_api_t api,\n" -" _In_ uint32_t attr_count, _In_ " -"sai_attribute_t *attr_list)\n" -"{\n" -" ...\n" -"\n" -" switch (api)\n" -" {\n" -" case SAI_COMMON_API_CREATE:\n" -" return m_vendorSai->create(metaKey, SAI_NULL_OBJECT_ID, " -"attr_count, attr_list);\n" -"\n" -" case SAI_COMMON_API_REMOVE:\n" -" return m_vendorSai->remove(metaKey);\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_THROW(\"api %s not supported\", " -"sai_serialize_common_api(api).c_str());\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::processEvent(_In_ sairedis::SelectableChannel& consumer)\n" -"{\n" -" // Loop all operations in the queue\n" -" std::lock_guard lock(m_mutex);\n" -" do {\n" -" swss::KeyOpFieldsValuesTuple kco;\n" -" consumer.pop(kco, isInitViewMode());\n" -" processSingleEvent(kco);\n" -" } while (!consumer.empty());\n" -"}\n" -"\n" -"sai_status_t Syncd::processSingleEvent(_In_ const swss::" -"KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" auto& op = kfvOp(kco);\n" -" ...\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_CREATE)\n" -" return processQuadEvent(SAI_COMMON_API_CREATE, kco);\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_REMOVE)\n" -" return processQuadEvent(SAI_COMMON_API_REMOVE, kco);\n" -" \n" -" ...\n" -"}\n" -"\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse operation\n" -" const std::string& key = kfvKey(kco);\n" -" const std::string& strObjectId = key.substr(key.find(\":\") + 1);\n" -"\n" -" sai_object_meta_key_t metaKey;\n" -" sai_deserialize_object_meta_key(key, metaKey);\n" -"\n" -" auto& values = kfvFieldsValues(kco);\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(metaKey.objecttype);\n" -"\n" -" // Process the operation\n" -" sai_status_t status;\n" -" if (info->isnonobjectid) {\n" -" status = processEntry(metaKey, api, attr_count, attr_list);\n" -" } else {\n" -" status = processOid(metaKey.objecttype, strObjectId, api, " -"attr_count, attr_list);\n" -" }\n" -"\n" -" // Send response\n" -" if (api == SAI_COMMON_API_GET) {\n" -" sai_object_id_t switchVid = VidManager::switchIdQuery(metaKey." -"objectkey.key.object_id);\n" -" sendGetResponse(metaKey.objecttype, strObjectId, switchVid, status, " -"attr_count, attr_list);\n" -" ...\n" -" } else {\n" -" sendApiResponse(api, status);\n" -" }\n" -"\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t Syncd::processEntry(_In_ sai_object_meta_key_t metaKey, _In_ " -"sai_common_api_t api,\n" -" _In_ uint32_t attr_count, _In_ " -"sai_attribute_t *attr_list)\n" -"{\n" -" ...\n" -"\n" -" switch (api)\n" -" {\n" -" case SAI_COMMON_API_CREATE:\n" -" return m_vendorSai->create(metaKey, SAI_NULL_OBJECT_ID, " -"attr_count, attr_list);\n" -"\n" -" case SAI_COMMON_API_REMOVE:\n" -" return m_vendorSai->remove(metaKey);\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_THROW(\"api %s not supported\", " -"sai_serialize_common_api(api).c_str());\n" -" }\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:549 -msgid "## ASIC状态变更上报" -msgstr "## ASIC status change reporting" - -#: src/5-1-syncd-and-sai.md:551 -msgid "" -"反过来,当ASIC状态发生任何变化,或者需要上报数据,它也会通过SAI来通知我们,此" -"时Syncd会监听这些通知,然后通过ASIC_DB上报给orchagent。其主要工作流如下:" -msgstr "" -"In turn, when any change in ASIC status occurs or data needs to be reported, " -"it will also notify us via SAI, at which point Syncd will listen for these " -"notifications and then report them to the orchagent via ASIC_DB. its main " -"workflow is as follows:" - -#: src/5-1-syncd-and-sai.md:553 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" box purple SAI实现事件处理线程\n" -" participant SAI as SAI Impl\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" participant SD as Syncd\n" -" participant RNP as RedisNotificationProducer\n" -" participant R as Redis\n" -" end\n" -"\n" -" loop SAI实现事件处理消息循环\n" -" SAI->>SAI: 通过ASIC SDK获取事件\n" -" SAI->>SAI: 解析事件,并转换成SAI通知对象\n" -" SAI->>NP: 将通知对象序列化,
并发送给通知处理线程的队列中\n" -" end\n" -"\n" -" loop 通知处理线程消息循环\n" -" NP->>NP: 从队列中获取通知\n" -" NP->>SD: 获取Syncd锁\n" -" critical 给Syncd加锁\n" -" NP->>NP: 反序列化通知对象,并做一些处理\n" -" NP->>RNP: 重新序列化通知对象,并请求发送\n" -" RNP->>R: 将通知以NotificationProducer
的形式写入ASIC_DB\n" -" end\n" -" end\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" box purple SAI实现事件处理线程\n" -" participant SAI as SAI Impl\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" participant SD as Syncd\n" -" participant RNP as RedisNotificationProducer\n" -" participant R as Redis\n" -" end\n" -"\n" -" loop SAI实现事件处理消息循环\n" -" SAI->>SAI: 通过ASIC SDK获取事件\n" -" SAI->>SAI: 解析事件,并转换成SAI通知对象\n" -" SAI->>NP: 将通知对象序列化,
并发送给通知处理线程的队列中\n" -" end\n" -"\n" -" loop 通知处理线程消息循环\n" -" NP->>NP: 从队列中获取通知\n" -" NP->>SD: 获取Syncd锁\n" -" critical 给Syncd加锁\n" -" NP->>NP: 反序列化通知对象,并做一些处理\n" -" NP->>RNP: 重新序列化通知对象,并请求发送\n" -" RNP->>R: 将通知以NotificationProducer
的形式写入ASIC_DB\n" -" end\n" -" end\n" -"```" - -#: src/5-1-syncd-and-sai.md:582 -msgid "" -"这里我们也来看一下具体的实现。为了更加深入的理解,我们还是借助开源的Mellanox" -"的SAI实现来进行分析。" -msgstr "" -"Here we also look at the specific implementation. For a more in-depth " -"understanding, we still analyze it with the help of the open source Mellanox " -"SAI implementation." - -#: src/5-1-syncd-and-sai.md:584 -msgid "" -"最开始,SAI的实现需要接受到ASIC的通知,这一步是通过ASIC的SDK来实现的," -"Mellanox的SAI会创建一个事件处理线程(event_thread),然后使用`select`函数来获" -"取并处理ASIC发送过来的通知,核心代码如下:" -msgstr "" -"At the very beginning, the SAI implementation needs to receive notifications " -"from ASIC, this step is implemented through the ASIC SDK. Mellanox's SAI " -"creates an event handling thread (event_thread) and then uses the `select` " -"function to get and process the notifications sent from ASIC, the core code " -"is as follows:" - -#: src/5-1-syncd-and-sai.md:586 -msgid "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_switch.c\n" -"static void event_thread_func(void *context)\n" -"{\n" -"#define MAX_PACKET_SIZE MAX(g_resource_limits.port_mtu_max, " -"SX_HOST_EVENT_BUFFER_SIZE_MAX)\n" -"\n" -" sx_status_t status;\n" -" sx_api_handle_t api_handle;\n" -" sx_user_channel_t port_channel, callback_channel;\n" -" fd_set descr_set;\n" -" int ret_val;\n" -" sai_object_id_t switch_id = " -"(sai_object_id_t)context;\n" -" sai_port_oper_status_notification_t port_data;\n" -" sai_fdb_event_notification_data_t *fdb_events = NULL;\n" -" sai_attribute_t *attr_list = NULL;\n" -" ...\n" -"\n" -" // Init SDK API\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_open(sai_log_cb, " -"&api_handle))) {\n" -" if (g_notification_callbacks.on_switch_shutdown_request) {\n" -" g_notification_callbacks.on_switch_shutdown_request(switch_id);\n" -" }\n" -" return;\n" -" }\n" -"\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_host_ifc_open(api_handle, " -"&port_channel.channel.fd))) {\n" -" goto out;\n" -" }\n" -" ...\n" -"\n" -" // Register for port and channel notifications\n" -" port_channel.type = SX_USER_CHANNEL_TYPE_FD;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_api_host_ifc_trap_id_register_set(api_handle, SX_ACCESS_CMD_REGISTER, " -"DEFAULT_ETH_SWID, SX_TRAP_ID_PUDE, &port_channel))) {\n" -" goto out;\n" -" }\n" -" ...\n" -" for (uint32_t ii = 0; ii < (sizeof(mlnx_trap_ids) / " -"sizeof(*mlnx_trap_ids)); ii++) {\n" -" status = sx_api_host_ifc_trap_id_register_set(api_handle, " -"SX_ACCESS_CMD_REGISTER, DEFAULT_ETH_SWID, mlnx_trap_ids[ii], " -"&callback_channel);\n" -" }\n" -"\n" -" while (!event_thread_asked_to_stop) {\n" -" FD_ZERO(&descr_set);\n" -" FD_SET(port_channel.channel.fd.fd, &descr_set);\n" -" FD_SET(callback_channel.channel.fd.fd, &descr_set);\n" -" ...\n" -"\n" -" ret_val = select(FD_SETSIZE, &descr_set, NULL, NULL, &timeout);\n" -" if (ret_val > 0) {\n" -" // Port state change event\n" -" if (FD_ISSET(port_channel.channel.fd.fd, &descr_set)) {\n" -" // Parse port state event here ...\n" -" if (g_notification_callbacks.on_port_state_change) {\n" -" g_notification_callbacks.on_port_state_change(1, " -"&port_data);\n" -" }\n" -" }\n" -"\n" -" if (FD_ISSET(callback_channel.channel.fd.fd, &descr_set)) {\n" -" // Receive notification event.\n" -" packet_size = MAX_PACKET_SIZE;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_lib_host_ifc_recv(&callback_channel.channel.fd, p_packet, &packet_size, " -"receive_info))) {\n" -" goto out;\n" -" }\n" -"\n" -" // BFD packet event\n" -" if (SX_TRAP_ID_BFD_PACKET_EVENT == receive_info->trap_id) {\n" -" const struct bfd_packet_event *event = (const struct " -"bfd_packet_event*)p_packet;\n" -" // Parse and check event valid here ...\n" -" status = mlnx_switch_bfd_packet_handle(event);\n" -" continue;\n" -" }\n" -"\n" -" // Same way to handle BFD timeout event, Bulk counter ready " -"event. Emiited.\n" -"\n" -" // FDB event and packet event handling\n" -" if (receive_info->trap_id == SX_TRAP_ID_FDB_EVENT) {\n" -" trap_name = \"FDB event\";\n" -" } else if (SAI_STATUS_SUCCESS != (status = " -"mlnx_translate_sdk_trap_to_sai(receive_info->trap_id, &trap_name, " -"&trap_oid))) {\n" -" continue;\n" -" }\n" -"\n" -" if (SX_TRAP_ID_FDB_EVENT == receive_info->trap_id) {\n" -" // Parse FDB events here ...\n" -"\n" -" if (g_notification_callbacks.on_fdb_event) {\n" -" g_notification_callbacks.on_fdb_event(event_count, " -"fdb_events);\n" -" }\n" -"\n" -" continue;\n" -" }\n" -"\n" -" // Packet event handling\n" -" status = mlnx_get_hostif_packet_data(receive_info, " -"&attrs_num, callback_data);\n" -" if (g_notification_callbacks.on_packet_event) {\n" -" g_notification_callbacks.on_packet_event(switch_id, " -"packet_size, p_packet, attrs_num, callback_data);\n" -" }\n" -" }\n" -" }\n" -" }\n" -"\n" -"out:\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/" -"mlnx_sai_switch.c\n" -"static void event_thread_func(void *context)\n" -"{\n" -"#define MAX_PACKET_SIZE MAX(g_resource_limits.port_mtu_max, " -"SX_HOST_EVENT_BUFFER_SIZE_MAX)\n" -"\n" -" sx_status_t status;\n" -" sx_api_handle_t api_handle;\n" -" sx_user_channel_t port_channel, callback_channel;\n" -" fd_set descr_set;\n" -" int ret_val;\n" -" sai_object_id_t switch_id = " -"(sai_object_id_t)context;\n" -" sai_port_oper_status_notification_t port_data;\n" -" sai_fdb_event_notification_data_t *fdb_events = NULL;\n" -" sai_attribute_t *attr_list = NULL;\n" -" ...\n" -"\n" -" // Init SDK API\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_open(sai_log_cb, " -"&api_handle))) {\n" -" if (g_notification_callbacks.on_switch_shutdown_request) {\n" -" g_notification_callbacks.on_switch_shutdown_request(switch_id);\n" -" }\n" -" return;\n" -" }\n" -"\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_host_ifc_open(api_handle, " -"&port_channel.channel.fd))) {\n" -" goto out;\n" -" }\n" -" ...\n" -"\n" -" // Register for port and channel notifications\n" -" port_channel.type = SX_USER_CHANNEL_TYPE_FD;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_api_host_ifc_trap_id_register_set(api_handle, SX_ACCESS_CMD_REGISTER, " -"DEFAULT_ETH_SWID, SX_TRAP_ID_PUDE, &port_channel))) {\n" -" goto out;\n" -" }\n" -" ...\n" -" for (uint32_t ii = 0; ii < (sizeof(mlnx_trap_ids) / " -"sizeof(*mlnx_trap_ids)); ii++) {\n" -" status = sx_api_host_ifc_trap_id_register_set(api_handle, " -"SX_ACCESS_CMD_REGISTER, DEFAULT_ETH_SWID, mlnx_trap_ids[ii], " -"&callback_channel);\n" -" }\n" -"\n" -" while (!event_thread_asked_to_stop) {\n" -" FD_ZERO(&descr_set);\n" -" FD_SET(port_channel.channel.fd.fd, &descr_set);\n" -" FD_SET(callback_channel.channel.fd.fd, &descr_set);\n" -" ...\n" -"\n" -" ret_val = select(FD_SETSIZE, &descr_set, NULL, NULL, &timeout);\n" -" if (ret_val > 0) {\n" -" // Port state change event\n" -" if (FD_ISSET(port_channel.channel.fd.fd, &descr_set)) {\n" -" // Parse port state event here ...\n" -" if (g_notification_callbacks.on_port_state_change) {\n" -" g_notification_callbacks.on_port_state_change(1, " -"&port_data);\n" -" }\n" -" }\n" -"\n" -" if (FD_ISSET(callback_channel.channel.fd.fd, &descr_set)) {\n" -" // Receive notification event.\n" -" packet_size = MAX_PACKET_SIZE;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_lib_host_ifc_recv(&callback_channel.channel.fd, p_packet, &packet_size, " -"receive_info))) {\n" -" goto out;\n" -" }\n" -"\n" -" // BFD packet event\n" -" if (SX_TRAP_ID_BFD_PACKET_EVENT == receive_info->trap_id) {\n" -" const struct bfd_packet_event *event = (const struct " -"bfd_packet_event*)p_packet;\n" -" // Parse and check event valid here ...\n" -" status = mlnx_switch_bfd_packet_handle(event);\n" -" continue;\n" -" }\n" -"\n" -" // Same way to handle BFD timeout event, Bulk counter ready " -"event. Emiited.\n" -"\n" -" // FDB event and packet event handling\n" -" if (receive_info->trap_id == SX_TRAP_ID_FDB_EVENT) {\n" -" trap_name = \"FDB event\";\n" -" } else if (SAI_STATUS_SUCCESS != (status = " -"mlnx_translate_sdk_trap_to_sai(receive_info->trap_id, &trap_name, " -"&trap_oid))) {\n" -" continue;\n" -" }\n" -"\n" -" if (SX_TRAP_ID_FDB_EVENT == receive_info->trap_id) {\n" -" // Parse FDB events here ...\n" -"\n" -" if (g_notification_callbacks.on_fdb_event) {\n" -" g_notification_callbacks.on_fdb_event(event_count, " -"fdb_events);\n" -" }\n" -"\n" -" continue;\n" -" }\n" -"\n" -" // Packet event handling\n" -" status = mlnx_get_hostif_packet_data(receive_info, " -"&attrs_num, callback_data);\n" -" if (g_notification_callbacks.on_packet_event) {\n" -" g_notification_callbacks.on_packet_event(switch_id, " -"packet_size, p_packet, attrs_num, callback_data);\n" -" }\n" -" }\n" -" }\n" -" }\n" -"\n" -"out:\n" -" ...\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:690 -msgid "" -"接下来,我们用FDB事件来举例,当ASIC收到FDB事件,就会被上面的事件处理循环获取" -"到,并调用`g_notification_callbacks.on_fdb_event`函数来处理。这个函数接下来就" -"会调用到`Syncd`初始化时设置好的`NotificationHandler::onFdbEvent`函数,这个函" -"数会将该事件序列化后,通过消息队列转发给通知处理线程来进行处理:" -msgstr "" -"Next, let's use the FDB event as an example. When ASIC receives an FDB " -"event, it will be fetched by the event handling loop above and the " -"`g_notification_callbacks.on_fdb_event` function will be called to handle " -"it. This function then calls the `NotificationHandler::onFdbEvent` function " -"that was set up when `Syncd` was initialized, which serializes the event and " -"forwards it through the message queue to the notification handler thread for " -"processing:" - -#: src/5-1-syncd-and-sai.md:692 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::onFdbEvent(_In_ uint32_t count, _In_ const " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" enqueueNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::onFdbEvent(_In_ uint32_t count, _In_ const " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" enqueueNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:701 -msgid "" -"而此时通知处理线程会被唤醒,从消息队列中取出该事件,然后通过`Syncd`获取到" -"`Syncd`的锁,再开始处理该通知:" -msgstr "" -"And then the notification handler thread is woken up, takes the event out of " -"the message queue, and then gets a lock on `Syncd` via `Syncd` and starts " -"processing the notification again: the" - -#: src/5-1-syncd-and-sai.md:703 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::ntf_process_function()\n" -"{\n" -" std::mutex ntf_mutex;\n" -" std::unique_lock ulock(ntf_mutex);\n" -"\n" -" while (m_runThread) {\n" -" // When notification arrives, it will signal this condition " -"variable.\n" -" m_cv.wait(ulock);\n" -"\n" -" // Process notifications in the queue.\n" -" swss::KeyOpFieldsValuesTuple item;\n" -" while (m_notificationQueue->tryDequeue(item)) {\n" -" processNotification(item);\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call from NotificationProcessor::processNotification\n" -"void Syncd::syncProcessNotification(_In_ const swss::KeyOpFieldsValuesTuple& " -"item)\n" -"{\n" -" std::lock_guard lock(m_mutex);\n" -" m_processor->syncProcessNotification(item);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::ntf_process_function()\n" -"{\n" -" std::mutex ntf_mutex;\n" -" std::unique_lock ulock(ntf_mutex);\n" -"\n" -" while (m_runThread) {\n" -" // When notification arrives, it will signal this condition " -"variable.\n" -" m_cv.wait(ulock);\n" -"\n" -" // Process notifications in the queue.\n" -" swss::KeyOpFieldsValuesTuple item;\n" -" while (m_notificationQueue->tryDequeue(item)) {\n" -" processNotification(item);\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call from NotificationProcessor::processNotification\n" -"void Syncd::syncProcessNotification(_In_ const swss::KeyOpFieldsValuesTuple& " -"item)\n" -"{\n" -" std::lock_guard lock(m_mutex);\n" -" m_processor->syncProcessNotification(item);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:731 -msgid "" -"接下来就是事件的分发和处理了,`syncProcessNotification`函数是一系列的`if-" -"else`语句,根据事件的类型,调用不同的处理函数来处理该事件:" -msgstr "" -"The next step is the distribution and processing of events. The " -"`syncProcessNotification` function is a series of `if-else` statements that, " -"depending on the type of event, call different handler functions to process " -"the event:" - -#: src/5-1-syncd-and-sai.md:733 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::syncProcessNotification( _In_ const swss::" -"KeyOpFieldsValuesTuple& item)\n" -"{\n" -" std::string notification = kfvKey(item);\n" -" std::string data = kfvOp(item);\n" -"\n" -" if (notification == SAI_SWITCH_NOTIFICATION_NAME_SWITCH_STATE_CHANGE) {\n" -" handle_switch_state_change(data);\n" -" } else if (notification == SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT) {\n" -" handle_fdb_event(data);\n" -" } else if ...\n" -" } else {\n" -" SWSS_LOG_ERROR(\"unknown notification: %s\", notification.c_str());\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::syncProcessNotification( _In_ const swss::" -"KeyOpFieldsValuesTuple& item)\n" -"{\n" -" std::string notification = kfvKey(item);\n" -" std::string data = kfvOp(item);\n" -"\n" -" if (notification == SAI_SWITCH_NOTIFICATION_NAME_SWITCH_STATE_CHANGE) {\n" -" handle_switch_state_change(data);\n" -" } else if (notification == SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT) {\n" -" handle_fdb_event(data);\n" -" } else if ...\n" -" } else {\n" -" SWSS_LOG_ERROR(\"unknown notification: %s\", notification.c_str());\n" -" }\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:751 -msgid "" -"而每个事件处理函数都类似,他们会对发送过来的事件进行反序列化,然后调用真正的" -"处理逻辑发送通知,比如,fdb事件对应的`handle_fdb_event`函数和" -"`process_on_fdb_event`:" -msgstr "" -"And each event handling function is similar in that they deserialize the " -"events sent to them and then call the real processing logic to send " -"notifications, for example, the `handle_fdb_event` function and " -"`process_on_fdb_event` corresponding to the fdb event:" - -#: src/5-1-syncd-and-sai.md:753 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::handle_fdb_event(_In_ const std::string &data)\n" -"{\n" -" uint32_t count;\n" -" sai_fdb_event_notification_data_t *fdbevent = NULL;\n" -" sai_deserialize_fdb_event_ntf(data, count, &fdbevent);\n" -"\n" -" process_on_fdb_event(count, fdbevent);\n" -"\n" -" sai_deserialize_free_fdb_event_ntf(count, fdbevent);\n" -"}\n" -"\n" -"void NotificationProcessor::process_on_fdb_event( _In_ uint32_t count, _In_ " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" for (uint32_t i = 0; i < count; i++) {\n" -" sai_fdb_event_notification_data_t *fdb = &data[i];\n" -" // Check FDB event notification data here\n" -"\n" -" fdb->fdb_entry.switch_id = m_translator->translateRidToVid(fdb-" -">fdb_entry.switch_id, SAI_NULL_OBJECT_ID);\n" -" fdb->fdb_entry.bv_id = m_translator->translateRidToVid(fdb-" -">fdb_entry.bv_id, fdb->fdb_entry.switch_id, true);\n" -" m_translator->translateRidToVid(SAI_OBJECT_TYPE_FDB_ENTRY, fdb-" -">fdb_entry.switch_id, fdb->attr_count, fdb->attr, true);\n" -"\n" -" ...\n" -" }\n" -"\n" -" // Send notification\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" sendNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::handle_fdb_event(_In_ const std::string &data)\n" -"{\n" -" uint32_t count;\n" -" sai_fdb_event_notification_data_t *fdbevent = NULL;\n" -" sai_deserialize_fdb_event_ntf(data, count, &fdbevent);\n" -"\n" -" process_on_fdb_event(count, fdbevent);\n" -"\n" -" sai_deserialize_free_fdb_event_ntf(count, fdbevent);\n" -"}\n" -"\n" -"void NotificationProcessor::process_on_fdb_event( _In_ uint32_t count, _In_ " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" for (uint32_t i = 0; i < count; i++) {\n" -" sai_fdb_event_notification_data_t *fdb = &data[i];\n" -" // Check FDB event notification data here\n" -"\n" -" fdb->fdb_entry.switch_id = m_translator->translateRidToVid(fdb-" -">fdb_entry.switch_id, SAI_NULL_OBJECT_ID);\n" -" fdb->fdb_entry.bv_id = m_translator->translateRidToVid(fdb-" -">fdb_entry.bv_id, fdb->fdb_entry.switch_id, true);\n" -" m_translator->translateRidToVid(SAI_OBJECT_TYPE_FDB_ENTRY, fdb-" -">fdb_entry.switch_id, fdb->attr_count, fdb->attr, true);\n" -"\n" -" ...\n" -" }\n" -"\n" -" // Send notification\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" sendNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:785 -msgid "" -"具体发送事件的逻辑就非常直接了,最终就是通过[NotificationProducer](./4-2-2-" -"redis-messaging-layer.html#notificationproducer--notificationconsumer)来发送" -"通知到ASIC_DB中:" -msgstr "" -"The logic for sending specific events is pretty straightforward, and " -"ultimately it's all about sending notifications to ASIC_DB via " -"[NotificationProducer](. /4-2-2-redis-messaging-layer." -"html#notificationproducer--notificationconsumer) to send notifications to " -"the ASIC_DB:" - -#: src/5-1-syncd-and-sai.md:787 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data)\n" -"{\n" -" std::vector entry;\n" -" sendNotification(op, data, entry);\n" -"}\n" -"\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data, _In_ std::vector " -"entry)\n" -"{\n" -" m_notifications->send(op, data, entry);\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/RedisNotificationProducer.cpp\n" -"void RedisNotificationProducer::send(_In_ const std::string& op, _In_ const " -"std::string& data, _In_ const std::vector& values)\n" -"{\n" -" std::vector vals = values;\n" -"\n" -" // The m_notificationProducer is created in the ctor of " -"RedisNotificationProducer as below:\n" -" // m_notificationProducer = std::make_shared(m_db.get(), " -"REDIS_TABLE_NOTIFICATIONS_PER_DB(dbName));\n" -" m_notificationProducer->send(op, data, vals);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data)\n" -"{\n" -" std::vector entry;\n" -" sendNotification(op, data, entry);\n" -"}\n" -"\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data, _In_ std::vector " -"entry)\n" -"{\n" -" m_notifications->send(op, data, entry);\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/RedisNotificationProducer.cpp\n" -"void RedisNotificationProducer::send(_In_ const std::string& op, _In_ const " -"std::string& data, _In_ const std::vector& values)\n" -"{\n" -" std::vector vals = values;\n" -"\n" -" // The m_notificationProducer is created in the ctor of " -"RedisNotificationProducer as below:\n" -" // m_notificationProducer = std::make_shared(m_db.get(), " -"REDIS_TABLE_NOTIFICATIONS_PER_DB(dbName));\n" -" m_notificationProducer->send(op, data, vals);\n" -"}\n" -"```" - -#: src/5-1-syncd-and-sai.md:811 -msgid "到此,`Syncd`中的通知上报的流程就结束了。" -msgstr "" -"At this point, the process of notification upload in `Syncd` is finished." - -#: src/5-1-syncd-and-sai.md:815 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"3. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"3. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" - -#: src/5-2-bgp.md:1 -msgid "# BGP" -msgstr "# BGP" - -#: src/5-2-bgp.md:3 -msgid "" -"[BGP][BGP]可能是交换机里面最常用,最重要,或者线上使用的最多的功能了。这一" -"节,我们就来深入的看一下BGP相关的工作流。" -msgstr "" -"[BGP][BGP] is probably the most common, important, or most used feature " -"inside a switch on the wire. In this section, let's take an in-depth look at " -"BGP-related workflows." - -#: src/5-2-bgp.md:5 -msgid "## BGP相关进程" -msgstr "## BGP-related processes" - -#: src/5-2-bgp.md:7 -msgid "" -"SONiC使用[FRRouting][FRRouting]作为BGP的实现,用于负责BGP的协议处理。" -"FRRouting是一个开源的路由软件,支持多种路由协议,包括BGP,OSPF,IS-IS,RIP," -"PIM,LDP等等。当FRR发布新版本后,SONiC会将其同步到[SONiC的FRR实现仓库:sonic-" -"frr][SONiCFRR]中,每一个版本都对应这一个分支,比如`frr/8.2`。" -msgstr "" -"SONiC uses [FRRouting][FRRouting] as the BGP implementation for protocol " -"handling of BGP. FRRouting is an open source routing software that supports " -"multiple routing protocols, including BGP, OSPF, IS-IS, RIP, PIM, LDP, and " -"so on. When a new version of FRR is released, SONiC will synchronize it to " -"[SONiC's FRR implementation repository: sonic-frr][SONiCFRR], and each " -"version corresponds to this branch, such as `frr/8.2`." - -#: src/5-2-bgp.md:9 -msgid "" -"FRR主要由两个大部分组成,第一个部分是各个协议的实现,这些进程的名字都叫做" -"`*d`,而当它们收到路由更新的通知的时候,就会告诉第二个部分,也就是`zebra`进" -"程,然后`zebra`进程会进行选路,并将最优的路由信息同步到kernel中,其主体结构如" -"下图所示:" -msgstr "" -"FRR consists of two main parts, the first part is the implementation of each " -"protocol, these processes are named `*d`, and when they receive notification " -"of routing updates, they tell the second part, which is the `zebra` process, " -"and then the `zebra` process will make the route selection and synchronize " -"the optimal routing information to the kernel, the main structure of which " -"is shown in the following figure The main structure is as follows" - -#: src/5-2-bgp.md:11 -msgid "" -"```\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -"|bgpd| |ripd| |ospfd| |ldpd| |pbrd| |pimd| |.....|\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -" | | | | | | |\n" -"+----v-------v--------v-------v-------v-------v--------v\n" -"| |\n" -"| Zebra |\n" -"| |\n" -"+------------------------------------------------------+\n" -" | | |\n" -" | | |\n" -"+------v------+ +---------v--------+ +------v------+\n" -"| | | | | |\n" -"| *NIX Kernel | | Remote dataplane | | ........... |\n" -"| | | | | |\n" -"+-------------+ +------------------+ +-------------+\n" -"```" -msgstr "" -"```\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -"|bgpd| |ripd| |ospfd| |ldpd| |pbrd| |pimd| |.....|\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -" | | | | | | |\n" -"+----v-------v--------v-------v-------v-------v--------v\n" -"| |\n" -"| Zebra |\n" -"| |\n" -"+------------------------------------------------------+\n" -" | | |\n" -" | | |\n" -"+------v------+ +---------v--------+ +------v------+\n" -"| | | | | |\n" -"| *NIX Kernel | | Remote dataplane | | ........... |\n" -"| | | | | |\n" -"+-------------+ +------------------+ +-------------+\n" -"```" - -#: src/5-2-bgp.md:30 -msgid "" -"在SONiC中,这些FRR的进程都跑在`bgp`的容器中。另外,为了将FRR和Redis连接起来," -"SONiC在`bgp`容器中还会运行一个叫做`fpgsyncd`的进程(Forwarding Plane Manager " -"syncd),它的主要功能是监听kernel的路由更新,然后将其同步到APP_DB中。但是因为" -"这个进程不是FRR的一部分,所以它的实现被放在了[sonic-swss][SONiCSWSS]仓库中。" -msgstr "" -"In SONiC, these FRR processes are running in the `bgp` container. Also, to " -"connect FRR to Redis, SONiC runs a process called `fpgsyncd` in the `bgp` " -"container (Forwarding Plane Manager syncd), whose main function is to listen " -"to the kernel for routing updates and then synchronize them to APP_DB. " -"However, since this process is not part of FRR, its implementation is placed " -"in the [sonic-swss][SONiCSWSS] repository." - -#: src/5-2-bgp.md:34 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-frr][SONiCFRR]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-frr][SONiCFRR]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" - -#: src/5-2-1-bgp-command-impl.md:1 -msgid "# BGP命令实现" -msgstr "# BGP command implementation" - -#: src/5-2-1-bgp-command-impl.md:3 -msgid "" -"由于BGP是使用FRR来实现的,所以自然而然的,`show`命令会将直接请求转发给FRR的" -"`vtysh`,核心代码如下:" -msgstr "" -"Since BGP is implemented using FRR, it is natural that the `show` command " -"will forward direct requests to `vtysh` in FRR, with the following core code:" - -#: src/5-2-1-bgp-command-impl.md:5 -msgid "" -"```python\n" -"# file: src/sonic-utilities/show/bgp_frr_v4.py\n" -"# 'summary' subcommand (\"show ip bgp summary\")\n" -"@bgp.command()\n" -"@multi_asic_util.multi_asic_click_options\n" -"def summary(namespace, display):\n" -" bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(\n" -" constants.IPV4, namespace, display)\n" -" bgp_util.display_bgp_summary(bgp_summary=bgp_summary, af=constants." -"IPV4)\n" -"\n" -"# file: src/sonic-utilities/utilities_common/bgp_util.py\n" -"def get_bgp_summary_from_all_bgp_instances(af, namespace, display):\n" -" # IPv6 case is omitted here for simplicity\n" -" vtysh_cmd = \"show ip bgp summary json\"\n" -" \n" -" for ns in device.get_ns_list_based_on_options():\n" -" cmd_output = run_bgp_show_command(vtysh_cmd, ns)\n" -"\n" -"def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, " -"vtysh_shell_cmd=constants.VTYSH_COMMAND):\n" -" cmd = ['sudo', vtysh_shell_cmd] + bgp_instance_id + ['-c', vtysh_cmd]\n" -" output, ret = clicommon.run_command(cmd, return_cmd=True)\n" -"```" -msgstr "" -"```python\n" -"# file: src/sonic-utilities/show/bgp_frr_v4.py\n" -"# 'summary' subcommand (\"show ip bgp summary\")\n" -"@bgp.command()\n" -"@multi_asic_util.multi_asic_click_options\n" -"def summary(namespace, display):\n" -" bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(\n" -" constants.IPV4, namespace, display)\n" -" bgp_util.display_bgp_summary(bgp_summary=bgp_summary, af=constants." -"IPV4)\n" -"\n" -"# file: src/sonic-utilities/utilities_common/bgp_util.py\n" -"def get_bgp_summary_from_all_bgp_instances(af, namespace, display):\n" -" # IPv6 case is omitted here for simplicity\n" -" vtysh_cmd = \"show ip bgp summary json\"\n" -" \n" -" for ns in device.get_ns_list_based_on_options():\n" -" cmd_output = run_bgp_show_command(vtysh_cmd, ns)\n" -"\n" -"def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, " -"vtysh_shell_cmd=constants.VTYSH_COMMAND):\n" -" cmd = ['sudo', vtysh_shell_cmd] + bgp_instance_id + ['-c', vtysh_cmd]\n" -" output, ret = clicommon.run_command(cmd, return_cmd=True)\n" -"```" - -#: src/5-2-1-bgp-command-impl.md:28 -msgid "这里,我们也可以通过直接运行`vtysh`来进行验证:" -msgstr "Here, we can also verify by running `vtysh` directly as follows:" - -#: src/5-2-1-bgp-command-impl.md:30 -msgid "" -"```bash\n" -"root@7260cx3:/etc/sonic/frr# which vtysh\n" -"/usr/bin/vtysh\n" -"\n" -"root@7260cx3:/etc/sonic/frr# vtysh\n" -"\n" -"Hello, this is FRRouting (version 7.5.1-sonic).\n" -"Copyright 1996-2005 Kunihiro Ishiguro, et al.\n" -"\n" -"7260cx3# show ip bgp summary\n" -"\n" -"IPv4 Unicast Summary:\n" -"BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0\n" -"BGP table version 6410\n" -"RIB entries 12809, using 2402 KiB of memory\n" -"Peers 4, using 85 KiB of memory\n" -"Peer groups 4, using 256 bytes of memory\n" -"\n" -"Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down " -"State/PfxRcd PfxSnt\n" -"10.0.0.57 4 64600 3702 3704 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.59 4 64600 3702 3704 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.61 4 64600 3705 3702 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.63 4 64600 3702 3702 0 0 0 " -"08:15:03 6401 6406\n" -"\n" -"Total number of neighbors 4\n" -"```" -msgstr "" -"```bash\n" -"root@7260cx3:/etc/sonic/frr# which vtysh\n" -"/usr/bin/vtysh\n" -"\n" -"root@7260cx3:/etc/sonic/frr# vtysh\n" -"\n" -"Hello, this is FRRouting (version 7.5.1-sonic).\n" -"Copyright 1996-2005 Kunihiro Ishiguro, et al.\n" -"\n" -"7260cx3# show ip bgp summary\n" -"\n" -"IPv4 Unicast Summary:\n" -"BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0\n" -"BGP table version 6410\n" -"RIB entries 12809, using 2402 KiB of memory\n" -"Peers 4, using 85 KiB of memory\n" -"Peer groups 4, using 256 bytes of memory\n" -"\n" -"Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down " -"State/PfxRcd PfxSnt\n" -"10.0.0.57 4 64600 3702 3704 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.59 4 64600 3702 3704 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.61 4 64600 3705 3702 0 0 0 " -"08:15:03 6401 6406\n" -"10.0.0.63 4 64600 3702 3702 0 0 0 " -"08:15:03 6401 6406\n" -"\n" -"Total number of neighbors 4\n" -"```" - -#: src/5-2-1-bgp-command-impl.md:57 -msgid "而`config`命令则是通过直接操作CONFIG_DB来实现的,核心代码如下:" -msgstr "" -"The `config` command, on the other hand, is implemented by directly " -"manipulating CONFIG_DB with the following core code:" - -#: src/5-2-1-bgp-command-impl.md:59 -msgid "" -"```python\n" -"# file: src/sonic-utilities/config/main.py\n" -"\n" -"@bgp.group(cls=clicommon.AbbreviationGroup)\n" -"def remove():\n" -" \"Remove BGP neighbor configuration from the device\"\n" -" pass\n" -"\n" -"@remove.command('neighbor')\n" -"@click.argument('neighbor_ip_or_hostname', " -"metavar='', required=True)\n" -"def remove_neighbor(neighbor_ip_or_hostname):\n" -" \"\"\"Deletes BGP neighbor configuration of given hostname or ip from " -"devices\n" -" User can specify either internal or external BGP neighbor to remove\n" -" \"\"\"\n" -" namespaces = [DEFAULT_NAMESPACE]\n" -" removed_neighbor = False\n" -" ...\n" -"\n" -" # Connect to CONFIG_DB in linux host (in case of single ASIC) or " -"CONFIG_DB in all the\n" -" # namespaces (in case of multi ASIC) and do the sepcified \"action\" on " -"the BGP neighbor(s)\n" -" for namespace in namespaces:\n" -" config_db = ConfigDBConnector(use_unix_socket_path=True, " -"namespace=namespace)\n" -" config_db.connect()\n" -" if _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname):\n" -" removed_neighbor = True\n" -" ...\n" -"```" -msgstr "" -"```python\n" -"# file: src/sonic-utilities/config/main.py\n" -"\n" -"@bgp.group(cls=clicommon.AbbreviationGroup)\n" -"def remove():\n" -" \"Remove BGP neighbor configuration from the device\"\n" -" pass\n" -"\n" -"@remove.command('neighbor')\n" -"@click.argument('neighbor_ip_or_hostname', " -"metavar='', required=True)\n" -"def remove_neighbor(neighbor_ip_or_hostname):\n" -" \"\"\"Deletes BGP neighbor configuration of given hostname or ip from " -"devices\n" -" User can specify either internal or external BGP neighbor to remove\n" -" \"\"\"\n" -" namespaces = [DEFAULT_NAMESPACE]\n" -" removed_neighbor = False\n" -" ...\n" -"\n" -" # Connect to CONFIG_DB in linux host (in case of single ASIC) or " -"CONFIG_DB in all the\n" -" # namespaces (in case of multi ASIC) and do the sepcified \"action\" on " -"the BGP neighbor(s)\n" -" for namespace in namespaces:\n" -" config_db = ConfigDBConnector(use_unix_socket_path=True, " -"namespace=namespace)\n" -" config_db.connect()\n" -" if _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname):\n" -" removed_neighbor = True\n" -" ...\n" -"```" - -#: src/5-2-1-bgp-command-impl.md:89 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-frr][SONiCFRR]\n" -"3. [Github repo: sonic-utilities][SONiCUtil]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-frr][SONiCFRR]\n" -"3. [Github repo: sonic-utilities][SONiCUtil]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" - -#: src/5-2-2-bgp-route-update-workflow.md:1 -msgid "# BGP路由变更下发" -msgstr "# BGP route change distribution" - -#: src/5-2-2-bgp-route-update-workflow.md:3 -msgid "" -"路由变更几乎是SONiC中最重要的工作流,它的整个流程从`bgpd`进程开始,到最终通过" -"SAI到达ASIC芯片,中间参与的进程较多,流程也较为复杂,但是弄清楚之后,我们就可" -"以很好的理解SONiC的设计思想,并且举一反三的理解其他配置下发的工作流了。所以这" -"一节,我们就一起来深入的分析一下它的整体流程。" -msgstr "" -"Routing changes are almost the most important workflow in SONiC. The whole " -"process starts from the `bgpd` process to the final arrival of the ASIC chip " -"through the SAI, and there are more processes involved in the middle and the " -"process is more complicated. So this section, we will come together to " -"analyze its overall process in depth." - -#: src/5-2-2-bgp-route-update-workflow.md:5 -msgid "" -"为了方便我们理解和从代码层面来展示,我们把这个流程分成两个大块来介绍,分别是" -"FRR是如何处理路由变化的,和SONiC的路由变更工作流以及它是如何与FRR进行整合的。" -msgstr "" -"To facilitate our understanding and to show it from the code level, we " -"present this flow in two big chunks, how FRR handles route changes, and " -"SONiC's route change workflow and how it is integrated with FRR." - -#: src/5-2-2-bgp-route-update-workflow.md:7 -msgid "## FRR处理路由变更" -msgstr "## FRR handles route changes" - -#: src/5-2-2-bgp-route-update-workflow.md:9 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant N as 邻居节点\n" -" box purple bgp容器\n" -" participant B as bgpd\n" -" participant ZH as zebra
(请求处理线程)\n" -" participant ZF as zebra
(路由处理线程)\n" -" participant ZD as zebra
(数据平面处理线程)\n" -" participant ZFPM as zebra
(FPM转发线程)\n" -" participant FPM as fpmsyncd\n" -" end\n" -" participant K as Linux Kernel\n" -"\n" -" N->>B: 建立BGP会话,
发送路由变更\n" -" B->>B: 选路,变更本地路由表(RIB)\n" -" alt 如果路由发生变化\n" -" B->>N: 通知其他邻居节点路由变化\n" -" end\n" -" B->>ZH: 通过zlient本地Socket
通知Zebra更新路由表\n" -" ZH->>ZH: 接受bgpd发送的请求\n" -" ZH->>ZF: 将路由请求放入
路由处理线程的队列中\n" -" ZF->>ZF: 更新本地路由表(RIB)\n" -" ZF->>ZD: 将路由表更新请求放入
数据平面处理线程
的消息队列中\n" -" ZF->>ZFPM: 请求FPM处理线程转发路由变更\n" -" ZFPM->>FPM: 通过FPM协议通知
fpmsyncd下发
路由变更\n" -" ZD->>K: 发送Netlink消息更新内核路由表\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant N as 邻居节点\n" -" box purple bgp容器\n" -" participant B as bgpd\n" -" participant ZH as zebra
(请求处理线程)\n" -" participant ZF as zebra
(路由处理线程)\n" -" participant ZD as zebra
(数据平面处理线程)\n" -" participant ZFPM as zebra
(FPM转发线程)\n" -" participant FPM as fpmsyncd\n" -" end\n" -" participant K as Linux Kernel\n" -"\n" -" N->>B: 建立BGP会话,
发送路由变更\n" -" B->>B: 选路,变更本地路由表(RIB)\n" -" alt 如果路由发生变化\n" -" B->>N: 通知其他邻居节点路由变化\n" -" end\n" -" B->>ZH: 通过zlient本地Socket
通知Zebra更新路由表\n" -" ZH->>ZH: 接受bgpd发送的请求\n" -" ZH->>ZF: 将路由请求放入
路由处理线程的队列中\n" -" ZF->>ZF: 更新本地路由表(RIB)\n" -" ZF->>ZD: 将路由表更新请求放入
数据平面处理线程
的消息队列中\n" -" ZF->>ZFPM: 请求FPM处理线程转发路由变更\n" -" ZFPM->>FPM: 通过FPM协议通知
fpmsyncd下发
路由变更\n" -" ZD->>K: 发送Netlink消息更新内核路由表\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:38 -msgid "" -"```admonish note\n" -"关于FRR的实现,这里更多的是从代码的角度来阐述其工作流的过程,而不是其对BGP的" -"实现细节,如果想要了解FRR的BGP实现细节,可以参考[官方文档](https://docs." -"frrouting.org/en/latest/bgp.html)。\n" -"```" -msgstr "" -"```admonish note\n" -"关于FRR的实现,这里更多的是从代码的角度来阐述其工作流的过程,而不是其对BGP的" -"实现细节,如果想要了解FRR的BGP实现细节,可以参考[官方文档](https://docs." -"frrouting.org/en/latest/bgp.html)。\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:42 -msgid "### bgpd处理路由变更" -msgstr "### bgpd handles route changes" - -#: src/5-2-2-bgp-route-update-workflow.md:44 -msgid "" -"`bgpd`是FRR中专门用来处理BGP会话的进程,它会开放TCP 179端口与邻居节点建立BGP" -"连接,并处理路由表的更新请求。当路由发生变化后,FRR也会通过它来通知其他邻居节" -"点。" -msgstr "" -"`bgpd` is a process in the FRR dedicated to handling BGP sessions. It opens " -"TCP port 179 to establish BGP connections with neighboring nodes and handles " -"routing table update requests. It is also used by FRR to notify other " -"neighboring nodes when a route has changed." - -#: src/5-2-2-bgp-route-update-workflow.md:46 -msgid "" -"请求来到`bgpd`之后,它会首先来到它的io线程:`bgp_io`。顾名思义,`bgpd`中的网" -"络读写工作都是在这个线程上完成的:" -msgstr "" -"After a request comes to `bgpd`, it first comes to its io thread: `bgp_io`. " -"As the name implies, the network reads and writes in `bgpd` are done on this " -"thread: `bgp_io`:" - -#: src/5-2-2-bgp-route-update-workflow.md:48 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_io.c\n" -"static int bgp_process_reads(struct thread *thread)\n" -"{\n" -" ...\n" -"\n" -" while (more) {\n" -" // Read packets here\n" -" ...\n" -" \n" -" // If we have more than 1 complete packet, mark it and process it " -"later.\n" -" if (ringbuf_remain(ibw) >= pktsize) {\n" -" ...\n" -" added_pkt = true;\n" -" } else break;\n" -" }\n" -" ...\n" -"\n" -" if (added_pkt)\n" -" thread_add_event(bm->master, bgp_process_packet, peer, 0, &peer-" -">t_process_packet);\n" -"\n" -" return 0;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_io.c\n" -"static int bgp_process_reads(struct thread *thread)\n" -"{\n" -" ...\n" -"\n" -" while (more) {\n" -" // Read packets here\n" -" ...\n" -" \n" -" // If we have more than 1 complete packet, mark it and process it " -"later.\n" -" if (ringbuf_remain(ibw) >= pktsize) {\n" -" ...\n" -" added_pkt = true;\n" -" } else break;\n" -" }\n" -" ...\n" -"\n" -" if (added_pkt)\n" -" thread_add_event(bm->master, bgp_process_packet, peer, 0, &peer-" -">t_process_packet);\n" -"\n" -" return 0;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:73 -msgid "" -"当数据包读完后,`bgpd`会将其发送到主线程进行路由处理。在这里,`bgpd`会根据数" -"据包的类型进行分发,其中路由更新的请求会交给`bpg_update_receive`来进行解析:" -msgstr "" -"When the packet has been read, `bgpd` sends it to the main thread for " -"routing. Here, `bgpd` distributes the packets according to their type, where " -"requests for routing updates are given to `bpg_update_receive` for parsing:" - -#: src/5-2-2-bgp-route-update-workflow.md:75 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_packet.c\n" -"int bgp_process_packet(struct thread *thread)\n" -"{\n" -" ...\n" -" unsigned int processed = 0;\n" -" while (processed < rpkt_quanta_old) {\n" -" uint8_t type = 0;\n" -" bgp_size_t size;\n" -" ...\n" -"\n" -" /* read in the packet length and type */\n" -" size = stream_getw(peer->curr);\n" -" type = stream_getc(peer->curr);\n" -" size -= BGP_HEADER_SIZE;\n" -"\n" -" switch (type) {\n" -" case BGP_MSG_OPEN:\n" -" ...\n" -" break;\n" -" case BGP_MSG_UPDATE:\n" -" ...\n" -" mprc = bgp_update_receive(peer, size);\n" -" ...\n" -" break;\n" -" ...\n" -"}\n" -"\n" -"// Process BGP UPDATE message for peer.\n" -"static int bgp_update_receive(struct peer *peer, bgp_size_t size)\n" -"{\n" -" struct stream *s;\n" -" struct attr attr;\n" -" struct bgp_nlri nlris[NLRI_TYPE_MAX];\n" -" ...\n" -"\n" -" // Parse attributes and NLRI\n" -" memset(&attr, 0, sizeof(struct attr));\n" -" attr.label_index = BGP_INVALID_LABEL_INDEX;\n" -" attr.label = MPLS_INVALID_LABEL;\n" -" ...\n" -"\n" -" memset(&nlris, 0, sizeof(nlris));\n" -" ...\n" -"\n" -" if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0)\n" -" || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) {\n" -" // More parsing here\n" -" ...\n" -"\n" -" if (afi && peer->afc[afi][safi]) {\n" -" struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);\n" -"\n" -" /* End-of-RIB received */\n" -" if (!CHECK_FLAG(peer->af_sflags[afi][safi], " -"PEER_STATUS_EOR_RECEIVED)) {\n" -" ...\n" -" if (gr_info->eor_required == gr_info->eor_received) {\n" -" ...\n" -" /* Best path selection */\n" -" if (bgp_best_path_select_defer( peer->bgp, afi, safi) < " -"0)\n" -" return BGP_Stop;\n" -" }\n" -" }\n" -" ...\n" -" }\n" -" }\n" -" ...\n" -"\n" -" return Receive_UPDATE_message;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_packet.c\n" -"int bgp_process_packet(struct thread *thread)\n" -"{\n" -" ...\n" -" unsigned int processed = 0;\n" -" while (processed < rpkt_quanta_old) {\n" -" uint8_t type = 0;\n" -" bgp_size_t size;\n" -" ...\n" -"\n" -" /* read in the packet length and type */\n" -" size = stream_getw(peer->curr);\n" -" type = stream_getc(peer->curr);\n" -" size -= BGP_HEADER_SIZE;\n" -"\n" -" switch (type) {\n" -" case BGP_MSG_OPEN:\n" -" ...\n" -" break;\n" -" case BGP_MSG_UPDATE:\n" -" ...\n" -" mprc = bgp_update_receive(peer, size);\n" -" ...\n" -" break;\n" -" ...\n" -"}\n" -"\n" -"// Process BGP UPDATE message for peer.\n" -"static int bgp_update_receive(struct peer *peer, bgp_size_t size)\n" -"{\n" -" struct stream *s;\n" -" struct attr attr;\n" -" struct bgp_nlri nlris[NLRI_TYPE_MAX];\n" -" ...\n" -"\n" -" // Parse attributes and NLRI\n" -" memset(&attr, 0, sizeof(struct attr));\n" -" attr.label_index = BGP_INVALID_LABEL_INDEX;\n" -" attr.label = MPLS_INVALID_LABEL;\n" -" ...\n" -"\n" -" memset(&nlris, 0, sizeof(nlris));\n" -" ...\n" -"\n" -" if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0)\n" -" || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) {\n" -" // More parsing here\n" -" ...\n" -"\n" -" if (afi && peer->afc[afi][safi]) {\n" -" struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);\n" -"\n" -" /* End-of-RIB received */\n" -" if (!CHECK_FLAG(peer->af_sflags[afi][safi], " -"PEER_STATUS_EOR_RECEIVED)) {\n" -" ...\n" -" if (gr_info->eor_required == gr_info->eor_received) {\n" -" ...\n" -" /* Best path selection */\n" -" if (bgp_best_path_select_defer( peer->bgp, afi, safi) < " -"0)\n" -" return BGP_Stop;\n" -" }\n" -" }\n" -" ...\n" -" }\n" -" }\n" -" ...\n" -"\n" -" return Receive_UPDATE_message;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:147 -msgid "" -"然后,`bgpd`会开始检查是否出现更优的路径,并更新自己的本地路由表(RIB," -"Routing Information Base):" -msgstr "" -"Then, `bgpd` will start checking if a better path appears and update its own " -"local routing table (RIB, Routing Information Base)::" - -#: src/5-2-2-bgp-route-update-workflow.md:149 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_route.c\n" -"/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */\n" -"int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_dest *dest;\n" -" int cnt = 0;\n" -" struct afi_safi_info *thread_info;\n" -" ...\n" -"\n" -" /* Process the route list */\n" -" for (dest = bgp_table_top(bgp->rib[afi][safi]);\n" -" dest && bgp->gr_info[afi][safi].gr_deferred != 0;\n" -" dest = bgp_route_next(dest))\n" -" {\n" -" ...\n" -" bgp_process_main_one(bgp, dest, afi, safi);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return 0;\n" -"}\n" -"\n" -"static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, " -"afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_path_info *new_select;\n" -" struct bgp_path_info *old_select;\n" -" struct bgp_path_info_pair old_and_new;\n" -" ...\n" -"\n" -" const struct prefix *p = bgp_dest_get_prefix(dest);\n" -" ...\n" -"\n" -" /* Best path selection. */\n" -" bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, " -"afi, safi);\n" -" old_select = old_and_new.old;\n" -" new_select = old_and_new.new;\n" -" ...\n" -"\n" -" /* FIB update. */\n" -" if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)\n" -" && !bgp_option_check(BGP_OPT_NO_FIB)) {\n" -"\n" -" if (new_select && new_select->type == ZEBRA_ROUTE_BGP\n" -" && (new_select->sub_type == BGP_ROUTE_NORMAL\n" -" || new_select->sub_type == BGP_ROUTE_AGGREGATE\n" -" || new_select->sub_type == BGP_ROUTE_IMPORTED)) {\n" -" ...\n" -"\n" -" if (old_select && is_route_parent_evpn(old_select))\n" -" bgp_zebra_withdraw(p, old_select, bgp, safi);\n" -"\n" -" bgp_zebra_announce(dest, p, new_select, bgp, afi, safi);\n" -" } else {\n" -" /* Withdraw the route from the kernel. */\n" -" ...\n" -" }\n" -" }\n" -"\n" -" /* EVPN route injection and clean up */\n" -" ...\n" -"\n" -" UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);\n" -" return;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_route.c\n" -"/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */\n" -"int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_dest *dest;\n" -" int cnt = 0;\n" -" struct afi_safi_info *thread_info;\n" -" ...\n" -"\n" -" /* Process the route list */\n" -" for (dest = bgp_table_top(bgp->rib[afi][safi]);\n" -" dest && bgp->gr_info[afi][safi].gr_deferred != 0;\n" -" dest = bgp_route_next(dest))\n" -" {\n" -" ...\n" -" bgp_process_main_one(bgp, dest, afi, safi);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return 0;\n" -"}\n" -"\n" -"static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, " -"afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_path_info *new_select;\n" -" struct bgp_path_info *old_select;\n" -" struct bgp_path_info_pair old_and_new;\n" -" ...\n" -"\n" -" const struct prefix *p = bgp_dest_get_prefix(dest);\n" -" ...\n" -"\n" -" /* Best path selection. */\n" -" bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, " -"afi, safi);\n" -" old_select = old_and_new.old;\n" -" new_select = old_and_new.new;\n" -" ...\n" -"\n" -" /* FIB update. */\n" -" if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)\n" -" && !bgp_option_check(BGP_OPT_NO_FIB)) {\n" -"\n" -" if (new_select && new_select->type == ZEBRA_ROUTE_BGP\n" -" && (new_select->sub_type == BGP_ROUTE_NORMAL\n" -" || new_select->sub_type == BGP_ROUTE_AGGREGATE\n" -" || new_select->sub_type == BGP_ROUTE_IMPORTED)) {\n" -" ...\n" -"\n" -" if (old_select && is_route_parent_evpn(old_select))\n" -" bgp_zebra_withdraw(p, old_select, bgp, safi);\n" -"\n" -" bgp_zebra_announce(dest, p, new_select, bgp, afi, safi);\n" -" } else {\n" -" /* Withdraw the route from the kernel. */\n" -" ...\n" -" }\n" -" }\n" -"\n" -" /* EVPN route injection and clean up */\n" -" ...\n" -"\n" -" UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);\n" -" return;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:217 -msgid "最后,`bgp_zebra_announce`会通过`zclient`通知`zebra`更新内核路由表。" -msgstr "" -"Finally, `bgp_zebra_announce` will notify `zebra` via `zclient` to update " -"the kernel routing table." - -#: src/5-2-2-bgp-route-update-workflow.md:219 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct " -"bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" ...\n" -" zclient_route_send(valid_nh_count ? ZEBRA_ROUTE_ADD : " -"ZEBRA_ROUTE_DELETE, zclient, &api);\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct " -"bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" ...\n" -" zclient_route_send(valid_nh_count ? ZEBRA_ROUTE_ADD : " -"ZEBRA_ROUTE_DELETE, zclient, &api);\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:228 -msgid "" -"`zclient`使用本地socket与`zebra`通信,并且提供一系列的回调函数用于接收`zebra`" -"的通知,核心代码如下:" -msgstr "" -"`zclient` uses a local socket to communicate with `zebra` and provides a " -"series of callback functions for receiving notifications from `zebra`, with " -"the following core code:" - -#: src/5-2-2-bgp-route-update-workflow.md:230 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_init(struct thread_master *master, unsigned short instance)\n" -"{\n" -" zclient_num_connects = 0;\n" -"\n" -" /* Set default values. */\n" -" zclient = zclient_new(master, &zclient_options_default);\n" -" zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs);\n" -" zclient->zebra_connected = bgp_zebra_connected;\n" -" zclient->router_id_update = bgp_router_id_update;\n" -" zclient->interface_add = bgp_interface_add;\n" -" zclient->interface_delete = bgp_interface_delete;\n" -" zclient->interface_address_add = bgp_interface_address_add;\n" -" ...\n" -"}\n" -"\n" -"int zclient_socket_connect(struct zclient *zclient)\n" -"{\n" -" int sock;\n" -" int ret;\n" -"\n" -" sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0);\n" -" ...\n" -"\n" -" /* Connect to zebra. */\n" -" ret = connect(sock, (struct sockaddr *)&zclient_addr, " -"zclient_addr_len);\n" -" ...\n" -"\n" -" zclient->sock = sock;\n" -" return sock;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_init(struct thread_master *master, unsigned short instance)\n" -"{\n" -" zclient_num_connects = 0;\n" -"\n" -" /* Set default values. */\n" -" zclient = zclient_new(master, &zclient_options_default);\n" -" zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs);\n" -" zclient->zebra_connected = bgp_zebra_connected;\n" -" zclient->router_id_update = bgp_router_id_update;\n" -" zclient->interface_add = bgp_interface_add;\n" -" zclient->interface_delete = bgp_interface_delete;\n" -" zclient->interface_address_add = bgp_interface_address_add;\n" -" ...\n" -"}\n" -"\n" -"int zclient_socket_connect(struct zclient *zclient)\n" -"{\n" -" int sock;\n" -" int ret;\n" -"\n" -" sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0);\n" -" ...\n" -"\n" -" /* Connect to zebra. */\n" -" ret = connect(sock, (struct sockaddr *)&zclient_addr, " -"zclient_addr_len);\n" -" ...\n" -"\n" -" zclient->sock = sock;\n" -" return sock;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:264 -msgid "" -"在`bgpd`容器中,我们可以在`/run/frr`目录下找到`zebra`通信使用的socket文件来进" -"行简单的验证:" -msgstr "" -"In the `bgpd` container, we can find the socket file used by `zebra` " -"communication in the `/run/frr` directory for a simple verification:" - -#: src/5-2-2-bgp-route-update-workflow.md:266 -msgid "" -"```bash\n" -"root@7260cx3:/run/frr# ls -l\n" -"total 12\n" -"...\n" -"srwx------ 1 frr frr 0 Jun 16 09:16 zserv.api\n" -"```" -msgstr "" -"```bash\n" -"root@7260cx3:/run/frr# ls -l\n" -"total 12\n" -"...\n" -"srwx------ 1 frr frr 0 Jun 16 09:16 zserv.api\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:273 -msgid "### zebra更新路由表" -msgstr "### zebra update routing table" - -#: src/5-2-2-bgp-route-update-workflow.md:275 -msgid "" -"由于FRR支持的路由协议很多,如果每个路由协议处理进程都单独的对内核进行操作则必" -"然会产生冲突,很难协调合作,所以FRR使用一个单独的进程用于和所有的路由协议处理" -"进程进行沟通,整合好信息之后统一的进行内核的路由表更新,这个进程就是`zebra`。" -msgstr "" -"Since FRR supports many routing protocols, if each routing protocol process " -"operates separately on the kernel, there will be conflicts and it is " -"difficult to coordinate and cooperate." - -#: src/5-2-2-bgp-route-update-workflow.md:277 -msgid "" -"在`zebra`中,内核的更新发生在一个独立的数据面处理线程中:`dplane_thread`。所" -"有的请求都会通过`zclient`发送给`zebra`,经过处理之后,最后转发给" -"`dplane_thread`来处理,这样路由的处理就是有序的了,也就不会产生冲突了。" -msgstr "" -"In `zebra`, kernel updates happen in a separate data-plane processing " -"thread: `dplane_thread`. All requests are sent to `zebra` via `zclient`, " -"processed and finally forwarded to `dplane_thread` for processing, so that " -"the routing is ordered and no conflicts arise." - -#: src/5-2-2-bgp-route-update-workflow.md:279 -msgid "" -"`zebra`启动时,会将所有的请求处理函数进行注册,当请求到来时,就可以根据请求的" -"类型调用相应的处理函数了,核心代码如下:" -msgstr "" -"When `zebra` is started, all request handling functions will be registered, " -"and when the request arrives, the corresponding handling function can be " -"called according to the type of request, and the core code is as follows:" - -#: src/5-2-2-bgp-route-update-workflow.md:281 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {\n" -" [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,\n" -" [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,\n" -" [ZEBRA_INTERFACE_ADD] = zread_interface_add,\n" -" [ZEBRA_INTERFACE_DELETE] = zread_interface_delete,\n" -" [ZEBRA_ROUTE_ADD] = zread_route_add,\n" -" [ZEBRA_ROUTE_DELETE] = zread_route_del,\n" -" [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add,\n" -" [ZEBRA_REDISTRIBUTE_DELETE] = zebra_redistribute_delete,\n" -" ...\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {\n" -" [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,\n" -" [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,\n" -" [ZEBRA_INTERFACE_ADD] = zread_interface_add,\n" -" [ZEBRA_INTERFACE_DELETE] = zread_interface_delete,\n" -" [ZEBRA_ROUTE_ADD] = zread_route_add,\n" -" [ZEBRA_ROUTE_DELETE] = zread_route_del,\n" -" [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add,\n" -" [ZEBRA_REDISTRIBUTE_DELETE] = zebra_redistribute_delete,\n" -" ...\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:295 -msgid "" -"我们这里拿添加路由`zread_route_add`作为例子,来继续分析后续的流程。从以下代码" -"我们可以看到,当新的路由到来后,`zebra`会开始查看并更新自己内部的路由表:" -msgstr "" -"Let's take adding a route `zread_route_add` as an example here to continue " -"the analysis of the subsequent process. As we can see from the following " -"code, `zebra` will start looking at and updating its own internal routing " -"table when a new route arrives:" - -#: src/5-2-2-bgp-route-update-workflow.md:297 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"static void zread_route_add(ZAPI_HANDLER_ARGS)\n" -"{\n" -" struct stream *s;\n" -" struct route_entry *re;\n" -" struct nexthop_group *ng = NULL;\n" -" struct nhg_hash_entry nhe;\n" -" ...\n" -"\n" -" // Decode zclient request\n" -" s = msg;\n" -" if (zapi_route_decode(s, &api) < 0) {\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" // Allocate new route entry.\n" -" re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));\n" -" re->type = api.type;\n" -" re->instance = api.instance;\n" -" ...\n" -" \n" -" // Init nexthop entry, if we have an id, then add route.\n" -" if (!re->nhe_id) {\n" -" zebra_nhe_init(&nhe, afi, ng->nexthop);\n" -" nhe.nhg.nexthop = ng->nexthop;\n" -" nhe.backup_info = bnhg;\n" -" }\n" -" ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, " -"&nhe);\n" -"\n" -" // Update stats. IPv6 is omitted here for simplicity.\n" -" if (ret > 0) client->v4_route_add_cnt++;\n" -" else if (ret < 0) client->v4_route_upd8_cnt++;\n" -"}\n" -"\n" -"// File: src/sonic-frr/frr/zebra/zebra_rib.c\n" -"int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p,\n" -" struct prefix_ipv6 *src_p, struct route_entry *re,\n" -" struct nhg_hash_entry *re_nhe)\n" -"{\n" -" struct nhg_hash_entry *nhe = NULL;\n" -" struct route_table *table;\n" -" struct route_node *rn;\n" -" int ret = 0;\n" -" ...\n" -"\n" -" /* Find table and nexthop entry */\n" -" table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re-" -">table);\n" -" if (re->nhe_id > 0) nhe = zebra_nhg_lookup_id(re->nhe_id);\n" -" else nhe = zebra_nhg_rib_find_nhe(re_nhe, afi);\n" -"\n" -" /* Attach the re to the nhe's nexthop group. */\n" -" route_entry_update_nhe(re, nhe);\n" -"\n" -" /* Make it sure prefixlen is applied to the prefix. */\n" -" /* Set default distance by route type. */\n" -" ...\n" -"\n" -" /* Lookup route node.*/\n" -" rn = srcdest_rnode_get(table, p, src_p);\n" -" ...\n" -"\n" -" /* If this route is kernel/connected route, notify the dataplane to " -"update kernel route table. */\n" -" if (RIB_SYSTEM_ROUTE(re)) {\n" -" dplane_sys_route_add(rn, re);\n" -" }\n" -"\n" -" /* Link new re to node. */\n" -" SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" rib_addnode(rn, re, 1);\n" -"\n" -" /* Clean up */\n" -" ...\n" -" return ret;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"static void zread_route_add(ZAPI_HANDLER_ARGS)\n" -"{\n" -" struct stream *s;\n" -" struct route_entry *re;\n" -" struct nexthop_group *ng = NULL;\n" -" struct nhg_hash_entry nhe;\n" -" ...\n" -"\n" -" // Decode zclient request\n" -" s = msg;\n" -" if (zapi_route_decode(s, &api) < 0) {\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" // Allocate new route entry.\n" -" re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));\n" -" re->type = api.type;\n" -" re->instance = api.instance;\n" -" ...\n" -" \n" -" // Init nexthop entry, if we have an id, then add route.\n" -" if (!re->nhe_id) {\n" -" zebra_nhe_init(&nhe, afi, ng->nexthop);\n" -" nhe.nhg.nexthop = ng->nexthop;\n" -" nhe.backup_info = bnhg;\n" -" }\n" -" ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, " -"&nhe);\n" -"\n" -" // Update stats. IPv6 is omitted here for simplicity.\n" -" if (ret > 0) client->v4_route_add_cnt++;\n" -" else if (ret < 0) client->v4_route_upd8_cnt++;\n" -"}\n" -"\n" -"// File: src/sonic-frr/frr/zebra/zebra_rib.c\n" -"int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p,\n" -" struct prefix_ipv6 *src_p, struct route_entry *re,\n" -" struct nhg_hash_entry *re_nhe)\n" -"{\n" -" struct nhg_hash_entry *nhe = NULL;\n" -" struct route_table *table;\n" -" struct route_node *rn;\n" -" int ret = 0;\n" -" ...\n" -"\n" -" /* Find table and nexthop entry */\n" -" table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re-" -">table);\n" -" if (re->nhe_id > 0) nhe = zebra_nhg_lookup_id(re->nhe_id);\n" -" else nhe = zebra_nhg_rib_find_nhe(re_nhe, afi);\n" -"\n" -" /* Attach the re to the nhe's nexthop group. */\n" -" route_entry_update_nhe(re, nhe);\n" -"\n" -" /* Make it sure prefixlen is applied to the prefix. */\n" -" /* Set default distance by route type. */\n" -" ...\n" -"\n" -" /* Lookup route node.*/\n" -" rn = srcdest_rnode_get(table, p, src_p);\n" -" ...\n" -"\n" -" /* If this route is kernel/connected route, notify the dataplane to " -"update kernel route table. */\n" -" if (RIB_SYSTEM_ROUTE(re)) {\n" -" dplane_sys_route_add(rn, re);\n" -" }\n" -"\n" -" /* Link new re to node. */\n" -" SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" rib_addnode(rn, re, 1);\n" -"\n" -" /* Clean up */\n" -" ...\n" -" return ret;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:375 -msgid "" -"`rib_addnode`会将这个路由添加请求转发给rib的处理线程,并由它顺序的进行处理:" -msgstr "" -"`rib_addnode` forwards this route add request to the rib's processing " -"thread, and it sequentially processes:" - -#: src/5-2-2-bgp-route-update-workflow.md:377 -msgid "" -"```cpp\n" -"static void rib_addnode(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" ...\n" -" rib_link(rn, re, process);\n" -"}\n" -"\n" -"static void rib_link(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" if (!dest) dest = zebra_rib_create_dest(rn);\n" -" re_list_add_head(&dest->routes, re);\n" -" ...\n" -"\n" -" if (process) rib_queue_add(rn);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"static void rib_addnode(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" ...\n" -" rib_link(rn, re, process);\n" -"}\n" -"\n" -"static void rib_link(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" if (!dest) dest = zebra_rib_create_dest(rn);\n" -" re_list_add_head(&dest->routes, re);\n" -" ...\n" -"\n" -" if (process) rib_queue_add(rn);\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:395 -msgid "" -"请求会来到RIB的处理线程:`rib_process`,并由它来进行进一步的选路,然后将最优" -"的路由添加到`zebra`的内部路由表(RIB)中:" -msgstr "" -"The request comes to the RIB's processing thread: `rib_process`, which " -"performs further routing and then adds the best route to `zebra`'s internal " -"routing table (RIB):" - -#: src/5-2-2-bgp-route-update-workflow.md:397 -msgid "" -"```cpp\n" -"/* Core function for processing routing information base. */\n" -"static void rib_process(struct route_node *rn)\n" -"{\n" -" struct route_entry *re;\n" -" struct route_entry *next;\n" -" struct route_entry *old_selected = NULL;\n" -" struct route_entry *new_selected = NULL;\n" -" struct route_entry *old_fib = NULL;\n" -" struct route_entry *new_fib = NULL;\n" -" struct route_entry *best = NULL;\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" dest = rib_dest_from_rnode(rn);\n" -" old_fib = dest->selected_fib;\n" -" ...\n" -"\n" -" /* Check every route entry and select the best route. */\n" -" RNODE_FOREACH_RE_SAFE (rn, re, next) {\n" -" ...\n" -"\n" -" if (CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) {\n" -" best = rib_choose_best(new_fib, re);\n" -" if (new_fib && best != new_fib)\n" -" UNSET_FLAG(new_fib->status, ROUTE_ENTRY_CHANGED);\n" -" new_fib = best;\n" -" } else {\n" -" best = rib_choose_best(new_selected, re);\n" -" if (new_selected && best != new_selected)\n" -" UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED);\n" -" new_selected = best;\n" -" }\n" -"\n" -" if (best != re)\n" -" UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" } /* RNODE_FOREACH_RE */\n" -" ...\n" -"\n" -" /* Update fib according to selection results */\n" -" if (new_fib && old_fib)\n" -" rib_process_update_fib(zvrf, rn, old_fib, new_fib);\n" -" else if (new_fib)\n" -" rib_process_add_fib(zvrf, rn, new_fib);\n" -" else if (old_fib)\n" -" rib_process_del_fib(zvrf, rn, old_fib);\n" -"\n" -" /* Remove all RE entries queued for removal */\n" -" /* Check if the dest can be deleted now. */\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"/* Core function for processing routing information base. */\n" -"static void rib_process(struct route_node *rn)\n" -"{\n" -" struct route_entry *re;\n" -" struct route_entry *next;\n" -" struct route_entry *old_selected = NULL;\n" -" struct route_entry *new_selected = NULL;\n" -" struct route_entry *old_fib = NULL;\n" -" struct route_entry *new_fib = NULL;\n" -" struct route_entry *best = NULL;\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" dest = rib_dest_from_rnode(rn);\n" -" old_fib = dest->selected_fib;\n" -" ...\n" -"\n" -" /* Check every route entry and select the best route. */\n" -" RNODE_FOREACH_RE_SAFE (rn, re, next) {\n" -" ...\n" -"\n" -" if (CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) {\n" -" best = rib_choose_best(new_fib, re);\n" -" if (new_fib && best != new_fib)\n" -" UNSET_FLAG(new_fib->status, ROUTE_ENTRY_CHANGED);\n" -" new_fib = best;\n" -" } else {\n" -" best = rib_choose_best(new_selected, re);\n" -" if (new_selected && best != new_selected)\n" -" UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED);\n" -" new_selected = best;\n" -" }\n" -"\n" -" if (best != re)\n" -" UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" } /* RNODE_FOREACH_RE */\n" -" ...\n" -"\n" -" /* Update fib according to selection results */\n" -" if (new_fib && old_fib)\n" -" rib_process_update_fib(zvrf, rn, old_fib, new_fib);\n" -" else if (new_fib)\n" -" rib_process_add_fib(zvrf, rn, new_fib);\n" -" else if (old_fib)\n" -" rib_process_del_fib(zvrf, rn, old_fib);\n" -"\n" -" /* Remove all RE entries queued for removal */\n" -" /* Check if the dest can be deleted now. */\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:450 -msgid "" -"对于新的路由,会调用`rib_process_add_fib`来将其添加到`zebra`的内部路由表中," -"然后通知dplane进行内核路由表的更新:" -msgstr "" -"For new routes, `rib_process_add_fib` is called to add them to `zebra`'s " -"internal routing table, and then dplane is notified of the kernel routing " -"table update at" - -#: src/5-2-2-bgp-route-update-workflow.md:452 -msgid "" -"```cpp\n" -"static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node " -"*rn, struct route_entry *new)\n" -"{\n" -" hook_call(rib_update, rn, \"new route selected\");\n" -" ...\n" -"\n" -" /* If labeled-unicast route, install transit LSP. */\n" -" if (zebra_rib_labeled_unicast(new))\n" -" zebra_mpls_lsp_install(zvrf, rn, new);\n" -"\n" -" rib_install_kernel(rn, new, NULL);\n" -" UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);\n" -"}\n" -"\n" -"void rib_install_kernel(struct route_node *rn, struct route_entry *re,\n" -" struct route_entry *old)\n" -"{\n" -" struct rib_table_info *info = srcdest_rnode_table_info(rn);\n" -" enum zebra_dplane_result ret;\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" ...\n" -"\n" -" /* Install the resolved nexthop object first. */\n" -" zebra_nhg_install_kernel(re->nhe);\n" -"\n" -" /* If this is a replace to a new RE let the originator of the RE know " -"that they've lost */\n" -" if (old && (old != re) && (old->type != re->type))\n" -" zsend_route_notify_owner(rn, old, ZAPI_ROUTE_BETTER_ADMIN_WON, info-" -">afi, info->safi);\n" -"\n" -" /* Update fib selection */\n" -" dest->selected_fib = re;\n" -"\n" -" /* Make sure we update the FPM any time we send new information to the " -"kernel. */\n" -" hook_call(rib_update, rn, \"installing in kernel\");\n" -"\n" -" /* Send add or update */\n" -" if (old) ret = dplane_route_update(rn, re, old);\n" -" else ret = dplane_route_add(rn, re);\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node " -"*rn, struct route_entry *new)\n" -"{\n" -" hook_call(rib_update, rn, \"new route selected\");\n" -" ...\n" -"\n" -" /* If labeled-unicast route, install transit LSP. */\n" -" if (zebra_rib_labeled_unicast(new))\n" -" zebra_mpls_lsp_install(zvrf, rn, new);\n" -"\n" -" rib_install_kernel(rn, new, NULL);\n" -" UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);\n" -"}\n" -"\n" -"void rib_install_kernel(struct route_node *rn, struct route_entry *re,\n" -" struct route_entry *old)\n" -"{\n" -" struct rib_table_info *info = srcdest_rnode_table_info(rn);\n" -" enum zebra_dplane_result ret;\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" ...\n" -"\n" -" /* Install the resolved nexthop object first. */\n" -" zebra_nhg_install_kernel(re->nhe);\n" -"\n" -" /* If this is a replace to a new RE let the originator of the RE know " -"that they've lost */\n" -" if (old && (old != re) && (old->type != re->type))\n" -" zsend_route_notify_owner(rn, old, ZAPI_ROUTE_BETTER_ADMIN_WON, info-" -">afi, info->safi);\n" -"\n" -" /* Update fib selection */\n" -" dest->selected_fib = re;\n" -"\n" -" /* Make sure we update the FPM any time we send new information to the " -"kernel. */\n" -" hook_call(rib_update, rn, \"installing in kernel\");\n" -"\n" -" /* Send add or update */\n" -" if (old) ret = dplane_route_update(rn, re, old);\n" -" else ret = dplane_route_add(rn, re);\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:494 -msgid "" -"这里有两个重要的操作,一个自然是调用`dplane_route_*`函数来进行内核的路由表更" -"新,另一个则是出现了两次的`hook_call`,fpm的钩子函数就是挂在这个地方,用来接" -"收并转发路由表的更新通知。这里我们一个一个来看:" -msgstr "" -"There are two important operations here, one is naturally the call to the " -"`dplane_route_*` function to perform kernel routing table updates, and the " -"other is the `hook_call` that appears twice, where the fpm hook function is " -"hung to receive and forward routing table update notifications. Here we look " -"at them one by one:" - -#: src/5-2-2-bgp-route-update-workflow.md:496 -msgid "#### dplane更新内核路由表" -msgstr "#### dplane updates the kernel routing table" - -#: src/5-2-2-bgp-route-update-workflow.md:498 -msgid "" -"首先是dplane的`dplane_route_*`函数,它们的做的事情都一样:把请求打包,然后放" -"入`dplane_thread`的消息队列中,并不会做任何实质的操作:" -msgstr "" -"First is the `dplane_route_*` function of dplane, which does the same thing: " -"it packs the request and puts it in the `dplane_thread` message queue, " -"without doing anything of substance: the" - -#: src/5-2-2-bgp-route-update-workflow.md:500 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"enum zebra_dplane_result dplane_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_ROUTE_INSTALL);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_route_update(struct route_node *rn, struct " -"route_entry *re, struct route_entry *old_re) {\n" -" return dplane_route_update_internal(rn, re, old_re, " -"DPLANE_OP_ROUTE_UPDATE);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_SYS_ROUTE_ADD);\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"dplane_route_update_internal(struct route_node *rn, struct route_entry *re, " -"struct route_entry *old_re, enum dplane_op_e op)\n" -"{\n" -" enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;\n" -" int ret = EINVAL;\n" -"\n" -" /* Create and init context */\n" -" struct zebra_dplane_ctx *ctx = ...;\n" -"\n" -" /* Enqueue context for processing */\n" -" ret = dplane_route_enqueue(ctx);\n" -"\n" -" /* Update counter */\n" -" atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, " -"memory_order_relaxed);\n" -"\n" -" if (ret == AOK)\n" -" result = ZEBRA_DPLANE_REQUEST_QUEUED;\n" -"\n" -" return result;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"enum zebra_dplane_result dplane_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_ROUTE_INSTALL);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_route_update(struct route_node *rn, struct " -"route_entry *re, struct route_entry *old_re) {\n" -" return dplane_route_update_internal(rn, re, old_re, " -"DPLANE_OP_ROUTE_UPDATE);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_SYS_ROUTE_ADD);\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"dplane_route_update_internal(struct route_node *rn, struct route_entry *re, " -"struct route_entry *old_re, enum dplane_op_e op)\n" -"{\n" -" enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;\n" -" int ret = EINVAL;\n" -"\n" -" /* Create and init context */\n" -" struct zebra_dplane_ctx *ctx = ...;\n" -"\n" -" /* Enqueue context for processing */\n" -" ret = dplane_route_enqueue(ctx);\n" -"\n" -" /* Update counter */\n" -" atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, " -"memory_order_relaxed);\n" -"\n" -" if (ret == AOK)\n" -" result = ZEBRA_DPLANE_REQUEST_QUEUED;\n" -"\n" -" return result;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:536 -msgid "" -"然后,我们就来到了数据面处理线程`dplane_thread`,其消息循环很简单,就是从队列" -"中一个个取出消息,然后通过调用其处理函数:" -msgstr "" -"Then, we come to the dataface processing thread `dplane_thread`, whose " -"message loop is simple: it takes messages one by one from the queue and then " -"calls its processing function by" - -#: src/5-2-2-bgp-route-update-workflow.md:538 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"static int dplane_thread_loop(struct thread *event)\n" -"{\n" -" ...\n" -"\n" -" while (prov) {\n" -" ...\n" -"\n" -" /* Process work here */\n" -" (*prov->dp_fp)(prov);\n" -"\n" -" /* Check for zebra shutdown */\n" -" /* Dequeue completed work from the provider */\n" -" ...\n" -"\n" -" /* Locate next provider */\n" -" DPLANE_LOCK();\n" -" prov = TAILQ_NEXT(prov, dp_prov_link);\n" -" DPLANE_UNLOCK();\n" -" }\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"static int dplane_thread_loop(struct thread *event)\n" -"{\n" -" ...\n" -"\n" -" while (prov) {\n" -" ...\n" -"\n" -" /* Process work here */\n" -" (*prov->dp_fp)(prov);\n" -"\n" -" /* Check for zebra shutdown */\n" -" /* Dequeue completed work from the provider */\n" -" ...\n" -"\n" -" /* Locate next provider */\n" -" DPLANE_LOCK();\n" -" prov = TAILQ_NEXT(prov, dp_prov_link);\n" -" DPLANE_UNLOCK();\n" -" }\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:562 -msgid "" -"默认情况下,`dplane_thread`会使用`kernel_dplane_process_func`来进行消息的处" -"理,内部会根据请求的类型对内核的操作进行分发:" -msgstr "" -"By default, `dplane_thread` uses `kernel_dplane_process_func` for message " -"processing, and internally distributes the kernel operations according to " -"the type of request:" - -#: src/5-2-2-bgp-route-update-workflow.md:564 -msgid "" -"```c\n" -"static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" struct zebra_dplane_ctx *ctx;\n" -" int counter, limit;\n" -" limit = dplane_provider_get_work_limit(prov);\n" -"\n" -" for (counter = 0; counter < limit; counter++) {\n" -" ctx = dplane_provider_dequeue_in_ctx(prov);\n" -" if (ctx == NULL) break;\n" -"\n" -" /* A previous provider plugin may have asked to skip the kernel " -"update. */\n" -" if (dplane_ctx_is_skip_kernel(ctx)) {\n" -" res = ZEBRA_DPLANE_REQUEST_SUCCESS;\n" -" goto skip_one;\n" -" }\n" -"\n" -" /* Dispatch to appropriate kernel-facing apis */\n" -" switch (dplane_ctx_get_op(ctx)) {\n" -" case DPLANE_OP_ROUTE_INSTALL:\n" -" case DPLANE_OP_ROUTE_UPDATE:\n" -" case DPLANE_OP_ROUTE_DELETE:\n" -" res = kernel_dplane_route_update(ctx);\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" /* Call into the synchronous kernel-facing code here */\n" -" res = kernel_route_update(ctx);\n" -" return res;\n" -"}\n" -"```" -msgstr "" -"```c\n" -"static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" struct zebra_dplane_ctx *ctx;\n" -" int counter, limit;\n" -" limit = dplane_provider_get_work_limit(prov);\n" -"\n" -" for (counter = 0; counter < limit; counter++) {\n" -" ctx = dplane_provider_dequeue_in_ctx(prov);\n" -" if (ctx == NULL) break;\n" -"\n" -" /* A previous provider plugin may have asked to skip the kernel " -"update. */\n" -" if (dplane_ctx_is_skip_kernel(ctx)) {\n" -" res = ZEBRA_DPLANE_REQUEST_SUCCESS;\n" -" goto skip_one;\n" -" }\n" -"\n" -" /* Dispatch to appropriate kernel-facing apis */\n" -" switch (dplane_ctx_get_op(ctx)) {\n" -" case DPLANE_OP_ROUTE_INSTALL:\n" -" case DPLANE_OP_ROUTE_UPDATE:\n" -" case DPLANE_OP_ROUTE_DELETE:\n" -" res = kernel_dplane_route_update(ctx);\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" /* Call into the synchronous kernel-facing code here */\n" -" res = kernel_route_update(ctx);\n" -" return res;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:606 -msgid "" -"而`kernel_route_update`则是真正的内核操作了,它会通过netlink来通知内核路由更" -"新:" -msgstr "" -"And `kernel_route_update` is a real kernel operation, which notifies the " -"kernel of routing updates via netlink: `kernel_route_update`:" - -#: src/5-2-2-bgp-route-update-workflow.md:608 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/rt_netlink.c\n" -"// Update or delete a prefix from the kernel, using info from a dataplane " -"context.\n" -"enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" int cmd, ret;\n" -" const struct prefix *p = dplane_ctx_get_dest(ctx);\n" -" struct nexthop *nexthop;\n" -"\n" -" if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {\n" -" cmd = RTM_DELROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) {\n" -" cmd = RTM_NEWROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) {\n" -" cmd = RTM_NEWROUTE;\n" -" }\n" -"\n" -" if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))\n" -" ret = netlink_route_multipath(cmd, ctx);\n" -" ...\n" -"\n" -" return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : " -"ZEBRA_DPLANE_REQUEST_FAILURE);\n" -"}\n" -"\n" -"// Routing table change via netlink interface, using a dataplane context " -"object\n" -"static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)\n" -"{\n" -" // Build netlink request.\n" -" struct {\n" -" struct nlmsghdr n;\n" -" struct rtmsg r;\n" -" char buf[NL_PKT_BUF_SIZE];\n" -" } req;\n" -"\n" -" req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\n" -"    req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;\n" -"    ...\n" -"\n" -" /* Talk to netlink socket. */\n" -" return netlink_talk_info(netlink_talk_filter, &req.n, " -"dplane_ctx_get_ns(ctx), 0);\n" -"}\n" -"```" -msgstr "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/rt_netlink.c\n" -"// Update or delete a prefix from the kernel, using info from a dataplane " -"context.\n" -"enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" int cmd, ret;\n" -" const struct prefix *p = dplane_ctx_get_dest(ctx);\n" -" struct nexthop *nexthop;\n" -"\n" -" if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {\n" -" cmd = RTM_DELROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) {\n" -" cmd = RTM_NEWROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) {\n" -" cmd = RTM_NEWROUTE;\n" -" }\n" -"\n" -" if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))\n" -" ret = netlink_route_multipath(cmd, ctx);\n" -" ...\n" -"\n" -" return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : " -"ZEBRA_DPLANE_REQUEST_FAILURE);\n" -"}\n" -"\n" -"// Routing table change via netlink interface, using a dataplane context " -"object\n" -"static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)\n" -"{\n" -" // Build netlink request.\n" -" struct {\n" -" struct nlmsghdr n;\n" -" struct rtmsg r;\n" -" char buf[NL_PKT_BUF_SIZE];\n" -" } req;\n" -"\n" -" req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\n" -"    req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;\n" -"    ...\n" -"\n" -" /* Talk to netlink socket. */\n" -" return netlink_talk_info(netlink_talk_filter, &req.n, " -"dplane_ctx_get_ns(ctx), 0);\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:651 -msgid "#### FPM路由更新转发" -msgstr "#### FPM Routing Update Forwarding" - -#: src/5-2-2-bgp-route-update-workflow.md:653 -msgid "" -"FPM(Forwarding Plane Manager)是FRR中用于通知其他进程路由变更的协议,其主要" -"逻辑代码在`src/sonic-frr/frr/zebra/zebra_fpm.c`中。它默认有两套协议实现:" -"protobuf和netlink,SONiC就是使用的是netlink协议。" -msgstr "" -"FPM (Forwarding Plane Manager) is a protocol used in FRR to notify other " -"processes of routing changes, and its main logic code is in `src/sonic-frr/" -"frr/zebra/zebra_fpm.c`. It has two protocol implementations by default: " -"protobuf and netlink, and SONiC is using the netlink protocol." - -#: src/5-2-2-bgp-route-update-workflow.md:655 -msgid "" -"上面我们已经提到,它通过钩子函数实现,监听RIB中的路由变化,并通过本地Socket转" -"发给其他的进程。这个钩子会在启动的时候就注册好,其中和我们现在看的最相关的就" -"是`rib_update`钩子了,如下所示:" -msgstr "" -"As we have already mentioned above, it is implemented through a hook " -"function that listens for routing changes in the RIB and forwards them to " -"other processes via local sockets. This hook will be registered at startup, " -"the most relevant one to what we are looking at is the `rib_update` hook, as " -"follows:" - -#: src/5-2-2-bgp-route-update-workflow.md:657 -msgid "" -"```c\n" -"static int zebra_fpm_module_init(void)\n" -"{\n" -" hook_register(rib_update, zfpm_trigger_update);\n" -" hook_register(zebra_rmac_update, zfpm_trigger_rmac_update);\n" -" hook_register(frr_late_init, zfpm_init);\n" -" hook_register(frr_early_fini, zfpm_fini);\n" -" return 0;\n" -"}\n" -"\n" -"FRR_MODULE_SETUP(.name = \"zebra_fpm\", .version = FRR_VERSION,\n" -" .description = \"zebra FPM (Forwarding Plane Manager) module\",\n" -" .init = zebra_fpm_module_init,\n" -");\n" -"```" -msgstr "" -"```c\n" -"static int zebra_fpm_module_init(void)\n" -"{\n" -" hook_register(rib_update, zfpm_trigger_update);\n" -" hook_register(zebra_rmac_update, zfpm_trigger_rmac_update);\n" -" hook_register(frr_late_init, zfpm_init);\n" -" hook_register(frr_early_fini, zfpm_fini);\n" -" return 0;\n" -"}\n" -"\n" -"FRR_MODULE_SETUP(.name = \"zebra_fpm\", .version = FRR_VERSION,\n" -" .description = \"zebra FPM (Forwarding Plane Manager) module\",\n" -" .init = zebra_fpm_module_init,\n" -");\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:673 -msgid "" -"当`rib_update`钩子被调用时,`zfpm_trigger_update`函数会被调用,它会将路由变更" -"信息再次放入fpm的转发队列中,并触发写操作:" -msgstr "" -"When the `rib_update` hook is called, the `zfpm_trigger_update` function is " -"invoked, which puts the route change information into the fpm forwarding " -"queue again and triggers a write operation: the" - -#: src/5-2-2-bgp-route-update-workflow.md:675 -msgid "" -"```c\n" -"static int zfpm_trigger_update(struct route_node *rn, const char *reason)\n" -"{\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" // Queue the update request\n" -" dest = rib_dest_from_rnode(rn);\n" -" SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);\n" -" TAILQ_INSERT_TAIL(&zfpm_g->dest_q, dest, fpm_q_entries);\n" -" ...\n" -"\n" -" zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static inline void zfpm_write_on(void) {\n" -" thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, &zfpm_g-" -">t_write);\n" -"}\n" -"```" -msgstr "" -"```c\n" -"static int zfpm_trigger_update(struct route_node *rn, const char *reason)\n" -"{\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" // Queue the update request\n" -" dest = rib_dest_from_rnode(rn);\n" -" SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);\n" -" TAILQ_INSERT_TAIL(&zfpm_g->dest_q, dest, fpm_q_entries);\n" -" ...\n" -"\n" -" zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static inline void zfpm_write_on(void) {\n" -" thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, &zfpm_g-" -">t_write);\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:696 -msgid "" -"这个写操作的回调就会将其从队列中取出,并转换成FPM的消息格式,然后通过本地" -"Socket转发给其他进程:" -msgstr "" -"The callback for this write operation then takes it out of the queue and " -"converts it into the FPM message format, which is then forwarded to other " -"processes via the local socket: the" - -#: src/5-2-2-bgp-route-update-workflow.md:698 -msgid "" -"```c\n" -"static int zfpm_write_cb(struct thread *thread)\n" -"{\n" -" struct stream *s;\n" -"\n" -" do {\n" -" int bytes_to_write, bytes_written;\n" -" s = zfpm_g->obuf;\n" -"\n" -" // Convert route info to buffer here.\n" -" if (stream_empty(s)) zfpm_build_updates();\n" -"\n" -" // Write to socket until we don' have anything to write or cannot " -"write anymore (partial write).\n" -" bytes_to_write = stream_get_endp(s) - stream_get_getp(s);\n" -" bytes_written = write(zfpm_g->sock, stream_pnt(s), bytes_to_write);\n" -" ...\n" -" } while (1);\n" -"\n" -" if (zfpm_writes_pending()) zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static void zfpm_build_updates(void)\n" -"{\n" -" struct stream *s = zfpm_g->obuf;\n" -" do {\n" -" /* Stop processing the queues if zfpm_g->obuf is full or we do not " -"have more updates to process */\n" -" if (zfpm_build_mac_updates() == FPM_WRITE_STOP) break;\n" -" if (zfpm_build_route_updates() == FPM_WRITE_STOP) break;\n" -" } while (zfpm_updates_pending());\n" -"}\n" -"```" -msgstr "" -"```c\n" -"static int zfpm_write_cb(struct thread *thread)\n" -"{\n" -" struct stream *s;\n" -"\n" -" do {\n" -" int bytes_to_write, bytes_written;\n" -" s = zfpm_g->obuf;\n" -"\n" -" // Convert route info to buffer here.\n" -" if (stream_empty(s)) zfpm_build_updates();\n" -"\n" -" // Write to socket until we don' have anything to write or cannot " -"write anymore (partial write).\n" -" bytes_to_write = stream_get_endp(s) - stream_get_getp(s);\n" -" bytes_written = write(zfpm_g->sock, stream_pnt(s), bytes_to_write);\n" -" ...\n" -" } while (1);\n" -"\n" -" if (zfpm_writes_pending()) zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static void zfpm_build_updates(void)\n" -"{\n" -" struct stream *s = zfpm_g->obuf;\n" -" do {\n" -" /* Stop processing the queues if zfpm_g->obuf is full or we do not " -"have more updates to process */\n" -" if (zfpm_build_mac_updates() == FPM_WRITE_STOP) break;\n" -" if (zfpm_build_route_updates() == FPM_WRITE_STOP) break;\n" -" } while (zfpm_updates_pending());\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:731 -msgid "到此,FRR的工作就完成了。" -msgstr "At this point, FRR's work is complete." - -#: src/5-2-2-bgp-route-update-workflow.md:733 -msgid "## SONiC路由变更工作流" -msgstr "## SONiC Routing Change Workflow" - -#: src/5-2-2-bgp-route-update-workflow.md:735 -msgid "" -"当FRR变更内核路由配置后,SONiC便会收到来自Netlink和FPM的通知,然后进行一系列" -"操作将其下发给ASIC,其主要流程如下:" -msgstr "" -"When the FRR changes the kernel routing configuration, SONiC receives a " -"notification from Netlink and FPM, and then performs a series of operations " -"to send it down to the ASIC, the main process of which is as follows:" - -#: src/5-2-2-bgp-route-update-workflow.md:737 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant K as Linux Kernel\n" -" box purple bgp容器\n" -" participant Z as zebra\n" -" participant FPM as fpmsyncd\n" -" end\n" -" box darkred database容器\n" -" participant R as Redis\n" -" end\n" -" box darkblue swss容器\n" -" participant OA as orchagent\n" -" end\n" -" box darkgreen syncd容器\n" -" participant SD as syncd\n" -" end\n" -" participant A as ASIC\n" -"\n" -" K->>FPM: 内核路由变更时通过Netlink发送通知\n" -" Z->>FPM: 通过FPM接口和Netlink
消息格式发送路由变更通知\n" -"\n" -" FPM->>R: 通过ProducerStateTable
将路由变更信息写入
APPL_DB\n" -"\n" -" R->>OA: 通过ConsumerStateTable
接收路由变更信息\n" -" \n" -" OA->>OA: 处理路由变更信息
生成SAI路由对象\n" -" OA->>SD: 通过ProducerTable
或者ZMQ将SAI路由对象
发给syncd\n" -"\n" -" SD->>R: 接收SAI路由对象,写入ASIC_DB\n" -" SD->>A: 通过SAI接口
配置ASIC\n" -"```" -msgstr "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant K as Linux Kernel\n" -" box purple bgp容器\n" -" participant Z as zebra\n" -" participant FPM as fpmsyncd\n" -" end\n" -" box darkred database容器\n" -" participant R as Redis\n" -" end\n" -" box darkblue swss容器\n" -" participant OA as orchagent\n" -" end\n" -" box darkgreen syncd容器\n" -" participant SD as syncd\n" -" end\n" -" participant A as ASIC\n" -"\n" -" K->>FPM: 内核路由变更时通过Netlink发送通知\n" -" Z->>FPM: 通过FPM接口和Netlink
消息格式发送路由变更通知\n" -"\n" -" FPM->>R: 通过ProducerStateTable
将路由变更信息写入
APPL_DB\n" -"\n" -" R->>OA: 通过ConsumerStateTable
接收路由变更信息\n" -" \n" -" OA->>OA: 处理路由变更信息
生成SAI路由对象\n" -" OA->>SD: 通过ProducerTable
或者ZMQ将SAI路由对象
发给syncd\n" -"\n" -" SD->>R: 接收SAI路由对象,写入ASIC_DB\n" -" SD->>A: 通过SAI接口
配置ASIC\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:770 -msgid "### fpmsyncd更新Redis中的路由配置" -msgstr "### fpmsyncd updates the routing configuration in Redis" - -#: src/5-2-2-bgp-route-update-workflow.md:772 -msgid "" -"首先,我们从源头看起。`fpmsyncd`在启动的时候便会开始监听FPM和Netlink的事件," -"用于接收路由变更消息:" -msgstr "" -"First, let's look at the source. `fpmsyncd` starts listening for FPM and " -"Netlink events when it starts up, and is used to receive routing change " -"messages:" - -#: src/5-2-2-bgp-route-update-workflow.md:774 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" ...\n" -"\n" -" DBConnector db(\"APPL_DB\", 0);\n" -" RedisPipeline pipeline(&db);\n" -" RouteSync sync(&pipeline);\n" -" \n" -" // Register netlink message handler\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" rtnl_route_read_protocol_names(DefaultRtProtoPath);\n" -" ...\n" -"\n" -" while (true) {\n" -" try {\n" -" // Launching FPM server and wait for zebra to connect.\n" -" FpmLink fpm(&sync);\n" -" ...\n" -"\n" -" fpm.accept();\n" -" ...\n" -" } catch (FpmLink::FpmConnectionClosedException &e) {\n" -" // If connection is closed, keep retrying until it succeeds, " -"before handling any other events.\n" -" cout << \"Connection lost, reconnecting...\" << endl;\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" ...\n" -"\n" -" DBConnector db(\"APPL_DB\", 0);\n" -" RedisPipeline pipeline(&db);\n" -" RouteSync sync(&pipeline);\n" -" \n" -" // Register netlink message handler\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" rtnl_route_read_protocol_names(DefaultRtProtoPath);\n" -" ...\n" -"\n" -" while (true) {\n" -" try {\n" -" // Launching FPM server and wait for zebra to connect.\n" -" FpmLink fpm(&sync);\n" -" ...\n" -"\n" -" fpm.accept();\n" -" ...\n" -" } catch (FpmLink::FpmConnectionClosedException &e) {\n" -" // If connection is closed, keep retrying until it succeeds, " -"before handling any other events.\n" -" cout << \"Connection lost, reconnecting...\" << endl;\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:813 -msgid "" -"这样,所有的路由变更消息都会以Netlink的形式发送给`RouteSync`,其中[EVPN Type " -"5][EVPN]必须以原始消息的形式进行处理,所以会发送给`onMsgRaw`,其他的消息都会" -"统一的发给处理Netlink的`onMsg`回调:(关于Netlink如何接收和处理消息,请移步" -"[4.1.2 Netlink](./4-1-2-netlink.html))" -msgstr "" -"In this way, all route change messages are sent to `RouteSync` as Netlink, " -"where [EVPN Type 5][EVPN] must be processed as raw messages, so they are " -"sent to `onMsgRaw`, and all other messages are sent uniformly to the `onMsg` " -"callback that handles Netlink: (for more information on For more information " -"on how Netlink receives and processes messages, please go to [4.1.2 Netlink]" -"(. /4-1-2-netlink.html))" - -#: src/5-2-2-bgp-route-update-workflow.md:815 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmlink.cpp\n" -"// Called from: FpmLink::readData()\n" -"void FpmLink::processFpmMessage(fpm_msg_hdr_t* hdr)\n" -"{\n" -" size_t msg_len = fpm_msg_len(hdr);\n" -" nlmsghdr *nl_hdr = (nlmsghdr *)fpm_msg_data(hdr);\n" -" ...\n" -"\n" -" /* Read all netlink messages inside FPM message */\n" -" for (; NLMSG_OK (nl_hdr, msg_len); nl_hdr = NLMSG_NEXT(nl_hdr, " -"msg_len))\n" -" {\n" -" /*\n" -" * EVPN Type5 Add Routes need to be process in Raw mode as they " -"contain\n" -" * RMAC, VLAN and L3VNI information.\n" -" * Where as all other route will be using rtnl api to extract " -"information\n" -" * from the netlink msg.\n" -" */\n" -" bool isRaw = isRawProcessing(nl_hdr);\n" -" \n" -" nl_msg *msg = nlmsg_convert(nl_hdr);\n" -" ...\n" -" nlmsg_set_proto(msg, NETLINK_ROUTE);\n" -"\n" -" if (isRaw) {\n" -" /* EVPN Type5 Add route processing */\n" -" /* This will call into onRawMsg() */\n" -" processRawMsg(nl_hdr);\n" -" } else {\n" -" /* This will call into onMsg() */\n" -" NetDispatcher::getInstance().onNetlinkMessage(msg);\n" -" }\n" -"\n" -" nlmsg_free(msg);\n" -" }\n" -"}\n" -"\n" -"void FpmLink::processRawMsg(struct nlmsghdr *h)\n" -"{\n" -" m_routesync->onMsgRaw(h);\n" -"};\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmlink.cpp\n" -"// Called from: FpmLink::readData()\n" -"void FpmLink::processFpmMessage(fpm_msg_hdr_t* hdr)\n" -"{\n" -" size_t msg_len = fpm_msg_len(hdr);\n" -" nlmsghdr *nl_hdr = (nlmsghdr *)fpm_msg_data(hdr);\n" -" ...\n" -"\n" -" /* Read all netlink messages inside FPM message */\n" -" for (; NLMSG_OK (nl_hdr, msg_len); nl_hdr = NLMSG_NEXT(nl_hdr, " -"msg_len))\n" -" {\n" -" /*\n" -" * EVPN Type5 Add Routes need to be process in Raw mode as they " -"contain\n" -" * RMAC, VLAN and L3VNI information.\n" -" * Where as all other route will be using rtnl api to extract " -"information\n" -" * from the netlink msg.\n" -" */\n" -" bool isRaw = isRawProcessing(nl_hdr);\n" -" \n" -" nl_msg *msg = nlmsg_convert(nl_hdr);\n" -" ...\n" -" nlmsg_set_proto(msg, NETLINK_ROUTE);\n" -"\n" -" if (isRaw) {\n" -" /* EVPN Type5 Add route processing */\n" -" /* This will call into onRawMsg() */\n" -" processRawMsg(nl_hdr);\n" -" } else {\n" -" /* This will call into onMsg() */\n" -" NetDispatcher::getInstance().onNetlinkMessage(msg);\n" -" }\n" -"\n" -" nlmsg_free(msg);\n" -" }\n" -"}\n" -"\n" -"void FpmLink::processRawMsg(struct nlmsghdr *h)\n" -"{\n" -" m_routesync->onMsgRaw(h);\n" -"};\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:858 -msgid "" -"接着,`RouteSync`收到路由变更的消息之后,会在`onMsg`和`onMsgRaw`中进行判断和" -"分发:" -msgstr "" -"Then, after `RouteSync` receives the route change message, it will determine " -"and distribute it in `onMsg` and `onMsgRaw`:" - -#: src/5-2-2-bgp-route-update-workflow.md:860 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onMsgRaw(struct nlmsghdr *h)\n" -"{\n" -" if ((h->nlmsg_type != RTM_NEWROUTE) && (h->nlmsg_type != RTM_DELROUTE))\n" -" return;\n" -" ...\n" -" onEvpnRouteMsg(h, len);\n" -"}\n" -"\n" -"void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // Refill Netlink cache here\n" -" ...\n" -"\n" -" struct rtnl_route *route_obj = (struct rtnl_route *)obj;\n" -" auto family = rtnl_route_get_family(route_obj);\n" -" if (family == AF_MPLS) {\n" -" onLabelRouteMsg(nlmsg_type, obj);\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" unsigned int master_index = rtnl_route_get_table(route_obj);\n" -" char master_name[IFNAMSIZ] = {0};\n" -" if (master_index) {\n" -" /* If the master device name starts with VNET_PREFIX, it is a VNET " -"route.\n" -" The VNET name is exactly the name of the associated master device. " -"*/\n" -" getIfName(master_index, master_name, IFNAMSIZ);\n" -" if (string(master_name).find(VNET_PREFIX) == 0) {\n" -" onVnetRouteMsg(nlmsg_type, obj, string(master_name));\n" -" }\n" -"\n" -" /* Otherwise, it is a regular route (include VRF route). */\n" -" else {\n" -" onRouteMsg(nlmsg_type, obj, master_name);\n" -" }\n" -" } else {\n" -" onRouteMsg(nlmsg_type, obj, NULL);\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onMsgRaw(struct nlmsghdr *h)\n" -"{\n" -" if ((h->nlmsg_type != RTM_NEWROUTE) && (h->nlmsg_type != RTM_DELROUTE))\n" -" return;\n" -" ...\n" -" onEvpnRouteMsg(h, len);\n" -"}\n" -"\n" -"void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // Refill Netlink cache here\n" -" ...\n" -"\n" -" struct rtnl_route *route_obj = (struct rtnl_route *)obj;\n" -" auto family = rtnl_route_get_family(route_obj);\n" -" if (family == AF_MPLS) {\n" -" onLabelRouteMsg(nlmsg_type, obj);\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" unsigned int master_index = rtnl_route_get_table(route_obj);\n" -" char master_name[IFNAMSIZ] = {0};\n" -" if (master_index) {\n" -" /* If the master device name starts with VNET_PREFIX, it is a VNET " -"route.\n" -" The VNET name is exactly the name of the associated master device. " -"*/\n" -" getIfName(master_index, master_name, IFNAMSIZ);\n" -" if (string(master_name).find(VNET_PREFIX) == 0) {\n" -" onVnetRouteMsg(nlmsg_type, obj, string(master_name));\n" -" }\n" -"\n" -" /* Otherwise, it is a regular route (include VRF route). */\n" -" else {\n" -" onRouteMsg(nlmsg_type, obj, master_name);\n" -" }\n" -" } else {\n" -" onRouteMsg(nlmsg_type, obj, NULL);\n" -" }\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:903 -msgid "" -"从上面的代码中,我们可以看到这里会有四种不同的路由处理入口,这些不同的路由会" -"被最终通过各自的[ProducerStateTable](./4-2-2-redis-messaging-layer." -"html#producerstatetable--consumerstatetable)写入到`APPL_DB`中的不同的Table" -"中:" -msgstr "" -"From the code above, we can see that there will be four different route " -"processing entries here, and these different routes will be eventually " -"written to different Tables in `APPL_DB` via their respective " -"[ProducerStateTable](. /4-2-2-redis-messaging-layer.html#producerstatetable--" -"consumerstatetable) to different Tables in `APPL_DB`:" - -#: src/5-2-2-bgp-route-update-workflow.md:905 -msgid "" -"| 路由类型 | 处理函数 | Table |\n" -"| --- | --- | --- |\n" -"| MPLS | `onLabelRouteMsg` | LABLE_ROUTE_TABLE |\n" -"| Vnet VxLan Tunnel Route | `onVnetRouteMsg` | VNET_ROUTE_TUNNEL_TABLE |\n" -"| 其他Vnet路由 | `onVnetRouteMsg` | VNET_ROUTE_TABLE |\n" -"| EVPN Type 5 | `onEvpnRouteMsg` | ROUTE_TABLE |\n" -"| 普通路由 | `onRouteMsg` | ROUTE_TABLE |" -msgstr "" -"| Routing Type | Handler | Table |\n" -"| --- | --- | --- |\n" -"| MPLS | `onLabelRouteMsg` | LABLE_ROUTE_TABLE |\n" -"| Vnet VxLan Tunnel Route | `onVnetRouteMsg` | VNET_ROUTE_TUNNEL_TABLE |\n" -"| Other Vnet Routes | `onVnetRouteMsg` | VNET_ROUTE_TABLE |\n" -"| EVPN Type 5 | `onEvpnRouteMsg` | ROUTE_TABLE |\n" -"| ROUTE_TABLE | `onRouteMsg` | ROUTE_TABLE |" - -#: src/5-2-2-bgp-route-update-workflow.md:913 -msgid "" -"这里以普通路由来举例子,其他的函数的实现虽然有所不同,但是主体的思路是一样" -"的:" -msgstr "" -"Here is an example of ordinary routing, the implementation of other " -"functions is different, but the main idea is the same: the" - -#: src/5-2-2-bgp-route-update-workflow.md:915 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char " -"*vrf)\n" -"{\n" -" // Parse route info from nl_object here.\n" -" ...\n" -" \n" -" // Get nexthop lists\n" -" string gw_list;\n" -" string intf_list;\n" -" string mpls_list;\n" -" getNextHopList(route_obj, gw_list, mpls_list, intf_list);\n" -" ...\n" -"\n" -" // Build route info here, including protocol, interface, next hops, " -"MPLS, weights etc.\n" -" vector fvVector;\n" -" FieldValueTuple proto(\"protocol\", proto_str);\n" -" FieldValueTuple gw(\"nexthop\", gw_list);\n" -" ...\n" -"\n" -" fvVector.push_back(proto);\n" -" fvVector.push_back(gw);\n" -" ...\n" -" \n" -" // Push to ROUTE_TABLE via ProducerStateTable.\n" -" m_routeTable.set(destipprefix, fvVector);\n" -" SWSS_LOG_DEBUG(\"RouteTable set msg: %s %s %s %s\", destipprefix, " -"gw_list.c_str(), intf_list.c_str(), mpls_list.c_str());\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char " -"*vrf)\n" -"{\n" -" // Parse route info from nl_object here.\n" -" ...\n" -" \n" -" // Get nexthop lists\n" -" string gw_list;\n" -" string intf_list;\n" -" string mpls_list;\n" -" getNextHopList(route_obj, gw_list, mpls_list, intf_list);\n" -" ...\n" -"\n" -" // Build route info here, including protocol, interface, next hops, " -"MPLS, weights etc.\n" -" vector fvVector;\n" -" FieldValueTuple proto(\"protocol\", proto_str);\n" -" FieldValueTuple gw(\"nexthop\", gw_list);\n" -" ...\n" -"\n" -" fvVector.push_back(proto);\n" -" fvVector.push_back(gw);\n" -" ...\n" -" \n" -" // Push to ROUTE_TABLE via ProducerStateTable.\n" -" m_routeTable.set(destipprefix, fvVector);\n" -" SWSS_LOG_DEBUG(\"RouteTable set msg: %s %s %s %s\", destipprefix, " -"gw_list.c_str(), intf_list.c_str(), mpls_list.c_str());\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:946 -msgid "### orchagent处理路由配置变化" -msgstr "### orchagent handles routing configuration changes" - -#: src/5-2-2-bgp-route-update-workflow.md:948 -msgid "" -"接下来,这些路由信息会来到orchagent。在orchagent启动的时候,它会创建好" -"`VNetRouteOrch`和`RouteOrch`对象,这两个对象分别用来监听和处理Vnet相关路由和" -"EVPN/普通路由:" -msgstr "" -"Next, this routing information comes to the orchagent. when the orchagent " -"starts, it creates `VNetRouteOrch` and `RouteOrch` objects, which are used " -"to listen to and process Vnet-related routes and EVPN/general routes, " -"respectively:" - -#: src/5-2-2-bgp-route-update-workflow.md:950 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"bool OrchDaemon::init()\n" -"{\n" -" ...\n" -"\n" -" vector vnet_tables = { APP_VNET_RT_TABLE_NAME, " -"APP_VNET_RT_TUNNEL_TABLE_NAME };\n" -" VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, " -"vnet_orch);\n" -" ...\n" -"\n" -" const int routeorch_pri = 5;\n" -" vector route_tables = {\n" -" { APP_ROUTE_TABLE_NAME, routeorch_pri },\n" -" { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri }\n" -" };\n" -" gRouteOrch = new RouteOrch(m_applDb, route_tables, gSwitchOrch, " -"gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch, gSrv6Orch);\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"bool OrchDaemon::init()\n" -"{\n" -" ...\n" -"\n" -" vector vnet_tables = { APP_VNET_RT_TABLE_NAME, " -"APP_VNET_RT_TUNNEL_TABLE_NAME };\n" -" VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, " -"vnet_orch);\n" -" ...\n" -"\n" -" const int routeorch_pri = 5;\n" -" vector route_tables = {\n" -" { APP_ROUTE_TABLE_NAME, routeorch_pri },\n" -" { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri }\n" -" };\n" -" gRouteOrch = new RouteOrch(m_applDb, route_tables, gSwitchOrch, " -"gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch, gSrv6Orch);\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:970 -msgid "" -"所有Orch对象的消息处理入口都是`doTask`,这里`RouteOrch`和`VNetRouteOrch`也不" -"例外,这里我们以`RouteOrch`为例子,看看它是如何处理路由变化的。" -msgstr "" -"The message processing entry for all Orch objects is `doTask`, here " -"`RouteOrch` and `VNetRouteOrch` are no exception, here we take `RouteOrch` " -"as an example to see how it handles route changes." - -#: src/5-2-2-bgp-route-update-workflow.md:972 -msgid "" -"```admonish note\n" -"从`RouteOrch`上,我们可以真切的感受到为什么这些类被命名为`Orch`。`RouteOrch`" -"有2500多行,其中会有和很多其他Orch的交互,以及各种各样的细节…… 代码是相对难" -"读,请大家读的时候一定保持耐心。\n" -"```" -msgstr "" -"```admonish note\n" -"从`RouteOrch`上,我们可以真切的感受到为什么这些类被命名为`Orch`。`RouteOrch`" -"有2500多行,其中会有和很多其他Orch的交互,以及各种各样的细节…… 代码是相对难" -"读,请大家读的时候一定保持耐心。\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:976 -msgid "`RouteOrch`在处理路由消息的时候有几点需要注意:" -msgstr "`RouteOrch` has a few points to note when processing routing messages:" - -#: src/5-2-2-bgp-route-update-workflow.md:978 -msgid "" -"- 从上面`init`函数,我们可以看到`RouteOrch`不仅会管理普通路由,还会管理MPLS路" -"由,这两种路由的处理逻辑是不一样的,所以在下面的代码中,为了简化,我们只展示" -"普通路由的处理逻辑。\n" -"- 因为`ProducerStateTable`在传递和接受消息的时候都是批量传输的,所以," -"`RouteOrch`在处理消息的时候,也是批量处理的。为了支持批量处理,`RouteOrch`会" -"借用`EntityBulker gRouteBulker`将需要改动的SAI路由对象缓存起" -"来,然后在`doTask()`函数的最后,一次性将这些路由对象的改动应用到SAI中。\n" -"- 路由的操作会需要很多其他的信息,比如每个Port的状态,每个Neighbor的状态,每" -"个VRF的状态等等。为了获取这些信息,`RouteOrch`会与其他的Orch对象进行交互,比" -"如`PortOrch`,`NeighOrch`,`VRFOrch`等等。" -msgstr "" -"- From the `init` function above, we can see that `RouteOrch` will manage " -"not only normal routes but also MPLS routes. The processing logic of these " -"two routes is different, so in the following code, for simplicity, we only " -"show the processing logic of normal routes.\n" -"- Because `ProducerStateTable` is transmitted in bulk when delivering and " -"receiving messages, `RouteOrch` is also processed in bulk when processing " -"messages. To support bulk processing, `RouteOrch` borrows " -"`EntityBulker gRouteBulker` to cache SAI routing objects " -"that need to be changed, and then applies the changes to those routing " -"objects to the SAI at once at the end of the `doTask()` function.\n" -"- The routing operation will require a lot of other information, such as the " -"state of each Port, the state of each Neighbor, the state of each VRF, and " -"so on. To get this information, `RouteOrch` will interact with other Orch " -"objects, such as `PortOrch`, `NeighOrch`, `VRFOrch`, etc." - -#: src/5-2-2-bgp-route-update-workflow.md:982 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"void RouteOrch::doTask(Consumer& consumer)\n" -"{\n" -" // Calling PortOrch to make sure all ports are ready before processing " -"route messages.\n" -" if (!gPortsOrch->allPortsReady()) { return; }\n" -"\n" -" // Call doLabelTask() instead, if the incoming messages are from MPLS " -"messages. Otherwise, move on as regular routes.\n" -" ...\n" -"\n" -" /* Default handling is for ROUTE_TABLE (regular routes) */\n" -" auto it = consumer.m_toSync.begin();\n" -" while (it != consumer.m_toSync.end()) {\n" -" // Add or remove routes with a route bulker\n" -" while (it != consumer.m_toSync.end())\n" -" {\n" -" KeyOpFieldsValuesTuple t = it->second;\n" -"\n" -" // Parse route operation from the incoming message here.\n" -" string key = kfvKey(t);\n" -" string op = kfvOp(t);\n" -" ...\n" -"\n" -" // resync application:\n" -" // - When routeorch receives 'resync' message (key = \"resync\", " -"op = \"SET\"), it marks all current routes as dirty\n" -" // and waits for 'resync complete' message. For all newly " -"received routes, if they match current dirty routes,\n" -" // it unmarks them dirty.\n" -" // - After receiving 'resync complete' (key = \"resync\", op != " -"\"SET\") message, it creates all newly added routes\n" -" // and removes all dirty routes.\n" -" ...\n" -"\n" -" // Parsing VRF and IP prefix from the incoming message here.\n" -" ...\n" -"\n" -" // Process regular route operations.\n" -" if (op == SET_COMMAND)\n" -" {\n" -" // Parse and validate route attributes from the incoming " -"message here.\n" -" string ips;\n" -" string aliases;\n" -" ...\n" -"\n" -" // If the nexthop_group is empty, create the next hop group " -"key based on the IPs and aliases. \n" -" // Otherwise, get the key from the NhgOrch. The result will " -"be stored in the \"nhg\" variable below.\n" -" NextHopGroupKey& nhg = ctx.nhg;\n" -" ...\n" -" if (nhg_index.empty())\n" -" {\n" -" // Here the nexthop_group is empty, so we create the " -"next hop group key based on the IPs and aliases.\n" -" ...\n" -"\n" -" string nhg_str = \"\";\n" -" if (blackhole) {\n" -" nhg = NextHopGroupKey();\n" -" } else if (srv6_nh == true) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" } else if (overlay_nh == false) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, weights);\n" -" } else {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" }\n" -" }\n" -" else\n" -" {\n" -" // Here we have a nexthop_group, so we get the key from " -"the NhgOrch.\n" -" const NhgBase& nh_group = getNhg(nhg_index);\n" -" nhg = nh_group.getNhgKey();\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" // Now we start to create the SAI route entry.\n" -" if (nhg.getSize() == 1 && nhg.hasIntfNextHop())\n" -" {\n" -" // Skip certain routes, such as not valid, directly " -"routes to tun0, linklocal or multicast routes, etc.\n" -" ...\n" -"\n" -" // Create SAI route entry in addRoute function.\n" -" if (addRoute(ctx, nhg)) it = consumer.m_toSync." -"erase(it);\n" -" else it++;\n" -" }\n" -"\n" -" /*\n" -" * Check if the route does not exist or needs to be updated " -"or\n" -" * if the route is using a temporary next hop group owned " -"by\n" -" * NhgOrch.\n" -" */\n" -" else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() " -"||\n" -" m_syncdRoutes.at(vrf_id).find(ip_prefix) == " -"m_syncdRoutes.at(vrf_id).end() ||\n" -" m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, " -"ctx.nhg_index) ||\n" -" gRouteBulker.bulk_entry_pending_removal(route_entry) ||\n" -" ctx.using_temp_nhg)\n" -" {\n" -" if (addRoute(ctx, nhg)) it = consumer.m_toSync." -"erase(it);\n" -" else it++;\n" -" }\n" -" ...\n" -" }\n" -" // Handle other ops, like DEL_COMMAND for route deletion, etc.\n" -" ...\n" -" }\n" -"\n" -" // Flush the route bulker, so routes will be written to syncd and " -"ASIC\n" -" gRouteBulker.flush();\n" -"\n" -" // Go through the bulker results.\n" -" // Handle SAI failures, update neighbors, counters, send " -"notifications in add/removeRoutePost functions.\n" -" ... \n" -"\n" -" /* Remove next hop group if the reference count decreases to zero " -"*/\n" -" ...\n" -" }\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"void RouteOrch::doTask(Consumer& consumer)\n" -"{\n" -" // Calling PortOrch to make sure all ports are ready before processing " -"route messages.\n" -" if (!gPortsOrch->allPortsReady()) { return; }\n" -"\n" -" // Call doLabelTask() instead, if the incoming messages are from MPLS " -"messages. Otherwise, move on as regular routes.\n" -" ...\n" -"\n" -" /* Default handling is for ROUTE_TABLE (regular routes) */\n" -" auto it = consumer.m_toSync.begin();\n" -" while (it != consumer.m_toSync.end()) {\n" -" // Add or remove routes with a route bulker\n" -" while (it != consumer.m_toSync.end())\n" -" {\n" -" KeyOpFieldsValuesTuple t = it->second;\n" -"\n" -" // Parse route operation from the incoming message here.\n" -" string key = kfvKey(t);\n" -" string op = kfvOp(t);\n" -" ...\n" -"\n" -" // resync application:\n" -" // - When routeorch receives 'resync' message (key = \"resync\", " -"op = \"SET\"), it marks all current routes as dirty\n" -" // and waits for 'resync complete' message. For all newly " -"received routes, if they match current dirty routes,\n" -" // it unmarks them dirty.\n" -" // - After receiving 'resync complete' (key = \"resync\", op != " -"\"SET\") message, it creates all newly added routes\n" -" // and removes all dirty routes.\n" -" ...\n" -"\n" -" // Parsing VRF and IP prefix from the incoming message here.\n" -" ...\n" -"\n" -" // Process regular route operations.\n" -" if (op == SET_COMMAND)\n" -" {\n" -" // Parse and validate route attributes from the incoming " -"message here.\n" -" string ips;\n" -" string aliases;\n" -" ...\n" -"\n" -" // If the nexthop_group is empty, create the next hop group " -"key based on the IPs and aliases. \n" -" // Otherwise, get the key from the NhgOrch. The result will " -"be stored in the \"nhg\" variable below.\n" -" NextHopGroupKey& nhg = ctx.nhg;\n" -" ...\n" -" if (nhg_index.empty())\n" -" {\n" -" // Here the nexthop_group is empty, so we create the " -"next hop group key based on the IPs and aliases.\n" -" ...\n" -"\n" -" string nhg_str = \"\";\n" -" if (blackhole) {\n" -" nhg = NextHopGroupKey();\n" -" } else if (srv6_nh == true) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" } else if (overlay_nh == false) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, weights);\n" -" } else {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" }\n" -" }\n" -" else\n" -" {\n" -" // Here we have a nexthop_group, so we get the key from " -"the NhgOrch.\n" -" const NhgBase& nh_group = getNhg(nhg_index);\n" -" nhg = nh_group.getNhgKey();\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" // Now we start to create the SAI route entry.\n" -" if (nhg.getSize() == 1 && nhg.hasIntfNextHop())\n" -" {\n" -" // Skip certain routes, such as not valid, directly " -"routes to tun0, linklocal or multicast routes, etc.\n" -" ...\n" -"\n" -" // Create SAI route entry in addRoute function.\n" -" if (addRoute(ctx, nhg)) it = consumer.m_toSync." -"erase(it);\n" -" else it++;\n" -" }\n" -"\n" -" /*\n" -" * Check if the route does not exist or needs to be updated " -"or\n" -" * if the route is using a temporary next hop group owned " -"by\n" -" * NhgOrch.\n" -" */\n" -" else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() " -"||\n" -" m_syncdRoutes.at(vrf_id).find(ip_prefix) == " -"m_syncdRoutes.at(vrf_id).end() ||\n" -" m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, " -"ctx.nhg_index) ||\n" -" gRouteBulker.bulk_entry_pending_removal(route_entry) ||\n" -" ctx.using_temp_nhg)\n" -" {\n" -" if (addRoute(ctx, nhg)) it = consumer.m_toSync." -"erase(it);\n" -" else it++;\n" -" }\n" -" ...\n" -" }\n" -" // Handle other ops, like DEL_COMMAND for route deletion, etc.\n" -" ...\n" -" }\n" -"\n" -" // Flush the route bulker, so routes will be written to syncd and " -"ASIC\n" -" gRouteBulker.flush();\n" -"\n" -" // Go through the bulker results.\n" -" // Handle SAI failures, update neighbors, counters, send " -"notifications in add/removeRoutePost functions.\n" -" ... \n" -"\n" -" /* Remove next hop group if the reference count decreases to zero " -"*/\n" -" ...\n" -" }\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1100 -msgid "" -"解析完路由操作后,`RouteOrch`会调用`addRoute`或者`removeRoute`函数来创建或者" -"删除路由。这里以添加路由`addRoute`为例子来继续分析。它的逻辑主要分为几个大部" -"分:" -msgstr "" -"After parsing the route operation, `RouteOrch` will call the `addRoute` or " -"`removeRoute` functions to create or remove routes. Here is an example of " -"adding a route `addRoute` to continue the analysis. Its logic is divided " -"into several main parts:" - -#: src/5-2-2-bgp-route-update-workflow.md:1102 -msgid "" -"1. 从NeighOrch中获取下一跳信息,并检查下一跳是否真的可用。\n" -"2. 如果是新路由,或者是重新添加正在等待删除的路由,那么就会创建一个新的SAI路" -"由对象\n" -"3. 如果是已有的路由,那么就更新已有的SAI路由对象" -msgstr "" -"1. Get the next hop information from NeighOrch and check if the next hop is " -"really available.\n" -"2. If it is a new route, or a re-add route that is waiting to be deleted, " -"then a new SAI route object is created\n" -"3. If it is an existing route, then update the existing SAI routing object" - -#: src/5-2-2-bgp-route-update-workflow.md:1106 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey " -"&nextHops)\n" -"{\n" -" // Get nexthop information from NeighOrch.\n" -" // We also need to check PortOrch for inband port, IntfsOrch to ensure " -"the related interface is created and etc.\n" -" ...\n" -" \n" -" // Start to sync the SAI route entry.\n" -" sai_route_entry_t route_entry;\n" -" route_entry.vr_id = vrf_id;\n" -" route_entry.switch_id = gSwitchId;\n" -" copy(route_entry.destination, ipPrefix);\n" -"\n" -" sai_attribute_t route_attr;\n" -" auto& object_statuses = ctx.object_statuses;\n" -" \n" -" // Create a new route entry in this case.\n" -" //\n" -" // In case the entry is already pending removal in the bulk, it would be " -"removed from m_syncdRoutes during the bulk call.\n" -" // Therefore, such entries need to be re-created rather than set " -"attribute.\n" -" if (it_route == m_syncdRoutes.at(vrf_id).end() || gRouteBulker." -"bulk_entry_pending_removal(route_entry)) {\n" -" if (blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_DROP;\n" -" } else {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID;\n" -" route_attr.value.oid = next_hop_id;\n" -" }\n" -"\n" -" /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD " -"*/\n" -" object_statuses.emplace_back();\n" -" sai_status_t status = gRouteBulker.create_entry(&object_statuses." -"back(), &route_entry, 1, &route_attr);\n" -" if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) {\n" -" return false;\n" -" }\n" -" }\n" -" \n" -" // Update existing route entry in this case.\n" -" else {\n" -" // Set the packet action to forward when there was no next hop " -"(dropped) and not pointing to blackhole.\n" -" if (it_route->second.nhg_key.getSize() == 0 && !blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD;\n" -"\n" -" object_statuses.emplace_back();\n" -" gRouteBulker.set_entry_attribute(&object_statuses.back(), " -"&route_entry, &route_attr);\n" -" }\n" -"\n" -" // Only 1 case is listed here as an example. Other cases are handled " -"with similar logic by calling set_entry_attributes as well.\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey " -"&nextHops)\n" -"{\n" -" // Get nexthop information from NeighOrch.\n" -" // We also need to check PortOrch for inband port, IntfsOrch to ensure " -"the related interface is created and etc.\n" -" ...\n" -" \n" -" // Start to sync the SAI route entry.\n" -" sai_route_entry_t route_entry;\n" -" route_entry.vr_id = vrf_id;\n" -" route_entry.switch_id = gSwitchId;\n" -" copy(route_entry.destination, ipPrefix);\n" -"\n" -" sai_attribute_t route_attr;\n" -" auto& object_statuses = ctx.object_statuses;\n" -" \n" -" // Create a new route entry in this case.\n" -" //\n" -" // In case the entry is already pending removal in the bulk, it would be " -"removed from m_syncdRoutes during the bulk call.\n" -" // Therefore, such entries need to be re-created rather than set " -"attribute.\n" -" if (it_route == m_syncdRoutes.at(vrf_id).end() || gRouteBulker." -"bulk_entry_pending_removal(route_entry)) {\n" -" if (blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_DROP;\n" -" } else {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID;\n" -" route_attr.value.oid = next_hop_id;\n" -" }\n" -"\n" -" /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD " -"*/\n" -" object_statuses.emplace_back();\n" -" sai_status_t status = gRouteBulker.create_entry(&object_statuses." -"back(), &route_entry, 1, &route_attr);\n" -" if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) {\n" -" return false;\n" -" }\n" -" }\n" -" \n" -" // Update existing route entry in this case.\n" -" else {\n" -" // Set the packet action to forward when there was no next hop " -"(dropped) and not pointing to blackhole.\n" -" if (it_route->second.nhg_key.getSize() == 0 && !blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD;\n" -"\n" -" object_statuses.emplace_back();\n" -" gRouteBulker.set_entry_attribute(&object_statuses.back(), " -"&route_entry, &route_attr);\n" -" }\n" -"\n" -" // Only 1 case is listed here as an example. Other cases are handled " -"with similar logic by calling set_entry_attributes as well.\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1162 -msgid "" -"在创建和设置好所有的路由后,`RouteOrch`会调用`gRouteBulker.flush()`来将所有的" -"路由写入到ASIC_DB中。`flush()`函数很简单,就是将所有的请求分批次进行处理,默" -"认情况下每一批是1000个,这个定义在`OrchDaemon`中,并通过构造函数传入:" -msgstr "" -"After all the routes have been created and set up, `RouteOrch` calls " -"`gRouteBulker.flush()` to write all the routes to the ASIC_DB. The `flush()` " -"function is simply a function that processes all requests in batches, by " -"default 1000 per batch, this is defined in the `OrchDaemon` and passed in " -"via the constructor:" - -#: src/5-2-2-bgp-route-update-workflow.md:1164 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"#define DEFAULT_MAX_BULK_SIZE 1000\n" -"size_t gMaxBulkSize = DEFAULT_MAX_BULK_SIZE;\n" -"\n" -"// File: src/sonic-swss/orchagent/bulker.h\n" -"template \n" -"class EntityBulker\n" -"{\n" -"public:\n" -" using Ts = SaiBulkerTraits;\n" -" using Te = typename Ts::entry_t;\n" -" ...\n" -"\n" -" void flush()\n" -" {\n" -" // Bulk remove entries\n" -" if (!removing_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" ...\n" -" flush_removing_entries(rs);\n" -" removing_entries.clear();\n" -" }\n" -"\n" -" // Bulk create entries\n" -" if (!creating_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call " -"flush_creating_entries to call SAI batch create API to create\n" -" // the objects in batch.\n" -" std::vector rs;\n" -" std::vector tss;\n" -" std::vector cs;\n" -" \n" -" for (auto const& i: creating_entries) {\n" -" sai_object_id_t *pid = std::get<0>(i);\n" -" auto const& attrs = std::get<1>(i);\n" -" if (*pid == SAI_NULL_OBJECT_ID) {\n" -" rs.push_back(pid);\n" -" tss.push_back(attrs.data());\n" -" cs.push_back((uint32_t)attrs.size());\n" -"\n" -" // Batch create here.\n" -" if (rs.size() >= max_bulk_size) {\n" -" flush_creating_entries(rs, tss, cs);\n" -" }\n" -" }\n" -" }\n" -"\n" -" flush_creating_entries(rs, tss, cs);\n" -" creating_entries.clear();\n" -" }\n" -"\n" -" // Bulk update existing entries\n" -" if (!setting_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" std::vector ts;\n" -" std::vector status_vector;\n" -" ...\n" -" flush_setting_entries(rs, ts, status_vector);\n" -" setting_entries.clear();\n" -" }\n" -" }\n" -"\n" -" sai_status_t flush_creating_entries(\n" -" _Inout_ std::vector &rs,\n" -" _Inout_ std::vector &tss,\n" -" _Inout_ std::vector &cs)\n" -" {\n" -" ...\n" -"\n" -" // Call SAI bulk create API\n" -" size_t count = rs.size();\n" -" std::vector statuses(count);\n" -" sai_status_t status = (*create_entries)((uint32_t)count, rs.data(), " -"cs.data(), tss.data()\n" -" , SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, statuses.data());\n" -"\n" -" // Set results back to input entries and clean up the batch below.\n" -" for (size_t ir = 0; ir < count; ir++) {\n" -" auto& entry = rs[ir];\n" -" sai_status_t *object_status = creating_entries[entry].second;\n" -" if (object_status) {\n" -" *object_status = statuses[ir];\n" -" }\n" -" }\n" -"\n" -" rs.clear(); tss.clear(); cs.clear();\n" -" return status;\n" -" }\n" -"\n" -" // flush_removing_entries and flush_setting_entries are similar to " -"flush_creating_entries, so we omit them here.\n" -" ...\n" -"};\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"#define DEFAULT_MAX_BULK_SIZE 1000\n" -"size_t gMaxBulkSize = DEFAULT_MAX_BULK_SIZE;\n" -"\n" -"// File: src/sonic-swss/orchagent/bulker.h\n" -"template \n" -"class EntityBulker\n" -"{\n" -"public:\n" -" using Ts = SaiBulkerTraits;\n" -" using Te = typename Ts::entry_t;\n" -" ...\n" -"\n" -" void flush()\n" -" {\n" -" // Bulk remove entries\n" -" if (!removing_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" ...\n" -" flush_removing_entries(rs);\n" -" removing_entries.clear();\n" -" }\n" -"\n" -" // Bulk create entries\n" -" if (!creating_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call " -"flush_creating_entries to call SAI batch create API to create\n" -" // the objects in batch.\n" -" std::vector rs;\n" -" std::vector tss;\n" -" std::vector cs;\n" -" \n" -" for (auto const& i: creating_entries) {\n" -" sai_object_id_t *pid = std::get<0>(i);\n" -" auto const& attrs = std::get<1>(i);\n" -" if (*pid == SAI_NULL_OBJECT_ID) {\n" -" rs.push_back(pid);\n" -" tss.push_back(attrs.data());\n" -" cs.push_back((uint32_t)attrs.size());\n" -"\n" -" // Batch create here.\n" -" if (rs.size() >= max_bulk_size) {\n" -" flush_creating_entries(rs, tss, cs);\n" -" }\n" -" }\n" -" }\n" -"\n" -" flush_creating_entries(rs, tss, cs);\n" -" creating_entries.clear();\n" -" }\n" -"\n" -" // Bulk update existing entries\n" -" if (!setting_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" std::vector ts;\n" -" std::vector status_vector;\n" -" ...\n" -" flush_setting_entries(rs, ts, status_vector);\n" -" setting_entries.clear();\n" -" }\n" -" }\n" -"\n" -" sai_status_t flush_creating_entries(\n" -" _Inout_ std::vector &rs,\n" -" _Inout_ std::vector &tss,\n" -" _Inout_ std::vector &cs)\n" -" {\n" -" ...\n" -"\n" -" // Call SAI bulk create API\n" -" size_t count = rs.size();\n" -" std::vector statuses(count);\n" -" sai_status_t status = (*create_entries)((uint32_t)count, rs.data(), " -"cs.data(), tss.data()\n" -" , SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, statuses.data());\n" -"\n" -" // Set results back to input entries and clean up the batch below.\n" -" for (size_t ir = 0; ir < count; ir++) {\n" -" auto& entry = rs[ir];\n" -" sai_status_t *object_status = creating_entries[entry].second;\n" -" if (object_status) {\n" -" *object_status = statuses[ir];\n" -" }\n" -" }\n" -"\n" -" rs.clear(); tss.clear(); cs.clear();\n" -" return status;\n" -" }\n" -"\n" -" // flush_removing_entries and flush_setting_entries are similar to " -"flush_creating_entries, so we omit them here.\n" -" ...\n" -"};\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1259 -msgid "### orchagent中的SAI对象转发" -msgstr "### SAI object forwarding in orchagent" - -#: src/5-2-2-bgp-route-update-workflow.md:1261 -msgid "" -"细心的小伙伴肯定已经发现了奇怪的地方,这里`EntityBulker`怎么看着像在直接调用" -"SAI API呢?难道它们不应该是在syncd中调用的吗?如果我们对传入`EntityBulker`的" -"SAI API对象进行跟踪,我们甚至会找到sai_route_api_t就是SAI的接口,而" -"`orchagent`中还有SAI的初始化代码,如下:" -msgstr "" -"Careful partners must have found a strange place, here `EntityBulker` how to " -"look like in a direct call to the SAI API it? Aren't they supposed to be " -"called in syncd? If we trace the SAI API object passed into `EntityBulker`, " -"we will even find that sai_route_api_t is the SAI interface, and there is " -"SAI initialization code in `orchagent`, as follows:" - -#: src/5-2-2-bgp-route-update-workflow.md:1263 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h\n" -"/**\n" -" * @brief Router entry methods table retrieved with sai_api_query()\n" -" */\n" -"typedef struct _sai_route_api_t\n" -"{\n" -" sai_create_route_entry_fn create_route_entry;\n" -" sai_remove_route_entry_fn remove_route_entry;\n" -" sai_set_route_entry_attribute_fn set_route_entry_attribute;\n" -" sai_get_route_entry_attribute_fn get_route_entry_attribute;\n" -"\n" -" sai_bulk_create_route_entry_fn create_route_entries;\n" -" sai_bulk_remove_route_entry_fn remove_route_entries;\n" -" sai_bulk_set_route_entry_attribute_fn " -"set_route_entries_attribute;\n" -" sai_bulk_get_route_entry_attribute_fn " -"get_route_entries_attribute;\n" -"} sai_route_api_t;\n" -"\n" -"// File: src/sonic-swss/orchagent/saihelper.cpp\n" -"void initSaiApi()\n" -"{\n" -" SWSS_LOG_ENTER();\n" -"\n" -" if (ifstream(CONTEXT_CFG_FILE))\n" -" {\n" -" SWSS_LOG_NOTICE(\"Context config file %s exists\", " -"CONTEXT_CFG_FILE);\n" -" gProfileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = CONTEXT_CFG_FILE;\n" -" }\n" -"\n" -" sai_api_initialize(0, (const sai_service_method_table_t " -"*)&test_services);\n" -" sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api);\n" -" ...\n" -" sai_api_query(SAI_API_NEIGHBOR, (void " -"**)&sai_neighbor_api);\n" -" sai_api_query(SAI_API_NEXT_HOP, (void " -"**)&sai_next_hop_api);\n" -" sai_api_query(SAI_API_NEXT_HOP_GROUP, (void " -"**)&sai_next_hop_group_api);\n" -" sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api);\n" -" ...\n" -"\n" -" sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -" sai_log_set(SAI_API_NEIGHBOR, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_ROUTE, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h\n" -"/**\n" -" * @brief Router entry methods table retrieved with sai_api_query()\n" -" */\n" -"typedef struct _sai_route_api_t\n" -"{\n" -" sai_create_route_entry_fn create_route_entry;\n" -" sai_remove_route_entry_fn remove_route_entry;\n" -" sai_set_route_entry_attribute_fn set_route_entry_attribute;\n" -" sai_get_route_entry_attribute_fn get_route_entry_attribute;\n" -"\n" -" sai_bulk_create_route_entry_fn create_route_entries;\n" -" sai_bulk_remove_route_entry_fn remove_route_entries;\n" -" sai_bulk_set_route_entry_attribute_fn " -"set_route_entries_attribute;\n" -" sai_bulk_get_route_entry_attribute_fn " -"get_route_entries_attribute;\n" -"} sai_route_api_t;\n" -"\n" -"// File: src/sonic-swss/orchagent/saihelper.cpp\n" -"void initSaiApi()\n" -"{\n" -" SWSS_LOG_ENTER();\n" -"\n" -" if (ifstream(CONTEXT_CFG_FILE))\n" -" {\n" -" SWSS_LOG_NOTICE(\"Context config file %s exists\", " -"CONTEXT_CFG_FILE);\n" -" gProfileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = CONTEXT_CFG_FILE;\n" -" }\n" -"\n" -" sai_api_initialize(0, (const sai_service_method_table_t " -"*)&test_services);\n" -" sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api);\n" -" ...\n" -" sai_api_query(SAI_API_NEIGHBOR, (void " -"**)&sai_neighbor_api);\n" -" sai_api_query(SAI_API_NEXT_HOP, (void " -"**)&sai_next_hop_api);\n" -" sai_api_query(SAI_API_NEXT_HOP_GROUP, (void " -"**)&sai_next_hop_group_api);\n" -" sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api);\n" -" ...\n" -"\n" -" sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -" sai_log_set(SAI_API_NEIGHBOR, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_ROUTE, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1311 -msgid "" -"相信大家第一次看到这个代码会感觉到非常的困惑。不过别着急,这其实就是" -"`orchagent`中SAI对象的转发机制。" -msgstr "" -"I believe you will feel very confused when you see this code for the first " -"time. But don't worry, this is actually the forwarding mechanism of SAI " -"objects in `orchagent`." - -#: src/5-2-2-bgp-route-update-workflow.md:1313 -msgid "" -"熟悉RPC的小伙伴一定不会对`proxy-stub`模式感到陌生 —— 利用统一的接口来定义通信" -"双方调用接口,在调用方实现序列化和发送,然后再接收方实现接收,反序列化与分" -"发。这里SONiC的做法也是类似的:利用SAI API本身作为统一的接口,并实现好序列化" -"和发送功能给`orchagent`来调用,然后再`syncd`中实现接收,反序列化与分发功能。" -msgstr "" -"The `proxy-stub` pattern must be familiar to the RPC partners will not be " -"unfamiliar to the `proxy-stub` pattern -- the use of a unified interface to " -"define the communication between the two sides to call the interface, the " -"caller to achieve serialization and send, and then the receiver to achieve " -"the reception, deserialization and distribution. The SONiC approach is " -"similar: use the SAI API itself as a unified interface, and implement the " -"serialization and sending functions for `orchagent` to call, and then " -"implement the receiving, deserialization and distribution functions in " -"`syncd`." - -#: src/5-2-2-bgp-route-update-workflow.md:1315 -msgid "" -"这里,发送端叫做`ClientSai`,实现在`src/sonic-sairedis/lib/ClientSai.*`中。而" -"序列化与反序列化实现在SAI metadata中:`src/sonic-sairedis/meta/sai_serialize." -"h`:" -msgstr "" -"Here, the sender is called `ClientSai`, and the implementation is in `src/" -"sonic-sairedis/lib/ClientSai.*`. And the serialization and deserialization " -"implementation is in the SAI metadata: `src/sonic-sairedis/meta/" -"sai_serialize.h`:" - -#: src/5-2-2-bgp-route-update-workflow.md:1317 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.h\n" -"namespace sairedis\n" -"{\n" -" class ClientSai:\n" -" public sairedis::SaiInterface\n" -" {\n" -" ...\n" -" };\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/meta/sai_serialize.h\n" -"// Serialize\n" -"std::string sai_serialize_route_entry(_In_ const sai_route_entry_t " -"&route_entry);\n" -"...\n" -"\n" -"// Deserialize\n" -"void sai_deserialize_route_entry(_In_ const std::string& s, _In_ " -"sai_route_entry_t &route_entry);\n" -"...\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.h\n" -"namespace sairedis\n" -"{\n" -" class ClientSai:\n" -" public sairedis::SaiInterface\n" -" {\n" -" ...\n" -" };\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/meta/sai_serialize.h\n" -"// Serialize\n" -"std::string sai_serialize_route_entry(_In_ const sai_route_entry_t " -"&route_entry);\n" -"...\n" -"\n" -"// Deserialize\n" -"void sai_deserialize_route_entry(_In_ const std::string& s, _In_ " -"sai_route_entry_t &route_entry);\n" -"...\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1338 -msgid "" -"`orchagent`在编译的时候,会去链接`libsairedis`,从而实现调用SAI API时,对SAI" -"对象进行序列化和发送:" -msgstr "" -"`orchagent` goes to link `libsairedis` at compile time, thus enabling " -"serialization and sending of SAI objects when calling the SAI API:" - -#: src/5-2-2-bgp-route-update-workflow.md:1340 -msgid "" -"```makefile\n" -"# File: src/sonic-swss/orchagent/Makefile.am\n" -"orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -" -"lsaimeta -lsaimetadata -lswsscommon -lzmq\n" -"```" -msgstr "" -"```makefile\n" -"# File: src/sonic-swss/orchagent/Makefile.am\n" -"orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -" -"lsaimeta -lsaimetadata -lswsscommon -lzmq\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1345 -msgid "" -"我们这里用Bulk Create作为例子,来看看`ClientSai`是如何实现序列化和发送的:" -msgstr "" -"We use Bulk Create as an example here to see how `ClientSai` implements " -"serialization and sends:" - -#: src/5-2-2-bgp-route-update-workflow.md:1347 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t switch_id,\n" -" _In_ uint32_t object_count,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Out_ sai_object_id_t *object_id,\n" -" _Out_ sai_status_t *object_statuses)\n" -"{\n" -" MUTEX();\n" -" REDIS_CHECK_API_INITIALIZED();\n" -"\n" -" std::vector serialized_object_ids;\n" -"\n" -" // Server is responsible for generate new OID but for that we need " -"switch ID\n" -" // to be sent to server as well, so instead of sending empty oids we " -"will\n" -" // send switch IDs\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" serialized_object_ids." -"emplace_back(sai_serialize_object_id(switch_id));\n" -" }\n" -" auto status = bulkCreate(object_type, serialized_object_ids, attr_count, " -"attr_list, mode, object_statuses);\n" -"\n" -" // Since user requested create, OID value was created remotely and it " -"was returned in m_lastCreateOids\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" if (object_statuses[idx] == SAI_STATUS_SUCCESS) {\n" -" object_id[idx] = m_lastCreateOids.at(idx);\n" -" } else {\n" -" object_id[idx] = SAI_NULL_OBJECT_ID;\n" -" }\n" -" }\n" -"\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ const std::vector &serialized_object_ids,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Inout_ sai_status_t *object_statuses)\n" -"{\n" -" ...\n" -"\n" -" // Calling SAI serialize APIs to serialize all objects\n" -" std::string str_object_type = sai_serialize_object_type(object_type);\n" -" std::vector entries;\n" -" for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) {\n" -" auto entry = SaiAttributeList::serialize_attr_list(object_type, " -"attr_count[idx], attr_list[idx], false);\n" -" if (entry.empty()) {\n" -" swss::FieldValueTuple null(\"NULL\", \"NULL\");\n" -" entry.push_back(null);\n" -" }\n" -"\n" -" std::string str_attr = Globals::joinFieldValues(entry);\n" -" swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , " -"str_attr);\n" -" entries.push_back(fvtNoStatus);\n" -" }\n" -" std::string key = str_object_type + \":\" + std::to_string(entries." -"size());\n" -"\n" -" // Send to syncd via the communication channel.\n" -" m_communicationChannel->set(key, entries, " -"REDIS_ASIC_STATE_COMMAND_BULK_CREATE);\n" -"\n" -" // Wait for response from syncd.\n" -" return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, " -"(uint32_t)serialized_object_ids.size(), object_statuses);\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t switch_id,\n" -" _In_ uint32_t object_count,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Out_ sai_object_id_t *object_id,\n" -" _Out_ sai_status_t *object_statuses)\n" -"{\n" -" MUTEX();\n" -" REDIS_CHECK_API_INITIALIZED();\n" -"\n" -" std::vector serialized_object_ids;\n" -"\n" -" // Server is responsible for generate new OID but for that we need " -"switch ID\n" -" // to be sent to server as well, so instead of sending empty oids we " -"will\n" -" // send switch IDs\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" serialized_object_ids." -"emplace_back(sai_serialize_object_id(switch_id));\n" -" }\n" -" auto status = bulkCreate(object_type, serialized_object_ids, attr_count, " -"attr_list, mode, object_statuses);\n" -"\n" -" // Since user requested create, OID value was created remotely and it " -"was returned in m_lastCreateOids\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" if (object_statuses[idx] == SAI_STATUS_SUCCESS) {\n" -" object_id[idx] = m_lastCreateOids.at(idx);\n" -" } else {\n" -" object_id[idx] = SAI_NULL_OBJECT_ID;\n" -" }\n" -" }\n" -"\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ const std::vector &serialized_object_ids,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Inout_ sai_status_t *object_statuses)\n" -"{\n" -" ...\n" -"\n" -" // Calling SAI serialize APIs to serialize all objects\n" -" std::string str_object_type = sai_serialize_object_type(object_type);\n" -" std::vector entries;\n" -" for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) {\n" -" auto entry = SaiAttributeList::serialize_attr_list(object_type, " -"attr_count[idx], attr_list[idx], false);\n" -" if (entry.empty()) {\n" -" swss::FieldValueTuple null(\"NULL\", \"NULL\");\n" -" entry.push_back(null);\n" -" }\n" -"\n" -" std::string str_attr = Globals::joinFieldValues(entry);\n" -" swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , " -"str_attr);\n" -" entries.push_back(fvtNoStatus);\n" -" }\n" -" std::string key = str_object_type + \":\" + std::to_string(entries." -"size());\n" -"\n" -" // Send to syncd via the communication channel.\n" -" m_communicationChannel->set(key, entries, " -"REDIS_ASIC_STATE_COMMAND_BULK_CREATE);\n" -"\n" -" // Wait for response from syncd.\n" -" return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, " -"(uint32_t)serialized_object_ids.size(), object_statuses);\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1418 -msgid "" -"最终,`ClientSai`会调用`m_communicationChannel->set()`,将序列化后的SAI对象发" -"送给`syncd`。而这个Channel,在202106版本之前,就是[基于Redis的ProducerTable]" -"(https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/" -"RedisChannel.h)了。可能是基于效率的考虑,从202111版本开始,这个Channel已经更" -"改为[ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/" -"ZeroMQChannel.h)了。" -msgstr "" -"Eventually, `ClientSai` will call `m_communicationChannel->set()` to send " -"the serialized SAI object to `syncd`. And this Channel, before version " -"202106, was the [Redis-based ProducerTable](https://github.com/sonic-net/" -"sonic-sairedis/blob/202106/lib/inc/RedisChannel.h). This Channel has been " -"changed to [ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/" -"ZeroMQChannel.h) since version 202111, probably due to efficiency " -"considerations." - -#: src/5-2-2-bgp-route-update-workflow.md:1420 -msgid "" -"```cpp\n" -"// File: https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/" -"RedisChannel.h\n" -"class RedisChannel: public Channel\n" -"{\n" -" ...\n" -"\n" -" /**\n" -" * @brief Asic state channel.\n" -" *\n" -" * Used to sent commands like create/remove/set/get to syncd.\n" -" */\n" -" std::shared_ptr m_asicState;\n" -"\n" -" ...\n" -"};\n" -"\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" m_communicationChannel = std::make_shared(\n" -" cc->m_zmqEndpoint,\n" -" cc->m_zmqNtfEndpoint,\n" -" std::bind(&ClientSai::handleNotification, this, _1, _2, _3));\n" -"\n" -" m_apiInitialized = true;\n" -"\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" -"```" -msgstr "" -"```cpp\n" -"// File: https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/" -"RedisChannel.h\n" -"class RedisChannel: public Channel\n" -"{\n" -" ...\n" -"\n" -" /**\n" -" * @brief Asic state channel.\n" -" *\n" -" * Used to sent commands like create/remove/set/get to syncd.\n" -" */\n" -" std::shared_ptr m_asicState;\n" -"\n" -" ...\n" -"};\n" -"\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" m_communicationChannel = std::make_shared(\n" -" cc->m_zmqEndpoint,\n" -" cc->m_zmqNtfEndpoint,\n" -" std::bind(&ClientSai::handleNotification, this, _1, _2, _3));\n" -"\n" -" m_apiInitialized = true;\n" -"\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" -"```" - -#: src/5-2-2-bgp-route-update-workflow.md:1454 -msgid "" -"关于进程通信的方法,这里就不再赘述了,大家可以参考第四章描述的[进程间的通信机" -"制](./4-2-2-redis-messaging-layer.html)。" -msgstr "" -"For more information about the method of process communication, you can " -"refer to [Inter-process communication mechanism] described in Chapter 4 " -"(. /4-2-2-redis-messaging-layer.html)." - -#: src/5-2-2-bgp-route-update-workflow.md:1456 -msgid "### syncd更新ASIC" -msgstr "### syncd update ASIC" - -#: src/5-2-2-bgp-route-update-workflow.md:1458 -msgid "" -"最后,当SAI对象生成好并发送给`syncd`后,`syncd`会接收,处理,更新ASIC_DB,最" -"后更新ASIC。这一段的工作流,我们已经在[Syncd-SAI工作流](./5-1-syncd-sai-" -"workflow.html)中详细介绍过了,这里就不再赘述了,大家可以移步去查看。" -msgstr "" -"Finally, when the SAI object is generated and sent to `syncd`, `syncd` will " -"receive it, process it, update ASIC_DB, and finally update ASIC. /5-1-syncd-" -"sai-workflow.html) in detail, so we won't repeat it here, you can move to " -"check it out." - -#: src/5-2-2-bgp-route-update-workflow.md:1462 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Github repo: sonic-frr][SONiCFRR]\n" -"5. [Github repo: sonic-utilities][SONiCUtil]\n" -"6. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"8. [FRRouting][FRRouting]\n" -"9. [FRRouting - BGP][BGP]\n" -"10. [FRRouting - FPM][FPM]\n" -"11. [Understanding EVPN Pure Type 5 Routes][EVPN]" -msgstr "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Github repo: sonic-frr][SONiCFRR]\n" -"5. [Github repo: sonic-utilities][SONiCUtil]\n" -"6. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"8. [FRRouting][FRRouting]\n" -"9. [FRRouting - BGP][BGP]\n" -"10. [FRRouting - FPM][FPM]\n" -"11. [Understanding EVPN Pure Type 5 Routes][EVPN]" - -#: src/6-boot.md:1 -msgid "# 启动流程" -msgstr "# Boot" - -#: src/6-1-cold-boot.md:1 -msgid "# 冷启动" -msgstr "# Cold boot " - -#: src/6-2-fast-boot.md:1 -msgid "# 快速启动" -msgstr "# Fast boot" - -#: src/6-3-warm-boot.md:1 -msgid "# 热启动" -msgstr "# Warm boot" diff --git a/po/messages.pot b/po/messages.pot index 0db604c..549a168 100644 --- a/po/messages.pot +++ b/po/messages.pot @@ -1,6830 +1,6537 @@ msgid "" msgstr "" -"Project-Id-Version: SONiC入门指南\n" -"POT-Creation-Date: \n" +"Project-Id-Version: Getting Started with SONiC\n" +"POT-Creation-Date: 2025-01-05T12:40:49-08:00\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: cn\n" +"Language: en\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: src/SUMMARY.md:3 -msgid "SONiC入门指南" +#: src\SUMMARY.md:1 +msgid "Summary" msgstr "" -#: src/SUMMARY.md:4 -msgid "安装" +#: src\SUMMARY.md:3 src\1-intro.md:1 +msgid "Getting Started with SONiC" msgstr "" -#: src/SUMMARY.md:5 -msgid "虚拟测试环境" +#: src\SUMMARY.md:4 src\1-1-install.md:1 +msgid "Installation" msgstr "" -#: src/SUMMARY.md:6 -msgid "常用命令 (WIP)" +#: src\SUMMARY.md:5 +msgid "Hello World! Virtually" msgstr "" -#: src/SUMMARY.md:7 -msgid "核心组件简介" +#: src\SUMMARY.md:6 +msgid "Commands Cheatsheet (WIP)" msgstr "" -#: src/SUMMARY.md:8 -msgid "Redis数据库" +#: src\SUMMARY.md:7 +msgid "Core Components Introduction" msgstr "" -#: src/SUMMARY.md:9 -msgid "服务与工作流简介" +#: src\SUMMARY.md:8 +msgid "Redis Database" msgstr "" -#: src/SUMMARY.md:10 -msgid "核心容器" +#: src\SUMMARY.md:9 +msgid "Services and Workflows" msgstr "" -#: src/SUMMARY.md:11 +#: src\SUMMARY.md:10 +msgid "Key Containers" +msgstr "" + +#: src\SUMMARY.md:11 src\2-4-sai-intro.md:1 msgid "SAI" msgstr "" -#: src/SUMMARY.md:12 -msgid "开发上手指南" +#: src\SUMMARY.md:12 src\3-dev-guide.md:1 +msgid "Developer Guide" msgstr "" -#: src/SUMMARY.md:13 -msgid "代码仓库" +#: src\SUMMARY.md:13 src\3-1-code-repos.md:1 +msgid "Code Repositories" msgstr "" -#: src/SUMMARY.md:14 -msgid "编译" +#: src\SUMMARY.md:14 src\3-2-build.md:1 +msgid "Build" msgstr "" -#: src/SUMMARY.md:15 -msgid "测试 (WIP)" +#: src\SUMMARY.md:15 +msgid "Testing (WIP)" msgstr "" -#: src/SUMMARY.md:16 -msgid "调试 (WIP)" +#: src\SUMMARY.md:16 +msgid "Debugging (WIP)" msgstr "" -#: src/SUMMARY.md:17 -msgid "SAI调试 (WIP)" +#: src\SUMMARY.md:17 +msgid "SAI Debugging (WIP)" msgstr "" -#: src/SUMMARY.md:18 -msgid "通信机制" +#: src\SUMMARY.md:18 +msgid "Service Communication" msgstr "" -#: src/SUMMARY.md:19 -msgid "与内核的通信" +#: src\SUMMARY.md:19 src\4-1-communicate-via-kernel.md:1 +msgid "Communicate via Kernel" msgstr "" -#: src/SUMMARY.md:20 -msgid "命令行调用" +#: src\SUMMARY.md:20 src\4-1-1-exec.md:1 +msgid "Command Line Invocation" msgstr "" -#: src/SUMMARY.md:21 +#: src\SUMMARY.md:21 src\4-1-2-netlink.md:1 msgid "Netlink" msgstr "" -#: src/SUMMARY.md:22 -msgid "基于Redis的通信" +#: src\SUMMARY.md:22 src\4-2-redis-based-channels.md:1 +msgid "Redis-based Channels" msgstr "" -#: src/SUMMARY.md:23 -msgid "Redis封装" +#: src\SUMMARY.md:23 src\4-2-1-redis-wrappers.md:1 +msgid "Redis Wrappers" msgstr "" -#: src/SUMMARY.md:24 -msgid "通信层" +#: src\SUMMARY.md:24 src\4-2-2-subscribe-state-table.md:1 +msgid "SubscribeStateTable" msgstr "" -#: src/SUMMARY.md:25 -msgid "基于ZMQ的通信 (WIP)" +#: src\SUMMARY.md:25 +msgid "NotificationProducer/Consumer" msgstr "" -#: src/SUMMARY.md:26 -msgid "服务层封装 - Orch" +#: src\SUMMARY.md:26 +msgid "Producer/ConsumerTable" msgstr "" -#: src/SUMMARY.md:27 -msgid "事件分发和错误处理" +#: src\SUMMARY.md:27 +msgid "Producer/ConsumerStateTable" msgstr "" -#: src/SUMMARY.md:28 -msgid "核心组件解析" +#: src\SUMMARY.md:28 +msgid "ZMQ-based Channels (WIP)" msgstr "" -#: src/SUMMARY.md:29 -msgid "Syncd与SAI" +#: src\SUMMARY.md:29 +msgid "Orch Layer" msgstr "" -#: src/SUMMARY.md:30 -msgid "BGP" +#: src\SUMMARY.md:30 +msgid "Event Polling and Error Handling" +msgstr "" + +#: src\SUMMARY.md:31 +msgid "Core Components Deep Dive" msgstr "" -#: src/SUMMARY.md:31 -msgid "BGP命令实现" +#: src\SUMMARY.md:32 src\5-1-syncd-and-sai.md:1 +msgid "Syncd and SAI" msgstr "" -#: src/SUMMARY.md:32 -msgid "BGP路由变更下发" +#: src\SUMMARY.md:33 src\5-2-bgp.md:1 +msgid "BGP" msgstr "" -#: src/SUMMARY.md:33 -msgid "启动流程 (WIP)" +#: src\SUMMARY.md:34 src\5-2-1-bgp-cli.md:1 +msgid "BGP CLI and vtysh" msgstr "" -#: src/SUMMARY.md:34 -msgid "冷启动 (WIP)" +#: src\SUMMARY.md:35 src\5-2-2-route-update-in-frr.md:1 +msgid "Route Update in FRR" msgstr "" -#: src/SUMMARY.md:35 -msgid "快速启动 (WIP)" +#: src\SUMMARY.md:36 src\5-2-3-route-update-in-sonic.md:1 +msgid "Route Update in SONiC" msgstr "" -#: src/SUMMARY.md:36 -msgid "热启动 (WIP)" +#: src\SUMMARY.md:37 +msgid "Boot Process (WIP)" msgstr "" -#: src/1-intro.md:1 -msgid "# SONiC入门指南" +#: src\SUMMARY.md:38 +msgid "Cold Boot (WIP)" msgstr "" -#: src/1-intro.md:3 -msgid "## 为什么要做SONiC" +#: src\SUMMARY.md:39 +msgid "Fast Boot (WIP)" msgstr "" -#: src/1-intro.md:5 -msgid "" -"我们知道交换机内部都有一套可大可小的操作系统,用于配置和查看交换机的状态。但是,从1986年第一台交换机面世开始,虽然各个厂商都在进行着相关的开发,到现在为止种类也相当的多,但是依然存在一些问题,比如:" +#: src\SUMMARY.md:40 +msgid "Warm Boot (WIP)" msgstr "" -#: src/1-intro.md:7 -msgid "" -"1. 生态封闭,不开源,主要是为了支持自家的硬件,无法很好的兼容其他厂商的设备\n" -"2. 支持的场景很有限,难以使用同一套系统去支撑大规模的数据中心中复杂多变的场景\n" -"3. 升级可能会导致网络中断,难以实现无缝升级,这对于云提供商来说有时候是致命的\n" -"4. 设备功能升级缓慢,难以很好的支持快速的产品迭代" +#: src\1-intro.md:3 +msgid "Why SONiC?" msgstr "" -#: src/1-intro.md:12 +#: src\1-intro.md:5 msgid "" -"所以,微软在2016年发起了开源项目SONiC,希望能够通过开源的方式,让SONiC能够成为一个通用的网络操作系统,从而解决上面的问题。而且,由于微软在Azure中大范围的使用SONiC,也保证了SONiC的实现确实能够承受大规模的生产环境的考验,这也是SONiC的一个优势。" +"We know that switches have their own operating systems for configuration, " +"monitoring and so on. However, since the first switch was introduced in " +"1986, despite ongoing development by various vendors, there are still some " +"issues, such as:" msgstr "" -#: src/1-intro.md:14 -msgid "## 主体架构" +#: src\1-intro.md:7 +msgid "" +"Closed ecosystem: Non-open source systems primarily support proprietary " +"hardware and are not compatible with devices from other vendors." msgstr "" -#: src/1-intro.md:16 -msgid "SONiC是微软开发的基于debian的开源的网络操作系统,它的设计核心思想有三个:" +#: src\1-intro.md:8 +msgid "" +"Limited use cases: It is difficult to use the same system to support complex " +"and diverse scenarios in large-scale data centers." msgstr "" -#: src/1-intro.md:18 +#: src\1-intro.md:9 msgid "" -"1. **硬件和软件解耦**:通过SAI(Switch Abstraction " -"Interface)将硬件的操作抽象出来,从而使得SONiC能够支持多种硬件平台。这一层抽象层由SONiC定义,由各个厂商来实现。\n" -"2. " -"**使用docker容器将软件微服务化**:SONiC上的主要功能都被拆分成了一个个的docker容器,和传统的网络操作系统不同,升级系统可以只对其中的某个容器进行升级,而不需要整体升级和重启,这样就可以很方便的进行升级和维护,支持快速的开发和迭代。\n" -"3. " -"**使用redis作为中心数据库对服务进行解耦**:绝大部分服务的配置和状态最后都被存储到中心的redis数据库中,这样不仅使得所有的服务可以很轻松的进行协作(数据存储和pubsub),也可以让我们很方便的在上面开发工具,使用统一的方法对各个服务进行操作和查询,而不用担心状态丢失和协议兼容问题,最后还可以很方便的进行状态的备份和恢复。" +"Disruptive upgrades: Upgrades can cause network interruptions, which can be " +"fatal for cloud providers." msgstr "" -#: src/1-intro.md:22 +#: src\1-intro.md:10 msgid "" -"这让SONiC拥有了非常开放的生态([Community][SONiCLanding],[Workgroups][SONiCWG],[Devices][SONiCDevices]),总体而言,SONiC的架构如下图所示:" +"Slow feature upgrades: It is challenging to support rapid product iterations " +"due to slow device feature upgrades." msgstr "" -#: src/1-intro.md:26 -#: src/2-core-components-intro.md:17 -msgid "_(Source: [SONiC Wiki - Architecture][SONiCArch])_" +#: src\1-intro.md:12 +msgid "" +"To address these issues, Microsoft initiated the SONiC open-source project " +"in 2016. The goal was to create a universal network operating system that " +"solves the aforementioned problems. Additionally, Microsoft's extensive use " +"of SONiC in Azure ensures its suitability for large-scale production " +"environments, which is another significant advantage." msgstr "" -#: src/1-intro.md:28 -msgid "当然,这样的设计也有一些缺点,比如:对磁盘的占用会变大,不过,现在一点点存储空间并不是什么很大的问题,而且这个问题也都可以通过一些方法来解决。" +#: src\1-intro.md:14 +msgid "Architecture" msgstr "" -#: src/1-intro.md:30 -msgid "## 发展方向" +#: src\1-intro.md:16 +msgid "" +"SONiC is an open-source network operating system (NOS) developed by " +"Microsoft based on Debian. It is designed with three core principles:" msgstr "" -#: src/1-intro.md:32 +#: src\1-intro.md:18 msgid "" -"虽然交换机已经发展很多很多年了,但是随着现在云的发展,对网络的要求也越来越高,不管是直观的需求,比如更大的带宽,更大的容量,还是最新的研究,比如,带内计算,端网融合等等,都对交换机的发展提出了更高的要求和挑战,也促使着各大厂商和研究机构不断的进行创新。SONiC也一样,随着时间的发展,需求一点没有减少。" +"**Hardware and software decoupling**: SONiC abstracts hardware operations " +"through the Switch Abstraction Interface (SAI), enabling SONiC to support " +"multiple hardware platforms. SAI defines this abstraction layer, which is " +"implemented by various vendors." msgstr "" -#: src/1-intro.md:34 +#: src\1-intro.md:19 msgid "" -"关于SONiC的发展方向,我们可以在它的[Roadmap][SONiCPlanning]中看到。如果大家对最新的动态感兴趣,也可以关注它的Workshop,比如,最近的[OCP " -"Global Summit 2022 - SONiC Workshop][SONiCWorkshop]。这里就不展开了。" +"**Microservices with Docker containers**: The main functionalities of SONiC " +"are divided into individual Docker containers. Unlike traditional network " +"operating systems, upgrading the system can be done by upgrading specific " +"containers without the need for a complete system upgrade or restart. This " +"allows for easy upgrades, maintenance, and supports rapid development and " +"iteration." msgstr "" -#: src/1-intro.md:36 -msgid "## 感谢" +#: src\1-intro.md:20 +msgid "" +"**Redis as a central database for service decoupling**: The configuration " +"and status of most services are stored in a central Redis database. This " +"enables seamless collaboration between services (data storage and pub/sub) " +"and provides a unified method for operating and querying various services " +"without concerns about data loss or protocol compatibility. It also " +"facilitates easy backup and recovery of states." msgstr "" -#: src/1-intro.md:38 -msgid "感谢以下朋友的帮助和贡献,没有你们也就没有这本入门指南!" +#: src\1-intro.md:22 +msgid "" +"These design choices gives SONiC a great open ecosystem " +"([Community](https://sonic-net.github.io/SONiC/index.html), " +"[Workgroups](https://sonic-net.github.io/SONiC/workgroups.html), " +"[Devices](https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html)). " +"Overall, the architecture of SONiC is illustrated in the following diagram:" msgstr "" -#: src/1-intro.md:40 -msgid "[@bingwang-ms](https://github.com/bingwang-ms)" +#: src\1-intro.md:24 +msgid "![](assets/chapter-1/sonic-arch.png)" msgstr "" -#: src/1-intro.md:42 -msgid "# License" +#: src\1-intro.md:26 src\2-core-components-intro.md:17 +msgid "" +"_(Source: [SONiC Wiki - " +"Architecture](https://github.com/sonic-net/SONiC/wiki/Architecture))_" msgstr "" -#: src/1-intro.md:44 +#: src\1-intro.md:28 msgid "" -"本书使用 [署名-非商业性使用-相同方式共享(CC BY-NC-SA)4.0 " -"许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/)。" +"Of course, this design has some drawbacks, such as relative large disk " +"usage. However, with the availability of storage space and various methods " +"to address this issue, it is not a significant concern." msgstr "" -#: src/1-intro.md:46 -#: src/1-1-install.md:164 -#: src/1-2-hello-world-virtually.md:187 -#: src/1-3-command-cheatsheet.md:184 -#: src/2-core-components-intro.md:19 -#: src/2-1-database.md:74 -#: src/2-2-services-intro.md:74 -#: src/2-3-key-containers.md:179 -#: src/2-4-sai-intro.md:164 -#: src/3-1-code-repos.md:131 -#: src/3-2-compile.md:202 -#: src/4-communications.md:19 -#: src/4-1-1-exec.md:36 -#: src/4-1-2-netlink.md:70 -#: src/4-2-1-redis-wrappers.md:33 -#: src/4-2-2-redis-messaging-layer.md:318 -#: src/4-4-orch-layer.md:34 -#: src/4-5-event-polling-and-error-handling.md:119 -#: src/5-1-syncd-and-sai.md:813 -#: src/5-2-bgp.md:32 -#: src/5-2-1-bgp-command-impl.md:87 -#: src/5-2-2-bgp-route-update-workflow.md:1460 -msgid "# 参考资料" +#: src\1-intro.md:30 +msgid "Future Direction" msgstr "" -#: src/1-intro.md:48 +#: src\1-intro.md:32 msgid "" -"1. [SONiC Wiki - Architecture][SONiCArch]\n" -"2. [SONiC Wiki - Roadmap Planning][SONiCPlanning]\n" -"3. [SONiC Landing Page][SONiCLanding]\n" -"4. [SONiC Workgroups][SONiCWG]\n" -"5. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"6. [SONiC User Manual][SONiCManual]\n" -"7. [OCP Global Summit 2022 - SONiC Workshop][SONiCWorkshop]" +"Although switches have been around for many years, the development of cloud " +"has raised higher demands and challenges for networks. These include " +"intuitive requirements like increased bandwidth and capacity, as well as " +"cutting-edge research such as in-band computing and edge-network " +"convergence. These factors drive innovation among major vendors and research " +"institutions. SONiC is no exception and continues to evolve to meet the " +"growing demands." msgstr "" -#: src/1-1-install.md:1 -msgid "# 安装" +#: src\1-intro.md:34 +msgid "" +"To learn more about the future direction of SONiC, you can refer to its " +"[Roadmap](https://github.com/sonic-net/SONiC/wiki/Sonic-Roadmap-Planning). " +"If you are interested in the latest updates, you can also follow its " +"workshops, such as the recent [OCP Global Summit 2022 - SONiC " +"Workshop](https://www.youtube.com/playlist?list=PLAG-eekRQBSjwK0DpyHJs76gOz1619KqW). " +"However, I won't go into detail here." msgstr "" -#: src/1-1-install.md:3 -msgid "如果你自己就拥有一台交换机,或者想购买一台交换机,在上面安装SONiC,那么请认真阅读这一小节,否则可以自行跳过。:D" +#: src\1-intro.md:36 +msgid "Acknowledgments" msgstr "" -#: src/1-1-install.md:5 -msgid "## 交换机选择和SONiC安装" +#: src\1-intro.md:38 +msgid "" +"Special thanks to the following individuals for their help and " +"contributions. Without them, this introductory guide would not have been " +"possible!" msgstr "" -#: src/1-1-install.md:7 -msgid "" -"首先,请确认你的交换机是否支持SONiC,SONiC目前支持的交换机型号可以在[这里][SONiCDevices]找到,如果你的交换机型号不在列表中,那么就需要联系厂商,看看是否有支持SONiC的计划。有很多交换机是不支持SONiC的,比如:" +#: src\1-intro.md:40 +msgid "[@bingwang-ms](https://github.com/bingwang-ms)" msgstr "" -#: src/1-1-install.md:9 +#: src\1-intro.md:42 +msgid "License" +msgstr "" + +#: src\1-intro.md:44 msgid "" -"1. 普通针对家用的交换机,这些交换机的硬件配置都比较低(即便支持的带宽很高,比如[MikroTik " -"CRS504-4XQ-IN][MikroTik100G],虽然它支持100GbE网络,但是它只有16MB的Flash存储和64MB的RAM,所以基本只能跑它自己的RouterOS了)。\n" -"2. 有些虽然是数据中心用的交换机,但是可能由于型号老旧,厂商并没有计划支持SONiC。" +"This book is licensed under the [Creative Commons " +"Attribution-NonCommercial-ShareAlike 4.0 International " +"License](https://creativecommons.org/licenses/by-nc-sa/4.0/)." msgstr "" -#: src/1-1-install.md:12 -msgid "对于安装过程,由于每一家厂商的交换机设计不同,其底层接口各有差别,所以,其安装方法也都有所差别,这些差别主要集中在两个地方:" +#: src\1-intro.md:46 src\1-1-install.md:164 +#: src\1-2-hello-world-virtually.md:188 src\1-3-command-cheatsheet.md:195 +#: src\2-core-components-intro.md:19 src\2-1-database.md:76 +#: src\2-2-services-intro.md:74 src\2-3-key-containers.md:181 +#: src\2-4-sai-intro.md:164 src\3-1-code-repos.md:133 src\3-2-build.md:211 +#: src\4-communications.md:19 src\4-1-1-exec.md:36 src\4-1-2-netlink.md:76 +#: src\4-2-1-redis-wrappers.md:32 src\4-2-2-subscribe-state-table.md:71 +#: src\4-2-3-notification-producer-consumer.md:52 +#: src\4-2-4-producer-consumer-table.md:128 +#: src\4-2-5-producer-consumer-state-table.md:123 src\4-4-orch-layer.md:34 +#: src\4-5-event-polling-and-error-handling.md:121 src\5-1-syncd-and-sai.md:822 +#: src\5-2-bgp.md:32 src\5-2-1-bgp-cli.md:89 +#: src\5-2-2-route-update-in-frr.md:735 src\5-2-3-route-update-in-sonic.md:734 +msgid "References" msgstr "" -#: src/1-1-install.md:14 +#: src\1-intro.md:48 msgid "" -"1. 每个厂商都会有自己的[SONiC " -"Build][SONiCDevices],还有的厂商会在SONiC的基础之上进行扩展开发,为自己的交换机支持更多的功能,比如:[Dell " -"Enterprise SONiC][DellSonic],[EdgeCore Enterprise " -"SONiC][EdgeCoreSONiC],所以需要根据自己的交换机选择对应的版本。\n" -"2. " -"每个厂商的交换机也会支持不同的安装方式,有一些是直接使用USB对ROM进行Flash,有一些是通过ONIE进行安装,这也需要根据自己的交换机来进行配置。" +"[SONiC Wiki - " +"Architecture](https://github.com/sonic-net/SONiC/wiki/Architecture)" msgstr "" -#: src/1-1-install.md:17 -msgid "所以,虽然安装方法各有差别,但是总体而言,安装的步骤都是差不多的。请联系自己的厂商,获取对应的安装文档,然后按照文档进行安装即可。" +#: src\1-intro.md:49 +msgid "" +"[SONiC Wiki - Roadmap " +"Planning](https://github.com/sonic-net/SONiC/wiki/Sonic-Roadmap-Planning)" msgstr "" -#: src/1-1-install.md:19 -msgid "## 配置交换机" +#: src\1-intro.md:50 +msgid "[SONiC Landing Page](https://sonic-net.github.io/SONiC/index.html)" msgstr "" -#: src/1-1-install.md:21 -msgid "安装好之后,我们需要进行一些基础设置,部分设置是通用的,我们在这里简单总结一下。" +#: src\1-intro.md:51 +msgid "[SONiC Workgroups](https://sonic-net.github.io/SONiC/workgroups.html)" msgstr "" -#: src/1-1-install.md:23 -msgid "### 设置admin密码" +#: src\1-intro.md:52 src\1-1-install.md:166 src\3-2-build.md:216 +msgid "" +"[SONiC Supported Devices and " +"Platforms](https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html)" msgstr "" -#: src/1-1-install.md:25 -msgid "默认SONiC的账号密码是admin:YourPaSsWoRd,使用默认密码显然不安全:" +#: src\1-intro.md:53 +msgid "" +"[SONiC User " +"Manual](https://github.com/sonic-net/SONiC/blob/master/doc/SONiC-User-Manual.md)" msgstr "" -#: src/1-1-install.md:27 +#: src\1-intro.md:54 msgid "" -"```bash\n" -"sudo passwd admin\n" -"```" +"[OCP Global Summit 2022 - SONiC " +"Workshop](https://www.youtube.com/playlist?list=PLAG-eekRQBSjwK0DpyHJs76gOz1619KqW)" msgstr "" -#: src/1-1-install.md:31 -msgid "### 设置风扇转速" +#: src\1-1-install.md:3 +msgid "" +"If you already own a switch or are planning to purchase one and install " +"SONiC on it, please read this section carefully. Otherwise, feel free to " +"skip it. :D" msgstr "" -#: src/1-1-install.md:33 -msgid "" -"数据中心用的交换机风扇声音都特别的大!比如,我用的交换机是Arista " -"7050QX-32S,上面有4个风扇,最高能到每分钟17000转,放在车库中,高频的啸叫即便是在二楼隔着3面墙还是能听得到,所以如果你是在家使用的话,建议对其进行一些设置,将转速调低。" +#: src\1-1-install.md:5 +msgid "Switch Selection and SONiC Installation" msgstr "" -#: src/1-1-install.md:35 +#: src\1-1-install.md:7 msgid "" -"可惜,[由于SONiC并没有cli对风扇转速的规则进行控制][SONiCThermal],所以我们需要通过手动修改pmon容器中的配置文件的方式来进行设置。" +"First, please confirm if your switch supports SONiC. The list of currently " +"supported switch models can be found " +"[here](https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html). " +"If your switch model is not on the list, you will need to contact the " +"manufacturer to see if they have plans to support SONiC. There are many " +"switches that do not support SONiC, such as:" msgstr "" -#: src/1-1-install.md:37 +#: src\1-1-install.md:9 msgid "" -"```bash\n" -"# Enter pmon container\n" -"sudo docker exec -it pmon bash\n" -"\n" -"# Use pwmconfig to detect all pwm fans and create configuration file. The " -"configuration file will be created at /etc/fancontrol.\n" -"pwmconfig\n" -"\n" -"# Start fancontrol and make sure it works. If it doesn't work, you can run " -"fancontrol directly to see what's wrong.\n" -"VERBOSE=1 /etc/init.d/fancontrol start\n" -"VERBOSE=1 /etc/init.d/fancontrol status\n" -"\n" -"# Exit pmon container\n" -"exit\n" -"\n" -"# Copy the configuration file from the container to the host, so that the " -"configuration will not be lost after reboot.\n" -"# This command needs to know what is the model of your switch, for example, " -"the command I need to run here is as follows. If your switch model is " -"different, please modify it yourself.\n" -"sudo docker cp pmon:/etc/fancontrol " -"/usr/share/sonic/device/x86_64-arista_7050_qx32s/fancontrol\n" -"```" +"Regular switches for home use. These switches have relatively low hardware " +"configurations (even if they support high bandwidth, such as [MikroTik " +"CRS504-4XQ-IN](https://mikrotik.com/product/crs504_4xq_in), which supports " +"100GbE networks but only has 16MB of flash storage and 64MB of RAM, so it " +"can basically only run its own RouterOS)." msgstr "" -#: src/1-1-install.md:56 -msgid "### 设置交换机Management Port IP" +#: src\1-1-install.md:10 +msgid "" +"Some data center switches may not support SONiC due to their outdated models " +"and lack of manufacturer plans." msgstr "" -#: src/1-1-install.md:58 +#: src\1-1-install.md:12 msgid "" -"一般的数据中心用的交换机都提供了Serial Console连接的方式,但是其速度实在是太慢了,所以我们在安装完成之后,都会尽快的把Management " -"Port给设置好,然后通过SSH的方式来进行管理。" +"Regarding the installation process, since each manufacturer's switch design " +"is different, the underlying interfaces are also different, so the " +"installation methods vary. These differences mainly focus on two areas:" msgstr "" -#: src/1-1-install.md:60 -msgid "一般来说,management port的设备名是eth0,所以我们可以通过SONiC的配置命令来进行设置:" +#: src\1-1-install.md:14 +msgid "" +"Each manufacturer will have their own [SONiC " +"Build](https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html), " +"and some manufacturers will extend development on top of SONiC to support " +"more features for their switches, such as [Dell Enterprise " +"SONiC](https://www.dell.com/en-us/shop/povw/sonic) and [EdgeCore Enterprise " +"SONiC](https://www.edge-core.com/sonic.php). Therefore, you need to choose " +"the corresponding version based on your switch model." msgstr "" -#: src/1-1-install.md:62 +#: src\1-1-install.md:15 msgid "" -"```bash\n" -"# sudo config interface ip add eth0 \n" -"# IPv4\n" -"sudo config interface ip add eth0 192.168.1.2/24 192.168.1.1\n" -"\n" -"# IPv6\n" -"sudo config interface ip add eth0 2001::8/64 2001::1\n" -"```" +"Each manufacturer's switch will also support different installation methods, " +"some using USB to flash the ROM directly, and some using ONIE for " +"installation. This configuration needs to be done according to your specific " +"switch." msgstr "" -#: src/1-1-install.md:71 -msgid "### 创建网络配置" +#: src\1-1-install.md:17 +msgid "" +"Although the installation methods may vary, the overall steps are similar. " +"Please contact your manufacturer to obtain the corresponding installation " +"documentation and follow the instructions to complete the installation." msgstr "" -#: src/1-1-install.md:73 -msgid "新安装完的SONiC交换机会有一个默认的网络配置,这个配置有很多问题,比如对于10.0.0.0的IP的使用,如下:" +#: src\1-1-install.md:19 +msgid "Configure the Switch" msgstr "" -#: src/1-1-install.md:75 -#: src/1-2-hello-world-virtually.md:118 +#: src\1-1-install.md:21 msgid "" -"```bash\n" -"admin@sonic:~$ show ip interfaces\n" -"Interface Master IPv4 address/mask Admin/Oper BGP Neighbor " -"Neighbor IP\n" -"----------- -------- ------------------- ------------ -------------- " -"-------------\n" -"Ethernet0 10.0.0.0/31 up/up ARISTA01T2 " -"10.0.0.1\n" -"Ethernet4 10.0.0.2/31 up/up ARISTA02T2 " -"10.0.0.3\n" -"Ethernet8 10.0.0.4/31 up/up ARISTA03T2 " -"10.0.0.5\n" -"```" +"After installation, we need to perform some basic settings. Some settings " +"are common, and we will summarize them here." msgstr "" -#: src/1-1-install.md:84 -msgid "" -"所以我们需要创建一个新的网络配置,然后将我们使用的Port都放入到这个网络配置中。这里简单的方法就是创建一个VLAN,使用VLAN Routing:" +#: src\1-1-install.md:23 +msgid "Set the admin password" msgstr "" -#: src/1-1-install.md:86 +#: src\1-1-install.md:25 msgid "" -"```bash\n" -"# Create untagged vlan\n" -"sudo config vlan add 2\n" -"\n" -"# Add IP to vlan\n" -"sudo config interface ip add Vlan2 10.2.0.0/24\n" -"\n" -"# Remove all default IP settings\n" -"show ip interfaces | tail -n +3 | grep Ethernet | awk '{print \"sudo config " -"interface ip remove\", $1, $2}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Add all ports to the new vlan\n" -"show interfaces status | tail -n +3 | grep Ethernet | awk '{print \"sudo " -"config vlan member add -u 2\", $1}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh\n" -"\n" -"# Enable proxy arp, so switch can respond to arp requests from hosts\n" -"sudo config vlan proxy_arp 2 enabled\n" -"\n" -"# Save config, so it will be persistent after reboot\n" -"sudo config save -y\n" -"```" +"The default SONiC account and password is `admin` and `YourPaSsWoRd`. Using " +"default password is obviously not secure. To change the password, we can run " +"the following command:" msgstr "" -#: src/1-1-install.md:106 -msgid "这样就完成了,我们可以通过show vlan brief来查看一下:" +#: src\1-1-install.md:31 +msgid "Set fan speed" msgstr "" -#: src/1-1-install.md:108 +#: src\1-1-install.md:33 msgid "" -"```\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-------------+----------------+-------------+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | " -"DHCP Helper Address |\n" -"+===========+==============+=============+================+=============+=======================+\n" -"| 2 | 10.2.0.0/24 | Ethernet0 | untagged | enabled | " -" |\n" -"...\n" -"| | | Ethernet124 | untagged | | " -" |\n" -"+-----------+--------------+-------------+----------------+-------------+-----------------------+\n" -"```" +"Data center switches are usually very noisy! For example, the switch I use " +"is Arista 7050QX-32S, which has 4 fans that can spin up to 17000 RPM. Even " +"if it is placed in the garage, the high-frequency whining can still be heard " +"behind 3 walls on the second floor. Therefore, if you are using it at home, " +"it is recommended to adjust the fan speed." msgstr "" -#: src/1-1-install.md:119 -msgid "### 配置主机" +#: src\1-1-install.md:35 +msgid "" +"Unfortunately, [SONiC does not have CLI control over fan " +"speed](https://github.com/sonic-net/SONiC/blob/master/thermal-control-design.md), " +"so we need to manually modify the configuration file in the pmon container " +"to adjust the fan speed." msgstr "" -#: src/1-1-install.md:121 -msgid "如果你家里只有一台主机使用多网口连接交换机进行测试,那么我们还需要在主机上进行一些配置,以保证流量会通过网卡,流经交换机,否则,请跳过这一步。" +#: src\1-1-install.md:38 +msgid "# Enter the pmon container" msgstr "" -#: src/1-1-install.md:123 +#: src\1-1-install.md:40 msgid "" -"这里网上的攻略很多,比如使用iptables中的DNAT和SNAT创建一个虚拟地址,但是过程非常繁琐,经过一些实验,我发现最简单的办法就是将其中一个网口移动到一个新的网络命名空间中,就可以了,即便使用的是同一个网段的IP,也不会有问题。" +"# Use pwmconfig to detect all PWM fans and create a configuration file. The " +"configuration file will be created at /etc/fancontrol." msgstr "" -#: src/1-1-install.md:125 +#: src\1-1-install.md:43 msgid "" -"比如,我家使用的是Netronome Agilio CX " -"2x40GbE,它会创建两个interface:`enp66s0np0`和`enp66s0np1`,我们这里可以将`enp66s0np1`移动到一个新的网络命名空间中,再配置好ip地址就可以了:" +"# Start fancontrol and make sure it works. If it doesn't work, you can run " +"fancontrol directly to see what's wrong." msgstr "" -#: src/1-1-install.md:127 -msgid "" -"```bash\n" -"# Create a new network namespace\n" -"sudo ip netns add toy-ns-1\n" -"\n" -"# Move the interface to the new namespace\n" -"sudo ip link set enp66s0np1 netns toy-ns-1\n" -"\n" -"# Setting up IP and default routes\n" -"sudo ip netns exec toy-ns-1 ip addr add 10.2.0.11/24 dev enp66s0np1\n" -"sudo ip netns exec toy-ns-1 ip link set enp66s0np1 up\n" -"sudo ip netns exec toy-ns-1 ip route add default via 10.2.0.1\n" -"```" +#: src\1-1-install.md:45 src\1-1-install.md:46 +msgid "1" msgstr "" -#: src/1-1-install.md:140 -msgid "这样就可以了,我们可以通过iperf来测试一下,并在交换机上进行确认:" +#: src\1-1-install.md:47 +msgid "# Exit the pmon container" msgstr "" -#: src/1-1-install.md:142 +#: src\1-1-install.md:50 msgid "" -"```bash\n" -"# On the host (enp66s0np0 has ip 10.2.0.10 assigned)\n" -"$ iperf -s --bind 10.2.0.10\n" -"\n" -"# Test within the new network namespace\n" -"$ sudo ip netns exec toy-ns-1 iperf -c 10.2.0.10 -i 1 -P 16\n" -"------------------------------------------------------------\n" -"Client connecting to 10.2.0.10, TCP port 5001\n" -"TCP window size: 85.0 KByte (default)\n" -"------------------------------------------------------------\n" -"...\n" -"[SUM] 0.0000-10.0301 sec 30.7 GBytes 26.3 Gbits/sec\n" -"[ CT] final connect times (min/avg/max/stdev) = 0.288/0.465/0.647/0.095 ms " -"(tot/err) = 16/0\n" -"\n" -"# Confirm on switch\n" -"admin@sonic:~$ show interfaces counters\n" -" IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR " -"RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP " -"TX_OVR\n" -"----------- ------- ---------- ------------ --------- -------- " -"-------- -------- ---------- ------------ --------- -------- -------- " -"--------\n" -" Ethernet4 U 2,580,140 6190.34 KB/s 0.12% 0 " -"3,783 0 51,263,535 2086.64 MB/s 41.73% 0 0 " -" 0\n" -" Ethernet12 U 51,261,888 2086.79 MB/s 41.74% 0 " -"1 0 2,580,317 6191.00 KB/s 0.12% 0 0 " -"0\n" -"```" +"# Copy the configuration file from the container to the host, so that the " +"configuration will not be lost after reboot." msgstr "" -#: src/1-1-install.md:166 +#: src\1-1-install.md:51 msgid "" -"1. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"2. [SONiC Thermal Control Design][SONiCThermal]\n" -"3. [Dell Enterprise SONiC Distribution][DellSONiC]\n" -"4. [Edgecore Enterprise SONiC Distribution][EdgeCoreSONiC]\n" -"5. [Mikrotik CRS504-4XQ-IN][MikroTik100G]" +"# This command needs to know what is the model of your switch. For example, " +"the command I need to run here is as follows. If your switch model is " +"different, please modify it accordingly." msgstr "" -#: src/1-2-hello-world-virtually.md:1 -msgid "# 虚拟测试环境" +#: src\1-1-install.md:56 +msgid "Set the Switch Management Port IP" msgstr "" -#: src/1-2-hello-world-virtually.md:3 +#: src\1-1-install.md:58 msgid "" -"虽然SONiC功能强大,但是大部分时候一台能够支持SONiC系统的交换机价格并不便宜,如果你只是想试一试SONiC,但是又不想花钱买一台SONiC的硬件设备,那么这一章一定不能错过,这一章会总结一下如何通过GNS3在本地搭建一个虚拟的SONiC的Lab,让你可以很快的在本地体验一把SONiC的基本功能。" +"Data center switches usually can be connected via Serial Console, but its " +"speed is very slow. Therefore, after installation, it is better to set up " +"the Management Port as soon as possible, then use SSH connection." msgstr "" -#: src/1-2-hello-world-virtually.md:5 +#: src\1-1-install.md:60 msgid "" -"在本地运行SONiC的方法很好几种,比如docker + " -"vswitch,p4软交换机等等,对于初次使用而言,用GNS3可能是最方便快捷的了,所以本文就以GNS3为例,介绍一下如何在本地搭建一个SONiC的Lab。那么,我们就开始吧!" +"Generally, the management port is named eth0, so we can use SONiC's " +"configuration command to set it up:" msgstr "" -#: src/1-2-hello-world-virtually.md:7 -msgid "## 安装GNS3" +#: src\1-1-install.md:63 +msgid "# sudo config interface ip add eth0 " msgstr "" -#: src/1-2-hello-world-virtually.md:9 -msgid "首先,为了让我们方便而且直观的建立测试用的虚拟网络,我们需要先来安装一下GNS3。" +#: src\1-1-install.md:63 +msgid "# IPv4" msgstr "" -#: src/1-2-hello-world-virtually.md:11 -msgid "" -"[GNS3,全称为Graphical Network Simulator " -"3,是一个图形化的网络仿真软件][GNS3]。它支持多种不同的虚拟化技术,比如:QEMU、VMware、VirtualBox等等。这样,我们在等会搭建虚拟网络的时候,就不需要手动的运行很多命令,或者写脚本了,大部分的工作都可以通过图形界面来完成了。" +#: src\1-1-install.md:66 +msgid "# IPv6" msgstr "" -#: src/1-2-hello-world-virtually.md:13 -msgid "### 安装依赖" +#: src\1-1-install.md:71 +msgid "Create Network Configuration" msgstr "" -#: src/1-2-hello-world-virtually.md:15 +#: src\1-1-install.md:73 msgid "" -"安装它之前,我们需要先安装几个其他的软件:docker, wireshark, putty, qemu, ubridge, " -"libvirt和bridge-utils,已经装好的小伙伴可以自行跳过。" +"A newly installed SONiC switch will have a default network configuration, " +"which has many issues, such as using 10.0.0.0 IP on Ethernet0, as shown " +"below:" msgstr "" -#: src/1-2-hello-world-virtually.md:17 +#: src\1-1-install.md:84 msgid "" -"首先是Docker,它们的安装过程,大家可以自己通过下面的传送门去安装:[https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)" +"Therefore, we need to update the ports with a new network configuration. A " +"simple method is to create a VLAN and use VLAN Routing:" msgstr "" -#: src/1-2-hello-world-virtually.md:19 -msgid "" -"其他的在ubuntu上安装都非常简单,只需要执行下面的命令就可以了。这里安装时要注意,ubridge和Wireshark的安装过程中会询问是不是要创建wireshark用户组来bypass " -"sudo,这里一定要选择Yes。" +#: src\1-1-install.md:87 +msgid "# Create untagged VLAN" msgstr "" -#: src/1-2-hello-world-virtually.md:21 -msgid "" -"```\n" -"sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients " -"bridge-utils wireshark putty ubridge\n" -"```" +#: src\1-1-install.md:89 +msgid "# Add IP to VLAN" msgstr "" -#: src/1-2-hello-world-virtually.md:25 -msgid "安装好了之后,我们就可以来安装GNS3了。" +#: src\1-1-install.md:92 +msgid "# Remove all default IP settings" msgstr "" -#: src/1-2-hello-world-virtually.md:27 -msgid "### 安装GNS3" +#: src\1-1-install.md:94 +msgid "'{print \"sudo config interface ip remove\", $1, $2}'" msgstr "" -#: src/1-2-hello-world-virtually.md:29 -msgid "在Ubuntu上,GNS3的安装非常简单,只需要执行下面的命令就可以了。" +#: src\1-1-install.md:95 +msgid "# Add all ports to the new VLAN" msgstr "" -#: src/1-2-hello-world-virtually.md:31 -msgid "" -"```bash\n" -"sudo add-apt-repository ppa:gns3/ppa\n" -"sudo apt update \n" -"sudo apt install gns3-gui gns3-server\n" -"```" +#: src\1-1-install.md:97 +msgid "'{print \"sudo config vlan member add -u 2\", $1}'" msgstr "" -#: src/1-2-hello-world-virtually.md:37 -msgid "然后把你的用户加入到如下的组中,这样GNS3就可以去访问docker,wireshark等功能而不用sudo了。" +#: src\1-1-install.md:98 +msgid "# Enable proxy ARP, so the switch can respond to ARP requests from hosts" msgstr "" -#: src/1-2-hello-world-virtually.md:39 -msgid "" -"```bash\n" -"for g in ubridge libvirt kvm wireshark docker; do\n" -" sudo usermod -aG $g \n" -"done\n" -"```" +#: src\1-1-install.md:101 +msgid "# Save the config, so it will be persistent after reboot" msgstr "" -#: src/1-2-hello-world-virtually.md:45 -msgid "如果你使用的不是Ubuntu,更详细的安装文档可以参考[他们的官方文档][GNS3Install]。" +#: src\1-1-install.md:106 +msgid "That's it! Now we can use `show vlan brief` to check it:" msgstr "" -#: src/1-2-hello-world-virtually.md:47 -msgid "## 准备SONiC的镜像" +#: src\1-1-install.md:119 +msgid "Configure the Host" msgstr "" -#: src/1-2-hello-world-virtually.md:49 +#: src\1-1-install.md:121 msgid "" -"在测试之前,我们还需要一个SONiC的镜像。由于需要支持大量不同的厂商,而每个厂商的底层实现都不一样,所以最后每个厂商都会编译一个自己的镜像。这里因为我们在创建虚拟的环境,所以我们需要使用基于VSwitch的镜像来创建虚拟交换机:sonic-vs.img.gz。" +"If you only have one host at home using multiple NICs to connect to the " +"switch for testing, we need to update some settings on the host to ensure " +"that traffic flows through the NIC and the switch. Otherwise, feel free to " +"skip this step." msgstr "" -#: src/1-2-hello-world-virtually.md:51 +#: src\1-1-install.md:123 msgid "" -"[SONiC镜像的项目在这里](https://github.com/sonic-net/sonic-buildimage),虽然我们可以自己去编译,但是速度实在有点慢,所以为了节省时间,我们可以直接[去这里下载最新的镜像](https://sonic-build.azurewebsites.net/ui/sonic/pipelines/142/builds?branchName=master)。只要找一个最新的成功的Build就行,在Artifacts中找到sonic-vs.img.gz,下载就可以了。" -msgstr "" - -#: src/1-2-hello-world-virtually.md:53 -msgid "然后,我们来准备一下项目:" +"There are many online guides for this, such as using DNAT and SNAT in " +"iptables to create a virtual address. However, after some experiments, I " +"found that the simplest way is to move one of the NICs to a new network " +"namespace, even if it uses the same IP subnet, it will still work." msgstr "" -#: src/1-2-hello-world-virtually.md:55 +#: src\1-1-install.md:125 msgid "" -"```bash\n" -"git clone --recurse-submodules " -"https://github.com/sonic-net/sonic-buildimage.git\n" -"cd sonic-buildimage/platform/vs\n" -"\n" -"# 将下载的镜像放在这个目录下,然后运行下面这个命令进行解压缩。\n" -"gzip -d sonic-vs.img.gz\n" -"\n" -"# 下面这个命令会生成GNS3的镜像配置文件\n" -"./sonic-gns3a.sh\n" -"```" +"For example, if I use Netronome Agilio CX 2x40GbE at home, it will create " +"two interfaces: `enp66s0np0` and `enp66s0np1`. Here, we can move " +"`enp66s0np1` to a new network namespace and configure the IP address:" msgstr "" -#: src/1-2-hello-world-virtually.md:66 -msgid "执行完成之后,我们运行`ls`命令就可以看到我们需要的镜像文件了。" +#: src\1-1-install.md:128 +msgid "# Create a new network namespace" msgstr "" -#: src/1-2-hello-world-virtually.md:68 -msgid "" -"```bash\n" -"r12f@r12f-svr:~/code/sonic/sonic-buildimage/platform/vs\n" -"$ l\n" -"total 2.8G\n" -"...\n" -"-rw-rw-r-- 1 r12f r12f 1.1K Apr 18 16:36 SONiC-latest.gns3a # <= " -"这个是GNS3的镜像配置文件\n" -"-rw-rw-r-- 1 r12f r12f 2.8G Apr 18 16:32 sonic-vs.img # <= " -"这个是我们解压出来的镜像\n" -"...\n" -"```" +#: src\1-1-install.md:130 +msgid "# Move the interface to the new namespace" msgstr "" -#: src/1-2-hello-world-virtually.md:78 -msgid "## 导入镜像" +#: src\1-1-install.md:133 +msgid "# Setting up IP and default routes" msgstr "" -#: src/1-2-hello-world-virtually.md:80 -msgid "" -"现在,在命令行里面输入`gns3`,就可以启动GNS3了。如果你是ssh到另外一台机器上,可以试着启用X11转发,这样就可以在远程运行GNS3,但是图形界面显示在本地了。我就是这样,将GNS3运行在了远程的服务器上,但是图形界面通过MobaXterm显示在了本地的Windows机器上。" +#: src\1-1-install.md:140 +msgid "That's it! We can start testing it using iperf and confirm on the switch:" msgstr "" -#: src/1-2-hello-world-virtually.md:82 -msgid "运行起来之后,GNS3会让我们创建一个项目,很简单,填个目录地址就好。如果你是使用的X11转发,请注意,这个目录是在你远程服务器上,而不是本地。" +#: src\1-1-install.md:143 +msgid "# On the host (enp66s0np0 has IP 10.2.0.10 assigned)" msgstr "" -#: src/1-2-hello-world-virtually.md:86 -msgid "然后,我们就可以通过`File -> Import appliance`来导入我们刚刚生成的镜像了。" +#: src\1-1-install.md:145 +msgid "# Test within the new network namespace" msgstr "" -#: src/1-2-hello-world-virtually.md:90 -msgid "选择我们刚刚生成的`SONiC-latest.gns3a`镜像配置文件,然后点击`Next`。" +#: src\1-1-install.md:155 +msgid "# Confirm on the switch" msgstr "" -#: src/1-2-hello-world-virtually.md:94 -msgid "这个时候就可以看到我们的镜像了,点击`Next`。" +#: src\1-1-install.md:167 +msgid "" +"[SONiC Thermal Control " +"Design](https://github.com/sonic-net/SONiC/blob/master/thermal-control-design.md)" msgstr "" -#: src/1-2-hello-world-virtually.md:98 +#: src\1-1-install.md:168 msgid "" -"这个时候会开始导入镜像,这个过程可能会比较慢,因为GNS3需要将镜像转换成qcow2格式,放入我们的项目目录中。导入完成之后,我们就可以看到我们的镜像了。" +"[Dell Enterprise SONiC " +"Distribution](https://www.dell.com/en-us/shop/povw/sonic)" msgstr "" -#: src/1-2-hello-world-virtually.md:102 -msgid "好的!完成!" +#: src\1-1-install.md:169 +msgid "" +"[Edgecore Enterprise SONiC " +"Distribution](https://www.edge-core.com/sonic.php)" msgstr "" -#: src/1-2-hello-world-virtually.md:104 -msgid "## 创建网络" +#: src\1-1-install.md:170 +msgid "[Mikrotik CRS504-4XQ-IN](https://mikrotik.com/product/crs504_4xq_in)" msgstr "" -#: src/1-2-hello-world-virtually.md:106 -msgid "好了!现在一切就绪,我们还是创建一个虚拟的网络吧!" +#: src\1-2-hello-world-virtually.md:1 +msgid "Hello, World! Virtually" msgstr "" -#: src/1-2-hello-world-virtually.md:108 +#: src\1-2-hello-world-virtually.md:3 msgid "" -"GNS3的图形界面非常的好用,基本上就是打开侧边栏,把交换机拖进来,把VPC拖进来,然后把线连起来就可以了。连接好之后记得点上面的Play按钮开始网络模拟。这里我们就不多说了,直接上图。" +"Although SONiC is powerful, most of the time the price of a switch that " +"supports SONiC OS is not cheap. If you just want to try SONiC without " +"spending money on a hardware device, then this chapter is a must-read. In " +"this chapter, we will summarize how to build a virtual SONiC lab using GNS3 " +"locally, allowing you to quickly experience the basic functionality of SONiC." msgstr "" -#: src/1-2-hello-world-virtually.md:112 +#: src\1-2-hello-world-virtually.md:5 msgid "" -"接着,在交换机上点击右键,选择`Custom " -"Console`,再选择Putty,就可以打开我们的上面看到的交换机的Console了。这里,SONiC的默认用户名和密码是`admin`和`YourPaSsWoRd`。登录进去之后,我们就可以运行熟悉的命令,用`show " -"interfaces status`或者`show ip " -"interface`来查看网络的状态了。我们这里也可以看到,前面两个我们连接好了的Interface的状态都是`up`的了。" -msgstr "" - -#: src/1-2-hello-world-virtually.md:114 -msgid "## 配置网络" +"There are several ways to run SONiC locally, such as docker + vswitch, p4 " +"software switch, etc. For first-time users, using GNS3 may be the most " +"convenient way, so here, we will use GNS3 as an example to explain how to " +"build a SONiC lab locally. So, let's get started!" msgstr "" -#: src/1-2-hello-world-virtually.md:116 -msgid "SONiC软交换机下,默认的端口使用的是10.0.0.x的子网(如下),而且都是eth pair:" +#: src\1-2-hello-world-virtually.md:7 +msgid "Prepare GNS3" msgstr "" -#: src/1-2-hello-world-virtually.md:127 -msgid "这里,我们比较方便的做法是创建一个小的vlan,把我们的端口都包在里面(我们这里用的是Ethernet4和Ethernet8):" +#: src\1-2-hello-world-virtually.md:9 +msgid "" +"First, in order to easily and intuitively establish a virtual network for " +"testing, we need to install GNS3." msgstr "" -#: src/1-2-hello-world-virtually.md:129 +#: src\1-2-hello-world-virtually.md:11 msgid "" -"```bash\n" -"# Remove old config\n" -"sudo config interface ip remove Ethernet4 10.0.0.2/31\n" -"sudo config interface ip remove Ethernet8 10.0.0.4/31\n" -"\n" -"# Create VLAN with id 2\n" -"sudo config vlan add 2\n" -"\n" -"# Add ports to VLAN\n" -"sudo config vlan member add -u 2 Ethernet4\n" -"sudo config vlan member add -u 2 Ethernet8\n" -"\n" -"# Add IP address to VLAN\n" -"sudo config interface ip add Vlan2 10.0.0.0/24\n" -"```" +"[GNS3, short for Graphical Network Simulator 3, is (obviously) a graphical " +"network simulation software](https://www.gns3.com/). It supports various " +"virtualization technologies such as QEMU, VMware, VirtualBox, etc. With it, " +"when we build a virtual network, we don't have to run many commands manually " +"or write scripts. Most of the work can be done through its GUI, which is " +"very convenient." msgstr "" -#: src/1-2-hello-world-virtually.md:145 -msgid "这样,我们的vlan就创建好了,我们可以通过`show vlan brief`来查看一下:" +#: src\1-2-hello-world-virtually.md:13 +msgid "Install Dependencies" msgstr "" -#: src/1-2-hello-world-virtually.md:147 +#: src\1-2-hello-world-virtually.md:15 msgid "" -"```bash\n" -"admin@sonic:~$ show vlan brief\n" -"+-----------+--------------+-----------+----------------+-------------+-----------------------+\n" -"| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | DHCP " -"Helper Address |\n" -"+===========+==============+===========+================+=============+=======================+\n" -"| 2 | 10.0.0.0/24 | Ethernet4 | untagged | disabled | " -" |\n" -"| | | Ethernet8 | untagged | | " -" |\n" -"+-----------+--------------+-----------+----------------+-------------+-----------------------+\n" -"```" +"Before installing it, we need to install several other software: docker, " +"wireshark, putty, qemu, ubridge, libvirt, and bridge-utils. If you have " +"already installed them, you can skip this step." msgstr "" -#: src/1-2-hello-world-virtually.md:157 -msgid "然后,我们就可以给所有的主机配置一个10.0.0.x的IP地址了。" +#: src\1-2-hello-world-virtually.md:17 +msgid "" +"First is Docker. You can install it by following the instructions in this " +"link: " +"[https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)" msgstr "" -#: src/1-2-hello-world-virtually.md:159 +#: src\1-2-hello-world-virtually.md:19 msgid "" -"```bash\n" -"# VPC1\n" -"ip 10.0.0.2 255.0.0.0 10.0.0.1\n" -"\n" -"# VPC2\n" -"ip 10.0.0.3 255.0.0.0 10.0.0.1\n" -"```" +"Installing the others on Ubuntu is very simple, just execute the following " +"command. Note that during the installation of ubridge and Wireshark, you " +"will be asked if you want to create the wireshark user group to bypass sudo. " +"Be sure to choose Yes." msgstr "" -#: src/1-2-hello-world-virtually.md:167 -msgid "好的,现在我们来Ping一下吧!" +#: src\1-2-hello-world-virtually.md:25 +msgid "Once completed, we can proceed to install GNS3." msgstr "" -#: src/1-2-hello-world-virtually.md:171 -msgid "通了!" +#: src\1-2-hello-world-virtually.md:27 +msgid "Install GNS3" msgstr "" -#: src/1-2-hello-world-virtually.md:173 -msgid "## 抓包" +#: src\1-2-hello-world-virtually.md:30 +msgid "" +"On Ubuntu, the installation of GNS3 is very simple, just execute the " +"following commands:" msgstr "" -#: src/1-2-hello-world-virtually.md:175 +#: src\1-2-hello-world-virtually.md:38 msgid "" -"上面,我们安装GNS3前,我们特意安装了Wireshark,这样我们就可以在GNS3里面抓包了。我们只需要右键点击图中我们想抓包的Link上,然后选择`Start " -"capture`,就可以开始抓包了。" +"Then add your user to the following groups, so that GNS3 can access docker, " +"wireshark, and other functionalities without using sudo." msgstr "" -#: src/1-2-hello-world-virtually.md:179 -msgid "稍等一下,Wireshark就会自动打开,实时的显示所有的包,非常的方便:" +#: src\1-2-hello-world-virtually.md:46 +msgid "" +"If you are not using Ubuntu, you can refer to [their official " +"documentation](https://docs.gns3.com/docs/getting-started/installation/linux/) " +"for more detailed installation instructions." msgstr "" -#: src/1-2-hello-world-virtually.md:183 -msgid "## 更多的网络" +#: src\1-2-hello-world-virtually.md:48 +msgid "Prepare the SONiC Image" msgstr "" -#: src/1-2-hello-world-virtually.md:185 +#: src\1-2-hello-world-virtually.md:50 msgid "" -"除了上面这种最简单的网络搭建,我们其实可以用GNS3搭建很多非常复杂的网络来进行测试,比如多层ECMP + eBGP等等。XFlow " -"Research发布了一篇非常详细的文档来介绍这些内容,感兴趣的小伙伴可以去传送到这篇文档去看看:[SONiC Deployment and " -"Testing Using GNS3][SONiCWithGNS3]。" +"Before testing, we need a SONiC image. Since SONiC supports various vendors " +"with different underlying implementations, each vendor has their own image. " +"In our case, since we are creating a virtual environment, we can use the " +"VSwitch-based image to create virtual switches: sonic-vs.img.gz." msgstr "" -#: src/1-2-hello-world-virtually.md:189 +#: src\1-2-hello-world-virtually.md:52 msgid "" -"1. [GNS3][GNS3]\n" -"2. [GNS3 Linux Install][GNS3Install]\n" -"3. [SONiC Deployment and Testing Using GNS3][SONiCWithGNS3]" +"[The SONiC image project is located " +"here](https://github.com/sonic-net/sonic-buildimage). Although we can " +"compile it ourselves, the process can be slow. To save time, we can directly " +"[download the latest image from " +"here](https://sonic-build.azurewebsites.net/ui/sonic/pipelines/142/builds?branchName=master). " +"Just find the latest successful build and download the sonic-vs.img.gz file " +"from the Artifacts section." msgstr "" -#: src/1-3-command-cheatsheet.md:1 -msgid "# 常用命令" +#: src\1-2-hello-world-virtually.md:54 +msgid "Next, let's prepare the project:" msgstr "" -#: src/1-3-command-cheatsheet.md:3 +#: src\1-2-hello-world-virtually.md:59 msgid "" -"为了帮助我们查看和配置SONiC的状态,SONiC提供了大量的CLI命令供我们调用。这些命令大多分为两类:`show`和`config`,他们的格式基本类似,大多都符合下面的格式:" +"# Place the downloaded image in this directory and then run the following " +"command to extract it." msgstr "" -#: src/1-3-command-cheatsheet.md:5 -msgid "" -"```bash\n" -"show [options]\n" -"config [options]\n" -"```" +#: src\1-2-hello-world-virtually.md:62 +msgid "# The following command will generate the GNS3 image configuration file." msgstr "" -#: src/1-3-command-cheatsheet.md:10 +#: src\1-2-hello-world-virtually.md:67 msgid "" -"SONiC的文档提供了非常详细的命令列表:[SONiC Command Line Interface " -"Guide][SONiCCommands],但是由于其命令众多,不便于我们初期的学习和使用,所以列出了一些平时最常用的命令和解释,供大家参考。" +"After executing the above commands, you can run the `ls` command to see the " +"required image file." msgstr "" -#: src/1-3-command-cheatsheet.md:12 -msgid "" -"```admonish info\n" -"SONiC中的所有命令的子命令都可以只打前三个字母,来帮助我们有效的节约输入命令的时间,比如:\n" -"\n" -" show interface transceiver error-status\n" -" \n" -"和下面这条命令是等价的:\n" -"\n" -" show int tra err\n" -"\n" -"为了帮助大家记忆和查找,下面的命令列表都用的全名,但是大家在实际使用的时候,可以大胆的使用缩写来减少工作量。\n" -"```" +#: src\1-2-hello-world-virtually.md:74 +msgid "# <= This is the GNS3 image configuration file" msgstr "" -#: src/1-3-command-cheatsheet.md:24 -msgid "" -"```admonish info\n" -"如果遇到不熟悉的命令,都可以通过输入`-h`或者`--help`来查看帮助信息,比如:\n" -"\n" -" show -h\n" -" show interface --help\n" -" show interface transceiver --help\n" -"\n" -"```" +#: src\1-2-hello-world-virtually.md:75 +msgid "# <= This is the image we extracted" msgstr "" -#: src/1-3-command-cheatsheet.md:33 -msgid "## General" +#: src\1-2-hello-world-virtually.md:79 +msgid "Import the Image" msgstr "" -#: src/1-3-command-cheatsheet.md:35 +#: src\1-2-hello-world-virtually.md:81 msgid "" -"```bash\n" -"show version\n" -"\n" -"show uptime\n" -"\n" -"show platform summary\n" -"```" -msgstr "" - -#: src/1-3-command-cheatsheet.md:43 -msgid "## Config" +"Now, run `gns3` in the command line to start GNS3. If you are SSHed into " +"another machine, you can try enabling X11 forwarding so that you can run " +"GNS3 remotely, with the GUI displayed locally on your machine. And I made " +"this working using MobaXterm on my local machine." msgstr "" -#: src/1-3-command-cheatsheet.md:45 +#: src\1-2-hello-world-virtually.md:83 msgid "" -"```bash\n" -"sudo config reload\n" -"sudo config load_minigraph\n" -"sudo config save -y\n" -"```" +"Once it's up and running, GNS3 will prompt us to create a project. It's " +"simple, just enter a directory. If you are using X11 forwarding, please note " +"that this directory is on your remote server, not local." msgstr "" -#: src/1-3-command-cheatsheet.md:51 -msgid "## Docker相关" +#: src\1-2-hello-world-virtually.md:85 +msgid "![](assets/chapter-1/gns3-new-project.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:53 +#: src\1-2-hello-world-virtually.md:87 msgid "" -"```bash\n" -"docker ps\n" -"```" +"Next, we can import the image we just generated by going to `File -> Import " +"appliance`." msgstr "" -#: src/1-3-command-cheatsheet.md:57 -msgid "" -"```bash\n" -"docker top |\n" -"```" +#: src\1-2-hello-world-virtually.md:89 +msgid "![](assets/chapter-1/gns3-import-appliance-menu.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:61 +#: src\1-2-hello-world-virtually.md:91 msgid "" -"```admonish note\n" -"\n" -"如果我们想对所有的docker container进行某个操作,我们可以通过`docker ps`命令来获取所有的container " -"id,然后pipe到`tail -n +2`来去掉第一行的标题,从而实现批量调用。\n" -"\n" -"比如,我们可以通过如下命令来查看所有container中正在运行的所有线程:\n" -"\n" -" $ for id in `docker ps | tail -n +2 | awk '{print $1}'`; do docker top " -"$id; done\n" -" UID PID PPID C " -" STIME TTY TIME CMD\n" -" root 7126 7103 0 " -" Jun09 pts/0 00:02:24 " -"/usr/bin/python3 /usr/local/bin/supervisord\n" -" root 7390 7126 0 " -" Jun09 pts/0 00:00:24 python3 " -"/usr/bin/supervisor-proc-exit-listener --container-name telemetry\n" -" ...\n" -"```" +"Select the `SONiC-latest.gns3a` image configuration file we just generated " +"and click `Next`." msgstr "" -#: src/1-3-command-cheatsheet.md:74 -msgid "## Interfaces / IPs" +#: src\1-2-hello-world-virtually.md:93 +msgid "![](assets/chapter-1/gns3-import-appliance-select-image.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:76 -msgid "" -"```bash\n" -"show interface status\n" -"show interface counters\n" -"show interface portchannel\n" -"show interface transceiver info\n" -"show interface transceiver error-status\n" -"sonic-clear counters\n" -"\n" -"TODO: config\n" -"```" +#: src\1-2-hello-world-virtually.md:95 +msgid "Now you can see our image, click `Next`." msgstr "" -#: src/1-3-command-cheatsheet.md:87 -msgid "## MAC / ARP / NDP" +#: src\1-2-hello-world-virtually.md:97 +msgid "![](assets/chapter-1/gns3-import-appliance-image.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:89 +#: src\1-2-hello-world-virtually.md:99 msgid "" -"```bash\n" -"# Show MAC (FDB) entries\n" -"show mac\n" -"\n" -"# Show IP ARP table\n" -"show arp\n" -"\n" -"# Show IPv6 NDP table\n" -"show ndp\n" -"```" +"At this point, the image import process will start, which may be slow " +"because GNS3 needs to convert the image to qcow2 format and place it in our " +"project directory. Once completed, we can see our image." msgstr "" -#: src/1-3-command-cheatsheet.md:100 -msgid "## BGP / Routes" +#: src\1-2-hello-world-virtually.md:101 +msgid "![](assets/chapter-1/gns3-import-appliance-done.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:102 -msgid "" -"```bash\n" -"show ip/ipv6 bgp summary\n" -"show ip/ipv6 bgp network\n" -"\n" -"show ip/ipv6 bgp neighbors [IP]\n" -"\n" -"show ip/ipv6 route\n" -"\n" -"TODO: add\n" -"config bgp shutdown neighbor \n" -"config bgp shutdown all\n" -"\n" -"TODO: IPv6\n" -"```" +#: src\1-2-hello-world-virtually.md:103 +msgid "Great! We're done!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:105 +msgid "Create the Network" msgstr "" -#: src/1-3-command-cheatsheet.md:117 -msgid "## LLDP" +#: src\1-2-hello-world-virtually.md:107 +msgid "Alright! Now that everything is set up, let's create a virtual network!" msgstr "" -#: src/1-3-command-cheatsheet.md:119 +#: src\1-2-hello-world-virtually.md:109 msgid "" -"```bash\n" -"# Show LLDP neighbors in table format\n" -"show lldp table\n" -"\n" -"# Show LLDP neighbors details\n" -"show lldp neighbors\n" -"```" +"The GNS3 graphical interface is very user-friendly. Basically, open the " +"sidebar, drag in the switch, drag in the VPC, and then connect the ports. " +"After connecting, remember to click the Play button at the top to start the " +"network simulation. We won't go into much detail here, let's just look at " +"the pictures." msgstr "" -#: src/1-3-command-cheatsheet.md:127 -msgid "## VLAN" +#: src\1-2-hello-world-virtually.md:111 +msgid "![](assets/chapter-1/gns3-console.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:129 +#: src\1-2-hello-world-virtually.md:113 msgid "" -"```bash\n" -"show vlan brief\n" -"```" +"Next, right-click on the switch, select `Custom Console`, then select Putty " +"to open the console of the switch we saw earlier. Here, the default username " +"and password for SONiC are `admin` and `YourPaSsWoRd`. Once logged in, we " +"can run familiar commands like `show interfaces status` or `show ip " +"interface` to check the network status. Here, we can also see that the " +"status of the first two interfaces we connected is `up`." msgstr "" -#: src/1-3-command-cheatsheet.md:133 -msgid "## QoS相关" +#: src\1-2-hello-world-virtually.md:115 +msgid "Configure the Network" msgstr "" -#: src/1-3-command-cheatsheet.md:135 +#: src\1-2-hello-world-virtually.md:117 msgid "" -"```bash\n" -"# Show PFC watchdog stats\n" -"show pfcwd stats\n" -"show queue counter\n" -"```" +"In SONiC software switches, the default ports use the 10.0.0.x subnet (as " +"shown below) with neighbor paired." msgstr "" -#: src/1-3-command-cheatsheet.md:141 -msgid "## ACL" +#: src\1-2-hello-world-virtually.md:128 +msgid "" +"Similar to what we mentioned in [installation](./1-1-install.md), we are " +"going to create a simple network by creating a small VLAN and including our " +"ports in it (in this case, Ethernet4 and Ethernet8):" msgstr "" -#: src/1-3-command-cheatsheet.md:143 -msgid "" -"```bash\n" -"show acl table\n" -"show acl rule\n" -"```" +#: src\1-2-hello-world-virtually.md:131 +msgid "# Remove old config" msgstr "" -#: src/1-3-command-cheatsheet.md:148 -msgid "## MUXcable / Dual ToR" +#: src\1-2-hello-world-virtually.md:134 +msgid "# Create VLAN with id 2" msgstr "" -#: src/1-3-command-cheatsheet.md:150 -msgid "### Muxcable mode" +#: src\1-2-hello-world-virtually.md:137 +msgid "# Add ports to VLAN" msgstr "" -#: src/1-3-command-cheatsheet.md:152 -msgid "" -"```bash\n" -"config muxcable mode {active} {|all} [--json]\n" -"config muxcable mode active Ethernet4 [--json]\n" -"```" +#: src\1-2-hello-world-virtually.md:141 +msgid "# Add IP address to VLAN" msgstr "" -#: src/1-3-command-cheatsheet.md:157 -msgid "### Muxcable config" +#: src\1-2-hello-world-virtually.md:146 +msgid "Now, our VLAN is created, and we can use `show vlan brief` to check:" msgstr "" -#: src/1-3-command-cheatsheet.md:159 +#: src\1-2-hello-world-virtually.md:152 msgid "" -"```bash\n" -"show muxcable config [portname] [--json]\n" -"```" +"==========+==============+===========+================+=============+=======================+" msgstr "" -#: src/1-3-command-cheatsheet.md:163 -msgid "### Muxcable status" +#: src\1-2-hello-world-virtually.md:158 +msgid "Now, let's assign a 10.0.0.x IP address to each host." msgstr "" -#: src/1-3-command-cheatsheet.md:165 -msgid "" -"```bash\n" -"show muxcable status [portname] [--json] \n" -"```" +#: src\1-2-hello-world-virtually.md:161 +msgid "# VPC1" msgstr "" -#: src/1-3-command-cheatsheet.md:169 -msgid "### Muxcable firmware" +#: src\1-2-hello-world-virtually.md:163 +msgid "# VPC2" msgstr "" -#: src/1-3-command-cheatsheet.md:171 -msgid "" -"```bash\n" -"# Firmware version:\n" -"show muxcable firmware version \n" -"\n" -"# Firmware download\n" -"# config muxcable firmware download \n" -"sudo config muxcable firmware download " -"AEC_WYOMING_B52Yb0_MS_0.6_20201218.bin Ethernet0\n" -"\n" -"# Rollback:\n" -"# config muxcable firmware rollback \n" -"sudo config muxcable firmware rollback Ethernet0\n" -"```" +#: src\1-2-hello-world-virtually.md:168 +msgid "Alright, let's start the ping!" +msgstr "" + +#: src\1-2-hello-world-virtually.md:170 +msgid "![](assets/chapter-1/gns3-ping.png)" msgstr "" -#: src/1-3-command-cheatsheet.md:186 -msgid "1. [SONiC Command Line Interface Guide][SONiCCommands]" +#: src\1-2-hello-world-virtually.md:172 +msgid "It works!" msgstr "" -#: src/2-core-components-intro.md:1 -msgid "# 核心组件简介" +#: src\1-2-hello-world-virtually.md:174 +msgid "Packet Capture" msgstr "" -#: src/2-core-components-intro.md:3 +#: src\1-2-hello-world-virtually.md:176 msgid "" -"我们也许会觉得交换机是一个很简单的网络设备,但是实际上交换机上的组件非常的多,而且由于SONiC中Redis的解耦,我们很难简单的对代码进行跟踪来理解服务之间的关系,这就需要我们先建立一个比较抽象的整体模型,然后再去深入的学习每个组件的细节。所以在深入其他部分之前,我们这里先对每个组件都做一个点到为止的介绍,帮助大家建立一个大概的整体模型。" +"Before installing GNS3, we installed Wireshark so that we can capture " +"packets within the virtual network created by GNS3. To start capturing, " +"simply right-click on the link you want to capture on and select `Start " +"capture`." +msgstr "" + +#: src\1-2-hello-world-virtually.md:178 +msgid "![](assets/chapter-1/gns3-capture.png)" msgstr "" -#: src/2-core-components-intro.md:5 +#: src\1-2-hello-world-virtually.md:180 msgid "" -"```admonish info\n" -"在阅读本章之前,有两个名词会经常在本章和SONiC的官方文档中出现:ASIC(Application-Specific Integrated " -"Circuit)和ASIC状态(State)。它们指的是交换机中用来进行包处理的Pipeline的状态,比如,ACL,转发方式等等,这个和其他交换机的硬件状态,比如,端口状态(端口速度,接口类型),IP信息等等硬件状态是非常不同的。\n" -"\n" -"如果大家有兴趣了解更深入的细节,可以先移步阅读两个相关资料:[SAI (Switch Abstraction Interface) " -"API][SAIAPI]和一篇RMT(Reprogrammable Match Table)的相关论文:[Forwarding " -"Metamorphosis: Fast Programmable Match-Action Processing in Hardware for " -"SDN][PISA]。\n" -"\n" -"这些都会对我们阅读SONiC的文档有很大的帮助。\n" -"```" +"After a moment, Wireshark will automatically open and display all the " +"packets in real-time. Very convenient!" msgstr "" -#: src/2-core-components-intro.md:13 -msgid "另外为了方便我们的理解和阅读,我们也把SONiC架构图在这里放在这一章的开头,作为引用:" -msgstr "" - -#: src/2-core-components-intro.md:21 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]" -msgstr "" - -#: src/2-1-database.md:1 -msgid "# Redis数据库" -msgstr "" - -#: src/2-1-database.md:3 -msgid "" -"首先,在SONiC里面最核心的服务,自然是当之无愧的中心数据库Redis了!它的主要目的有两个:存储所有服务的配置和状态,并且为各个服务提供通信的媒介。" -msgstr "" - -#: src/2-1-database.md:5 -msgid "" -"为了提供这些功能,SONiC会在Redis中创建一个名为`sonic-db`的数据库实例,其配置和分库信息我们可以在`/var/run/redis/sonic-db/database_config.json`中找到:" -msgstr "" - -#: src/2-1-database.md:7 -msgid "" -"```bash\n" -"admin@sonic:~$ cat /var/run/redis/sonic-db/database_config.json\n" -"{\n" -" \"INSTANCES\": {\n" -" \"redis\": {\n" -" \"hostname\": \"127.0.0.1\",\n" -" \"port\": 6379,\n" -" \"unix_socket_path\": \"/var/run/redis/redis.sock\",\n" -" \"persistence_for_warm_boot\": \"yes\"\n" -" }\n" -" },\n" -" \"DATABASES\": {\n" -" \"APPL_DB\": { \"id\": 0, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"ASIC_DB\": { \"id\": 1, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"COUNTERS_DB\": { \"id\": 2, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"LOGLEVEL_DB\": { \"id\": 3, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"CONFIG_DB\": { \"id\": 4, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"PFC_WD_DB\": { \"id\": 5, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"FLEX_COUNTER_DB\": { \"id\": 5, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"STATE_DB\": { \"id\": 6, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"SNMP_OVERLAY_DB\": { \"id\": 7, \"separator\": \"|\", " -"\"instance\": \"redis\" },\n" -" \"RESTAPI_DB\": { \"id\": 8, \"separator\": \"|\", \"instance\": " -"\"redis\" },\n" -" \"GB_ASIC_DB\": { \"id\": 9, \"separator\": \":\", \"instance\": " -"\"redis\" },\n" -" \"GB_COUNTERS_DB\": { \"id\": 10, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"GB_FLEX_COUNTER_DB\": { \"id\": 11, \"separator\": \":\", " -"\"instance\": \"redis\" },\n" -" \"APPL_STATE_DB\": { \"id\": 14, \"separator\": \":\", \"instance\": " -"\"redis\" }\n" -" },\n" -" \"VERSION\": \"1.0\"\n" -"}\n" -"```" +#: src\1-2-hello-world-virtually.md:182 +msgid "![](assets/chapter-1/gns3-capture-live.png)" msgstr "" -#: src/2-1-database.md:38 -msgid "虽然我们可以看到SONiC中的数据库有十来个,但是我们大部分时候只需要关注以下几个最重要的数据库就可以了:" +#: src\1-2-hello-world-virtually.md:184 +msgid "More Networks" msgstr "" -#: src/2-1-database.md:40 +#: src\1-2-hello-world-virtually.md:186 msgid "" -"- **CONFIG_DB(ID = " -"4)**:存储所有服务的**配置信息**,比如端口配置,VLAN配置等等。它代表着**用户想要交换机达到的状态**的数据模型,这也是所有CLI和外部应用程序修改配置时的主要操作对象。\n" -"- **APPL_DB(Application DB, ID = " -"0)**:存储**所有服务的内部状态信息**。这些信息有两种:一种是各个服务在读取了CONFIG_DB的配置信息后,自己计算出来的。我们可以理解为**各个服务想要交换机达到的状态**(Goal " -"State),还有一种是当最终硬件状态发生变化被写回时,有些服务会直接写回到APPL_DB,而不是我们下面马上要介绍的STATE_DB。这些信息我们可以理解为**各个服务认为交换机当前的状态**(Current " -"State)。\n" -"- **STATE_DB(ID = 6)**:存储着交换机**各个部件当前的状态**(Current " -"State)。当SONiC中的服务收到了STATE_DB的状态变化,但是发现和Goal " -"State不一致的时候,SONiC就会重新下发配置,直到两者一致。(当然,对于那些回写到APPL_DB状态,服务就会监听APPL_DB的变化,而不是STATE_DB了。)\n" -"- **ASIC_DB(ID = " -"1)**:存储着**SONiC想要交换机ASIC达到状态信息**,比如,ACL,路由等等。和APPL_DB不同,这个数据库里面的数据模型是面向ASIC设计的,而不是面向服务抽象的。这样做的目的是为了方便各个厂商进行SAI和ASIC驱动的开发。" +"In addition to the simplest network setup we discussed above, we can " +"actually use GNS3 to build much more complex networks for testing, such as " +"multi-layer ECMP + eBGP, and more. XFlow Research has published a very " +"detailed document that covers these topics. Interested folks can refer to " +"the document: [SONiC Deployment and Testing Using " +"GNS3](https://xflowresearch.com/wp-content/uploads/2023/05/SONiC-Deployment-and-Testing-Using-GNS3.pdf)." msgstr "" -#: src/2-1-database.md:45 -msgid "" -"这里,我们会发现一个很直观的问题:交换机里面这么多服务,难道所有的配置和状态都放在一个数据库里面没有隔离的么?如果两个服务用了同一个Redis " -"Key怎么办呢?这个问题非常的好,SONiC的解决也很直接,那就是在每个数据库里面继续分表!" +#: src\1-2-hello-world-virtually.md:190 +msgid "[GNS3](https://www.gns3.com/)" msgstr "" -#: src/2-1-database.md:47 +#: src\1-2-hello-world-virtually.md:191 msgid "" -"我们知道Redis在每个数据库里面并没有表的概念,而是使用key-value的方式来存储数据。所以,为了进一步分表,SONiC的解决方法是将表的名字放入key中,并且使用分隔符将表和key隔开。上面的配置文件中`separator`字段就是做这个了。比如:`APPL_DB`中的`PORT_TABLE`表中的`Ethernet4`端口的状态,我们可以通过`PORT_TABLE:Ethernet4`来获取,如下:" +"[GNS3 Linux " +"Install](https://docs.gns3.com/docs/getting-started/installation/linux/)" msgstr "" -#: src/2-1-database.md:49 +#: src\1-2-hello-world-virtually.md:192 msgid "" -"```bash\n" -"127.0.0.1:6379> select 0\n" -"OK\n" -"\n" -"127.0.0.1:6379> hgetall PORT_TABLE:Ethernet4\n" -" 1) \"admin_status\"\n" -" 2) \"up\"\n" -" 3) \"alias\"\n" -" 4) \"Ethernet6/1\"\n" -" 5) \"index\"\n" -" 6) \"6\"\n" -" 7) \"lanes\"\n" -" 8) \"13,14,15,16\"\n" -" 9) \"mtu\"\n" -"10) \"9100\"\n" -"11) \"speed\"\n" -"12) \"40000\"\n" -"13) \"description\"\n" -"14) \"\"\n" -"15) \"oper_status\"\n" -"16) \"up\"\n" -"```" +"[SONiC Deployment and Testing Using " +"GNS3](https://xflowresearch.com/wp-content/uploads/2023/05/SONiC-Deployment-and-Testing-Using-GNS3.pdf)" +msgstr "" + +#: src\1-3-command-cheatsheet.md:2 +msgid "Common Commands" msgstr "" -#: src/2-1-database.md:72 -msgid "当然在SONiC中,不仅仅是数据模型,包括通信机制,都是使用类似的方法来实现“表”级别的隔离的。" +#: src\1-3-command-cheatsheet.md:4 +msgid "" +"To help us check and configure the state of SONiC, SONiC provides a large " +"number of CLI commands for us to use. These commands are mostly divided into " +"two categories: `show` and `config`. Their formats are generally similar, " +"mostly following the format below:" msgstr "" -#: src/2-1-database.md:76 -#: src/2-2-services-intro.md:76 -msgid "1. [SONiC Architecture][SONiCArch]" +#: src\1-3-command-cheatsheet.md:11 +msgid "" +"The SONiC documentation provides a very detailed list of commands: [SONiC " +"Command Line Interface " +"Guide](https://github.com/sonic-net/sonic-utilities/blob/master/doc/Command-Reference.md), " +"but due to the large number of commands, it is not very convenient for us to " +"ramp up, so we listed some of the most commonly used commands and " +"explanations for reference." msgstr "" -#: src/2-2-services-intro.md:1 -msgid "# 服务与工作流简介" +#: src\1-3-command-cheatsheet.md:34 +msgid "Basic system information" msgstr "" -#: src/2-2-services-intro.md:3 -msgid "" -"SONiC里面的服务(常驻进程)非常的多,有二三十种,它们会在随着交换机启动而启动,并一直保持运行,直到交换机关机。如果我们想快速掌握SONiC,一个一个服务的去了解,会很容易陷入细节的泥潭,所以,我们最好把这些服务和控制流进行一个大的分类,以帮助我们建立一个宏观的概念。" +#: src\1-3-command-cheatsheet.md:37 +msgid "# Show system version, platform info and docker containers" msgstr "" -#: src/2-2-services-intro.md:5 -msgid "" -"```admonish note\n" -"我们这里不会深入到某一个具体的服务中去,而是先从整体上来看看SONiC中的服务的结构,帮助我们建立一个整体的认识。关于具体的服务,我们会在工作流一章中,对常用的工作流进行介绍,而关于详细的技术细节,大家也可以查阅每个服务相关的设计文档。\n" -"```" +#: src\1-3-command-cheatsheet.md:39 +msgid "# Show system uptime" msgstr "" -#: src/2-2-services-intro.md:9 -msgid "## 服务分类" +#: src\1-3-command-cheatsheet.md:42 +msgid "# Show platform information, such as HWSKU" msgstr "" -#: src/2-2-services-intro.md:11 -msgid "总体而言,SONiC中的服务可以分为以下几类:`*syncd`, `*mgrd`,feature实现,`orchagent`和`syncd`。" +#: src\1-3-command-cheatsheet.md:47 +msgid "Config" msgstr "" -#: src/2-2-services-intro.md:13 -msgid "### `*syncd`服务" +#: src\1-3-command-cheatsheet.md:50 +msgid "# Reload all config." msgstr "" -#: src/2-2-services-intro.md:15 +#: src\1-3-command-cheatsheet.md:50 msgid "" -"这类服务名字中都以`syncd`结尾。它们做的事情都很类似:它们负责将硬件状态同步到Redis中,一般目标都以APPL_DB或者STATE_DB为主。" +"# WARNING: This will restart almost all services and will cause network " +"interruption." msgstr "" -#: src/2-2-services-intro.md:17 +#: src\1-3-command-cheatsheet.md:53 msgid "" -"比如,`portsyncd`就是通过监听netlink的事件,将交换机中所有Port的状态同步到STATE_DB中,而`natsyncd`则是监听netlink的事件,将交换机中所有的NAT状态同步到APPL_DB中。" +"# Save the current config from redis DB to disk, which makes the config " +"persistent across reboots." msgstr "" -#: src/2-2-services-intro.md:19 -msgid "### `*mgrd`服务" +#: src\1-3-command-cheatsheet.md:54 +msgid "# NOTE: The config file is saved to `/etc/sonic/config_db.json`" msgstr "" -#: src/2-2-services-intro.md:21 -msgid "" -"这类服务名字中都以`mgrd`结尾。顾名思义,这些服务是所谓的“Manager”服务,也就是说它们负责各个硬件的配置,和`*syncd`完全相反。它们的逻辑主要有两个部分:" +#: src\1-3-command-cheatsheet.md:59 +msgid "Docker Related" msgstr "" -#: src/2-2-services-intro.md:23 -msgid "" -"1. " -"**配置下发**:负责读取配置文件和监听Redis中的配置和状态改变(主要是CONFIG_DB,APPL_DB和STATE_DB),然后将这些修改推送到交换机硬件中去。推送的方法有多种,取决于更新的目标是什么,可以通过更新APPL_DB并发布更新消息,或者是直接调用linux下的命令行,对系统进行修改。比如:`nbrmgr`就是监听CONFIG_DB,APPL_DB和STATE_DB中neighbor的变化,并调用netlink和command " -"line来对neighbor和route进行修改,而`intfmgr`除了调用command line还会将一些状态更新到APPL_DB中去。\n" -"2. " -"**状态同步**:对于需要Reconcile的服务,`*mgrd`还会监听STATE_DB中的状态变化,如果发现硬件状态和当前期望状态不一致,就会重新发起配置流程,将硬件状态设置为期望状态。这些STATE_DB中的状态变化一般都是`*syncd`服务推送的。比如:`intfmgr`就会监听STATE_DB中,由`portsyncd`推送的,端口的Up/Down状态和MTU变化,一旦发现和其内存中保存的期望状态不一致,就会重新下发配置。" +#: src\1-3-command-cheatsheet.md:62 +msgid "# Show all docker containers" msgstr "" -#: src/2-2-services-intro.md:26 -msgid "### 功能实现服务" +#: src\1-3-command-cheatsheet.md:64 +msgid "# Show processes running in a container" msgstr "" -#: src/2-2-services-intro.md:28 -msgid "" -"有一些功能并不是依靠OS本身来完成的,而是由一些特定的进程来实现的,比如BGP,或者一些外部接口。这些服务名字中经常以`d`结尾,表示deamon,比如:`bgpd`,`lldpd`,`snmpd`,`teamd`等,或者干脆就是这个功能的名字,比如:`fancontrol`。" +#: src\1-3-command-cheatsheet.md:67 +msgid "# Enter the container" msgstr "" -#: src/2-2-services-intro.md:30 -msgid "### `orchagent`服务" +#: src\1-3-command-cheatsheet.md:85 +msgid "Interfaces / IPs" msgstr "" -#: src/2-2-services-intro.md:32 -msgid "" -"这个是SONiC中最重要的一个服务,不像其他的服务只负责一两个特定的功能,`orchagent`作为交换机ASIC状态的编排者(orchestrator),会检查数据库中所有来自`*syncd`服务的状态,整合起来并下发给用于保存交换机ASIC配置的数据库:ASIC_DB。这些状态最后会被`syncd`接收,并调用SAI " -"API经过各个厂商提供的SAI实现和ASIC SDK和ASIC进行交互,最终将配置下发到交换机硬件中。" +#: src\1-3-command-cheatsheet.md:98 +msgid "MAC / ARP / NDP" msgstr "" -#: src/2-2-services-intro.md:34 -msgid "### `syncd`服务" +#: src\1-3-command-cheatsheet.md:101 +msgid "# Show MAC (FDB) entries" msgstr "" -#: src/2-2-services-intro.md:36 -msgid "`syncd`服务是`orchagent`的下游,它虽然名字叫`syncd`,但是它却同时肩负着ASIC的`*mgrd`和`*syncd`的工作。" +#: src\1-3-command-cheatsheet.md:103 +msgid "# Show IP ARP table" msgstr "" -#: src/2-2-services-intro.md:38 -msgid "" -"- 首先,作为`*mgrd`,它会监听ASIC_DB的状态变化,一旦发现,就会获取其新的状态并调用SAI API,将配置下发到交换机硬件中。\n" -"- " -"然后,作为`*syncd`,如果ASIC发送了任何的通知给SONiC,它也会将这些通知通过消息的方式发送到Redis中,以便`orchagent`和`*mgrd`服务获取到这些变化,并进行处理。这些通知的类型我们可以在[SwitchNotifications.h][SAISwitchNotify]中找到。" +#: src\1-3-command-cheatsheet.md:106 +msgid "# Show IPv6 NDP table" msgstr "" -#: src/2-2-services-intro.md:41 -msgid "## 服务间控制流分类" +#: src\1-3-command-cheatsheet.md:111 +msgid "BGP / Routes" msgstr "" -#: src/2-2-services-intro.md:43 -msgid "" -"有了这些分类,我们就可以更加清晰的来理解SONiC中的服务了,而其中非常重要的就是理解服务之间的控制流。有了上面的分类,我们这里也可以把主要的控制流有分为两类:配置下发和状态同步。" +#: src\1-3-command-cheatsheet.md:128 +msgid "LLDP" msgstr "" -#: src/2-2-services-intro.md:45 -msgid "### 配置下发" +#: src\1-3-command-cheatsheet.md:131 +msgid "# Show LLDP neighbors in table format" msgstr "" -#: src/2-2-services-intro.md:47 -msgid "配置下发的流程一般是这样的:" +#: src\1-3-command-cheatsheet.md:133 +msgid "# Show LLDP neighbors details" msgstr "" -#: src/2-2-services-intro.md:49 -msgid "" -"1. **修改配置**:用户可以通过CLI或者REST " -"API修改配置,这些配置会被写入到CONFIG_DB中并通过Redis发送更新通知。或者外部程序可以通过特定的接口,比如BGP的API,来修改配置,这种配置会通过内部的TCP " -"Socket发送给`*mgrd`服务。\n" -"2. " -"**`*mgrd`下发配置**:服务监听到CONFIG_DB中的配置变化,然后将这些配置推送到交换机硬件中。这里由两种主要情况(并且可以同时存在):\n" -" 1. **直接下发**:\n" -" 1. `*mgrd`服务直接调用linux下的命令行,或者是通过netlink来修改系统配置\n" -" 2. `*syncd`服务会通过netlink或者其他方式监听到系统配置的变化,并将这些变化推送到STATE_DB或者APPL_DB中。\n" -" 3. " -"`*mgrd`服务监听到STATE_DB或者APPL_DB中的配置变化,然后将这些配置和其内存中存储的配置进行比较,如果发现不一致,就会重新调用命令行或者netlink来修改系统配置,直到它们一致为止。\n" -" 2. **间接下发**:\n" -" 1. `*mgrd`将状态推送到APPL_DB并通过Redis发送更新通知。\n" -" 2. `orchagent`服务监听到配置变化,然后根据所有相关的状态,计算出此时ASIC应该达到的状态,并下发到ASIC_DB中。\n" -" 3. `syncd`服务监听到ASIC_DB的变化,然后将这些新的配置通过统一的SAI API接口,调用ASIC " -"Driver更新交换机ASIC中的配置。" +#: src\1-3-command-cheatsheet.md:138 +msgid "VLAN" msgstr "" -#: src/2-2-services-intro.md:60 -msgid "配置初始化和配置下发类似,不过是在服务启动的时候读取配置文件,这里就不展开了。" +#: src\1-3-command-cheatsheet.md:144 +msgid "QoS Related" msgstr "" -#: src/2-2-services-intro.md:62 -msgid "### 状态同步" +#: src\1-3-command-cheatsheet.md:147 +msgid "# Show PFC watchdog stats" msgstr "" -#: src/2-2-services-intro.md:64 -msgid "如果这个时候,出现了一些情况,比如网口坏了,ASIC中的状态变了等等,这个时候我们就需要进行状态更新和同步了。这个流程一般是这样的:" +#: src\1-3-command-cheatsheet.md:152 +msgid "ACL" msgstr "" -#: src/2-2-services-intro.md:66 -msgid "" -"1. **检测状态变化**:这个状态变化主要来源于`*syncd`服务(netlink等等)和`syncd`服务([SAI Switch " -"Notification][SAISwitchNotify]),这些服务在检测到变化后,会将它们发送给STATE_DB或者APPL_DB。\n" -"2. " -"**处理状态变化**:`orchagent`和`*mgrd`服务会监听到这些变化,然后开始处理,将新的配置重新通过命令行和netlink下发给系统,或者下发到ASIC_DB中,让`syncd`服务再次对ASIC进行更新。" +#: src\1-3-command-cheatsheet.md:159 +msgid "MUXcable / Dual ToR" msgstr "" -#: src/2-2-services-intro.md:69 -msgid "### 具体例子" +#: src\1-3-command-cheatsheet.md:161 +msgid "Muxcable mode" msgstr "" -#: src/2-2-services-intro.md:71 -msgid "" -"SONiC的官方文档中给出了几个典型的控制流流转的例子,这里就不过多的展开了,有兴趣的朋友可以去这里看看:[SONiC Subsystem " -"Interactions](https://github.com/sonic-net/SONiC/wiki/Architecture#sonic-subsystems-interactions)。我们在后面工作流一章中,也会选择一些非常常用的工作流进行展开。" +#: src\1-3-command-cheatsheet.md:168 +msgid "Muxcable config" msgstr "" -#: src/2-3-key-containers.md:1 -msgid "# 核心容器" +#: src\1-3-command-cheatsheet.md:174 +msgid "Muxcable status" msgstr "" -#: src/2-3-key-containers.md:3 -msgid "SONiC的设计中最具特色的地方:容器化。" +#: src\1-3-command-cheatsheet.md:180 +msgid "Muxcable firmware" msgstr "" -#: src/2-3-key-containers.md:5 -msgid "" -"从SONiC的上面的设计图中,我们可以看出来,SONiC中,所有的服务都是以容器的形式存在的。在登录进交换机之后,我们可以通过`docker " -"ps`命令来查看当前运行的容器:" +#: src\1-3-command-cheatsheet.md:183 +msgid "# Firmware version:" msgstr "" -#: src/2-3-key-containers.md:7 -msgid "" -"```bash\n" -"admin@sonic:~$ docker ps\n" -"CONTAINER ID IMAGE COMMAND " -"CREATED STATUS PORTS NAMES\n" -"ddf09928ec58 docker-snmp:latest \"/usr/local/bin/supe…\" " -" 2 days ago Up 32 hours snmp\n" -"c480f3cf9dd7 docker-sonic-mgmt-framework:latest \"/usr/local/bin/supe…\" " -" 2 days ago Up 32 hours mgmt-framework\n" -"3655aff31161 docker-lldp:latest \"/usr/bin/docker-lld…\" " -" 2 days ago Up 32 hours lldp\n" -"78f0b12ed10e docker-platform-monitor:latest \"/usr/bin/docker_ini…\" " -" 2 days ago Up 32 hours pmon\n" -"f9d9bcf6c9a6 docker-router-advertiser:latest \"/usr/bin/docker-ini…\" " -" 2 days ago Up 32 hours radv\n" -"2e5dbee95844 docker-fpm-frr:latest \"/usr/bin/docker_ini…\" " -" 2 days ago Up 32 hours bgp\n" -"bdfa58009226 docker-syncd-brcm:latest \"/usr/local/bin/supe…\" " -" 2 days ago Up 32 hours syncd\n" -"655e550b7a1b docker-teamd:latest \"/usr/local/bin/supe…\" " -" 2 days ago Up 32 hours teamd\n" -"1bd55acc181c docker-orchagent:latest \"/usr/bin/docker-ini…\" " -" 2 days ago Up 32 hours swss\n" -"bd20649228c8 docker-eventd:latest \"/usr/local/bin/supe…\" " -" 2 days ago Up 32 hours eventd\n" -"b2f58447febb docker-database:latest \"/usr/local/bin/dock…\" " -" 2 days ago Up 32 hours database\n" -"```" +#: src\1-3-command-cheatsheet.md:185 +msgid "# Firmware download" +msgstr "" + +#: src\1-3-command-cheatsheet.md:186 +msgid "# config muxcable firmware download " msgstr "" -#: src/2-3-key-containers.md:23 -msgid "这里我们来简单介绍一下这些容器。" +#: src\1-3-command-cheatsheet.md:189 +msgid "# Rollback:" msgstr "" -#: src/2-3-key-containers.md:25 -msgid "## 数据库容器:database" +#: src\1-3-command-cheatsheet.md:190 +msgid "# config muxcable firmware rollback " msgstr "" -#: src/2-3-key-containers.md:27 +#: src\1-3-command-cheatsheet.md:197 msgid "" -"这个容器中运行的就是我们多次提到的SONiC中的中心数据库Redis了,它里面存放着所有交换机的配置和状态信息,SONiC也是主要通过它来向各个服务提供底层的通信机制。" +"[SONiC Command Line Interface " +"Guide](https://github.com/sonic-net/sonic-utilities/blob/master/doc/Command-Reference.md)" msgstr "" -#: src/2-3-key-containers.md:29 -msgid "我们通过docker进入这个容器,就可以看到里面正在运行的redis进程了:" +#: src\2-core-components-intro.md:1 +msgid "Core components" msgstr "" -#: src/2-3-key-containers.md:31 +#: src\2-core-components-intro.md:3 msgid "" -"```bash\n" -"admin@sonic:~$ sudo docker exec -it database bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 82 13.7 1.7 130808 71692 pts/0 Sl Apr26 393:27 " -"/usr/bin/redis-server 127.0.0.1:6379\n" -"...\n" -"\n" -"root@sonic:/# cat /var/run/redis/redis.pid\n" -"82\n" -"```" +"We might feel that a switch is a simple network device, but in fact, there " +"could be many components running on the switch." msgstr "" -#: src/2-3-key-containers.md:44 +#: src\2-core-components-intro.md:5 msgid "" -"那么别的容器是如何来访问这个Redis数据库的呢?答案是通过Unix Socket。我们可以在database容器中看到这个Unix " -"Socket,它将交换机上的`/var/run/redis`目录map进database容器,让database容器可以创建这个socket:" +"Since SONiC decoupled all its services using Redis, it can be difficult to " +"understand the relationships between services by simpling tracking the code. " +"To get started on SONiC quickly, it is better to first establish a " +"high-level model, and then delve into the details of each component. " +"Therefore, before diving into other parts, we will first give a brief " +"introduction to each component to help everyone build a rough overall model." msgstr "" -#: src/2-3-key-containers.md:46 +#: src\2-core-components-intro.md:13 msgid "" -"```bash\n" -"# In database container\n" -"root@sonic:/# ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"\n" -"# On host\n" -"admin@sonic:~$ ls /var/run/redis\n" -"redis.pid redis.sock sonic-db\n" -"```" +"In addition, to help us get started, we placed the SONiC architecture " +"diagram here again as a reference:" msgstr "" -#: src/2-3-key-containers.md:56 -msgid "然后再将这个socket给map到其他的容器中,这样所有容器就都可以来访问这个中心数据库啦,比如,swss容器:" +#: src\2-core-components-intro.md:15 +msgid "![](assets/chapter-2/sonic-arch.png)" msgstr "" -#: src/2-3-key-containers.md:58 +#: src\2-core-components-intro.md:21 src\2-1-database.md:78 +#: src\2-2-services-intro.md:76 src\2-3-key-containers.md:183 +#: src\2-4-sai-intro.md:166 src\3-1-code-repos.md:135 +#: src\4-communications.md:21 src\4-2-1-redis-wrappers.md:34 +#: src\4-2-2-subscribe-state-table.md:73 +#: src\4-2-3-notification-producer-consumer.md:54 +#: src\4-2-4-producer-consumer-table.md:130 +#: src\4-2-5-producer-consumer-state-table.md:125 src\4-4-orch-layer.md:36 +#: src\4-5-event-polling-and-error-handling.md:123 src\5-1-syncd-and-sai.md:824 +#: src\5-2-bgp.md:34 src\5-2-1-bgp-cli.md:91 +#: src\5-2-2-route-update-in-frr.md:737 src\5-2-3-route-update-in-sonic.md:736 msgid "" -"```bash\n" -"admin@sonic:~$ docker inspect swss\n" -"...\n" -" \"HostConfig\": {\n" -" \"Binds\": [\n" -" ...\n" -" \"/var/run/redis:/var/run/redis:rw\",\n" -" ...\n" -" ],\n" -"...\n" -"```" +"[SONiC Architecture](https://github.com/sonic-net/SONiC/wiki/Architecture)" msgstr "" -#: src/2-3-key-containers.md:70 -msgid "## 交换机状态管理容器:swss(Switch State Service)" -msgstr "" - -#: src/2-3-key-containers.md:72 -msgid "" -"这个容器可以说是SONiC中最关键的容器了,**它是SONiC的大脑**,里面运行着大量的`*syncd`和`*mgrd`服务,用来管理交换机方方面面的配置,比如Port,neighbor,ARP,VLAN,Tunnel等等等等。另外里面还运行着上面提到的`orchagent`,用来统一处理和ASIC相关的配置和状态变化。" -msgstr "" - -#: src/2-3-key-containers.md:74 -msgid "这些服务大概的功能和流程我们上面已经提过了,所以就不再赘述了。这里我们可以通过`ps`命令来看一下这个容器中运行的服务:" -msgstr "" - -#: src/2-3-key-containers.md:76 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it swss bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 43 0.0 0.2 91016 9688 pts/0 Sl Apr26 0:18 " -"/usr/bin/portsyncd\n" -"root 49 0.1 0.6 558420 27592 pts/0 Sl Apr26 4:31 " -"/usr/bin/orchagent -d /var/log/swss -b 8192 -s -m 00:1c:73:f2:bc:b4\n" -"root 74 0.0 0.2 91240 9776 pts/0 Sl Apr26 0:19 " -"/usr/bin/coppmgrd\n" -"root 93 0.0 0.0 4400 3432 pts/0 S Apr26 0:09 /bin/bash " -"/usr/bin/arp_update\n" -"root 94 0.0 0.2 91008 8568 pts/0 Sl Apr26 0:09 " -"/usr/bin/neighsyncd\n" -"root 96 0.0 0.2 91168 9800 pts/0 Sl Apr26 0:19 " -"/usr/bin/vlanmgrd\n" -"root 99 0.0 0.2 91320 9848 pts/0 Sl Apr26 0:20 " -"/usr/bin/intfmgrd\n" -"root 103 0.0 0.2 91136 9708 pts/0 Sl Apr26 0:19 " -"/usr/bin/portmgrd\n" -"root 104 0.0 0.2 91380 9844 pts/0 Sl Apr26 0:20 " -"/usr/bin/buffermgrd -l /usr/share/sonic/hwsku/pg_profile_lookup.ini\n" -"root 107 0.0 0.2 91284 9836 pts/0 Sl Apr26 0:20 " -"/usr/bin/vrfmgrd\n" -"root 109 0.0 0.2 91040 8600 pts/0 Sl Apr26 0:19 " -"/usr/bin/nbrmgrd\n" -"root 110 0.0 0.2 91184 9724 pts/0 Sl Apr26 0:19 " -"/usr/bin/vxlanmgrd\n" -"root 112 0.0 0.2 90940 8804 pts/0 Sl Apr26 0:09 " -"/usr/bin/fdbsyncd\n" -"root 113 0.0 0.2 91140 9656 pts/0 Sl Apr26 0:20 " -"/usr/bin/tunnelmgrd\n" -"root 208 0.0 0.0 5772 1636 pts/0 S Apr26 0:07 " -"/usr/sbin/ndppd\n" -"...\n" -"```" +#: src\2-core-components-intro.md:22 src\2-4-sai-intro.md:167 +#: src\3-1-code-repos.md:138 +msgid "[SAI API](https://github.com/opencomputeproject/SAI/wiki/SAI-APIs)" msgstr "" -#: src/2-3-key-containers.md:99 -msgid "## ASIC管理容器:syncd" +#: src\2-core-components-intro.md:23 src\2-4-sai-intro.md:168 +msgid "" +"[Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " +"Hardware for " +"SDN](http://yuba.stanford.edu/~grg/docs/sdn-chip-sigcomm-2013.pdf)" msgstr "" -#: src/2-3-key-containers.md:101 -msgid "" -"这个容器中主要是用于管理交换机上的ASIC的,里面运行着`syncd`服务。我们之前提到的各个厂商提供的SAI(Switch Abstraction " -"Interface)和ASIC " -"Driver都是放在这个容器中的。正是因为这个容器的存在,才使得SONiC可以支持多种不同的ASIC,而不需要修改上层的服务。换句话说,如果没有这个容器,那SONiC就是一个缸中大脑,除了一些基本的配置,其他只能靠想的,什么都干不了。" +#: src\2-1-database.md:1 +msgid "Redis database" msgstr "" -#: src/2-3-key-containers.md:103 +#: src\2-1-database.md:3 msgid "" -"在syncd容器中运行的服务并不多,就是syncd,我们可以通过`ps`命令来查看,而在`/usr/lib`目录下,我们也可以找到这个为了支持ASIC而编译出来的巨大无比的SAI文件:" +"First and foremost, the core service in SONiC is undoubtedly the central " +"database - Redis! It has two major purposes: storing the configuration and " +"state of all services, and providing a communication channel for these " +"services." msgstr "" -#: src/2-3-key-containers.md:105 +#: src\2-1-database.md:5 msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 20 0.0 0.0 87708 1544 pts/0 Sl Apr26 0:00 " -"/usr/bin/dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b " -"/tmp/break_before_make_objects\n" -"root 32 10.7 14.9 2724404 599408 pts/0 Sl Apr26 386:49 " -"/usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b " -"/tmp/break_before_make_objects\n" -"...\n" -"\n" -"root@sonic:/# ls -lh /usr/lib\n" -"total 343M\n" -"...\n" -"lrwxrwxrwx 1 root root 13 Apr 25 04:38 libsai.so.1 -> libsai.so.1.0\n" -"-rw-r--r-- 1 root root 343M Feb 1 06:10 libsai.so.1.0\n" -"...\n" -"```" +"To provide these functionalities, SONiC creates a database instance in Redis " +"named `sonic-db`. The configuration and database partitioning information " +"can be found in `/var/run/redis/sonic-db/database_config.json`:" msgstr "" -#: src/2-3-key-containers.md:123 -msgid "## 各种实现特定功能的容器" +#: src\2-1-database.md:10 +msgid "\"INSTANCES\"" msgstr "" -#: src/2-3-key-containers.md:125 -msgid "" -"SONiC中还有很多的容器是为了实现一些特定功能而存在的。这些容器一般都有着特殊的外部接口(非SONiC CLI和REST " -"API)和实现(非OS或ASIC),比如:" +#: src\2-1-database.md:11 src\2-1-database.md:19 src\2-1-database.md:20 +#: src\2-1-database.md:21 src\2-1-database.md:22 src\2-1-database.md:23 +#: src\2-1-database.md:24 src\2-1-database.md:25 src\2-1-database.md:26 +#: src\2-1-database.md:27 src\2-1-database.md:28 src\2-1-database.md:29 +#: src\2-1-database.md:30 src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"redis\"" msgstr "" -#: src/2-3-key-containers.md:127 -msgid "" -"- bgp:用来实现BGP协议(Border Gateway Protocol,边界网关协议)的容器\n" -"- lldp:用来实现LLDP协议(Link Layer Discovery Protocol,链路层发现协议)的容器\n" -"- teamd:用来实现Link Aggregation(链路聚合)的容器\n" -"- snmp:用来实现SNMP协议(Simple Network Management Protocol,简单网络管理协议)的容器" +#: src\2-1-database.md:12 +msgid "\"hostname\"" msgstr "" -#: src/2-3-key-containers.md:132 -msgid "和SWSS类似,为了适应SONiC的架构,它们中间也都会运行着上面我们提到的那几种服务:" +#: src\2-1-database.md:12 +msgid "\"127.0.0.1\"" msgstr "" -#: src/2-3-key-containers.md:134 -msgid "" -"- 配置管理和下发(类似`*mgrd`):`lldpmgrd`,`zebra`(bgp)\n" -"- 状态同步(类似`*syncd`):`lldpsyncd`,`fpmsyncd`(bgp),`teamsyncd`\n" -"- 服务实现或者外部接口(`*d`):`lldpd`,`bgpd`,`teamd`,`snmpd`" +#: src\2-1-database.md:13 +msgid "\"port\"" msgstr "" -#: src/2-3-key-containers.md:138 -msgid "## 管理服务容器:mgmt-framework" +#: src\2-1-database.md:14 +msgid "\"unix_socket_path\"" msgstr "" -#: src/2-3-key-containers.md:140 -msgid "" -"我们在之前的章节中已经看过如何使用SONiC的CLI来进行一些交换机的配置,但是在实际生产环境中,手动登录交换机使用CLI来配置所有的交换机是不现实的,所以SONiC提供了一个REST " -"API来解决这个问题。这个REST API的实现就是在`mgmt-framework`容器中。我们可以通过`ps`命令来查看:" +#: src\2-1-database.md:14 +msgid "\"/var/run/redis/redis.sock\"" msgstr "" -#: src/2-3-key-containers.md:142 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it mgmt-framework bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 16 0.3 1.2 1472804 52036 pts/0 Sl 16:20 0:02 " -"/usr/sbin/rest_server -ui /rest_ui -logtostderr -cert /tmp/cert.pem -key " -"/tmp/key.pem\n" -"...\n" -"```" +#: src\2-1-database.md:15 +msgid "\"persistence_for_warm_boot\"" msgstr "" -#: src/2-3-key-containers.md:151 -msgid "" -"其实除了REST API,SONiC还可以通过其他方式来进行管理,如gNMI,这些也都是运行在这个容器中的。其整体架构如下图所示 " -"[\\[2\\]][SONiCMgmtFramework]:" +#: src\2-1-database.md:15 +msgid "\"yes\"" msgstr "" -#: src/2-3-key-containers.md:155 -msgid "这里我们也可以发现,其实我们使用的CLI,底层也是通过调用这个REST API来实现的~" +#: src\2-1-database.md:18 +msgid "\"DATABASES\"" msgstr "" -#: src/2-3-key-containers.md:157 -msgid "## 平台监控容器:pmon(Platform Monitor)" +#: src\2-1-database.md:19 src\5-2-3-route-update-in-sonic.md:50 +msgid "\"APPL_DB\"" msgstr "" -#: src/2-3-key-containers.md:159 -msgid "" -"这个容器里面的服务基本都是用来监控交换机一些基础硬件的运行状态的,比如温度,电源,风扇,SFP事件等等。同样,我们可以用`ps`命令来查看这个容器中运行的服务:" +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"id\"" msgstr "" -#: src/2-3-key-containers.md:161 -msgid "" -"```bash\n" -"admin@sonic:~$ docker exec -it pmon bash\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 28 0.0 0.8 49972 33192 pts/0 S Apr26 0:23 python3 " -"/usr/local/bin/ledd\n" -"root 29 0.9 1.0 278492 43816 pts/0 Sl Apr26 34:41 python3 " -"/usr/local/bin/xcvrd\n" -"root 30 0.4 1.0 57660 40412 pts/0 S Apr26 18:41 python3 " -"/usr/local/bin/psud\n" -"root 32 0.0 1.0 57172 40088 pts/0 S Apr26 0:02 python3 " -"/usr/local/bin/syseepromd\n" -"root 33 0.0 1.0 58648 41400 pts/0 S Apr26 0:27 python3 " -"/usr/local/bin/thermalctld\n" -"root 34 0.0 1.3 70044 53496 pts/0 S Apr26 0:46 " -"/usr/bin/python3 /usr/local/bin/pcied\n" -"root 42 0.0 0.0 55320 1136 ? Ss Apr26 0:15 " -"/usr/sbin/sensord -f daemon\n" -"root 45 0.0 0.8 58648 32220 pts/0 S Apr26 2:45 python3 " -"/usr/local/bin/thermalctld\n" -"...\n" -"```" +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"separator\"" msgstr "" -#: src/2-3-key-containers.md:177 -msgid "" -"其中大部分的服务从名字我们就能猜出来是做什么的了,中间只有xcvrd不是那么明显,这里xcvr是transceiver的缩写,它是用来监控交换机的光模块的,比如SFP,QSFP等等。" +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:24 src\2-1-database.md:25 +#: src\2-1-database.md:29 src\2-1-database.md:30 src\2-1-database.md:31 +#: src\2-1-database.md:32 src\5-1-syncd-and-sai.md:498 +#: src\5-2-3-route-update-in-sonic.md:682 +msgid "\":\"" msgstr "" -#: src/2-3-key-containers.md:181 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Management Framework][SONiCMgmtFramework]" +#: src\2-1-database.md:19 src\2-1-database.md:20 src\2-1-database.md:21 +#: src\2-1-database.md:22 src\2-1-database.md:23 src\2-1-database.md:24 +#: src\2-1-database.md:25 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 src\2-1-database.md:29 src\2-1-database.md:30 +#: src\2-1-database.md:31 src\2-1-database.md:32 +msgid "\"instance\"" msgstr "" -#: src/2-4-sai-intro.md:1 -msgid "# SAI" +#: src\2-1-database.md:20 +msgid "\"ASIC_DB\"" msgstr "" -#: src/2-4-sai-intro.md:3 -msgid "" -"SAI(Switch Abstraction " -"Interface,交换机抽象接口)是SONiC的基石,正因为有了它,SONiC才能支持多种硬件平台。我们在[这个SAI " -"API的文档][SAIAPI]中,可以看到它定义的所有接口。" +#: src\2-1-database.md:21 +msgid "\"COUNTERS_DB\"" msgstr "" -#: src/2-4-sai-intro.md:5 -msgid "" -"[在核心容器一节中我们提到,SAI运行在`syncd`容器中](./2-3-key-containers.html)。不过和其他组件不同,它并不是一个服务,而是一组公共的头文件和动态链接库(.so)。其中,所有的抽象接口都以c语言头文件的方式定义在了[OCP的SAI仓库][OCPSAI]中,而.so文件则由各个硬件厂商提供,用于实现SAI的接口。" +#: src\2-1-database.md:22 +msgid "\"LOGLEVEL_DB\"" msgstr "" -#: src/2-4-sai-intro.md:7 -msgid "## SAI接口" +#: src\2-1-database.md:23 +msgid "\"CONFIG_DB\"" msgstr "" -#: src/2-4-sai-intro.md:9 -msgid "为了有一个更加直观的理解,我们拿一小部分代码来展示一下SAI的接口定义和初始化的方法,如下:" +#: src\2-1-database.md:23 src\2-1-database.md:26 src\2-1-database.md:27 +#: src\2-1-database.md:28 +msgid "\"|\"" msgstr "" -#: src/2-4-sai-intro.md:11 -msgid "" -"```cpp\n" -"// File: meta/saimetadata.h\n" -"typedef struct _sai_apis_t {\n" -" sai_switch_api_t* switch_api;\n" -" sai_port_api_t* port_api;\n" -" ...\n" -"} sai_apis_t;\n" -"\n" -"// File: inc/saiswitch.h\n" -"typedef struct _sai_switch_api_t\n" -"{\n" -" sai_create_switch_fn create_switch;\n" -" sai_remove_switch_fn remove_switch;\n" -" sai_set_switch_attribute_fn set_switch_attribute;\n" -" sai_get_switch_attribute_fn get_switch_attribute;\n" -" ...\n" -"} sai_switch_api_t;\n" -"\n" -"// File: inc/saiport.h\n" -"typedef struct _sai_port_api_t\n" -"{\n" -" sai_create_port_fn create_port;\n" -" sai_remove_port_fn remove_port;\n" -" sai_set_port_attribute_fn set_port_attribute;\n" -" sai_get_port_attribute_fn get_port_attribute;\n" -" ...\n" -"} sai_port_api_t;\n" -"```" +#: src\2-1-database.md:24 +msgid "\"PFC_WD_DB\"" msgstr "" -#: src/2-4-sai-intro.md:40 -msgid "" -"其中,`sai_apis_t`结构体是SAI所有模块的接口的集合,其中每个成员都是一个特定模块的接口列表的指针。我们用`sai_switch_api_t`来举例,它定义了SAI " -"Switch模块的所有接口,我们在`inc/saiswitch.h`中可以看到它的定义。同样的,我们在`inc/saiport.h`中可以看到SAI " -"Port模块的接口定义。" +#: src\2-1-database.md:25 +msgid "\"FLEX_COUNTER_DB\"" msgstr "" -#: src/2-4-sai-intro.md:42 -msgid "## SAI初始化" +#: src\2-1-database.md:26 +msgid "\"STATE_DB\"" msgstr "" -#: src/2-4-sai-intro.md:44 -msgid "SAI的初始化其实就是想办法获取上面这些函数指针,这样我们就可以通过SAI的接口来操作ASIC了。" +#: src\2-1-database.md:27 +msgid "\"SNMP_OVERLAY_DB\"" msgstr "" -#: src/2-4-sai-intro.md:46 -msgid "参与SAI初始化的主要函数有两个,他们都定义在`inc/sai.h`中:" +#: src\2-1-database.md:28 +msgid "\"RESTAPI_DB\"" msgstr "" -#: src/2-4-sai-intro.md:48 -msgid "- `sai_api_initialize`:初始化SAI\n- `sai_api_query`:传入SAI的API的类型,获取对应的接口列表" +#: src\2-1-database.md:29 +msgid "\"GB_ASIC_DB\"" msgstr "" -#: src/2-4-sai-intro.md:51 -msgid "虽然大部分厂商的SAI实现是闭源的,但是mellanox却开源了自己的SAI实现,所以这里我们可以借助其更加深入的理解SAI是如何工作的。" +#: src\2-1-database.md:30 +msgid "\"GB_COUNTERS_DB\"" msgstr "" -#: src/2-4-sai-intro.md:53 -msgid "比如,`sai_api_initialize`函数其实就是简单的设置设置两个全局变量,然后返回`SAI_STATUS_SUCCESS`:" +#: src\2-1-database.md:31 +msgid "\"GB_FLEX_COUNTER_DB\"" msgstr "" -#: src/2-4-sai-intro.md:55 -msgid "" -"```cpp\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const " -"sai_service_method_table_t* services)\n" -"{\n" -" if (g_initialized) {\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -" // Validate parameters here (code omitted)\n" -"\n" -" memcpy(&g_mlnx_services, services, sizeof(g_mlnx_services));\n" -" g_initialized = true;\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" -"```" +#: src\2-1-database.md:32 +msgid "\"APPL_STATE_DB\"" msgstr "" -#: src/2-4-sai-intro.md:70 -msgid "初始化完成后,我们就可以使用`sai_api_query`函数,通过传入API的类型来查询对应的接口列表,而每一个接口列表其实都是一个全局变量:" +#: src\2-1-database.md:34 +msgid "\"VERSION\"" msgstr "" -#: src/2-4-sai-intro.md:72 -msgid "" -"```cpp\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c\n" -"sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" if (!g_initialized) {\n" -" return SAI_STATUS_UNINITIALIZED;\n" -" }\n" -" ...\n" -"\n" -" return sai_api_query_eth(sai_api_id, api_method_table);\n" -"}\n" -"\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery_eth.c\n" -"sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** " -"api_method_table)\n" -"{\n" -" switch (sai_api_id) {\n" -" case SAI_API_BRIDGE:\n" -" *(const sai_bridge_api_t**)api_method_table = &mlnx_bridge_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" case SAI_API_SWITCH:\n" -" *(const sai_switch_api_t**)api_method_table = &mlnx_switch_api;\n" -" return SAI_STATUS_SUCCESS;\n" -" ...\n" -" default:\n" -" if (sai_api_id >= (sai_api_t)SAI_API_EXTENSIONS_RANGE_END) {\n" -" return SAI_STATUS_INVALID_PARAMETER;\n" -" } else {\n" -" return SAI_STATUS_NOT_IMPLEMENTED;\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_bridge.c\n" -"const sai_bridge_api_t mlnx_bridge_api = {\n" -" mlnx_create_bridge,\n" -" mlnx_remove_bridge,\n" -" mlnx_set_bridge_attribute,\n" -" mlnx_get_bridge_attribute,\n" -" ...\n" -"};\n" -"\n" -"\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_switch.c\n" -"const sai_switch_api_t mlnx_switch_api = {\n" -" mlnx_create_switch,\n" -" mlnx_remove_switch,\n" -" mlnx_set_switch_attribute,\n" -" mlnx_get_switch_attribute,\n" -" ...\n" -"};\n" -"```" +#: src\2-1-database.md:34 +msgid "\"1.0\"" msgstr "" -#: src/2-4-sai-intro.md:124 -msgid "## SAI的使用" +#: src\2-1-database.md:38 +msgid "" +"Although we can see that there are about a dozen databases in SONiC, most of " +"the time we only need to focus on the following most important ones:" msgstr "" -#: src/2-4-sai-intro.md:126 +#: src\2-1-database.md:40 msgid "" -"在`syncd`容器中,SONiC会在启动时启动`syncd`服务,而`syncd`服务会加载当前系统中的SAI组件。这个组件由各个厂商提供,它们会根据自己的硬件平台来实现上面展现的SAI的接口,从而让SONiC使用统一的上层逻辑来控制多种不同的硬件平台。" +"**CONFIG_DB (ID = 4)**: Stores the **configuration** of all services, such " +"as port configuration, VLAN configuration, etc. It represents the data model " +"of the **desired state of the switch** as intended by the user. This is also " +"the main object of operation when all CLI and external applications modify " +"the configuration." msgstr "" -#: src/2-4-sai-intro.md:128 -msgid "我们可以通过`ps`, `ls`和`nm`命令来简单的对这个进行验证:" +#: src\2-1-database.md:41 +msgid "" +"**APPL_DB (Application DB, ID = 0)**: Stores **internal state information of " +"all services**. It contains two types of information:" msgstr "" -#: src/2-4-sai-intro.md:130 +#: src\2-1-database.md:42 msgid "" -"```bash\n" -"# Enter into syncd container\n" -"admin@sonic:~$ docker exec -it syncd bash\n" -"\n" -"# List all processes. We will only see syncd process here.\n" -"root@sonic:/# ps aux\n" -"USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND\n" -"...\n" -"root 21 0.0 0.0 87708 1532 pts/0 Sl 16:20 0:00 " -"/usr/bin/dsserve /usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b " -"/tmp/break_before_make_objects\n" -"root 33 11.1 15.0 2724396 602532 pts/0 Sl 16:20 36:30 " -"/usr/bin/syncd --diag -u -s -p /etc/sai.d/sai.profile -b " -"/tmp/break_before_make_objects\n" -"...\n" -"\n" -"# Find all libsai*.so.* files.\n" -"root@sonic:/# find / -name libsai*.so.*\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimeta.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0.0.0\n" -"/usr/lib/x86_64-linux-gnu/libsairedis.so.0\n" -"/usr/lib/x86_64-linux-gnu/libsaimetadata.so.0\n" -"/usr/lib/libsai.so.1\n" -"/usr/lib/libsai.so.1.0\n" -"\n" -"# Copy the file out of switch and check libsai.so on your own dev machine.\n" -"# We will see the most important SAI export functions here.\n" -"$ nm -C -D ./libsai.so.1.0 > ./sai-exports.txt\n" -"$ vim sai-exports.txt\n" -"...\n" -"0000000006581ae0 T sai_api_initialize\n" -"0000000006582700 T sai_api_query\n" -"0000000006581da0 T sai_api_uninitialize\n" -"...\n" -"```" +"One is calculated by each service after reading the configuration " +"information from CONFIG_DB, which can be understood as the **desired state " +"of the switch** (Goal State) but from the perspective of each service." msgstr "" -#: src/2-4-sai-intro.md:166 +#: src\2-1-database.md:43 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SAI API][SAIAPI]\n" -"3. [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in " -"Hardware for SDN][PISA]\n" -"4. [Github: sonic-net/sonic-sairedis][SONiCSAIRedis]\n" -"5. [Github: opencomputeproject/SAI][OCPSAI]\n" -"6. [Arista 7050QX Series 10/40G Data Center Switches Data " -"Sheet][Arista7050QX]\n" -"7. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" +"The other is when the ASIC state changes and is written back, some services " +"write directly to APPL_DB instead of the STATE_DB we will introduce next. " +"This information can be understood as the **current state of the switch** as " +"perceived by each service." msgstr "" -#: src/3-dev-guide.md:1 -msgid "# 开发上手指南" +#: src\2-1-database.md:44 +msgid "" +"**STATE_DB (ID = 6)**: Stores the **current state** of various components of " +"the switch. When a service in SONiC receives a state change from STATE_DB " +"and finds it inconsistent with the Goal State, SONiC will reapply the " +"configuration until the two states are consistent. (Of course, for those " +"states written back to APPL_DB, the service will monitor changes in APPL_DB " +"instead of STATE_DB.)" msgstr "" -#: src/3-1-code-repos.md:1 -msgid "# 代码仓库" +#: src\2-1-database.md:45 +msgid "" +"**ASIC_DB (ID = 1)**: Stores the **desired state information** of the switch " +"ASIC in SONiC, such as ACL, routing, etc. Unlike APPL_DB, the data model in " +"this database is designed for ASIC rather than service abstraction. This " +"design facilitates the development of SAI and ASIC drivers by various " +"vendors." msgstr "" -#: src/3-1-code-repos.md:3 +#: src\2-1-database.md:47 msgid "" -"SONiC的代码都托管在[GitHub的sonic-net账号][SONiCGitHub]上,仓库数量有30几个之多,所以刚开始看SONiC的代码时,肯定是会有点懵的,不过不用担心,我们这里就来一起看看~" +"Now, we have an intuitive question: with so many services in the switch, are " +"all configurations and states stored in a single database without isolation? " +"What if two services use the same Redis Key? This is a very good question, " +"and SONiC's solution is straightforward: continue to partition each database " +"into tables!" msgstr "" -#: src/3-1-code-repos.md:5 -msgid "## 核心仓库" +#: src\2-1-database.md:49 +msgid "" +"We know that Redis does not have the concept of tables within each database " +"but uses key-value pairs to store data. Therefore, to further partition " +"tables, SONiC's solution is to include the table name in the key and " +"separate the table and key with a delimiter. The `separator` field in the " +"configuration file above serves this purpose. For example, the state of the " +"`Ethernet4` port in the `PORT_TABLE` table in `APPL_DB` can be accessed " +"using `PORT_TABLE:Ethernet4` as follows:" msgstr "" -#: src/3-1-code-repos.md:7 -msgid "首先是SONiC中最重要的两个核心仓库:SONiC和sonic-buildimage。" +#: src\2-1-database.md:56 +msgid "\"admin_status\"" msgstr "" -#: src/3-1-code-repos.md:9 -msgid "### Landing仓库:SONiC" +#: src\2-1-database.md:57 src\2-1-database.md:71 src\4-1-2-netlink.md:68 +msgid "\"up\"" msgstr "" -#: src/3-1-code-repos.md:11 -msgid "" +#: src\2-1-database.md:58 +msgid "\"alias\"" msgstr "" -#: src/3-1-code-repos.md:13 -msgid "" -"这个仓库里面存储着SONiC的Landing " -"Page和大量的文档,Wiki,教程,以往的Talk的Slides,等等等等。这个仓库可以说是每个新人上手最常用的仓库了,但是注意,这个仓库里面**没有任何的代码**,只有文档。" +#: src\2-1-database.md:59 +msgid "\"Ethernet6/1\"" msgstr "" -#: src/3-1-code-repos.md:15 -msgid "### 镜像构建仓库:sonic-buildimage" +#: src\2-1-database.md:60 +msgid "\"index\"" msgstr "" -#: src/3-1-code-repos.md:17 -msgid "" +#: src\2-1-database.md:61 +msgid "\"6\"" msgstr "" -#: src/3-1-code-repos.md:19 -msgid "这个构建仓库为什么对于我们十分重要?和其他项目不同,**SONiC的构建仓库其实才是它的主仓库**!这个仓库里面包含:" +#: src\2-1-database.md:62 +msgid "\"lanes\"" msgstr "" -#: src/3-1-code-repos.md:21 -msgid "" -"- 所有的功能实现仓库,它们都以submodule的形式被加入到了这个仓库中(`src`目录)\n" -"- 所有设备厂商的支持文件(`device`目录),比如每个型号的交换机的配置文件,用来访问硬件的支持脚本,等等等等,比如:我的交换机是Arista " -"7050 QX-32S,那么我就可以在`device/arista/x86_64-arista_7050_qx32s`目录中找到它的支持文件。\n" -"- " -"所有ASIC芯片厂商提供的支持文件(`platform`目录),比如每个平台的驱动程序,BSP,底层支持的脚本等等。这里我们可以看到几乎所有的主流芯片厂商的支持文件,比如:Broadcom,Mellanox,等等,也有用来做模拟软交换机的实现,比如vs和p4。\n" -"- SONiC用来构建所有容器镜像的Dockerfile(`dockers`目录)\n" -"- 各种各样通用的配置文件和脚本(`files`目录)\n" -"- 用来做构建的编译容器的dockerfile(`sonic-slave-*`目录)\n" -"- 等等……" +#: src\2-1-database.md:63 +msgid "\"13,14,15,16\"" msgstr "" -#: src/3-1-code-repos.md:29 -msgid "" -"正因为这个仓库里面将所有相关的资源全都放在了一起,所以我们学习SONiC的代码时,基本只需要下载这一个源码仓库就可以了,不管是搜索还是跳转都非常方便!" +#: src\2-1-database.md:64 +msgid "\"mtu\"" msgstr "" -#: src/3-1-code-repos.md:31 -msgid "## 功能实现仓库" +#: src\2-1-database.md:65 +msgid "\"9100\"" msgstr "" -#: src/3-1-code-repos.md:33 -msgid "" -"除了核心仓库,SONiC下还有很多功能实现仓库,里面都是各个容器和子服务的实现,这些仓库都被以submodule的形式放在了sonic-buildimage的`src`目录下,如果我们想对SONiC进行修改和贡献,我们也需要了解一下。" +#: src\2-1-database.md:66 +msgid "\"speed\"" msgstr "" -#: src/3-1-code-repos.md:35 -msgid "### SWSS(Switch State Service)相关仓库" +#: src\2-1-database.md:67 +msgid "\"40000\"" msgstr "" -#: src/3-1-code-repos.md:37 -msgid "" -"在上一篇中我们介绍过,SWSS容器是SONiC的大脑,在SONiC下,它由两个repo组成:[sonic-swss-common](https://github.com/sonic-net/sonic-swss-common)和[sonic-swss](https://github.com/sonic-net/sonic-swss)。" +#: src\2-1-database.md:68 +msgid "\"description\"" msgstr "" -#: src/3-1-code-repos.md:39 -msgid "#### SWSS公共库:sonic-swss-common" +#: src\2-1-database.md:69 src\5-2-3-route-update-in-sonic.md:307 +msgid "\"\"" msgstr "" -#: src/3-1-code-repos.md:41 -msgid "" -"首先是公共库:sonic-swss-common()。" +#: src\2-1-database.md:70 src\4-1-2-netlink.md:68 +msgid "\"oper_status\"" msgstr "" -#: src/3-1-code-repos.md:43 +#: src\2-1-database.md:74 msgid "" -"这个仓库里面包含了所有`*mgrd`和`*syncd`服务所需要的公共功能,比如,logger,json,netlink的封装,Redis操作和基于Redis的各种服务间通讯机制的封装等等。虽然能看出来这个仓库一开始的目标是专门给swss服务使用的,但是也正因为功能多,很多其他的仓库都有它的引用,比如`swss-sairedis`和`swss-restapi`。" +"Of course, in SONiC, not only the data model but also the communication " +"mechanism uses a similar method to achieve \"table\" level isolation." msgstr "" -#: src/3-1-code-repos.md:45 -msgid "#### SWSS主仓库:sonic-swss" +#: src\2-2-services-intro.md:2 +msgid "Introduction to Services and Workflows" msgstr "" -#: src/3-1-code-repos.md:47 -msgid "然后就是SWSS的主仓库sonic-swss了:。" +#: src\2-2-services-intro.md:4 +msgid "" +"There are many services (daemon processes) in SONiC, around twenty to " +"thirty. They start with the switch and keep running until the switch is shut " +"down. If we want to quickly understand how SONiC works, diving into each " +"service one by one is obviously not a good option. Therefore, it is better " +"to categorize these services and control flows on high level to help us " +"build a big picture." msgstr "" -#: src/3-1-code-repos.md:49 -msgid "我们可以在这个仓库中找到:" +#: src\2-2-services-intro.md:10 +msgid "Service Categories" msgstr "" -#: src/3-1-code-repos.md:51 +#: src\2-2-services-intro.md:12 msgid "" -"- 绝大部分的`*mgrd`和`*syncd`服务:`orchagent`, " -"`portsyncd/portmgrd/intfmgrd`,`neighsyncd/nbrmgrd`,`natsyncd/natmgrd`,`buffermgrd`,`coppmgrd`,`macsecmgrd`,`sflowmgrd`,`tunnelmgrd`,`vlanmgrd`,`vrfmgrd`,`vxlanmgrd`,等等。\n" -"- `swssconfig`:在`swssconfig`目录下,用于在快速重启时(fast reboot)恢复FDB和ARP表。\n" -"- " -"`swssplayer`:也在`swssconfig`目录下,用来记录所有通过SWSS进行的配置下发操作,这样我们就可以利用它来做replay,从而对问题进行重现和调试。\n" -"- 甚至一些不在SWSS容器中的服务,比如`fpmsyncd`(bgp容器)和`teamsyncd/teammgrd`(teamd容器)。" +"Generally speaking, the services in SONiC can be divided into the following " +"categories: `*syncd`, `*mgrd`, feature implementations, `orchagent`, and " +"`syncd`." msgstr "" -#: src/3-1-code-repos.md:56 -msgid "### SAI/平台相关仓库" +#: src\2-2-services-intro.md:14 +msgid "`*syncd` Services" msgstr "" -#: src/3-1-code-repos.md:58 +#: src\2-2-services-intro.md:16 msgid "" -"接下来就是作为交换机抽象接口的SAI了,[虽然SAI是微软提出来并在2015年3月份发布了0.1版本](https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf),但是[在2015年9月份,SONiC都还没有发布第一个版本的时候,就已经被OCP接收并作为一个公共的标准了](https://azure.microsoft.com/en-us/blog/switch-abstraction-interface-sai-officially-accepted-by-the-open-compute-project-ocp/),这也是SONiC能够在这么短的时间内就得到了这么多厂商的支持的原因之一。而也因为如此,SAI的代码仓库也被分成了两部分:" +"These services have names ending with `syncd`. They perform similar tasks: " +"synchronizing hardware states to Redis, usually into APPL_DB or STATE_DB." msgstr "" -#: src/3-1-code-repos.md:60 +#: src\2-2-services-intro.md:18 msgid "" -"- " -"OCP下的OpenComputeProject/SAI:。里面包含了有关SAI标准的所有代码,包括SAI的头文件,behavior " -"model,测试用例,文档等等。\n" -"- " -"SONiC下的sonic-sairedis:。里面包含了SONiC中用来和SAI交互的所有代码,比如syncd服务,和各种调试统计,比如用来做replay的`saiplayer`和用来导出asic状态的`saidump`。" +"For example, `portsyncd` listens to netlink events and synchronizes the " +"status of all ports in the switch to STATE_DB, while `natsyncd` listens to " +"netlink events and synchronizes all NAT statuses in the switch to APPL_DB." msgstr "" -#: src/3-1-code-repos.md:63 -msgid "" -"除了这两个仓库之外,还有一个平台相关的仓库,比如:[sonic-platform-vpp](https://github.com/sonic-net/sonic-platform-vpp),它的作用是通过SAI的接口,利用vpp来实现数据平面的功能,相当于一个高性能的软交换机,个人感觉未来可能会被合并到buildimage仓库中,作为platform目录下的一部分。" +#: src\2-2-services-intro.md:20 +msgid "`*mgrd` Services" msgstr "" -#: src/3-1-code-repos.md:65 -msgid "### 管理服务(mgmt)相关仓库" +#: src\2-2-services-intro.md:22 +msgid "" +"These services have names ending with `mgrd`. As the name suggests, these " +"are \"Manager\" services responsible for configuring various hardware, " +"opposite to `*syncd`. Their logic mainly consists of two parts:" msgstr "" -#: src/3-1-code-repos.md:67 -msgid "然后是SONiC中所有和[管理服务][SONiCMgmtFramework]相关的仓库:" +#: src\2-2-services-intro.md:24 +msgid "" +"**Configuration Deployment**: Responsible for reading configuration files " +"and listening to configuration and state changes in Redis (mainly CONFIG_DB, " +"APPL_DB, and STATE_DB), then pushing these changes to the switch hardware. " +"The method of pushing varies depending on the target, either by updating " +"APPL_DB and publishing update messages or directly calling Linux command " +"lines to modify the system. For example, `nbrmgr` listens to changes in " +"CONFIG_DB, APPL_DB, and STATE_DB for neighbors and modifies neighbors and " +"routes using netlink and command lines, while `intfmgr` not only calls " +"command lines but also updates some states to APPL_DB." msgstr "" -#: src/3-1-code-repos.md:69 +#: src\2-2-services-intro.md:25 msgid "" -"| 名称 | 说明 |\n" -"| --- | --- |\n" -"| [sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common) | " -"管理服务的基础库,里面包含着`translib`,yang model相关的代码 |\n" -"| [sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework) " -"| 使用Go来实现的REST Server,是下方架构图中的REST Gateway(进程名:`rest_server`) |\n" -"| [sonic-gnmi](https://github.com/sonic-net/sonic-gnmi) | " -"和sonic-mgmt-framework类似,是下方架构图中,基于gRPC的gNMI(gRPC Network Management " -"Interface)Server |\n" -"| [sonic-restapi](https://github.com/sonic-net/sonic-restapi) | " -"这是SONiC使用go来实现的另一个配置管理的REST " -"Server,和mgmt-framework不同,这个server在收到消息后会直接对CONFIG_DB进行操作,而不是走translib(下图中没有,进程名:`go-server-server`) " -"|\n" -"| [sonic-mgmt](https://github.com/sonic-net/sonic-mgmt) | " -"各种自动化脚本(`ansible`目录),测试(`tests`目录),用来搭建test bed和测试上报(`test_reporting`目录)之类的, " -"|" +"**State Synchronization**: For services that need reconciliation, `*mgrd` " +"also listens to state changes in STATE_DB. If it finds that the hardware " +"state is inconsistent with the expected state, it will re-initiate the " +"configuration process to set the hardware state to the expected state. These " +"state changes in STATE_DB are usually pushed by `*syncd` services. For " +"example, `intfmgr` listens to port up/down status and MTU changes pushed by " +"`portsyncd` in STATE_DB. If it finds inconsistencies with the expected state " +"stored in its memory, it will re-deploy the configuration." msgstr "" -#: src/3-1-code-repos.md:77 -msgid "这里还是附上SONiC管理服务的架构图,方便大家配合食用 [\\[4\\]][SONiCMgmtFramework]:" +#: src\2-2-services-intro.md:27 +msgid "`orchagent` Service" msgstr "" -#: src/3-1-code-repos.md:81 -msgid "### 平台监控相关仓库:sonic-platform-common和sonic-platform-daemons" +#: src\2-2-services-intro.md:29 +msgid "" +"This is the most important service in SONiC. Unlike other services that are " +"responsible for one or two specific functions, `orchagent`, as the " +"orchestrator of the switch ASIC state, checks all states from `*syncd` " +"services in the database, integrates them, and deploys them to ASIC_DB, " +"which is used to store the switch ASIC configuration. These states are " +"eventually received by `syncd`, which calls the SAI API through the SAI " +"implementation and ASIC SDK provided by various vendors to interact with the " +"ASIC, ultimately deploying the configuration to the switch hardware." msgstr "" -#: src/3-1-code-repos.md:83 -msgid "以下两个仓库都和平台监控和控制相关,比如LED,风扇,电源,温控等等:" +#: src\2-2-services-intro.md:31 +msgid "Feature Implementation Services" msgstr "" -#: src/3-1-code-repos.md:85 +#: src\2-2-services-intro.md:33 msgid "" -"| 名称 | 说明 |\n" -"| --- | --- |\n" -"| " -"[sonic-platform-common](https://github.com/sonic-net/sonic-platform-common) " -"| 这是给厂商们提供的基础包,用来定义访问风扇,LED,电源管理,温控等等模块的接口定义,这些接口都是用python来实现的 |\n" -"| " -"[sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-daemons) " -"| " -"这里包含了SONiC中pmon容器中运行的各种监控服务:`chassisd`,`ledd`,`pcied`,`psud`,`syseepromd`,`thermalctld`,`xcvrd`,`ycabled`,它们都使用python实现,通过和中心数据库Redis进行连接,和加载并调用各个厂商提供的接口实现来对各个模块进行监控和控制 " -"|" +"Some features are not implemented by the OS itself but by specific " +"processes, such as BGP or some external-facing interfaces. These services " +"often have names ending with `d`, indicating daemon, such as `bgpd`, " +"`lldpd`, `snmpd`, `teamd`, etc., or simply the name of the feature, such as " +"`fancontrol`." msgstr "" -#: src/3-1-code-repos.md:90 -msgid "### 其他功能实现仓库" +#: src\2-2-services-intro.md:35 +msgid "`syncd` Service" msgstr "" -#: src/3-1-code-repos.md:92 -msgid "除了上面这些仓库以外,SONiC还有很多实现其方方面面功能的仓库,有些是一个或多个进程,有些是一些库,它们的作用如下表所示:" -msgstr "" - -#: src/3-1-code-repos.md:94 +#: src\2-2-services-intro.md:37 msgid "" -"| 仓库 | 介绍 |\n" -"| --- | --- |\n" -"| [sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent) | " -"[AgentX](https://www.ietf.org/rfc/rfc2741.txt) SNMP " -"subagent的实现(`sonic_ax_impl`),用于连接Redis数据库,给snmpd提供所需要的各种信息,可以把它理解成snmpd的控制面,而snmpd是数据面,用于响应外部SNMP的请求 " -"|\n" -"| [sonic-frr](https://github.com/sonic-net/sonic-frr) | " -"FRRouting,各种路由协议的实现,所以这个仓库中我们可以找到如`bgpd`,`zebra`这类的路由相关的进程实现 |\n" -"| [sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd) | Dual ToR " -"support,检查Link的状态,并且控制ToR的连接 |\n" -"| [sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay) | DHCP " -"relay agent |\n" -"| [sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon) | " -"监控DHCP的状态,并报告给中心数据库Redis |\n" -"| [sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd) | " -"`lldp_syncd`服务,但是repo的名字没取好,叫做dbsyncd |\n" -"| [sonic-pins](https://github.com/sonic-net/sonic-pins) | " -"Google开发的基于P4的网络栈支持(P4 Integrated Network " -"Stack,PINS),更多信息可以参看[PINS的官网][SONiCPINS]。 |\n" -"| [sonic-stp](https://github.com/sonic-net/sonic-stp) | STP(Spanning Tree " -"Protocol)的支持 |\n" -"| [sonic-ztp](https://github.com/sonic-net/sonic-ztp) | [Zero Touch " -"Provisioning][SONiCZTP] |\n" -"| [DASH](https://github.com/sonic-net/DASH) | [Disaggregated API for SONiC " -"Hosts][SONiCDASH] |\n" -"| [sonic-host-services](https://github.com/sonic-net/sonic-host-services) | " -"运行在host上通过dbus用来为容器中的服务提供支持的服务,比如保存和重新加载配置,保存dump之类的非常有限的功能,类似一个host broker " -"|\n" -"| [sonic-fips](https://github.com/sonic-net/sonic-fips) | FIPS(Federal " -"Information Processing Standards)的支持,里面有很多为了支持FIPS标准而加入的各种补丁文件 |\n" -"| [sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant) " -"| 各种无线网络协议的支持 |" +"The `syncd` service is downstream of `orchagent`. Although its name is " +"`syncd`, it shoulders the work of both `*mgrd` and `*syncd` for the ASIC." msgstr "" -#: src/3-1-code-repos.md:110 -msgid "## 工具仓库:sonic-utilities" +#: src\2-2-services-intro.md:39 +msgid "" +"First, as `*mgrd`, it listens to state changes in ASIC_DB. Once detected, it " +"retrieves the new state and calls the SAI API to deploy the configuration to " +"the switch hardware." msgstr "" -#: src/3-1-code-repos.md:112 -msgid "" +#: src\2-2-services-intro.md:40 +msgid "" +"Then, as `*syncd`, if the ASIC sends any notifications to SONiC, it will " +"send these notifications to Redis as messages, allowing `orchagent` and " +"`*mgrd` services to obtain these changes and process them. The types of " +"these notifications can be found in " +"[SwitchNotifications.h](https://github.com/sonic-net/sonic-sairedis/blob/master/syncd/SwitchNotifications.h)." msgstr "" -#: src/3-1-code-repos.md:114 -msgid "这个仓库存放着SONiC所有的命令行下的工具:" +#: src\2-2-services-intro.md:42 +msgid "Control Flow Between Services" msgstr "" -#: src/3-1-code-repos.md:116 +#: src\2-2-services-intro.md:44 msgid "" -"- `config`,`show`,`clear`目录:这是三个SONiC " -"CLI的主命令的实现。需要注意的是,具体的命令实现并不一定在这几个目录里面,大量的命令是通过调用其他命令来实现的,这几个命令只是提供了一个入口。\n" -"- " -"`scripts`,`sfputil`,`psuutil`,`pcieutil`,`fwutil`,`ssdutil`,`acl_loader`目录:这些目录下提供了大量的工具命令,但是它们大多并不是直接给用户使用的,而是被`config`,`show`和`clear`目录下的命令调用的,比如:`show " -"platform fan`命令,就是通过调用`scripts`目录下的`fanshow`命令来实现的。\n" -"- " -"`utilities_common`,`flow_counter_util`,`syslog_util`目录:这些目录和上面类似,但是提供的是基础类,可以直接在python中import调用。\n" -"- " -"另外还有很多其他的命令:`fdbutil`,`pddf_fanutil`,`pddf_ledutil`,`pddf_psuutil`,`pddf_thermalutil`,等等,用于查看和控制各个模块的状态。\n" -"- `connect`和`consutil`目录:这两个目录下的命令是用来连接到其他SONiC设备并对其进行管理的。\n" -"- `crm`目录:用来配置和查看SONiC中的[CRM(Critical Resource " -"Monitoring)][SONiCCRM]。这个命令并没有被包含在`config`和`show`命令中,所以用户可以直接使用。\n" -"- `pfc`目录:用来配置和查看SONiC中的[PFC(Priority-based Flow Control)][SONiCPFC]。\n" -"- `pfcwd`目录:用来配置和查看SONiC中的[PFC Watch Dog][SONiCPFCWD],比如启动,停止,修改polling " -"interval之类的操作。" +"With service categories, we can now better understand the services in SONiC. " +"To get started, it is crucial to understand the control flow between " +"services. Based on the above categories, we can divide the main control " +"flows into two categories: configuration deployment and state " +"synchronization." msgstr "" -#: src/3-1-code-repos.md:125 -msgid "## 内核补丁:sonic-linux-kernel" +#: src\2-2-services-intro.md:46 +msgid "Configuration Deployment" msgstr "" -#: src/3-1-code-repos.md:127 -msgid "" +#: src\2-2-services-intro.md:48 +msgid "The configuration deployment process generally follows these steps:" msgstr "" -#: src/3-1-code-repos.md:129 +#: src\2-2-services-intro.md:50 msgid "" -"虽然SONiC是基于debian的,但是默认的debian内核却不一定能运行SONiC,比如某个模块默认没有启动,或者某些老版本的驱动有问题,所以SONiC需要或多或少有一些修改的Linux内核。而这个仓库就是用来存放所有的内核补丁的。" +"**Modify Configuration**: Users can modify configurations through CLI or " +"REST API. These configurations are written to CONFIG_DB and send update " +"notifications through Redis. Alternatively, external programs can modify " +"configurations through specific interfaces, such as the BGP API. These " +"configurations are sent to `*mgrd` services through internal TCP sockets." msgstr "" -#: src/3-1-code-repos.md:133 +#: src\2-2-services-intro.md:51 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [SONiC Source Repositories][SONiCRepo]\n" -"3. [SONiC Management Framework][SONiCMgmtFramework]\n" -"4. [SAI API][SAIAPI]\n" -"5. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"6. [SONiC Zero Touch Provisioning][SONiCZTP]\n" -"7. [SONiC Critical Resource Monitoring][SONiCCRM]\n" -"8. [SONiC P4 Integrated Network Stack][SONiCPINS]\n" -"9. [SONiC Disaggregated API for Switch Hosts][SONiCDash]\n" -"10. [SAI spec for OCP][SAISpec]" +"**`*mgrd` Deploys Configuration**: Services listen to configuration changes " +"in CONFIG_DB and then push these configurations to the switch hardware. " +"There are two main scenarios (which can coexist):" msgstr "" -#: src/3-2-compile.md:1 -msgid "# 编译" +#: src\2-2-services-intro.md:52 +msgid "**Direct Deployment**:" msgstr "" -#: src/3-2-compile.md:3 -msgid "## 编译环境" +#: src\2-2-services-intro.md:53 +msgid "" +"`*mgrd` services directly call Linux command lines or modify system " +"configurations through netlink." msgstr "" -#: src/3-2-compile.md:5 +#: src\2-2-services-intro.md:54 msgid "" -"由于SONiC是基于debian开发的,为了保证我们无论在什么平台下都可以成功的编译SONiC,并且编译出来的程序能在对应的平台上运行,SONiC使用了容器化的编译环境 " -"—— " -"它将所有的工具和依赖都安装在对应debian版本的docker容器中,然后将我们的代码挂载进容器,最后在容器内部进行编译工作,这样我们就可以很轻松的在任何平台上编译SONiC,而不用担心依赖不匹配的问题,比如有一些包在debian里的版本比ubuntu更高,这样就可能导致最后的程序在debian上运行的时候出现一些意外的错误。" +"`*syncd` services listen to system configuration changes through netlink or " +"other methods and push these changes to STATE_DB or APPL_DB." msgstr "" -#: src/3-2-compile.md:7 -msgid "## 初始化编译环境" +#: src\2-2-services-intro.md:55 +msgid "" +"`*mgrd` services listen to configuration changes in STATE_DB or APPL_DB, " +"compare these configurations with those stored in their memory, and if " +"inconsistencies are found, they re-call command lines or netlink to modify " +"system configurations until they are consistent." msgstr "" -#: src/3-2-compile.md:9 -msgid "### 安装Docker" +#: src\2-2-services-intro.md:56 +msgid "**Indirect Deployment**:" msgstr "" -#: src/3-2-compile.md:11 -msgid "为了支持容器化的编译环境,第一步,我们需要保证我们的机器上安装了docker。" +#: src\2-2-services-intro.md:57 +msgid "" +"`*mgrd` pushes states to APPL_DB and sends update notifications through " +"Redis." msgstr "" -#: src/3-2-compile.md:13 -msgid "Docker的安装方法可以参考[官方文档][DockerInstall],这里我们以Ubuntu为例,简单介绍一下安装方法。" +#: src\2-2-services-intro.md:58 +msgid "" +"`orchagent` listens to configuration changes, calculates the state the ASIC " +"should achieve based on all related states, and deploys it to ASIC_DB." msgstr "" -#: src/3-2-compile.md:15 -msgid "首先,我们需要把docker的源和证书加入到apt的源列表中:" +#: src\2-2-services-intro.md:59 +msgid "" +"`syncd` listens to changes in ASIC_DB and updates the switch ASIC " +"configuration through the unified SAI API interface by calling the ASIC " +"Driver." msgstr "" -#: src/3-2-compile.md:17 +#: src\2-2-services-intro.md:61 msgid "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install ca-certificates curl gnupg\n" -"\n" -"sudo install -m 0755 -d /etc/apt/keyrings\n" -"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor " -"-o /etc/apt/keyrings/docker.gpg\n" -"sudo chmod a+r /etc/apt/keyrings/docker.gpg\n" -"\n" -"echo \\\n" -" \"deb [arch=\"$(dpkg --print-architecture)\" " -"signed-by=/etc/apt/keyrings/docker.gpg] " -"https://download.docker.com/linux/ubuntu \\\n" -" \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n" -" sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\n" -"```" +"Configuration initialization is similar to configuration deployment but " +"involves reading configuration files when services start, which will not be " +"expanded here." msgstr "" -#: src/3-2-compile.md:31 -msgid "然后,我们就可以通过apt来快速安装啦:" +#: src\2-2-services-intro.md:63 +msgid "State Synchronization" msgstr "" -#: src/3-2-compile.md:33 +#: src\2-2-services-intro.md:65 msgid "" -"```bash\n" -"sudo apt-get update\n" -"sudo apt-get install docker-ce docker-ce-cli containerd.io " -"docker-buildx-plugin docker-compose-plugin\n" -"```" +"If situations arise, such as a port failure or changes in the ASIC state, " +"state updates and synchronization are needed. The process generally follows " +"these steps:" msgstr "" -#: src/3-2-compile.md:38 +#: src\2-2-services-intro.md:67 msgid "" -"安装完docker的程序之后,我们还需要把当前的账户添加到docker的用户组中,然后**退出并重新登录当前用户**,这样我们就可以不用sudo来运行docker命令了!**这一点非常重要**,因为后续SONiC的build是不允许使用sudo的。" +"**Detect State Changes**: These state changes mainly come from `*syncd` " +"services (netlink, etc.) and `syncd` services ([SAI Switch " +"Notification](https://github.com/sonic-net/sonic-sairedis/blob/master/syncd/SwitchNotifications.h)). " +"After detecting changes, these services send them to STATE_DB or APPL_DB." msgstr "" -#: src/3-2-compile.md:40 +#: src\2-2-services-intro.md:68 msgid "" -"```bash\n" -"sudo gpasswd -a ${USER} docker\n" -"```" +"**Process State Changes**: `orchagent` and `*mgrd` services listen to these " +"changes, process them, and re-deploy new configurations to the system " +"through command lines and netlink or to ASIC_DB for `syncd` services to " +"update the ASIC again." msgstr "" -#: src/3-2-compile.md:44 -msgid "安装完成之后,别忘了通过以下命令来验证一下是否安装成功(注意,此处不需要sudo!):" +#: src\2-2-services-intro.md:70 +msgid "Specific Examples" msgstr "" -#: src/3-2-compile.md:46 +#: src\2-2-services-intro.md:72 msgid "" -"```bash\n" -"docker run hello-world\n" -"```" +"The official SONiC documentation provides several typical examples of " +"control flow. Interested readers can refer to [SONiC Subsystem " +"Interactions](https://github.com/sonic-net/SONiC/wiki/Architecture#sonic-subsystems-interactions). " +"In the workflow chapter, we will also expand on some very common workflows." msgstr "" -#: src/3-2-compile.md:50 -msgid "### 安装其他依赖" +#: src\2-3-key-containers.md:1 +msgid "Key containers" msgstr "" -#: src/3-2-compile.md:52 +#: src\2-3-key-containers.md:3 msgid "" -"```bash\n" -"sudo apt install -y python3-pip\n" -"pip3 install --user j2cli\n" -"```" +"One of the most distinctive features of SONiC's design is containerization." msgstr "" -#: src/3-2-compile.md:57 -msgid "### 拉取代码" +#: src\2-3-key-containers.md:5 +msgid "" +"From the design diagram of SONiC, we can see that all services in SONiC run " +"in the form of containers. After logging into the switch, we can use the " +"`docker ps` command to see all containers that are currently running:" msgstr "" -#: src/3-2-compile.md:59 -msgid "" -"在[3.1 " -"代码仓库](./3-1-code-repos)一章中,我们提到了SONiC的主仓库是[sonic-buildimage][SonicBuildimageRepo]。它也是我们目前为止唯一需要安装关注的repo。" +#: src\2-3-key-containers.md:10 src\2-3-key-containers.md:11 +#: src\2-3-key-containers.md:16 src\2-3-key-containers.md:17 +#: src\2-3-key-containers.md:19 +msgid "\"/usr/local/bin/supe…\"" msgstr "" -#: src/3-2-compile.md:61 -msgid "" -"因为这个仓库通过submodule的形式将其他所有和编译相关的仓库包含在内,我们通过git命令拉取代码时需要注意加上`--recuse-submodules`的选项:" +#: src\2-3-key-containers.md:12 +msgid "\"/usr/bin/docker-lld…\"" msgstr "" -#: src/3-2-compile.md:63 -msgid "" -"```bash\n" -"git clone --recurse-submodules " -"https://github.com/sonic-net/sonic-buildimage.git\n" -"```" +#: src\2-3-key-containers.md:13 src\2-3-key-containers.md:15 +msgid "\"/usr/bin/docker_ini…\"" msgstr "" -#: src/3-2-compile.md:67 -msgid "如果在拉取代码的时候忘记拉取submodule,可以通过以下命令来补上:" +#: src\2-3-key-containers.md:14 src\2-3-key-containers.md:18 +msgid "\"/usr/bin/docker-ini…\"" msgstr "" -#: src/3-2-compile.md:69 -msgid "" -"```bash\n" -"git submodule update --init --recursive\n" -"```" +#: src\2-3-key-containers.md:20 +msgid "\"/usr/local/bin/dock…\"" msgstr "" -#: src/3-2-compile.md:73 -msgid "" -"当代码下载完毕之后,或者对于已有的repo,我们就可以通过以下命令来初始化编译环境了。这个命令更新当前所有的submodule到需要的版本,以帮助我们成功编译:" +#: src\2-3-key-containers.md:23 +msgid "Here we will briefly introduce these containers." msgstr "" -#: src/3-2-compile.md:75 -msgid "" -"```bash\n" -"sudo modprobe overlay\n" -"make init\n" -"```" +#: src\2-3-key-containers.md:25 +msgid "Database Container: `database`" msgstr "" -#: src/3-2-compile.md:80 -msgid "## 了解并设置你的目标平台" +#: src\2-3-key-containers.md:27 +msgid "" +"This container contains the central database - Redis, which we have " +"mentioned multiple times. It stores all the configuration and status of the " +"switch, and SONiC also uses it to provide the underlying communication " +"mechanism to various services." msgstr "" -#: src/3-2-compile.md:82 +#: src\2-3-key-containers.md:29 msgid "" -"[SONiC虽然支持非常多种不同的交换机][SONiCDevices],但是由于不同型号的交换机使用的ASIC不同,所使用的驱动和SDK也会不同。SONiC通过SAI来封装这些变化,为上层提供统一的配置接口,但是在编译的时候,我们需要正确的设置好,这样才能保证我们编译出来的SONiC可以在我们的目标平台上运行。" +"By entering this container via Docker, we can see the running Redis process:" msgstr "" -#: src/3-2-compile.md:84 -msgid "现在,SONiC主要支持如下几个平台:" +#: src\2-3-key-containers.md:44 +msgid "How does other container access this Redis database?" msgstr "" -#: src/3-2-compile.md:86 +#: src\2-3-key-containers.md:46 msgid "" -"- barefoot\n" -"- broadcom\n" -"- marvell\n" -"- mellanox\n" -"- cavium\n" -"- centec\n" -"- nephos\n" -"- innovium\n" -"- vs" +"The answer is through Unix Socket. We can see this Unix Socket in the " +"database container, which is mapped from the `/var/run/redis` directory on " +"the switch." msgstr "" -#: src/3-2-compile.md:96 -msgid "在确认好平台之后,我们就可以运行如下命令来配置我们的编译环境了:" +#: src\2-3-key-containers.md:49 +msgid "# In database container" msgstr "" -#: src/3-2-compile.md:98 -msgid "" -"```bash\n" -"make PLATFORM= configure\n" -"# e.g.: make PLATFORM=mellanox configure\n" -"```" +#: src\2-3-key-containers.md:52 +msgid "# On host" msgstr "" -#: src/3-2-compile.md:103 +#: src\2-3-key-containers.md:58 msgid "" -"```admonish note\n" -"所有的make命令(除了`make init`)一开始都会检查并创建所有debian版本的docker " -"builder:bullseye,stretch,jessie,buster。每个builder都需要几十分钟的时间才能创建完成,这对于我们平时开发而言实在完全没有必要,一般来说,我们只需要创建最新的版本即可(当前为bullseye,bookwarm暂时还没有支持),具体命令如下:\n" -"\n" -" NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make PLATFORM= configure\n" -"\n" -"当然,为了以后开发更加方便,避免重复输入,我们可以将这个命令写入到`~/.bashrc`中,这样每次打开终端的时候,就会设置好这些环境变量了。\n" -"\n" -" export NOJESSIE=1\n" -" export NOSTRETCH=1\n" -" export NOBUSTER=1\n" -"```" +"Then SONiC maps `/var/run/redis` folder into all relavent containers, " +"allowing other services to access the central database. For example, the " +"swss container:" msgstr "" -#: src/3-2-compile.md:115 -msgid "## 编译代码" +#: src\2-3-key-containers.md:63 +msgid "\"HostConfig\"" msgstr "" -#: src/3-2-compile.md:117 -msgid "### 编译全部代码" +#: src\2-3-key-containers.md:64 +msgid "\"Binds\"" msgstr "" -#: src/3-2-compile.md:119 -msgid "设置好平台之后,我们就可以开始编译代码了:" +#: src\2-3-key-containers.md:66 +msgid "\"/var/run/redis:/var/run/redis:rw\"" msgstr "" -#: src/3-2-compile.md:121 +#: src\2-3-key-containers.md:72 +msgid "SWitch State Service Container: `swss`" +msgstr "" + +#: src\2-3-key-containers.md:74 msgid "" -"```bash\n" -"# The number of jobs can be the number of cores on your machine.\n" -"# Say, if you have 16 cores, then feel free to set it to 16 to speed up the " -"build.\n" -"make SONIC_BUILD_JOBS=4 all\n" -"```" +"This container can be considered the most critical container in SONiC. **It " +"is the brain of SONiC**, running numerous `*syncd` and `*mgrd` services to " +"manage various configurations of the switch, such as Port, neighbor, ARP, " +"VLAN, Tunnel, etc. Additionally, it runs the `orchagent`, which handles many " +"configurations and state changes related to the ASIC." msgstr "" -#: src/3-2-compile.md:127 +#: src\2-3-key-containers.md:76 msgid "" -"```admonish note\n" -"当然,对于开发而言,我们可以把SONIC_BUILD_JOBS和上面其他变量一起也加入`~/.bashrc`中,减少我们的输入。\n" -"\n" -" export SONIC_BUILD_JOBS=\n" -"```" +"We have already discussed the general functions and processes of these " +"services, so we won't repeat them here. We can use the `ps` command to see " +"the services running in this container:" +msgstr "" + +#: src\2-3-key-containers.md:101 +msgid "ASIC Management Container: `syncd`" msgstr "" -#: src/3-2-compile.md:133 -msgid "### 编译子项目代码" +#: src\2-3-key-containers.md:103 +msgid "" +"This container is mainly used for managing the ASIC on the switch, running " +"the `syncd` service. The SAI (Switch Abstraction Interface) implementation " +"and ASIC Driver provided by various vendors are placed in this container. It " +"allows SONiC to support multiple different ASICs without modifying the " +"upper-layer services. In other words, without this container, SONiC would be " +"a brain in a jar, capable of only thinking but nothing else." msgstr "" -#: src/3-2-compile.md:135 +#: src\2-3-key-containers.md:105 msgid "" -"我们从SONiC的Build " -"Pipeline中就会发现,编译整个项目是非常耗时的,而绝大部分时候,我们的代码改动只会影响很小部分的代码,所以有没有办法减少我们编译的工作量呢?答案是肯定的,我们可以通过指定make " -"target来仅编译我们需要的子项目。" +"We don't have too many services running in the syncd container, mainly " +"syncd. We can check them using the `ps` command, and in the `/usr/lib` " +"directory, we can find the enormous SAI file compiled to support the ASIC:" msgstr "" -#: src/3-2-compile.md:137 -msgid "SONiC中每个子项目生成的文件都可以在`target`目录中找到,比如:" +#: src\2-3-key-containers.md:125 +msgid "Feature Containers" msgstr "" -#: src/3-2-compile.md:139 +#: src\2-3-key-containers.md:127 msgid "" -"- Docker containers: " -"target/.gz,比如:`target/docker-orchagent.gz`\n" -"- Deb packages: " -"target/debs//.deb,比如:`target/debs/bullseye/libswsscommon_1.0.0_amd64.deb`\n" -"- Python wheels: " -"target/python-wheels//.whl,比如:`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl`" +"There are many containers in SONiC designed to implement specific features. " +"These containers usually have special external interfaces (non-SONiC CLI and " +"REST API) and implementations (non-OS or ASIC), such as:" msgstr "" -#: src/3-2-compile.md:143 -msgid "当我们找到了我们需要的子项目之后,我们便可以将其生成的文件删除,然后重新调用make命令,这里我们用`libswsscommon`来举例子,如下:" +#: src\2-3-key-containers.md:129 +msgid "" +"`bgp`: Container for implementing the BGP and other routing protocol (Border " +"Gateway Protocol)" msgstr "" -#: src/3-2-compile.md:145 +#: src\2-3-key-containers.md:130 msgid "" -"```bash\n" -"# Remove the deb package for bullseye\n" -"rm target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"\n" -"# Build the deb package for bullseye\n" -"NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make " -"target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"```" +"`lldp`: Container for implementing the LLDP protocol (Link Layer Discovery " +"Protocol)" msgstr "" -#: src/3-2-compile.md:153 -msgid "### 检查和处理编译错误" +#: src\2-3-key-containers.md:131 +msgid "`teamd`: Container for implementing Link Aggregation" msgstr "" -#: src/3-2-compile.md:155 +#: src\2-3-key-containers.md:132 msgid "" -"如果不巧在编译的时候发生了错误,我们可以通过检查失败项目的日志文件来查看具体的原因。在SONiC中,每一个子编译项目都会生成其相关的日志文件,我们可以很容易的在`target`目录中找到,如下:" +"`snmp`: Container for implementing the SNMP protocol (Simple Network " +"Management Protocol)" msgstr "" -#: src/3-2-compile.md:157 +#: src\2-3-key-containers.md:134 msgid "" -"```bash\n" -"$ ls -l\n" -"...\n" -"-rw-r--r-- 1 r12f r12f 103M Jun 8 22:35 docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 26K Jun 8 22:35 docker-database.gz.log // Log " -"file for docker-database.gz\n" -"-rw-r--r-- 1 r12f r12f 106M Jun 8 22:44 docker-dhcp-relay.gz\n" -"-rw-r--r-- 1 r12f r12f 106K Jun 8 22:44 docker-dhcp-relay.gz.log // Log " -"file for docker-dhcp-relay.gz\n" -"```" +"Similar to SWSS, these containers also run the services we mentioned earlier " +"to adapt to SONiC's architecture:" msgstr "" -#: src/3-2-compile.md:166 +#: src\2-3-key-containers.md:136 msgid "" -"如果我们不想每次在更新代码之后都去代码的根目录下重新编译,然后查看日志文件,SONiC还提供了一个更加方便的方法,能让我们在编译完成之后停在docker " -"builder中,这样我们就可以直接去对应的目录运行`make`命令来重新编译了:" +"Configuration management and deployment (similar to `*mgrd`): `lldpmgrd`, " +"`zebra` (bgp)" msgstr "" -#: src/3-2-compile.md:168 +#: src\2-3-key-containers.md:137 msgid "" -"```bash\n" -"# KEEP_SLAVE_ON=yes make \n" -"KEEP_SLAVE_ON=yes make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb\n" -"KEEP_SLAVE_ON=yes make all\n" -"```" +"State synchronization (similar to `*syncd`): `lldpsyncd`, `fpmsyncd` (bgp), " +"`teamsyncd`" msgstr "" -#: src/3-2-compile.md:174 +#: src\2-3-key-containers.md:138 msgid "" -"```admonish note\n" -"有些仓库中的部分代码在全量编译的时候是不会编译的,比如,`sonic-swss-common`中的gtest,所以使用这种方法重编译的时候,请一定注意查看原仓库的编译指南,以避免出错,如:。\n" -"```" +"Service implementation or external interface (`*d`): `lldpd`, `bgpd`, " +"`teamd`, `snmpd`" msgstr "" -#: src/3-2-compile.md:178 -msgid "## 获取正确的镜像文件" +#: src\2-3-key-containers.md:140 +msgid "Management Service Container: `mgmt-framework`" msgstr "" -#: src/3-2-compile.md:180 +#: src\2-3-key-containers.md:142 msgid "" -"编译完成之后,我们就可以在`target`目录中找到我们需要的镜像文件了,但是这里有一个问题:我们到底要用哪一种镜像来把SONiC安装到我们的交换机上呢?这里主要取决于交换机使用什么样的BootLoader或者安装程序,其映射关系如下:" +"In previous chapters, we have seen how to use SONiC's CLI to configure some " +"aspects of the switch. However, in a production environment, manually " +"logging into the switch and using the CLI to configure all switches is " +"unrealistic. Therefore, SONiC provides a REST API to solve this problem. " +"This REST API is implemented in the `mgmt-framework` container. We can check " +"it using the `ps` command:" msgstr "" -#: src/3-2-compile.md:182 +#: src\2-3-key-containers.md:153 msgid "" -"| Bootloader | 后缀 |\n" -"| --- | --- |\n" -"| Aboot | .swi |\n" -"| ONIE | .bin |\n" -"| Grub | .img.gz |" +"In addition to the REST API, SONiC can also be managed through other methods " +"such as gNMI, all of which run in this container. The overall architecture " +"is shown in the figure below " +"[\\[2\\]](https://github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%20Framework.md):" msgstr "" -#: src/3-2-compile.md:188 -msgid "## 部分升级" +#: src\2-3-key-containers.md:155 +msgid "![](assets/chapter-2/sonic-mgmt-framework.jpg)" msgstr "" -#: src/3-2-compile.md:190 +#: src\2-3-key-containers.md:157 msgid "" -"显然,在开发的时候,每次都编译安装镜像然后进行全量安装的效率是相当低下的,所以我们可以选择不安装镜像而使用直接升级deb包的方式来进行部分升级,从而提高我们的开发效率。" +"Here we can also see that the CLI we use can be implemented by calling this " +"REST API at the bottom layer." +msgstr "" + +#: src\2-3-key-containers.md:159 +msgid "Platform Monitor Container: `pmon`" msgstr "" -#: src/3-2-compile.md:192 +#: src\2-3-key-containers.md:161 msgid "" -"我们可以将deb包上传到交换机的`/etc/sonic`目录下,这个目录下的文件会被map到所有容器的`/etc/sonic`目录下,接着我们可以进入到容器中,然后使用`dpkg`命令来安装deb包,如下:" +"The services in this container are mainly used to monitor the basic hardware " +"status of the switch, such as temperature, power supply, fans, SFP events, " +"etc. Similarly, we can use the `ps` command to check the services running in " +"this container:" msgstr "" -#: src/3-2-compile.md:194 +#: src\2-3-key-containers.md:179 msgid "" -"```bash\n" -"# Enter the docker container\n" -"docker exec -it bash\n" -"\n" -"# Install deb package\n" -"dpkg -i \n" -"```" +"The purpose of most of these services can be told from their names. The only " +"one that is not so obvious is `xcvrd`, where xcv stands for transceiver. It " +"is used to monitor the optical modules of the switch, such as SFP, QSFP, etc." +msgstr "" + +#: src\2-3-key-containers.md:184 src\3-1-code-repos.md:137 +msgid "" +"[SONiC Management " +"Framework](https://github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%20Framework.md)" msgstr "" -#: src/3-2-compile.md:204 +#: src\2-4-sai-intro.md:3 msgid "" -"1. [SONiC Build Guide][SONiCBuild]\n" -"2. [Install Docker Engine][DockerInstall]\n" -"3. [Github repo: sonic-buildimage][SonicBuildimageRepo]\n" -"4. [SONiC Supported Devices and Platforms][SONiCDevices]\n" -"5. [Wrapper for starting make inside sonic-slave " -"container][SONiCBuildImageMakeFile]" +"SAI (Switch Abstraction Interface) is the cornerstone of SONiC, while " +"enables it to support multiple hardware platforms. In [this SAI API " +"document](https://github.com/opencomputeproject/SAI/wiki/SAI-APIs), we can " +"see all the interfaces it defines." msgstr "" -#: src/3-3-testing.md:1 -msgid "# 测试" +#: src\2-4-sai-intro.md:5 +msgid "" +"[In the core container section, we mentioned that SAI runs in the `syncd` " +"container](./2-3-key-containers.html). However, unlike other components, it " +"is not a service but a set of common header files and dynamic link libraries " +"(.so). All abstract interfaces are defined as C language header files in the " +"[OCP SAI repository](https://github.com/opencomputeproject/SAI), and the " +"hardware vendors provides the .so files that implement the SAI interfaces." +msgstr "" + +#: src\2-4-sai-intro.md:7 +msgid "SAI Interface" msgstr "" -#: src/3-4-debugging.md:1 -msgid "# 调试" +#: src\2-4-sai-intro.md:9 +msgid "" +"To make things more intuitive, let's take a small portion of the code to " +"show how SAI interfaces look like and how it works, as follows:" msgstr "" -#: src/3-4-sai-debugging.md:1 -msgid "# SAI调试" +#: src\2-4-sai-intro.md:12 +msgid "// File: meta/saimetadata.h" msgstr "" -#: src/4-communications.md:1 -msgid "# 通信机制" +#: src\2-4-sai-intro.md:18 +msgid "// File: inc/saiswitch.h" msgstr "" -#: src/4-communications.md:3 -msgid "SONiC中主要的通信机制有三种:与内核的通信,基于Redis和基于ZMQ的服务间的通信。" +#: src\2-4-sai-intro.md:28 +msgid "// File: inc/saiport.h" msgstr "" -#: src/4-communications.md:5 +#: src\2-4-sai-intro.md:40 msgid "" -"- 与内核通信主要有两种方法:命令行调用和Netlink消息。\n" -"- " -"基于Redis的服务间通信主要有四种方法:SubscriberStateTable,NotificationProducer/Consumer,Producer/ConsumerTable,Producer/ConsumerStateTable。虽然它们都是基于Redis的,但是它们解决的问题和方法却非常不同。\n" -"- 基于ZMQ的服务间通信:现在只在`orchagent`和`syncd`的通信中使用了这种通信机制。" +"The `sai_apis_t` structure is a collection of interfaces for all SAI " +"modules, with each member being a pointer to a specific module's interface " +"list. For example, `sai_switch_api_t` defines all the interfaces for the SAI " +"Switch module, and its definition can be found in `inc/saiswitch.h`. " +"Similarly, the interface definitions for the SAI Port module can be found in " +"`inc/saiport.h`." msgstr "" -#: src/4-communications.md:9 +#: src\2-4-sai-intro.md:42 +msgid "SAI Initialization" +msgstr "" + +#: src\2-4-sai-intro.md:44 msgid "" -"```admonish note\n" -"虽然大部分的通信机制都支持多消费者的PubSub的模式,但是请特别注意:在SONiC中,所有的通信都是点对点的,即一个生产者对应一个消费者,绝对不会出现一个生产者对应多个消费者的情况!\n" -"\n" -"一旦多消费者出现,那么一个消息的处理逻辑将可能发生在多个进程中,这将导致很大的问题,因为对于任何一种特定的消息,SONiC中只有一个地方来处理,所以这会导致部分消息不可避免的出错或者丢失。\n" -"```" +"SAI initialization is essentially about obtaining these function pointers so " +"that we can operate the ASIC through the SAI interfaces." msgstr "" -#: src/4-communications.md:15 +#: src\2-4-sai-intro.md:46 msgid "" -"所有这些基础的通信机制的实现都在[sonic-swss-common][SONiCSWSSCommon]这个repo中的`common`目录下。另外在其之上,为了方便各个服务使用,SONiC还在[sonic-swss][SONiCSWSS]中封装了一层Orch,将常用的表放在其中。" +"The main functions involved in SAI initialization are defined in `inc/sai.h`:" msgstr "" -#: src/4-communications.md:17 -msgid "这一章,我们就主要来看看这些通信机制的实现吧!" +#: src\2-4-sai-intro.md:48 +msgid "`sai_api_initialize`: Initialize SAI" msgstr "" -#: src/4-communications.md:21 -#: src/4-2-1-redis-wrappers.md:35 -#: src/4-4-orch-layer.md:36 -#: src/4-5-event-polling-and-error-handling.md:121 +#: src\2-4-sai-intro.md:49 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]" +"`sai_api_query`: Pass in the type of SAI API to get the corresponding " +"interface list" msgstr "" -#: src/4-1-1-exec.md:1 -msgid "# 命令行调用" +#: src\2-4-sai-intro.md:51 +msgid "" +"Although most vendors' SAI implementations are closed-source, Mellanox has " +"open-sourced its SAI implementation, allowing us to gain a deeper " +"understanding of how SAI works." msgstr "" -#: src/4-1-1-exec.md:3 +#: src\2-4-sai-intro.md:53 msgid "" -"SONiC中的与内核通信最简单的方式就是命令行调用了,其实现放在[common/exec.h](https://github.com/sonic-net/sonic-swss-common/blob/master/common/exec.h)文件下,且十分简单,接口如下:" +"For example, the `sai_api_initialize` function simply sets two global " +"variables and returns `SAI_STATUS_SUCCESS`:" msgstr "" -#: src/4-1-1-exec.md:5 +#: src\2-4-sai-intro.md:56 src\2-4-sai-intro.md:73 msgid "" -"```cpp\n" -"// File: common/exec.h\n" -"// Namespace: swss\n" -"int exec(const std::string &cmd, std::string &stdout);\n" -"```" +"// File: " +"https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_interfacequery.c" msgstr "" -#: src/4-1-1-exec.md:11 -msgid "" -"其中,`cmd`是要执行的命令,`stdout`是命令执行的输出。这里的`exec`函数是一个同步调用,调用者会一直阻塞,直到命令执行完毕。其内部通过调用`popen`函数来创建子进程,并且通过`fgets`函数来获取输出。不过,**虽然这个函数返回了输出,但是基本上并没有人使用**,而只是通过返回值来判断是否成功,甚至连错误log中都不会写入输出的结果。" +#: src\2-4-sai-intro.md:62 +msgid "// Validate parameters here (code omitted)" msgstr "" -#: src/4-1-1-exec.md:13 -msgid "这个函数虽然粗暴,但是使用广泛,特别是在各个`*mgrd`服务中,比如`portmgrd`中就用它来设置每一个Port的状态等等。" +#: src\2-4-sai-intro.md:70 +msgid "" +"After initialization, we can use the `sai_api_query` function to query the " +"corresponding interface list by passing in the type of API, where each " +"interface list is actually a global variable:" msgstr "" -#: src/4-1-1-exec.md:15 +#: src\2-4-sai-intro.md:83 msgid "" -"```cpp\n" -"// File: sonic-swss - cfgmgr/portmgr.cpp\n" -"bool PortMgr::setPortAdminStatus(const string &alias, const bool up)\n" -"{\n" -" stringstream cmd;\n" -" string res, cmd_str;\n" -"\n" -" // ip link set dev [up|down]\n" -" cmd << IP_CMD << \" link set dev \" << shellquote(alias) << (up ? \" " -"up\" : \" down\");\n" -" cmd_str = cmd.str();\n" -" int ret = swss::exec(cmd_str, res);\n" -"\n" -" // ...\n" -"```" +"// File: " +"https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_interfacequery_eth.c" msgstr "" -#: src/4-1-1-exec.md:30 +#: src\2-4-sai-intro.md:103 msgid "" -"```admonish note\n" -"**为什么说命令行调用是一种通信机制呢**?\n" -"\n" -"原因是当`*mgrd`服务调用`exec`函数对系统进行的修改,会触发下面马上会提到的netlink事件,从而通知其他服务进行相应的修改,比如`*syncd`,这样就间接的构成了一种通信。所以这里我们把命令行调用看作一种通信机制能帮助我们以后更好的理解SONiC的各种工作流。\n" -"```" +"// File: " +"https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_bridge.c" msgstr "" -#: src/4-1-1-exec.md:38 -#: src/4-1-2-netlink.md:72 -msgid "1. [Github repo: sonic-swss-common][SONiCSWSSCommon]" +#: src\2-4-sai-intro.md:113 src\5-1-syncd-and-sai.md:395 +#: src\5-1-syncd-and-sai.md:592 +msgid "" +"// File: " +"https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_switch.c" msgstr "" -#: src/4-1-2-netlink.md:1 -msgid "# Netlink" +#: src\2-4-sai-intro.md:124 +msgid "Using SAI" msgstr "" -#: src/4-1-2-netlink.md:3 +#: src\2-4-sai-intro.md:126 msgid "" -"Netlink是Linux内核中用于内核与用户空间进程之间的一种基于消息的通信机制。它通过套接字接口和自定义的协议族来实现,可以用来传递各种类型的内核消息,包括网络设备状态、路由表更新、防火墙规则变化、系统资源使用情况等等。而SONiC的`*sync`服务就大量使用了Netlink的机制来监听系统中网络设备的变化,并将最新的状态同步到Redis中,并通知其他服务进行相应的修改。" +"In the `syncd` container, SONiC starts the `syncd` service at startup, which " +"loads the SAI component present in the system. This component is provided by " +"various vendors, who implement the SAI interfaces based on their hardware " +"platforms, allowing SONiC to use a unified upper-layer logic to control " +"various hardware platforms." msgstr "" -#: src/4-1-2-netlink.md:5 -msgid "" -"Netlink的实现主要在这几个文件中:[common/netmsg.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netmsg.h)、[common/netlink.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netlink.h) " -"和 " -"[common/netdispatcher.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netdispatcher.h),具体类图如下:" +#: src\2-4-sai-intro.md:128 +msgid "We can verify this using the `ps`, `ls`, and `nm` commands:" msgstr "" -#: src/4-1-2-netlink.md:9 -#: src/4-2-1-redis-wrappers.md:9 -msgid "其中:" +#: src\2-4-sai-intro.md:131 +msgid "# Enter into syncd container" msgstr "" -#: src/4-1-2-netlink.md:11 -msgid "" -"- **Netlink**:封装了Netlink的套接字接口,提供了Netlink消息的接口和接收消息的回调。\n" -"- " -"**NetDispatcher**:它是一个单例,提供了Handler注册的接口。当Netlink类接收到原始的消息后,就会调用NetDispatcher将其解析成nl_onject,并根据消息的类型调用相应的Handler。\n" -"- **NetMsg**:Netlink消息Handler的基类,仅提供了onMsg的接口,其中没有实现。" +#: src\2-4-sai-intro.md:133 +msgid "# List all processes. We will only see syncd process here." msgstr "" -#: src/4-1-2-netlink.md:15 -msgid "" -"举一个例子,当`portsyncd`启动的时候,它会创建一个Netlink对象,用来监听Link相关的状态变化,并且会实现NetMsg的接口,对Link相关的消息进行处理。具体的实现如下:" +#: src\2-4-sai-intro.md:141 +msgid "# Find all libsai*.so.* files." msgstr "" -#: src/4-1-2-netlink.md:17 +#: src\2-4-sai-intro.md:152 msgid "" -"```cpp\n" -"// File: sonic-swss - portsyncd/portsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create Netlink object to listen to link messages\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" // Here SONiC request a fulldump of current state, so that it can get " -"the current state of all links\n" -" netlink.dumpRequest(RTM_GETLINK); \n" -" cout << \"Listen to link messages...\" << endl;\n" -" // ...\n" -"\n" -" // Register handler for link messages\n" -" LinkSync sync(&appl_db, &state_db);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" // ...\n" -"}\n" -"```" +"# Copy the file out of switch and check libsai.so on your own dev machine." msgstr "" -#: src/4-1-2-netlink.md:41 -msgid "上面的LinkSync,就是一个NetMsg的实现,它实现了onMsg接口,用来处理Link相关的消息:" +#: src\2-4-sai-intro.md:153 +msgid "# We will see the most important SAI export functions here." msgstr "" -#: src/4-1-2-netlink.md:43 +#: src\2-4-sai-intro.md:169 msgid "" -"```cpp\n" -"// File: sonic-swss - portsyncd/linksync.h\n" -"class LinkSync : public NetMsg\n" -"{\n" -"public:\n" -" LinkSync(DBConnector *appl_db, DBConnector *state_db);\n" -"\n" -" // NetMsg interface\n" -" virtual void onMsg(int nlmsg_type, struct nl_object *obj);\n" -"\n" -" // ...\n" -"};\n" -"\n" -"// File: sonic-swss - portsyncd/linksync.cpp\n" -"void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // ...\n" -"\n" -" // Write link state to Redis DB\n" -" FieldValueTuple fv(\"oper_status\", oper ? \"up\" : \"down\");\n" -" vector fvs;\n" -" fvs.push_back(fv);\n" -" m_stateMgmtPortTable.set(key, fvs);\n" -" // ...\n" -"}\n" -"```" +"[Github: " +"sonic-net/sonic-sairedis](https://github.com/sonic-net/sonic-sairedis/)" msgstr "" -#: src/4-2-1-redis-wrappers.md:1 -msgid "# Redis封装" +#: src\2-4-sai-intro.md:170 +msgid "" +"[Github: opencomputeproject/SAI](https://github.com/opencomputeproject/SAI)" msgstr "" -#: src/4-2-1-redis-wrappers.md:3 -msgid "## Redis数据库操作层" +#: src\2-4-sai-intro.md:171 +msgid "" +"[Arista 7050QX Series 10/40G Data Center Switches Data " +"Sheet](https://www.arista.com/assets/data/pdf/Datasheets/7050QX-32_32S_Datasheet_S.pdf)" msgstr "" -#: src/4-2-1-redis-wrappers.md:5 -msgid "第一层,也是最底层,是Redis的数据库操作层,封装了各种基本命令,比如,DB的连接,命令的执行,事件通知的回调接口等等。具体的类图如下:" +#: src\2-4-sai-intro.md:172 src\5-1-syncd-and-sai.md:826 +msgid "" +"[Github repo: Nvidia (Mellanox) SAI " +"implementation](https://github.com/Mellanox/SAI-Implementation/tree/master)" msgstr "" -#: src/4-2-1-redis-wrappers.md:11 +#: src\3-1-code-repos.md:3 msgid "" -"- " -"**[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:封装并保持着与Redis的连接,当其销毁时会将其连接关闭。\n" -"- " -"**[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:封装了所有的底层使用到的Redis的命令,比如`SET`、`GET`、`DEL`等等。\n" -"- " -"**[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redistran.h)**:封装了Redis的事务操作,用于在一个事务中执行多个命令,比如`MULTI`、`EXEC`等等。\n" -"- " -"**[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redispipeline.h)**:封装了hiredis的redisAppendFormattedCommand " -"API,提供了一个类似队列的异步的执行Redis命令的接口(虽然大部分使用方法依然是同步的)。它也是少有的对`SCRIPT " -"LOAD`命令进行了封装的类,用于在Redis中加载Lua脚本实现存储过程。SONiC中绝大部分需要执行Lua脚本的类,都会使用这个类来进行加载和调用。\n" -"- " -"**[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redisselect.h)**:它实现了Selectable的接口,用来支持基于epoll的事件通知机制(Event " -"Polling)。主要是在我们收到了Redis的回复,用来触发epoll进行回调(我们最后会更详细的介绍)。\n" -"- " -"**[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:这个类是一个“静态类”,它主要实现了SONiC " -"DB的配置文件的读取和解析。其他的数据库操作类,如果需要任何的配置信息,都会通过这个类来获取。" +"The code of SONiC is hosted on the [sonic-net account on " +"GitHub](https://github.com/sonic-net), with over 30 repositories. It can be " +"a bit overwhelming at first, but don't worry, we'll go through them together " +"here." msgstr "" -#: src/4-2-1-redis-wrappers.md:19 -msgid "## 表(Table)抽象层" +#: src\3-1-code-repos.md:5 +msgid "Core Repositories" msgstr "" -#: src/4-2-1-redis-wrappers.md:21 +#: src\3-1-code-repos.md:7 msgid "" -"在Redis数据库操作层之上,便是SONiC自己利用Redis中间的Key建立的表(Table)的抽象了,因为每一个Redis的Key的格式都是``,所以SONiC在访问数据库时需要对其进行一次转换(没有印象的小伙伴可以移步[我之前的博客了解更多的信息](/posts/sonic-2-key-components/#数据库))。" +"First, let's look at the two most important core repositories in SONiC: " +"SONiC and sonic-buildimage." msgstr "" -#: src/4-2-1-redis-wrappers.md:23 -msgid "相关类的主要类图如下:" +#: src\3-1-code-repos.md:9 +msgid "Landing Repository: `SONiC`" msgstr "" -#: src/4-2-1-redis-wrappers.md:27 -msgid "其中关键的类有三个:" +#: src\3-1-code-repos.md:11 +msgid "" msgstr "" -#: src/4-2-1-redis-wrappers.md:29 +#: src\3-1-code-repos.md:13 msgid "" -"- " -"**[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**:这个类是所有表的基类,它主要封装了表的基本信息,如表的名字,Redis " -"Key的打包,每个表发生修改时用于通信的Channel的名字,等等。\n" -"- " -"**[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**:这个类就是对于每个表增删改查的封装了,里面包含了表的名称和分隔符,这样就可以在调用时构造最终的key了。\n" -"- " -"**[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertablebase.h)**:这个类是各种SubscriptionTable的基类,里面主要是封装了一个简单的队列和其pop操作(对,只有pop,没有push),用来给上层调用。" +"This repository contains the SONiC Landing Page and a large number of " +"documents, Wiki, tutorials, slides from past talks, and so on. This " +"repository is the most commonly used by newcomers, but note that there is " +"**no code** in this repository, only documentation." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:1 -msgid "# 通信层" +#: src\3-1-code-repos.md:15 +msgid "Image Build Repository: `sonic-buildimage`" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:3 -msgid "在Redis的封装和表抽象之上,便是SONiC的通信层了,由于需求的不同,这一层中提供了四种不同的PubSub的封装,用于服务间的通信。" +#: src\3-1-code-repos.md:17 +msgid "" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:5 -msgid "## SubscribeStateTable" +#: src\3-1-code-repos.md:19 +msgid "" +"Why is this build repository so important to us? Unlike other projects, " +"**the build repository of SONiC is actually its main repository**! This " +"repository contains:" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:7 +#: src\3-1-code-repos.md:21 msgid "" -"最直接的就是[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/subscriberstatetable.h)了。" +"All the feature implementation repositories, in the form of git submodules " +"(under the `src` directory)." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:9 +#: src\3-1-code-repos.md:22 msgid "" -"它的原理是利用Redis数据库中自带的keyspace消息通知机制 [\\[4\\]][RedisKeyspace] —— " -"当数据库中的任何一个key对应的值发生了变化,就会触发Redis发送两个keyspace的事件通知,一个是`__keyspace@__:`下的``事件,一个是`__keyspace@__:`下的`>`事件,比如,在数据库0中删除了一个key,那么就会触发两个事件通知:" +"Support files for each device from switch manufactures (under the `device` " +"directory), such as device configuration files for each model of switch, " +"scripts, and so on. For example, my switch is an Arista 7050QX-32S, so I can " +"find its support files in the `device/arista/x86_64-arista_7050_qx32s` " +"directory." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:11 +#: src\3-1-code-repos.md:23 msgid "" -"```redis\n" -"PUBLISH __keyspace@0__:foo del\n" -"PUBLISH __keyevent@0__:del foo\n" -"```" +"Support files provided by all ASIC chip manufacturers (in the `platform` " +"directory), such as drivers, BSP, and low-level support scripts for each " +"platform. Here we can see support files from almost all major chip " +"manufacturers, such as Broadcom, Mellanox, etc., as well as implementations " +"for simulated software switches, such as vs and p4. But for protecting IPs " +"from each vendor, most of the time, the repo only contains the Makefiles " +"that downloads these things for build purpose." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:16 +#: src\3-1-code-repos.md:24 msgid "" -"而SubscriberStateTable就是监听了第一个事件通知,然后调用相应的回调函数。和其直接相关的主要的类的类图如下,这里可以看到它继承了ConsumerTableBase,因为它是Redis的消息的Consumer:" +"Dockerfiles for building all container images used by SONiC (in the " +"`dockers` directory)." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:20 -msgid "在初始化时,我们可以看到它是如何订阅Redis的事件通知的:" +#: src\3-1-code-repos.md:25 +msgid "" +"Various general configuration files and scripts (in the `files` directory)." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:22 +#: src\3-1-code-repos.md:26 msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"SubscriberStateTable::SubscriberStateTable(DBConnector *db, const string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri), m_table(db, " -"tableName)\n" -"{\n" -" m_keyspace = \"__keyspace@\";\n" -" m_keyspace += to_string(db->getDbId()) + \"__:\" + tableName + " -"m_table.getTableNameSeparator() + \"*\";\n" -" psubscribe(m_db, m_keyspace);\n" -" // ...\n" -"```" +"Dockerfiles for the build containers used for building (in the " +"`sonic-slave-*` directories)." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:33 -msgid "其事件接收和分发主要由两个函数负责:" +#: src\3-1-code-repos.md:27 +msgid "And more..." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:35 +#: src\3-1-code-repos.md:29 msgid "" -"- `readData()`负责将redis中待读取的事件读取出来,并放入ConsumerTableBase中的队列中\n" -"- `pops()`:负责将队列中的原始事件取出来,并且进行解析,然后通过函数参数传递给调用方" +"Because this repository brings all related resources together, we basically " +"only need to checkout this single repository to get all SONiC's code. It " +"makes searching and navigating the code much more convenient than checking " +"out the repos one by one!" +msgstr "" + +#: src\3-1-code-repos.md:31 +msgid "Feature Repositories" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:38 +#: src\3-1-code-repos.md:33 msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/subscriberstatetable.cpp\n" -"uint64_t SubscriberStateTable::readData()\n" -"{\n" -" // ...\n" -" reply = nullptr;\n" -" int status;\n" -" do {\n" -" status = redisGetReplyFromReader(m_subscribe->getContext(), " -"reinterpret_cast(&reply));\n" -" if(reply != nullptr && status == REDIS_OK) {\n" -" " -"m_keyspace_event_buffer.emplace_back(make_shared(reply));\n" -" }\n" -" } while(reply != nullptr && status == REDIS_OK);\n" -" // ...\n" -" return 0;\n" -"}\n" -"\n" -"void SubscriberStateTable::pops(deque &vkco, const " -"string& /*prefix*/)\n" -"{\n" -" vkco.clear();\n" -" // ...\n" -"\n" -" // Pop from m_keyspace_event_buffer, which is filled by readData()\n" -" while (auto event = popEventBuffer()) {\n" -" KeyOpFieldsValuesTuple kco;\n" -" // Parsing here ...\n" -" vkco.push_back(kco);\n" -" }\n" -"\n" -" m_keyspace_event_buffer.clear();\n" -"}\n" -"```" +"In addition to the core repositories, SONiC also has many feature " +"repositories, which contain the implementations of various containers and " +"services. These repositories are imported as submodules in the `src` " +"directory of sonic-buildimage. If we would like to modify and contribute to " +"SONiC, we also need to understand them." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:71 -msgid "## NotificationProducer / NotificationConsumer" +#: src\3-1-code-repos.md:35 +msgid "SWSS (Switch State Service) Related Repositories" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:73 +#: src\3-1-code-repos.md:37 msgid "" -"说到消息通信,我们很容易就会联想到消息队列,这就是我们的第二种通信方式 —— " -"[NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationproducer.h)和[NotificationConsumer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationconsumer.h)。" +"As introduced in the previous section, the SWSS container is the brain of " +"SONiC. In SONiC, it consists of two repositories: " +"[sonic-swss-common](https://github.com/sonic-net/sonic-swss-common) and " +"[sonic-swss](https://github.com/sonic-net/sonic-swss)." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:75 -msgid "" -"这种通信方式通过Redis的自带的PubSub来实现,主要是对`PUBLISH`和`SUBSCRIBE`命令的包装,很有限的应用在最简单的通知型的场景中,比如orchagent中的timeout " -"check, restart check之类,非传递用户配置和数据的场景:" +#: src\3-1-code-repos.md:39 +msgid "SWSS Common Library: `sonic-swss-common`" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:79 +#: src\3-1-code-repos.md:41 msgid "" -"这种通信模式下,消息的发送方Producer,主要会做两件事情:一是将消息打包成JSON格式,二是调用Redis的`PUBLISH`命令将消息发送出去。而且由于`PUBLISH`命令只能携带一个消息,所以请求中的`op`和`data`字段会被放在`values`的最前面,然后再调用`buildJson`函数将其打包成一个JSON数组的格式:" +"The first one is the common library: `sonic-swss-common` " +"()." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:81 +#: src\3-1-code-repos.md:43 msgid "" -"```cpp\n" -"int64_t swss::NotificationProducer::send(const std::string &op, const " -"std::string &data, std::vector &values)\n" -"{\n" -" // Pack the op and data into values array, then pack everything into a " -"JSON string as the message\n" -" FieldValueTuple opdata(op, data);\n" -" values.insert(values.begin(), opdata);\n" -" std::string msg = JSon::buildJson(values);\n" -" values.erase(values.begin());\n" -"\n" -" // Publish message to Redis channel\n" -" RedisCommand command;\n" -" command.format(\"PUBLISH %s %s\", m_channel.c_str(), msg.c_str());\n" -" // ...\n" -" RedisReply reply = m_pipe->push(command);\n" -" reply.checkReplyType(REDIS_REPLY_INTEGER);\n" -" return reply.getReply();\n" -"}\n" -"```" +"This repository contains all the common functionalities needed by `*mgrd` " +"and `*syncd` services, such as logger, JSON, netlink encapsulation, Redis " +"operations, and various inter-service communication mechanisms based on " +"Redis. Although it was initially intended for swss services, its extensive " +"functionalities have led to its use in many other repositories, such as " +"`swss-sairedis` and `swss-restapi`." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:100 -msgid "接收方则是利用`SUBSCRIBE`命令来接收所有的通知:" +#: src\3-1-code-repos.md:45 +msgid "Main SWSS Repository: `sonic-swss`" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:102 +#: src\3-1-code-repos.md:47 msgid "" -"```cpp\n" -"void swss::NotificationConsumer::subscribe()\n" -"{\n" -" // ...\n" -" m_subscribe = new DBConnector(m_db->getDbId(),\n" -" m_db->getContext()->unix_sock.path,\n" -" NOTIFICATION_SUBSCRIBE_TIMEOUT);\n" -" // ...\n" -"\n" -" // Subscribe to Redis channel\n" -" std::string s = \"SUBSCRIBE \" + m_channel;\n" -" RedisReply r(m_subscribe, s, REDIS_REPLY_ARRAY);\n" -"}\n" -"```" +"Next is the main SWSS repository: `sonic-swss` " +"()." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:117 -msgid "## ProducerTable / ConsumerTable" +#: src\3-1-code-repos.md:49 +msgid "In this repository, we can find:" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:119 +#: src\3-1-code-repos.md:51 msgid "" -"我们可以看到NotificationProducer/Consumer实现简单粗暴,但是由于API的限制 " -"[\\[8\\]][RedisClientHandling],它并不适合用来传递数据,所以,SONiC中提供了一种和它非常接近的另外一种基于消息队列的通信机制 " -"—— " -"[ProducerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producertable.h)和[ConsumerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertable.h)。" +"Most of the `*mgrd` and `*syncd` services: `orchagent`, " +"`portsyncd/portmgrd/intfmgrd`, `neighsyncd/nbrmgrd`, `natsyncd/natmgrd`, " +"`buffermgrd`, `coppmgrd`, `macsecmgrd`, `sflowmgrd`, `tunnelmgrd`, " +"`vlanmgrd`, `vrfmgrd`, `vxlanmgrd`, and more." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:121 +#: src\3-1-code-repos.md:52 msgid "" -"这种通信方式通过Redis的List来实现,和Notification不同的地方在于,发布给Channel中的消息非常的简单(单字符\"G\"),所有的数据都存储在List中,从而解决了Notification中消息大小限制的问题。在SONiC中,它主要用在FlexCounter,`syncd`服务和`ASIC_DB`中:" +"`swssconfig`: Located in the `swssconfig` directory, used to restore FDB and " +"ARP tables during fast reboot." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:123 +#: src\3-1-code-repos.md:53 msgid "" -"1. **消息格式**:每条消息都是一个(Key, FieldValuePairs, " -"Op)的三元组,如果用JSON来表达这个消息,那么它的格式如下:(这里的Key是Table中数据的Key,被操作的数据是[Hash][RedisHash],所以Field就是Hash中的Field,Value就是Hash中的Value了,也就是说一个消息可以对很多个Field进行操作)\n" -"\n" -" ```json\n" -" [ \"Key\", \"[\\\"Field1\\\", \\\"Value1\\\", \\\"Field2\", " -"\\\"Value2\\\", ...]\", \"Op\" ]\n" -" ```\n" -"\n" -"2. **Enqueue**:ProducerTable通过Lua脚本将消息三元组原子的写入消息队列中(Key = " -"`_KEY_VALUE_OP_QUEUE`,并且发布更新通知到特定的Channel(Key = " -"`_CHANNEL`)中。\n" -"3. " -"**Pop**:ConsumerTable也通过Lua脚本从消息队列中原子的读取消息三元组,并**在读取过程中**将其中请求的改动真正的写入到数据库中。" +"`swssplayer`: Also in the `swssconfig` directory, used to record all " +"configuration operations performed through SWSS, allowing us to replay them " +"for troubleshooting and debugging." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:132 +#: src\3-1-code-repos.md:54 msgid "" -"```admonish note\n" -"**注意**:Redis中Lua脚本和MULTI/EXEC的原子性和通常说的数据库ACID中的原子性(Atomicity)不同,Redis中的原子性其实更接近于ACID中的隔离性(Isolation),他保证Lua脚本中所有的命令在执行的时候不会有其他的命令执行,但是并不保证Lua脚本中的所有命令都会执行成功,比如,如果Lua脚本中的第二个命令执行失败了,那么第一个命令依然会被提交,只是后面的命令就不会继续执行了。更多的细节可以参考Redis官方文档 " -"[\\[5\\]][RedisTx] [\\[6\\]][RedisLuaAtomicity]。\n" -"```" +"Even some services not in the SWSS container, such as `fpmsyncd` (BGP " +"container) and `teamsyncd/teammgrd` (teamd container)." +msgstr "" + +#: src\3-1-code-repos.md:56 +msgid "SAI/Platform Related Repositories" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:136 +#: src\3-1-code-repos.md:58 msgid "" -"其主要类图如下,这里我们可以看到在ProducerTable中的`m_shaEnqueue`和ConsumerTable中的`m_shaPop`,它们就是上面我们提到的这两个Lua脚本在加载时获得的SHA了,而之后我们就可以使用Redis的`EVALSHA`命令对他们进行原子的调用了:" +"Next is the Switch Abstraction Interface (SAI). [Although SAI was proposed " +"by Microsoft and released version 0.1 in March " +"2015](https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf), " +"[by September 2015, before SONiC had even released its first version, it was " +"already accepted by OCP as a public " +"standard](https://azure.microsoft.com/en-us/blog/switch-abstraction-interface-sai-officially-accepted-by-the-open-compute-project-ocp/). " +"This shows how quickly SONiC and SAI was getting supports from the community " +"and vendors." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:140 -msgid "ProducerTable的核心逻辑如下,我们可以看到对Values的JSON打包,和使用`EVALSHA`来进行Lua脚本的调用:" +#: src\3-1-code-repos.md:60 +msgid "Overall, the SAI code is divided into two parts:" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:142 +#: src\3-1-code-repos.md:62 msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/producertable.cpp\n" -"ProducerTable::ProducerTable(RedisPipeline *pipeline, const string " -"&tableName, bool buffered)\n" -" // ...\n" -"{\n" -" string luaEnque =\n" -" \"redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);\"\n" -" \"redis.call('PUBLISH', KEYS[2], ARGV[4]);\";\n" -"\n" -" m_shaEnque = m_pipe->loadRedisScript(luaEnque);\n" -"}\n" -"\n" -"void ProducerTable::set(const string &key, const vector " -"&values, const string &op, const string &prefix)\n" -"{\n" -" enqueueDbChange(key, JSon::buildJson(values), \"S\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::del(const string &key, const string &op, const string " -"&prefix)\n" -"{\n" -" enqueueDbChange(key, \"{}\", \"D\" + op, prefix);\n" -"}\n" -"\n" -"void ProducerTable::enqueueDbChange(const string &key, const string &value, " -"const string &op, const string& /* prefix */)\n" -"{\n" -" RedisCommand command;\n" -"\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %s %s %s %s\",\n" -" m_shaEnque.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" getChannelName(m_pipe->getDbId()).c_str(),\n" -" key.c_str(),\n" -" value.c_str(),\n" -" op.c_str(),\n" -" \"G\");\n" -"\n" -" m_pipe->push(command, REDIS_REPLY_NIL);\n" -"}\n" -"```" +"OpenComputeProject/SAI under OCP: " +". This repository contains all " +"the code related to the SAI standard, including SAI header files, behavior " +"models, test cases, documentation, and more." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:182 +#: src\3-1-code-repos.md:63 msgid "" -"而另一侧的ConsumerTable就稍稍复杂一点,因为其支持的op类型很多,所以逻辑都写在了一个单独的文件中(`common/consumer_table_pops.lua`),我们这里就不贴代码了,有兴趣的同学可以自己去看看。" +"sonic-sairedis under SONiC: . " +"This repository contains all the code used by SONiC to interact with SAI, " +"such as the syncd service and various debugging tools like `saiplayer` for " +"replay and `saidump` for exporting ASIC states." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:184 +#: src\3-1-code-repos.md:65 msgid "" -"```cpp\n" -"// File: sonic-swss-common - common/consumertable.cpp\n" -"ConsumerTable::ConsumerTable(DBConnector *db, const string &tableName, int " -"popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeyValueOpQueues(tableName)\n" -" , m_modifyRedis(true)\n" -"{\n" -" std::string luaScript = loadLuaScript(\"consumer_table_pops.lua\");\n" -" m_shaPop = loadRedisScript(db, luaScript);\n" -" // ...\n" -"}\n" -"\n" -"void ConsumerTable::pops(deque &vkco, const string " -"&prefix)\n" -"{\n" -" // Note that here we are processing the messages in bulk with " -"POP_BATCH_SIZE!\n" -" RedisCommand command;\n" -" command.format(\n" -" \"EVALSHA %s 2 %s %s %d %d\",\n" -" m_shaPop.c_str(),\n" -" getKeyValueOpQueueTableName().c_str(),\n" -" (prefix+getTableName()).c_str(),\n" -" POP_BATCH_SIZE,\n" -"\n" -" RedisReply r(m_db, command, REDIS_REPLY_ARRAY);\n" -" vkco.clear();\n" -"\n" -" // Parse and pack the messages in bulk\n" -" // ...\n" -"}\n" -"```" +"In addition to these two repositories, there is another platform-related " +"repository, such as " +"[sonic-platform-vpp](https://github.com/sonic-net/sonic-platform-vpp), which " +"uses SAI interfaces to implement data plane functionalities through VPP, " +"essentially acting as a high-performance soft switch. I personally feel it " +"might be merged into the buildimage repository in the future as part of the " +"platform directory." msgstr "" -#: src/4-2-2-redis-messaging-layer.md:215 -msgid "## ProducerStateTable / ConsumerStateTable" +#: src\3-1-code-repos.md:67 +msgid "Management Service (mgmt) Related Repositories" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:217 +#: src\3-1-code-repos.md:69 msgid "" -"Producer/ConsumerTable虽然直观,而且保序,但是它一个消息只能处理一个Key,并且还需要JSON的序列化,然而很多时候我们并用不到保序的功能,反而更需要更大的吞吐量,所以为了优化性能,SONiC就引入了第四种通信方式,也是最常用的通信方式:[ProducerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producerstatetable.h)和[ConsumerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertatetable.h)。" +"Next are all the repositories related to [management " +"services](https://github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%20Framework.md) " +"in SONiC:" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:219 -msgid "" -"与ProducerTable不同,ProducerStateTable使用Hash的方式来存储消息,而不是List。这样虽然不能保证消息的顺序,但是却可以很好的提升性能!首先,我们省下了JSON的序列化的开销,其次,对于同一个Key下的相同的Field如果被变更多次,那么只需要保留最后一次的变更,这样就将关于这个Key的所有变更消息就合并成了一条,减少了很多不必要的消息处理。" +#: src\3-1-code-repos.md:71 src\3-1-code-repos.md:87 +msgid "Name" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:221 -msgid "" -"Producer/ConsumerStateTable的底层实现相比于Producer/ConsumerTable也更加复杂一些。其相关联的类的主要类图如下,这里我们依然可以看到它的实现是通过`EVALSHA`调用Lua脚本来实现的,`m_shaSet`和`m_shaDel`就是用来存放修改和发送消息的,而另一边`m_shaPop`就是用来获取消息的:" +#: src\3-1-code-repos.md:71 src\3-1-code-repos.md:87 src\3-1-code-repos.md:96 +msgid "Description" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:225 -msgid "在传递消息时:" +#: src\3-1-code-repos.md:73 +msgid "[sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common)" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:227 +#: src\3-1-code-repos.md:73 msgid "" -"- " -"首先,每个消息会被存放成两个部分:一个是KEY_SET,用来保存当前有哪些Key发生了修改,它以Set的形式存放在``的key下,另一个是所有被修改的Key的内容,它以Hash的形式存放在`_`的key下。\n" -"- " -"然后,消息存放之后Producer如果发现是新的Key,那么就是调用`PUBLISH`命令,来通知`_CHANNEL@`Channel,有新的Key出现了。\n" -"\n" -" ```cpp\n" -" // File: sonic-swss-common - common/producerstatetable.cpp\n" -" ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const " -"string &tableName, bool buffered)\n" -" : TableBase(tableName, " -"SonicDBConfig::getSeparator(pipeline->getDBConnector()))\n" -" , TableName_KeySet(tableName)\n" -" // ...\n" -" {\n" -" string luaSet =\n" -" \"local added = redis.call('SADD', KEYS[2], ARGV[2])\\n" -"\"\n" -" \"for i = 0, #KEYS - 3 do\\n" -"\"\n" -" \" redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i " -"* 2])\\n" -"\"\n" -" \"end\\n" -"\"\n" -" \" if added > 0 then \\n" -"\"\n" -" \" redis.call('PUBLISH', KEYS[1], ARGV[1])\\n" -"\"\n" -" \"end\\n" -"\";\n" -"\n" -" m_shaSet = m_pipe->loadRedisScript(luaSet);\n" -" ```\n" -"\n" -"- " -"最后,Consumer会通过`SUBSCRIBE`命令来订阅`_CHANNEL@`Channel,一旦有新的消息到来,就会使用Lua脚本调用`HGETALL`命令来获取所有的Key,并将其中的值读取出来并真正的写入到数据库中去。\n" -"\n" -" ```cpp\n" -" ConsumerStateTable::ConsumerStateTable(DBConnector *db, const std::string " -"&tableName, int popBatchSize, int pri)\n" -" : ConsumerTableBase(db, tableName, popBatchSize, pri)\n" -" , TableName_KeySet(tableName)\n" -" {\n" -" std::string luaScript = " -"loadLuaScript(\"consumer_state_table_pops.lua\");\n" -" m_shaPop = loadRedisScript(db, luaScript);\n" -" // ...\n" -" \n" -" subscribe(m_db, getChannelName(m_db->getDbId()));\n" -" // ...\n" -" ```" -msgstr "" - -#: src/4-2-2-redis-messaging-layer.md:264 -msgid "为了方便理解,我们这里举一个例子:启用Port Ethernet0:" -msgstr "" - -#: src/4-2-2-redis-messaging-layer.md:266 -msgid "" -"- 首先,我们在命令行下调用`config interface startup " -"Ethernet0`来启用Ethernet0,这会导致`portmgrd`通过ProducerStateTable向APP_DB发送状态更新消息,如下:\n" -"\n" -" ```redis\n" -" EVALSHA \"\" \"6\" \"PORT_TABLE_CHANNEL@0\" " -"\"PORT_TABLE_KEY_SET\" \n" -" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" " -"\"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"G\"\n" -" \"Ethernet0\" \"alias\" \"Ethernet5/1\" \"index\" \"5\" \"lanes\" " -"\"9,10,11,12\" \"speed\" \"40000\"\n" -" ```\n" -"\n" -" 这个命令会在其中调用如下的命令来创建和发布消息:\n" -"\n" -" ```redis\n" -" SADD \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"_PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" PUBLISH \"PORT_TABLE_CHANNEL@0\" \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" 所以最终这个消息会在APPL_DB中被存放成如下的形式:\n" -"\n" -" ```redis\n" -" PORT_TABLE_KEY_SET:\n" -" _PORT_TABLE:Ethernet0\n" -"\n" -" _PORT_TABLE:Ethernet0:\n" -" alias: Ethernet5/1\n" -" index: 5\n" -" lanes: 9,10,11,12\n" -" speed: 40000\n" -" ```\n" -"\n" -"- 当ConsumerStateTable收到消息后,也会调用`EVALSHA`命令来执行Lua脚本,如下:\n" -"\n" -" ```redis\n" -" EVALSHA \"\" \"3\" \"PORT_TABLE_KEY_SET\" \"PORT_TABLE:\" " -"\"PORT_TABLE_DEL_SET\" \"8192\" \"_\"\n" -" ```\n" -"\n" -" " -"和Producer类似,这个脚本会执行如下命令,将`PORT_TABLE_KEY_SET`中的key,也就是`_PORT_TABLE:Ethernet0`读取出来,然后再将其对应的Hash读取出来,并更新到`PORT_TABLE:Ethernet0`去,同时将`_PORT_TABLE:Ethernet0`从数据库和`PORT_TABLE_KEY_SET`中删除。\n" -"\n" -" ```redis\n" -" SPOP \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" -" HGETALL \"_PORT_TABLE:Ethernet0\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" -" HSET \"PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" -" DEL \"_PORT_TABLE:Ethernet0\"\n" -" ```\n" -"\n" -" 到这里,数据的更新才算是完成了。" +"Base library for management services, containing `translib`, YANG " +"model-related code" msgstr "" -#: src/4-2-2-redis-messaging-layer.md:320 -msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Redis keyspace notifications][RedisKeyspace]\n" -"5. [Redis Transactions][RedisTx]\n" -"6. [Redis Atomicity with Lua][RedisLuaAtomicity]\n" -"7. [Redis hashes][RedisHash]\n" -"8. [Redis client handling][RedisClientHandling]" +#: src\3-1-code-repos.md:74 +msgid "[sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework)" msgstr "" -#: src/4-3-zmq-messaging.md:1 -msgid "# 基于ZMQ的通信" +#: src\3-1-code-repos.md:74 +msgid "" +"REST Server implemented in Go, acting as the REST Gateway in the " +"architecture diagram below (process name: `rest_server`)" msgstr "" -#: src/4-4-orch-layer.md:1 -msgid "# 服务层 - Orch" +#: src\3-1-code-repos.md:75 +msgid "[sonic-gnmi](https://github.com/sonic-net/sonic-gnmi)" msgstr "" -#: src/4-4-orch-layer.md:3 +#: src\3-1-code-repos.md:75 msgid "" -"最后,为了方便各个服务使用,SONiC还在通信层上进行了更进一步的封装,为各个服务提供了一个基类:[Orch](https://github.com/sonic-net/sonic-swss/blob/master/src/orchagent/orch.hcommon/consumertatetable.h)。" +"Similar to sonic-mgmt-framework, this is the gNMI (gRPC Network Management " +"Interface) Server based on gRPC in the architecture diagram below" msgstr "" -#: src/4-4-orch-layer.md:5 -msgid "由于有了上面这些封装,Orch中关于消息通信的封装就相对简单了,主要的类图如下:" +#: src\3-1-code-repos.md:76 +msgid "[sonic-restapi](https://github.com/sonic-net/sonic-restapi)" msgstr "" -#: src/4-4-orch-layer.md:9 +#: src\3-1-code-repos.md:76 msgid "" -"```admonish note\n" -"注意:由于这一层是服务层,所以其代码是在`sonic-swss`的仓库中,而不是`sonic-swss`。这个类中除了消息通信的封装以外,还提供了很多和服务实现相关的公共函数,比如,日志文件等等。\n" -"```" +"Another configuration management REST Server implemented in Go. Unlike " +"mgmt-framework, this server directly operates on CONFIG_DB upon receiving " +"messages, instead of using translib (not shown in the diagram, process name: " +"`go-server-server`)" msgstr "" -#: src/4-4-orch-layer.md:13 -msgid "" -"可以看到,Orch主要是封装了`SubscriberStateTable`和`ConsumerStateTable`来简化和统一消息的订阅,核心代码非常简单,就是根据不同的数据库类型来创建不同的Consumer,如下:" -msgstr "" - -#: src/4-4-orch-layer.md:15 -msgid "" -"```cpp\n" -"void Orch::addConsumer(DBConnector *db, string tableName, int pri)\n" -"{\n" -" if (db->getDbId() == CONFIG_DB || db->getDbId() == STATE_DB || " -"db->getDbId() == CHASSIS_APP_DB) {\n" -" addExecutor(\n" -" new Consumer(\n" -" new SubscriberStateTable(db, tableName, " -"TableConsumable::DEFAULT_POP_BATCH_SIZE, pri),\n" -" this,\n" -" tableName));\n" -" } else {\n" -" addExecutor(\n" -" new Consumer(\n" -" new ConsumerStateTable(db, tableName, gBatchSize, pri),\n" -" this,\n" -" tableName));\n" -" }\n" -"}\n" -"```" +#: src\3-1-code-repos.md:77 +msgid "[sonic-mgmt](https://github.com/sonic-net/sonic-mgmt)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:1 -msgid "# 事件分发和错误处理" +#: src\3-1-code-repos.md:77 +msgid "" +"Various automation scripts (in the `ansible` directory), tests (in the " +"`tests` directory), test bed setup and test reporting (in the " +"`test_reporting` directory), and more" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:3 -msgid "## 基于epoll的事件分发机制" +#: src\3-1-code-repos.md:79 +msgid "" +"Here is the architecture diagram of SONiC management services for reference " +"[\\[4\\]](https://github.com/sonic-net/SONiC/blob/master/doc/mgmt/Management%20Framework.md):" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:5 -msgid "和很多的Linux服务一样,SONiC底层使用了epoll作为事件分发机制:" +#: src\3-1-code-repos.md:81 +msgid "![](assets/chapter-3/sonic-mgmt-framework.jpg)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:7 +#: src\3-1-code-repos.md:83 msgid "" -"- 所有需要支持事件分发的类都需要继承`Selectable`类,并实现两个最核心的函数:`int " -"getFd();`(用于返回epoll能用来监听事件的fd)和`uint64_t " -"readData()`(用于在监听到事件到来之后进行读取)。而对于一般服务而言,这个fd就是redis通信使用的fd,所以`getFd()`函数的调用,都会被最终转发到Redis的库中。\n" -"- " -"所有需要参与事件分发的对象,都需要注册到`Select`类中,这个类会将所有的`Selectable`对象的fd注册到epoll中,并在事件到来时调用`Selectable`的`readData()`函数。" +"Platform Monitoring Related Repositories: `sonic-platform-common` and " +"`sonic-platform-daemons`" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:10 -msgid "其类图如下:" +#: src\3-1-code-repos.md:85 +msgid "" +"The following two repositories are related to platform monitoring and " +"control, such as LEDs, fans, power supplies, thermal control, and more:" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:14 -msgid "在Select类中,我们可以很容易的找到其最核心的代码,实现也非常的简单:" +#: src\3-1-code-repos.md:89 +msgid "" +"[sonic-platform-common](https://github.com/sonic-net/sonic-platform-common)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:16 +#: src\3-1-code-repos.md:89 msgid "" -"```cpp\n" -"int Select::poll_descriptors(Selectable **c, unsigned int timeout, bool " -"interrupt_on_signal = false)\n" -"{\n" -" int sz_selectables = static_cast(m_objects.size());\n" -" std::vector events(sz_selectables);\n" -" int ret;\n" -"\n" -" while(true) {\n" -" ret = ::epoll_wait(m_epoll_fd, events.data(), sz_selectables, " -"timeout);\n" -" // ...\n" -" }\n" -" // ...\n" -"\n" -" for (int i = 0; i < ret; ++i)\n" -" {\n" -" int fd = events[i].data.fd;\n" -" Selectable* sel = m_objects[fd];\n" -"\n" -" sel->readData();\n" -" // error handling here ...\n" -"\n" -" m_ready.insert(sel);\n" -" }\n" -"\n" -" while (!m_ready.empty())\n" -" {\n" -" auto sel = *m_ready.begin();\n" -" m_ready.erase(sel);\n" -" \n" -" // After update callback ...\n" -" return Select::OBJECT;\n" -" }\n" -"\n" -" return Select::TIMEOUT;\n" -"}\n" -"```" +"A base package provided to manufacturers, defining interfaces for accessing " +"fans, LEDs, power management, thermal control, and other modules, all " +"implemented in Python" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:53 +#: src\3-1-code-repos.md:90 msgid "" -"然而,问题来了…… " -"回调呢?我们上面提过,`readData()`只是把消息读出来放在一个待处理队列中,并不会真正的处理消息,真正的消息处理需要调用`pops()`函数,将消息拿出来处理,所以什么地方会调用每一个上层封装的消息处理呢?" +"[sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-daemons)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:55 +#: src\3-1-code-repos.md:90 msgid "" -"这里我们还是找到我们的老朋友`portmgrd`的`main`函数,从下面简化的代码中,我们可以看到和一般的Event " -"Loop实现不同,SONiC中,最后的事件处理不是通过回调来实现的,而是需要最外层的Event Loop来主动调用完成:" +"Contains various monitoring services running in the pmon container in SONiC, " +"such as `chassisd`, `ledd`, `pcied`, `psud`, `syseepromd`, `thermalctld`, " +"`xcvrd`, `ycabled`. All these services are implemented in Python, and used " +"for monitoring and controlling the platform modules, by calling the " +"interface implementations provided by manufacturers." msgstr "" -#: src/4-5-event-polling-and-error-handling.md:57 +#: src\3-1-code-repos.md:92 +msgid "Other Feature Repositories" +msgstr "" + +#: src\3-1-code-repos.md:94 msgid "" -"```cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" // ...\n" -"\n" -" // Create PortMgr, which implements Orch interface.\n" -" PortMgr portmgr(&cfgDb, &appDb, &stateDb, cfg_port_tables);\n" -" vector cfgOrchList = {&portmgr};\n" -"\n" -" // Create Select object for event loop and add PortMgr to it.\n" -" swss::Select s;\n" -" for (Orch *o : cfgOrchList) {\n" -" s.addSelectables(o->getSelectables());\n" -" }\n" -"\n" -" // Event loop\n" -" while (true)\n" -" {\n" -" Selectable *sel;\n" -" int ret;\n" -"\n" -" // When anyone of the selectables gets signaled, select() will call\n" -" // into readData() and fetch all events, then return.\n" -" ret = s.select(&sel, SELECT_TIMEOUT);\n" -" // ...\n" -"\n" -" // Then, we call into execute() explicitly to process all events.\n" -" auto *c = (Executor *)sel;\n" -" c->execute();\n" -" }\n" -" return -1;\n" -"}\n" -"```" +"In addition to the repositories above, SONiC has many repositories " +"implementing various functionalities. They can be services or libraries " +"described in the table below:" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:91 -msgid "## 错误处理" +#: src\3-1-code-repos.md:96 +msgid "Repository" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:93 -msgid "" -"关于Event Loop我们还有一个问题,那就是错误处理,比如,如果Redis的命令执行出错了,连接断开了,故障了等等的情况下,我们的服务会发生什么呢?" +#: src\3-1-code-repos.md:98 +msgid "[sonic-frr](https://github.com/sonic-net/sonic-frr)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:95 +#: src\3-1-code-repos.md:98 msgid "" -"从代码上来看,SONiC中的错误处理是非常简单的,就是直接抛出异常(比如,获取命令执行结果的代码,如下),然后在Event " -"Loop中捕获异常,打印日志,接着继续执行。" +"FRRouting, implementing various routing protocols, so in this repository, we " +"can find implementations of routing-related processes like `bgpd`, `zebra`, " +"etc." +msgstr "" + +#: src\3-1-code-repos.md:99 +msgid "[sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:97 +#: src\3-1-code-repos.md:99 msgid "" -"```cpp\n" -"RedisReply::RedisReply(RedisContext *ctx, const RedisCommand& command)\n" -"{\n" -" int rc = redisAppendFormattedCommand(ctx->getContext(), command.c_str(), " -"command.length());\n" -" if (rc != REDIS_OK)\n" -" {\n" -" // The only reason of error is REDIS_ERR_OOM (Out of memory)\n" -" // ref: https://github.com/redis/hiredis/blob/master/hiredis.c\n" -" throw bad_alloc();\n" -" }\n" -"\n" -" rc = redisGetReply(ctx->getContext(), (void**)&m_reply);\n" -" if (rc != REDIS_OK)\n" -" {\n" -" throw RedisError(\"Failed to redisGetReply with \" + " -"string(command.c_str()), ctx->getContext());\n" -" }\n" -" guard([&]{checkReply();}, command.c_str());\n" -"}\n" -"```" +"Implementation of [AgentX](https://www.ietf.org/rfc/rfc2741.txt) SNMP " +"subagent (`sonic_ax_impl`), used to connect to the Redis database and " +"provide various information needed by snmpd. It can be understood as the " +"control plane of snmpd, while snmpd is the data plane, responding to " +"external SNMP requests" +msgstr "" + +#: src\3-1-code-repos.md:100 +msgid "[sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd)" msgstr "" -#: src/4-5-event-polling-and-error-handling.md:117 +#: src\3-1-code-repos.md:100 msgid "" -"关于异常和错误的种类及其原因,在代码里面并没有看到用于统计和Telemetry的代码,所以监控上说是比较薄弱的。另外还需要考虑数据出错的场景,比如数据库写到一半突然断开导致的脏数据,不过简单的重启相关的`*syncd`和`*mgrd`服务可能可以解决此类问题,因为启动时会进行全量同步。" +"Dual ToR support, checking the status of links and controlling ToR " +"connections" msgstr "" -#: src/5-core-components.md:1 -msgid "# 核心组件解析" +#: src\3-1-code-repos.md:101 +msgid "[sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay)" msgstr "" -#: src/5-core-components.md:3 -msgid "这一章,我们会从代码的层面上来深入的分析一下SONiC中一些比较有代表性的核心组件和它们的工作流。" +#: src\3-1-code-repos.md:101 +msgid "DHCP relay agent" msgstr "" -#: src/5-core-components.md:5 -msgid "" -"```admonish note\n" -"为了方便阅读和理解,所有的代码都只是列出了最核心的代码来展现流程,并不是完整的代码,如果需要查看完整代码,请参考[仓库中的原始代码](./3-1-code-repos.html)。\n" -"\n" -"另外,每个代码块的开头都给出了相关文件的路径,其使用的是仓库均为SONiC的主仓库:[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage)。\n" -"```" +#: src\3-1-code-repos.md:102 +msgid "[sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon)" +msgstr "" + +#: src\3-1-code-repos.md:102 +msgid "Monitors the status of DHCP and reports to the central Redis database" msgstr "" -#: src/5-1-syncd-and-sai.md:1 -msgid "# Syncd和SAI" +#: src\3-1-code-repos.md:103 +msgid "[sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd)" msgstr "" -#: src/5-1-syncd-and-sai.md:3 +#: src\3-1-code-repos.md:103 msgid "" -"[Syncd容器](./2-3-key-containers.html#asic管理容器syncd)是SONiC中专门负责管理ASIC的容器,其中核心进程`syncd`负责与Redis数据库沟通,加载SAI并与其交互,以完成ASIC的初始化,配置和状态上报的处理等等。" +"`lldp_syncd` service, but the repository name is not well-chosen, called " +"dbsyncd" +msgstr "" + +#: src\3-1-code-repos.md:104 +msgid "[sonic-pins](https://github.com/sonic-net/sonic-pins)" msgstr "" -#: src/5-1-syncd-and-sai.md:5 +#: src\3-1-code-repos.md:104 msgid "" -"由于SONiC中大量的工作流最后都需要通过Syncd和SAI来和ASIC进行交互,所以这一部分也就成为了这些工作流的公共部分,所以,在展开其他工作流之前,我们先来看一下Syncd和SAI是如何工作的。" +"Google's P4-based network stack support (P4 Integrated Network Stack, PINS). " +"More information can be found on the [PINS " +"website](https://opennetworking.org/pins/)" msgstr "" -#: src/5-1-syncd-and-sai.md:7 -msgid "## Syncd启动流程" +#: src\3-1-code-repos.md:105 +msgid "[sonic-stp](https://github.com/sonic-net/sonic-stp)" msgstr "" -#: src/5-1-syncd-and-sai.md:9 -msgid "`syncd`进程的入口在`syncd_main.cpp`中的`syncd_main`函数,其启动的整体流程大致分为两部分。" +#: src\3-1-code-repos.md:105 +msgid "STP (Spanning Tree Protocol) support" msgstr "" -#: src/5-1-syncd-and-sai.md:11 -msgid "第一部分是创建各个对象,并进行初始化:" +#: src\3-1-code-repos.md:106 +msgid "[sonic-ztp](https://github.com/sonic-net/sonic-ztp)" msgstr "" -#: src/5-1-syncd-and-sai.md:13 +#: src\3-1-code-repos.md:106 msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -"\n" -" SDM->>+SD: 调用构造函数\n" -" SD->>SD: 加载和解析命令行参数和配置文件\n" -" SD->>SD: 创建数据库相关对象,如:
ASIC_DB Connector和FlexCounterManager\n" -" SD->>SD: 创建MDIO IPC服务器\n" -" SD->>SD: 创建SAI上报处理逻辑\n" -" SD->>SD: 创建RedisSelectableChannel用于接收Redis通知\n" -" SD->>-SAI: 初始化SAI\n" -"```" +"[Zero Touch " +"Provisioning](https://github.com/sonic-net/SONiC/blob/master/doc/ztp/ztp.md)" msgstr "" -#: src/5-1-syncd-and-sai.md:29 -msgid "第二个部分是启动主循环,并且处理初始化事件:" +#: src\3-1-code-repos.md:107 +msgid "[DASH](https://github.com/sonic-net/DASH)" msgstr "" -#: src/5-1-syncd-and-sai.md:31 +#: src\3-1-code-repos.md:107 msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" box purple 主线程\n" -" participant SDM as syncd_main\n" -" participant SD as Syncd\n" -" participant SAI as VendorSai\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" end\n" -" box darkgreen MDIO IPC服务器线程\n" -" participant MIS as MdioIpcServer\n" -" end\n" -"\n" -" SDM->>+SD: 启动主线程循环\n" -" SD->>NP: 启动SAI上报处理线程\n" -" NP->>NP: 开始通知处理循环\n" -" SD->>MIS: 启动MDIO IPC服务器线程\n" -" MIS->>MIS: 开始MDIO IPC服务器事件循环\n" -" SD->>SD: 初始化并启动事件分发机制,开始主循环\n" -"\n" -" loop 处理事件\n" -" alt 如果是创建Switch的事件或者是WarmBoot\n" -" SD->>SAI: 创建Switch对象,设置通知回调\n" -" else 如果是其他事件\n" -" SD->>SD: 处理事件\n" -" end\n" -" end\n" -"\n" -" SD->>-SDM: 退出主循环返回\n" -"```" +"[Disaggregated API for SONiC " +"Hosts](https://github.com/sonic-net/DASH/blob/main/documentation/general/dash-high-level-design.md)" msgstr "" -#: src/5-1-syncd-and-sai.md:64 -msgid "然后我们再从代码的角度来更加仔细的看一下这个流程。" +#: src\3-1-code-repos.md:108 +msgid "[sonic-host-services](https://github.com/sonic-net/sonic-host-services)" msgstr "" -#: src/5-1-syncd-and-sai.md:66 -msgid "### syncd_main函数" +#: src\3-1-code-repos.md:108 +msgid "" +"Services running on the host, providing support to services in containers " +"via dbus, such as saving and reloading configurations, saving dumps, etc., " +"similar to a host broker" msgstr "" -#: src/5-1-syncd-and-sai.md:68 -msgid "`syncd_main`函数本身非常简单,主要逻辑就是创建Syncd对象,然后调用其`run`方法:" +#: src\3-1-code-repos.md:109 +msgid "[sonic-fips](https://github.com/sonic-net/sonic-fips)" msgstr "" -#: src/5-1-syncd-and-sai.md:70 +#: src\3-1-code-repos.md:109 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/syncd_main.cpp\n" -"int syncd_main(int argc, char **argv)\n" -"{\n" -" auto vendorSai = std::make_shared();\n" -" auto syncd = std::make_shared(vendorSai, commandLineOptions, " -"isWarmStart);\n" -" syncd->run();\n" -" return EXIT_SUCCESS;\n" -"}\n" -"```" +"FIPS (Federal Information Processing Standards) support, containing various " +"patch files added to support FIPS standards" msgstr "" -#: src/5-1-syncd-and-sai.md:81 -msgid "其中,`Syncd`对象的构造函数负责初始化`Syncd`中的各个功能,而`run`方法则负责启动Syncd的主循环。" +#: src\3-1-code-repos.md:110 +msgid "[sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant)" msgstr "" -#: src/5-1-syncd-and-sai.md:83 -msgid "### Syncd构造函数" +#: src\3-1-code-repos.md:110 +msgid "Support for various wireless network protocols" msgstr "" -#: src/5-1-syncd-and-sai.md:85 -msgid "" -"`Syncd`对象的构造函数负责创建或初始化`Syncd`中的各个功能,比如用于连接数据库的对象,统计管理,和ASIC通知的处理逻辑等等,其主要代码如下:" +#: src\3-1-code-repos.md:112 +msgid "Tooling Repository: `sonic-utilities`" msgstr "" -#: src/5-1-syncd-and-sai.md:87 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"Syncd::Syncd(\n" -" _In_ std::shared_ptr vendorSai,\n" -" _In_ std::shared_ptr cmd,\n" -" _In_ bool isWarmStart):\n" -" m_vendorSai(vendorSai),\n" -" ...\n" -"{\n" -" ...\n" -"\n" -" // Load context config\n" -" auto ccc = " -"sairedis::ContextConfigContainer::loadFromFile(m_commandLineOptions->m_contextConfig.c_str());\n" -" m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext);\n" -" ...\n" -"\n" -" // Create FlexCounter manager\n" -" m_manager = std::make_shared(m_vendorSai, " -"m_contextConfig->m_dbCounters);\n" -"\n" -" // Create DB related objects\n" -" m_dbAsic = " -"std::make_shared(m_contextConfig->m_dbAsic, 0);\n" -" m_mdioIpcServer = std::make_shared(m_vendorSai, " -"m_commandLineOptions->m_globalContext);\n" -" m_selectableChannel = " -"std::make_shared(m_dbAsic, " -"ASIC_STATE_TABLE, REDIS_TABLE_GETRESPONSE, TEMP_PREFIX, modifyRedis);\n" -"\n" -" // Create notification processor and handler\n" -" m_notifications = " -"std::make_shared(m_contextConfig->m_dbAsic);\n" -" m_client = std::make_shared(m_dbAsic);\n" -" m_processor = std::make_shared(m_notifications, " -"m_client, std::bind(&Syncd::syncProcessNotification, this, _1));\n" -"\n" -" m_handler = std::make_shared(m_processor);\n" -" m_sn.onFdbEvent = std::bind(&NotificationHandler::onFdbEvent, " -"m_handler.get(), _1, _2);\n" -" m_sn.onNatEvent = std::bind(&NotificationHandler::onNatEvent, " -"m_handler.get(), _1, _2);\n" -" // Init many other event handlers here\n" -" m_handler->setSwitchNotifications(m_sn.getSwitchNotifications());\n" -" ...\n" -"\n" -" // Initialize SAI\n" -" sai_status_t status = vendorSai->initialize(0, &m_test_services);\n" -" ...\n" -"}\n" -"```" +#: src\3-1-code-repos.md:114 +msgid "" msgstr "" -#: src/5-1-syncd-and-sai.md:129 -msgid "### SAI的初始化与VendorSai" +#: src\3-1-code-repos.md:116 +msgid "This repository contains all the command-line tools for SONiC:" msgstr "" -#: src/5-1-syncd-and-sai.md:131 +#: src\3-1-code-repos.md:118 msgid "" -"`Syncd`初始化的最后也是最重要的一步,就是对SAI进行初始化。[在核心组件的SAI介绍中,我们简单的展示了SAI的初始化,实现,以及它是如何为SONiC提供不同平台的支持](./2-4-sai-intro.html),所以这里我们主要来看看`Syncd`是如何对SAI进行封装和调用的。" +"`config`, `show`, `clear` directories: These are the implementations of the " +"three main SONiC CLI commands. Note that the specific command " +"implementations may not necessarily be in these directories; many commands " +"are implemented by calling other commands, with these directories providing " +"an entry point." msgstr "" -#: src/5-1-syncd-and-sai.md:133 +#: src\3-1-code-repos.md:119 msgid "" -"`Syncd`使用`VendorSai`来对SAI的所有API进行封装,方便上层调用。其初始化过程也非常直接,基本就是对上面两个函数的直接调用和错误处理,如下:" +"`scripts`, `sfputil`, `psuutil`, `pcieutil`, `fwutil`, `ssdutil`, " +"`acl_loader` directories: These directories provide many tool commands, but " +"most are not directly used by users; instead, they are called by commands in " +"the `config`, `show`, and `clear` directories. For example, the `show " +"platform fan` command is implemented by calling the `fanshow` command in the " +"`scripts` directory." msgstr "" -#: src/5-1-syncd-and-sai.md:135 +#: src\3-1-code-repos.md:120 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" // Initialize SAI\n" -" memcpy(&m_service_method_table, service_method_table, " -"sizeof(m_service_method_table));\n" -" auto status = sai_api_initialize(flags, service_method_table);\n" -"\n" -" // If SAI is initialized successfully, query all SAI API methods.\n" -" // sai_metadata_api_query will also update all extern global sai_*_api " -"variables, so we can also use\n" -" // sai_metadata_get_object_type_info to get methods for a specific SAI " -"object type.\n" -" if (status == SAI_STATUS_SUCCESS) {\n" -" memset(&m_apis, 0, sizeof(m_apis));\n" -" int failed = sai_metadata_apis_query(sai_api_query, &m_apis);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return status;\n" -"}\n" -"```" +"`utilities_common`, `flow_counter_util`, `syslog_util` directories: Similar " +"to the above, but they provide base classes that can be directly imported " +"and called in Python." msgstr "" -#: src/5-1-syncd-and-sai.md:161 -msgid "当获取好所有的SAI API之后,我们就可以通过`VendorSai`对象来调用SAI的API了。当前调用SAI的API方式主要有两种。" +#: src\3-1-code-repos.md:121 +msgid "" +"There are also many other commands: `fdbutil`, `pddf_fanutil`, " +"`pddf_ledutil`, `pddf_psuutil`, `pddf_thermalutil`, etc., used to view and " +"control the status of various modules." msgstr "" -#: src/5-1-syncd-and-sai.md:163 -msgid "第一种是通过`sai_object_type_into_t`来调用,它类似于为所有的SAI Object实现了一个虚表,如下:" +#: src\3-1-code-repos.md:122 +msgid "" +"`connect` and `consutil` directories: Commands in these directories are used " +"to connect to and manage other SONiC devices." msgstr "" -#: src/5-1-syncd-and-sai.md:165 +#: src\3-1-code-repos.md:123 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/VendorSai.cpp\n" -"sai_status_t VendorSai::set(\n" -" _In_ sai_object_type_t objectType,\n" -" _In_ sai_object_id_t objectId,\n" -" _In_ const sai_attribute_t *attr)\n" -"{\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(objectType);\n" -" sai_object_meta_key_t mk = { .objecttype = objectType, .objectkey = { " -".key = { .object_id = objectId } } };\n" -" return info->set(&mk, attr);\n" -"}\n" -"```" +"`crm` directory: Used to configure and view [CRM (Critical Resource " +"Monitoring)](https://github.com/sonic-net/SONiC/wiki/Critical-Resource-Monitoring-High-Level-Design) " +"in SONiC. This command is not included in the `config` and `show` commands, " +"so users can use it directly." msgstr "" -#: src/5-1-syncd-and-sai.md:180 +#: src\3-1-code-repos.md:124 msgid "" -"另外一种是通过保存在`VendorSai`对象中的`m_apis`来调用,这种方式更加直接,但是调用前需要先根据SAI " -"Object的类型来调用不同的API。" +"`pfc` directory: Used to configure and view PFC (Priority-based Flow " +"Control) in SONiC." msgstr "" -#: src/5-1-syncd-and-sai.md:182 +#: src\3-1-code-repos.md:125 msgid "" -"```cpp\n" -"sai_status_t VendorSai::getStatsExt(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t object_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters)\n" -"{\n" -" sai_status_t (*ptr)(\n" -" _In_ sai_object_id_t port_id,\n" -" _In_ uint32_t number_of_counters,\n" -" _In_ const sai_stat_id_t *counter_ids,\n" -" _In_ sai_stats_mode_t mode,\n" -" _Out_ uint64_t *counters);\n" -"\n" -" switch ((int)object_type)\n" -" {\n" -" case SAI_OBJECT_TYPE_PORT:\n" -" ptr = m_apis.port_api->get_port_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_ROUTER_INTERFACE:\n" -" ptr = " -"m_apis.router_interface_api->get_router_interface_stats_ext;\n" -" break;\n" -" case SAI_OBJECT_TYPE_POLICER:\n" -" ptr = m_apis.policer_api->get_policer_stats_ext;\n" -" break;\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_ERROR(\"not implemented, FIXME\");\n" -" return SAI_STATUS_FAILURE;\n" -" }\n" -"\n" -" return ptr(object_id, number_of_counters, counter_ids, mode, counters);\n" -"}\n" -"```" +"`pfcwd` directory: Used to configure and view [PFC Watch " +"Dog](https://github.com/sonic-net/SONiC/wiki/PFC-Watchdog) in SONiC, such as " +"starting, stopping, modifying polling intervals, and more." msgstr "" -#: src/5-1-syncd-and-sai.md:220 -msgid "可以明显看出,第一种调用方式代码要精炼和直观许多。" +#: src\3-1-code-repos.md:127 +msgid "Kernel Patches: sonic-linux-kernel" msgstr "" -#: src/5-1-syncd-and-sai.md:222 -msgid "### Syncd主循环" +#: src\3-1-code-repos.md:129 +msgid "" msgstr "" -#: src/5-1-syncd-and-sai.md:224 +#: src\3-1-code-repos.md:131 msgid "" -"`Syncd`的主循环也是使用的SONiC中标准的[事件分发](./4-3-event-polling-and-error-handling.html)机制:在启动时,`Syncd`会将所有用于事件处理的`Selectable`对象注册到用于获取事件的`Select`对象中,然后在主循环中调用`Select`的`select`方法,等待事件的发生。核心代码如下:" +"Although SONiC is based on Debian, the default Debian kernel may not " +"necessarily run SONiC, such as certain modules not being enabled by default " +"or issues with older drivers. Therefore, SONiC requires some modifications " +"to the Linux kernel. This repository is used to store all the kernel patches." msgstr "" -#: src/5-1-syncd-and-sai.md:226 +#: src\3-1-code-repos.md:136 msgid "" -"```c\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::run()\n" -"{\n" -" volatile bool runMainLoop = true;\n" -" std::shared_ptr s = std::make_shared();\n" -" onSyncdStart(m_commandLineOptions->m_startType == " -"SAI_START_TYPE_WARM_BOOT);\n" -"\n" -" // Start notification processing thread\n" -" m_processor->startNotificationsProcessingThread();\n" -"\n" -" // Start MDIO threads\n" -" for (auto& sw: m_switches) { " -"m_mdioIpcServer->setSwitchId(sw.second->getRid()); }\n" -" m_mdioIpcServer->startMdioThread();\n" -"\n" -" // Registering selectable for event polling\n" -" s->addSelectable(m_selectableChannel.get());\n" -" s->addSelectable(m_restartQuery.get());\n" -" s->addSelectable(m_flexCounter.get());\n" -" s->addSelectable(m_flexCounterGroup.get());\n" -"\n" -" // Main event loop\n" -" while (runMainLoop)\n" -" {\n" -" swss::Selectable *sel = NULL;\n" -" int result = s->select(&sel);\n" -"\n" -" ...\n" -" if (sel == m_restartQuery.get()) {\n" -" // Handling switch restart event and restart switch here.\n" -" } else if (sel == m_flexCounter.get()) {\n" -" processFlexCounterEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_flexCounterGroup.get()) {\n" -" processFlexCounterGroupEvent(*(swss::ConsumerTable*)sel);\n" -" } else if (sel == m_selectableChannel.get()) {\n" -" // Handle redis updates here.\n" -" processEvent(*m_selectableChannel.get());\n" -" } else {\n" -" SWSS_LOG_ERROR(\"select failed: %d\", result);\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" +"[SONiC Source " +"Repositories](https://github.com/sonic-net/SONiC/blob/master/sourcecode.md)" msgstr "" -#: src/5-1-syncd-and-sai.md:272 +#: src\3-1-code-repos.md:139 src\3-1-code-repos.md:141 msgid "" -"其中,`m_selectableChannel`就是主要负责处理Redis数据库中的事件的对象。它使用[ProducerTable / " -"ConsumerTable](./4-2-2-redis-messaging-layer.md#producertable--consumertable)的方式与Redis数据库进行交互,所以,所有`orchagent`发送过来的操作都会以三元组的形式保存在Redis中的list中,等待`Syncd`的处理。其核心定义如下:" +"[SONiC Critical Resource " +"Monitoring](https://github.com/sonic-net/SONiC/wiki/Critical-Resource-Monitoring-High-Level-Design)" msgstr "" -#: src/5-1-syncd-and-sai.md:274 +#: src\3-1-code-repos.md:140 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/meta/RedisSelectableChannel.h\n" -"class RedisSelectableChannel: public SelectableChannel\n" -"{\n" -" public:\n" -" RedisSelectableChannel(\n" -" _In_ std::shared_ptr dbAsic,\n" -" _In_ const std::string& asicStateTable,\n" -" _In_ const std::string& getResponseTable,\n" -" _In_ const std::string& tempPrefix,\n" -" _In_ bool modifyRedis);\n" -"\n" -" public: // SelectableChannel overrides\n" -" virtual bool empty() override;\n" -" ...\n" -"\n" -" public: // Selectable overrides\n" -" virtual int getFd() override;\n" -" virtual uint64_t readData() override;\n" -" ...\n" -"\n" -" private:\n" -" std::shared_ptr m_dbAsic;\n" -" std::shared_ptr m_asicState;\n" -" std::shared_ptr m_getResponse;\n" -" ...\n" -"};\n" -"```" +"[SONiC Zero Touch " +"Provisioning](https://github.com/sonic-net/SONiC/blob/master/doc/ztp/ztp.md)" msgstr "" -#: src/5-1-syncd-and-sai.md:303 -msgid "另外,在主循环启动时,`Syncd`还会额外启动两个线程:" +#: src\3-1-code-repos.md:142 +msgid "[SONiC P4 Integrated Network Stack](https://opennetworking.org/pins/)" msgstr "" -#: src/5-1-syncd-and-sai.md:305 +#: src\3-1-code-repos.md:143 msgid "" -"- 用于接收ASIC上报通知的通知处理线程:`m_processor->startNotificationsProcessingThread();`\n" -"- 用于处理MDIO通信的MDIO IPC处理线程:`m_mdioIpcServer->startMdioThread();`" +"[SONiC Disaggregated API for Switch " +"Hosts](https://github.com/sonic-net/DASH/blob/main/documentation/general/dash-high-level-design.md)" msgstr "" -#: src/5-1-syncd-and-sai.md:308 -msgid "它们的细节我们在初始化的部分不做过多展开,等后面介绍相关工作流时再来详细介绍。" +#: src\3-1-code-repos.md:144 +msgid "" +"[SAI spec for " +"OCP](https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf)" msgstr "" -#: src/5-1-syncd-and-sai.md:310 -msgid "### 创建Switch对象,初始化通知机制" +#: src\3-1-code-repos.md:145 +msgid "[PFC Watchdog](https://github.com/sonic-net/SONiC/wiki/PFC-Watchdog)" msgstr "" -#: src/5-1-syncd-and-sai.md:312 +#: src\3-2-build.md:3 msgid "" -"在主循环启动后,`Syncd`就会开始调用SAI的API来创建Switch对象,这里的入口有两个,一个是ASIC_DB收到创建Switch的通知,另外一个是Warm " -"Boot时,`Syncd`来主动调用,但是创建Switch这一步的内部流程都类似。" +"To ensure that we can successfully build SONiC on any platform as well, " +"SONiC leverages docker to build its build environment. It installs all tools " +"and dependencies in a docker container of the corresponding Debian version, " +"mounts its code into the container, and then start the build process inside " +"the container. This way, we can easily build SONiC on any platform without " +"worrying about dependency mismatches. For example, some packages in Debian " +"have higher versions than in Ubuntu, which might cause unexpected errors " +"during build time or runtime." msgstr "" -#: src/5-1-syncd-and-sai.md:314 -msgid "" -"在这一步中间,有一个很重要的步骤,就是初始化SAI内部实现中的通知回调,将我们之前已经创建好的通知处理逻辑传递给SAI的实现,比如FDB的事件等等。这些回调函数会被当做Switch的属性(Attributes)通过参数的形式传给SAI的`create_switch`方法,SAI的实现会将其保存起来,这样就可以在事件发生时调用回调函数,来通知`Syncd`了。这里的核心代码如下:" +#: src\3-2-build.md:5 +msgid "Setup the Build Environment" +msgstr "" + +#: src\3-2-build.md:7 +msgid "Install Docker" msgstr "" -#: src/5-1-syncd-and-sai.md:316 +#: src\3-2-build.md:9 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse event into SAI object\n" -" sai_object_meta_key_t metaKey;\n" -" ...\n" -"\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -"\n" -" // Update notifications pointers in attribute list\n" -" if (metaKey.objecttype == SAI_OBJECT_TYPE_SWITCH && (api == " -"SAI_COMMON_API_CREATE || api == SAI_COMMON_API_SET))\n" -" {\n" -" m_handler->updateNotificationsPointers(attr_count, attr_list);\n" -" }\n" -"\n" -" if (isInitViewMode())\n" -" {\n" -" // ProcessQuadEventInInitViewMode will eventually call into " -"VendorSai, which calls create_swtich function in SAI.\n" -" sai_status_t status = " -"processQuadEventInInitViewMode(metaKey.objecttype, strObjectId, api, " -"attr_count, attr_list);\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -" }\n" -" ...\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::updateNotificationsPointers(_In_ uint32_t " -"attr_count, _In_ sai_attribute_t *attr_list) const\n" -"{\n" -" for (uint32_t index = 0; index < attr_count; ++index) {\n" -" ...\n" -"\n" -" sai_attribute_t &attr = attr_list[index];\n" -" switch (attr.id) {\n" -" ...\n" -"\n" -" case SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY:\n" -" attr.value.ptr = " -"(void*)m_switchNotifications.on_switch_shutdown_request;\n" -" break;\n" -"\n" -" case SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY:\n" -" attr.value.ptr = (void*)m_switchNotifications.on_fdb_event;\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call stack: processQuadEvent\n" -"// -> processQuadEventInInitViewMode\n" -"// -> processQuadInInitViewModeCreate\n" -"// -> onSwitchCreateInInitViewMode\n" -"void Syncd::onSwitchCreateInInitViewMode(_In_ sai_object_id_t switchVid, " -"_In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list)\n" -"{\n" -" if (m_switches.find(switchVid) == m_switches.end()) {\n" -" sai_object_id_t switchRid;\n" -" sai_status_t status;\n" -" status = m_vendorSai->create(SAI_OBJECT_TYPE_SWITCH, &switchRid, 0, " -"attr_count, attr_list);\n" -" ...\n" -"\n" -" m_switches[switchVid] = std::make_shared(switchVid, " -"switchRid, m_client, m_translator, m_vendorSai);\n" -" m_mdioIpcServer->setSwitchId(switchRid);\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" +"To support the containerized build environment, the first step is to ensure " +"that Docker is installed on our machine." msgstr "" -#: src/5-1-syncd-and-sai.md:390 -msgid "从Mellanox的SAI实现,我们可以看到其具体的保存的方法:" +#: src\3-2-build.md:11 +msgid "" +"You can refer to the [official " +"documentation](https://docs.docker.com/engine/install/) for Docker " +"installation methods. Here, we briefly introduce the installation method for " +"Ubuntu." msgstr "" -#: src/5-1-syncd-and-sai.md:392 +#: src\3-2-build.md:13 msgid "" -"```cpp\n" -"static sai_status_t mlnx_create_switch(_Out_ sai_object_id_t * " -"switch_id,\n" -" _In_ uint32_t " -"attr_count,\n" -" _In_ const sai_attribute_t " -"*attr_list)\n" -"{\n" -" ...\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SWITCH_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_state_change = " -"(sai_switch_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_switch_shutdown_request =\n" -" (sai_switch_shutdown_request_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_fdb_event = " -"(sai_fdb_event_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PORT_STATE_CHANGE_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_port_state_change = " -"(sai_port_state_change_notification_fn)attr_val->ptr;\n" -" }\n" -"\n" -" status = find_attrib_in_list(attr_count, attr_list, " -"SAI_SWITCH_ATTR_PACKET_EVENT_NOTIFY, &attr_val, &attr_idx);\n" -" if (!SAI_ERR(status)) {\n" -" g_notification_callbacks.on_packet_event = " -"(sai_packet_event_notification_fn)attr_val->ptr;\n" -" }\n" -" ...\n" -"}\n" -"```" +"First, we need to add docker's source and certificate to the apt source list:" msgstr "" -#: src/5-1-syncd-and-sai.md:428 -msgid "## ASIC状态更新" +#: src\3-2-build.md:24 +msgid "\"deb [arch=\"" msgstr "" -#: src/5-1-syncd-and-sai.md:430 +#: src\3-2-build.md:24 msgid "" -"ASIC状态更新是`Syncd`中最重要的工作流之一,当`orchagent`发现任何变化并开始修改ASIC_DB时,就会触发该工作流,通过SAI来对ASIC进行更新。在了解了`Syncd`的主循环之后,理解ASIC状态更新的工作流就很简单了。" +"\" signed-by=/etc/apt/keyrings/docker.gpg] " +"https://download.docker.com/linux/ubuntu \\" msgstr "" -#: src/5-1-syncd-and-sai.md:432 -msgid "所有的步骤都发生在主线程一个线程中,顺序执行,总结成时序图如下:" +#: src\3-2-build.md:25 +msgid "\"$VERSION_CODENAME\"" msgstr "" -#: src/5-1-syncd-and-sai.md:434 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant SD as Syncd\n" -" participant RSC as RedisSelectableChannel\n" -" participant SAI as VendorSai\n" -" participant R as Redis\n" -"\n" -" loop 主线程循环\n" -" SD->>RSC: 收到epoll通知,通知获取所有到来的消息\n" -" RSC->>R: 通过ConsumerTable获取所有到来的消息\n" -"\n" -" critical 给Syncd加锁\n" -" loop 所有收到的消息\n" -" SD->>RSC: 获取一个消息\n" -" SD->>SD: 解析消息,获取操作类型和操作对象\n" -" SD->>SAI: 调用对应的SAI API,更新ASIC\n" -" SD->>RSC: 发送调用结果给Redis\n" -" RSC->>R: 将调用结果写入Redis\n" -" end\n" -" end\n" -" end\n" -"```" +#: src\3-2-build.md:25 +msgid "\" stable\"" msgstr "" -#: src/5-1-syncd-and-sai.md:458 -msgid "" -"首先,`orchagent`通过Redis发送过来的操作会被`RedisSelectableChannel`对象接收,然后在主循环中被处理。当`Syncd`处理到`m_selectableChannel`时,就会调用`processEvent`方法来处理该操作。这几步的核心代码我们上面介绍主循环时已经介绍过了,这里就不再赘述。" +#: src\3-2-build.md:29 +msgid "Then, we can quickly install docker via apt:" msgstr "" -#: src/5-1-syncd-and-sai.md:460 +#: src\3-2-build.md:36 msgid "" -"然后,`processEvent`会根据其中的操作类型,调用对应的SAI的API来对ASIC进行更新。其逻辑是一个巨大的switch-case语句,如下:" +"After installing docker, we need to add the current user to the docker user " +"group and **log out and log back in**. This way, we can run any docker " +"commands without sudo! **This is very important** because subsequent SONiC " +"builds do not allow the use of sudo." msgstr "" -#: src/5-1-syncd-and-sai.md:462 +#: src\3-2-build.md:42 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"void Syncd::processEvent(_In_ sairedis::SelectableChannel& consumer)\n" -"{\n" -" // Loop all operations in the queue\n" -" std::lock_guard lock(m_mutex);\n" -" do {\n" -" swss::KeyOpFieldsValuesTuple kco;\n" -" consumer.pop(kco, isInitViewMode());\n" -" processSingleEvent(kco);\n" -" } while (!consumer.empty());\n" -"}\n" -"\n" -"sai_status_t Syncd::processSingleEvent(_In_ const " -"swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" auto& op = kfvOp(kco);\n" -" ...\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_CREATE)\n" -" return processQuadEvent(SAI_COMMON_API_CREATE, kco);\n" -"\n" -" if (op == REDIS_ASIC_STATE_COMMAND_REMOVE)\n" -" return processQuadEvent(SAI_COMMON_API_REMOVE, kco);\n" -" \n" -" ...\n" -"}\n" -"\n" -"sai_status_t Syncd::processQuadEvent(\n" -" _In_ sai_common_api_t api,\n" -" _In_ const swss::KeyOpFieldsValuesTuple &kco)\n" -"{\n" -" // Parse operation\n" -" const std::string& key = kfvKey(kco);\n" -" const std::string& strObjectId = key.substr(key.find(\":\") + 1);\n" -"\n" -" sai_object_meta_key_t metaKey;\n" -" sai_deserialize_object_meta_key(key, metaKey);\n" -"\n" -" auto& values = kfvFieldsValues(kco);\n" -" SaiAttributeList list(metaKey.objecttype, values, false);\n" -" sai_attribute_t *attr_list = list.get_attr_list();\n" -" uint32_t attr_count = list.get_attr_count();\n" -" ...\n" -"\n" -" auto info = sai_metadata_get_object_type_info(metaKey.objecttype);\n" -"\n" -" // Process the operation\n" -" sai_status_t status;\n" -" if (info->isnonobjectid) {\n" -" status = processEntry(metaKey, api, attr_count, attr_list);\n" -" } else {\n" -" status = processOid(metaKey.objecttype, strObjectId, api, " -"attr_count, attr_list);\n" -" }\n" -"\n" -" // Send response\n" -" if (api == SAI_COMMON_API_GET) {\n" -" sai_object_id_t switchVid = " -"VidManager::switchIdQuery(metaKey.objectkey.key.object_id);\n" -" sendGetResponse(metaKey.objecttype, strObjectId, switchVid, status, " -"attr_count, attr_list);\n" -" ...\n" -" } else {\n" -" sendApiResponse(api, status);\n" -" }\n" -"\n" -" syncUpdateRedisQuadEvent(status, api, kco);\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t Syncd::processEntry(_In_ sai_object_meta_key_t metaKey, _In_ " -"sai_common_api_t api,\n" -" _In_ uint32_t attr_count, _In_ " -"sai_attribute_t *attr_list)\n" -"{\n" -" ...\n" -"\n" -" switch (api)\n" -" {\n" -" case SAI_COMMON_API_CREATE:\n" -" return m_vendorSai->create(metaKey, SAI_NULL_OBJECT_ID, " -"attr_count, attr_list);\n" -"\n" -" case SAI_COMMON_API_REMOVE:\n" -" return m_vendorSai->remove(metaKey);\n" -" ...\n" -"\n" -" default:\n" -" SWSS_LOG_THROW(\"api %s not supported\", " -"sai_serialize_common_api(api).c_str());\n" -" }\n" -"}\n" -"```" +"After installation, don't forget to verify the installation with the " +"following command (note, no sudo is needed here!):" msgstr "" -#: src/5-1-syncd-and-sai.md:549 -msgid "## ASIC状态变更上报" +#: src\3-2-build.md:48 +msgid "Install Other Dependencies" msgstr "" -#: src/5-1-syncd-and-sai.md:551 -msgid "" -"反过来,当ASIC状态发生任何变化,或者需要上报数据,它也会通过SAI来通知我们,此时Syncd会监听这些通知,然后通过ASIC_DB上报给orchagent。其主要工作流如下:" +#: src\3-2-build.md:55 +msgid "Pull the Code" msgstr "" -#: src/5-1-syncd-and-sai.md:553 +#: src\3-2-build.md:57 msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" box purple SAI实现事件处理线程\n" -" participant SAI as SAI Impl\n" -" end\n" -" box darkblue 通知处理线程\n" -" participant NP as NotificationProcessor\n" -" participant SD as Syncd\n" -" participant RNP as RedisNotificationProducer\n" -" participant R as Redis\n" -" end\n" -"\n" -" loop SAI实现事件处理消息循环\n" -" SAI->>SAI: 通过ASIC SDK获取事件\n" -" SAI->>SAI: 解析事件,并转换成SAI通知对象\n" -" SAI->>NP: 将通知对象序列化,
并发送给通知处理线程的队列中\n" -" end\n" -"\n" -" loop 通知处理线程消息循环\n" -" NP->>NP: 从队列中获取通知\n" -" NP->>SD: 获取Syncd锁\n" -" critical 给Syncd加锁\n" -" NP->>NP: 反序列化通知对象,并做一些处理\n" -" NP->>RNP: 重新序列化通知对象,并请求发送\n" -" RNP->>R: 将通知以NotificationProducer
的形式写入ASIC_DB\n" -" end\n" -" end\n" -"```" +"In [Chapter 3.1 Code Repositories](./3-1-code-repos), we mentioned that the " +"main repository of SONiC is " +"[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage). It is " +"also the only repo we need to focus on for now." msgstr "" -#: src/5-1-syncd-and-sai.md:582 -msgid "这里我们也来看一下具体的实现。为了更加深入的理解,我们还是借助开源的Mellanox的SAI实现来进行分析。" +#: src\3-2-build.md:59 +msgid "" +"Since this repository includes all other build-related repositories as " +"submodules, we need to use the `--recurse-submodules` option when pulling " +"the code with git:" msgstr "" -#: src/5-1-syncd-and-sai.md:584 +#: src\3-2-build.md:65 msgid "" -"最开始,SAI的实现需要接受到ASIC的通知,这一步是通过ASIC的SDK来实现的,Mellanox的SAI会创建一个事件处理线程(event_thread),然后使用`select`函数来获取并处理ASIC发送过来的通知,核心代码如下:" +"If you forget to pull the submodules when pulling the code, you can make up " +"for it with the following command:" msgstr "" -#: src/5-1-syncd-and-sai.md:586 +#: src\3-2-build.md:71 msgid "" -"```cpp\n" -"// File: " -"platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_switch.c\n" -"static void event_thread_func(void *context)\n" -"{\n" -"#define MAX_PACKET_SIZE MAX(g_resource_limits.port_mtu_max, " -"SX_HOST_EVENT_BUFFER_SIZE_MAX)\n" -"\n" -" sx_status_t status;\n" -" sx_api_handle_t api_handle;\n" -" sx_user_channel_t port_channel, callback_channel;\n" -" fd_set descr_set;\n" -" int ret_val;\n" -" sai_object_id_t switch_id = " -"(sai_object_id_t)context;\n" -" sai_port_oper_status_notification_t port_data;\n" -" sai_fdb_event_notification_data_t *fdb_events = NULL;\n" -" sai_attribute_t *attr_list = NULL;\n" -" ...\n" -"\n" -" // Init SDK API\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_open(sai_log_cb, " -"&api_handle))) {\n" -" if (g_notification_callbacks.on_switch_shutdown_request) {\n" -" g_notification_callbacks.on_switch_shutdown_request(switch_id);\n" -" }\n" -" return;\n" -" }\n" -"\n" -" if (SX_STATUS_SUCCESS != (status = sx_api_host_ifc_open(api_handle, " -"&port_channel.channel.fd))) {\n" -" goto out;\n" -" }\n" -" ...\n" -"\n" -" // Register for port and channel notifications\n" -" port_channel.type = SX_USER_CHANNEL_TYPE_FD;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_api_host_ifc_trap_id_register_set(api_handle, SX_ACCESS_CMD_REGISTER, " -"DEFAULT_ETH_SWID, SX_TRAP_ID_PUDE, &port_channel))) {\n" -" goto out;\n" -" }\n" -" ...\n" -" for (uint32_t ii = 0; ii < (sizeof(mlnx_trap_ids) / " -"sizeof(*mlnx_trap_ids)); ii++) {\n" -" status = sx_api_host_ifc_trap_id_register_set(api_handle, " -"SX_ACCESS_CMD_REGISTER, DEFAULT_ETH_SWID, mlnx_trap_ids[ii], " -"&callback_channel);\n" -" }\n" -"\n" -" while (!event_thread_asked_to_stop) {\n" -" FD_ZERO(&descr_set);\n" -" FD_SET(port_channel.channel.fd.fd, &descr_set);\n" -" FD_SET(callback_channel.channel.fd.fd, &descr_set);\n" -" ...\n" -"\n" -" ret_val = select(FD_SETSIZE, &descr_set, NULL, NULL, &timeout);\n" -" if (ret_val > 0) {\n" -" // Port state change event\n" -" if (FD_ISSET(port_channel.channel.fd.fd, &descr_set)) {\n" -" // Parse port state event here ...\n" -" if (g_notification_callbacks.on_port_state_change) {\n" -" g_notification_callbacks.on_port_state_change(1, " -"&port_data);\n" -" }\n" -" }\n" -"\n" -" if (FD_ISSET(callback_channel.channel.fd.fd, &descr_set)) {\n" -" // Receive notification event.\n" -" packet_size = MAX_PACKET_SIZE;\n" -" if (SX_STATUS_SUCCESS != (status = " -"sx_lib_host_ifc_recv(&callback_channel.channel.fd, p_packet, &packet_size, " -"receive_info))) {\n" -" goto out;\n" -" }\n" -"\n" -" // BFD packet event\n" -" if (SX_TRAP_ID_BFD_PACKET_EVENT == receive_info->trap_id) {\n" -" const struct bfd_packet_event *event = (const struct " -"bfd_packet_event*)p_packet;\n" -" // Parse and check event valid here ...\n" -" status = mlnx_switch_bfd_packet_handle(event);\n" -" continue;\n" -" }\n" -"\n" -" // Same way to handle BFD timeout event, Bulk counter ready " -"event. Emiited.\n" -"\n" -" // FDB event and packet event handling\n" -" if (receive_info->trap_id == SX_TRAP_ID_FDB_EVENT) {\n" -" trap_name = \"FDB event\";\n" -" } else if (SAI_STATUS_SUCCESS != (status = " -"mlnx_translate_sdk_trap_to_sai(receive_info->trap_id, &trap_name, " -"&trap_oid))) {\n" -" continue;\n" -" }\n" -"\n" -" if (SX_TRAP_ID_FDB_EVENT == receive_info->trap_id) {\n" -" // Parse FDB events here ...\n" -"\n" -" if (g_notification_callbacks.on_fdb_event) {\n" -" g_notification_callbacks.on_fdb_event(event_count, " -"fdb_events);\n" -" }\n" -"\n" -" continue;\n" -" }\n" -"\n" -" // Packet event handling\n" -" status = mlnx_get_hostif_packet_data(receive_info, " -"&attrs_num, callback_data);\n" -" if (g_notification_callbacks.on_packet_event) {\n" -" g_notification_callbacks.on_packet_event(switch_id, " -"packet_size, p_packet, attrs_num, callback_data);\n" -" }\n" -" }\n" -" }\n" -" }\n" -"\n" -"out:\n" -" ...\n" -"}\n" -"```" +"After the code is downloaded, or for an existing repo, we can initialize the " +"compilation environment with the following command. This command updates all " +"current submodules to the required versions to help us successfully compile:" msgstr "" -#: src/5-1-syncd-and-sai.md:690 -msgid "" -"接下来,我们用FDB事件来举例,当ASIC收到FDB事件,就会被上面的事件处理循环获取到,并调用`g_notification_callbacks.on_fdb_event`函数来处理。这个函数接下来就会调用到`Syncd`初始化时设置好的`NotificationHandler::onFdbEvent`函数,这个函数会将该事件序列化后,通过消息队列转发给通知处理线程来进行处理:" +#: src\3-2-build.md:78 +msgid "Set Your Target Platform" msgstr "" -#: src/5-1-syncd-and-sai.md:692 +#: src\3-2-build.md:80 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationHandler.cpp\n" -"void NotificationHandler::onFdbEvent(_In_ uint32_t count, _In_ const " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" enqueueNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" +"[Although SONiC supports many different types of " +"switches](https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html), " +"different models of switches use different ASICs, which means different " +"drivers and SDKs. Although SONiC uses SAI to hide these differences and " +"provide a unified interface for the upper layers. However, we need to set " +"target platform correctly to ensure that the right SAI will be used, so the " +"SONiC we build can run on our target devices." msgstr "" -#: src/5-1-syncd-and-sai.md:701 -msgid "而此时通知处理线程会被唤醒,从消息队列中取出该事件,然后通过`Syncd`获取到`Syncd`的锁,再开始处理该通知:" +#: src\3-2-build.md:82 +msgid "Currently, SONiC mainly supports the following platforms:" msgstr "" -#: src/5-1-syncd-and-sai.md:703 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::ntf_process_function()\n" -"{\n" -" std::mutex ntf_mutex;\n" -" std::unique_lock ulock(ntf_mutex);\n" -"\n" -" while (m_runThread) {\n" -" // When notification arrives, it will signal this condition " -"variable.\n" -" m_cv.wait(ulock);\n" -"\n" -" // Process notifications in the queue.\n" -" swss::KeyOpFieldsValuesTuple item;\n" -" while (m_notificationQueue->tryDequeue(item)) {\n" -" processNotification(item);\n" -" }\n" -" }\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/Syncd.cpp\n" -"// Call from NotificationProcessor::processNotification\n" -"void Syncd::syncProcessNotification(_In_ const swss::KeyOpFieldsValuesTuple& " -"item)\n" -"{\n" -" std::lock_guard lock(m_mutex);\n" -" m_processor->syncProcessNotification(item);\n" -"}\n" -"```" +#: src\3-2-build.md:84 +msgid "broadcom" msgstr "" -#: src/5-1-syncd-and-sai.md:731 -msgid "" -"接下来就是事件的分发和处理了,`syncProcessNotification`函数是一系列的`if-else`语句,根据事件的类型,调用不同的处理函数来处理该事件:" +#: src\3-2-build.md:85 +msgid "mellanox" msgstr "" -#: src/5-1-syncd-and-sai.md:733 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::syncProcessNotification( _In_ const " -"swss::KeyOpFieldsValuesTuple& item)\n" -"{\n" -" std::string notification = kfvKey(item);\n" -" std::string data = kfvOp(item);\n" -"\n" -" if (notification == SAI_SWITCH_NOTIFICATION_NAME_SWITCH_STATE_CHANGE) {\n" -" handle_switch_state_change(data);\n" -" } else if (notification == SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT) {\n" -" handle_fdb_event(data);\n" -" } else if ...\n" -" } else {\n" -" SWSS_LOG_ERROR(\"unknown notification: %s\", notification.c_str());\n" -" }\n" -"}\n" -"```" +#: src\3-2-build.md:86 +msgid "marvell" msgstr "" -#: src/5-1-syncd-and-sai.md:751 -msgid "" -"而每个事件处理函数都类似,他们会对发送过来的事件进行反序列化,然后调用真正的处理逻辑发送通知,比如,fdb事件对应的`handle_fdb_event`函数和`process_on_fdb_event`:" +#: src\3-2-build.md:87 +msgid "barefoot" msgstr "" -#: src/5-1-syncd-and-sai.md:753 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::handle_fdb_event(_In_ const std::string &data)\n" -"{\n" -" uint32_t count;\n" -" sai_fdb_event_notification_data_t *fdbevent = NULL;\n" -" sai_deserialize_fdb_event_ntf(data, count, &fdbevent);\n" -"\n" -" process_on_fdb_event(count, fdbevent);\n" -"\n" -" sai_deserialize_free_fdb_event_ntf(count, fdbevent);\n" -"}\n" -"\n" -"void NotificationProcessor::process_on_fdb_event( _In_ uint32_t count, _In_ " -"sai_fdb_event_notification_data_t *data)\n" -"{\n" -" for (uint32_t i = 0; i < count; i++) {\n" -" sai_fdb_event_notification_data_t *fdb = &data[i];\n" -" // Check FDB event notification data here\n" -"\n" -" fdb->fdb_entry.switch_id = " -"m_translator->translateRidToVid(fdb->fdb_entry.switch_id, " -"SAI_NULL_OBJECT_ID);\n" -" fdb->fdb_entry.bv_id = " -"m_translator->translateRidToVid(fdb->fdb_entry.bv_id, " -"fdb->fdb_entry.switch_id, true);\n" -" m_translator->translateRidToVid(SAI_OBJECT_TYPE_FDB_ENTRY, " -"fdb->fdb_entry.switch_id, fdb->attr_count, fdb->attr, true);\n" -"\n" -" ...\n" -" }\n" -"\n" -" // Send notification\n" -" std::string s = sai_serialize_fdb_event_ntf(count, data);\n" -" sendNotification(SAI_SWITCH_NOTIFICATION_NAME_FDB_EVENT, s);\n" -"}\n" -"```" +#: src\3-2-build.md:88 +msgid "cavium" msgstr "" -#: src/5-1-syncd-and-sai.md:785 -msgid "" -"具体发送事件的逻辑就非常直接了,最终就是通过[NotificationProducer](./4-2-2-redis-messaging-layer.html#notificationproducer--notificationconsumer)来发送通知到ASIC_DB中:" +#: src\3-2-build.md:89 +msgid "centec" msgstr "" -#: src/5-1-syncd-and-sai.md:787 -msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data)\n" -"{\n" -" std::vector entry;\n" -" sendNotification(op, data, entry);\n" -"}\n" -"\n" -"void NotificationProcessor::sendNotification(_In_ const std::string& op, " -"_In_ const std::string& data, _In_ std::vector " -"entry)\n" -"{\n" -" m_notifications->send(op, data, entry);\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/syncd/RedisNotificationProducer.cpp\n" -"void RedisNotificationProducer::send(_In_ const std::string& op, _In_ const " -"std::string& data, _In_ const std::vector& values)\n" -"{\n" -" std::vector vals = values;\n" -"\n" -" // The m_notificationProducer is created in the ctor of " -"RedisNotificationProducer as below:\n" -" // m_notificationProducer = " -"std::make_shared(m_db.get(), " -"REDIS_TABLE_NOTIFICATIONS_PER_DB(dbName));\n" -" m_notificationProducer->send(op, data, vals);\n" -"}\n" -"```" +#: src\3-2-build.md:90 +msgid "nephos" +msgstr "" + +#: src\3-2-build.md:91 +msgid "innovium" msgstr "" -#: src/5-1-syncd-and-sai.md:811 -msgid "到此,`Syncd`中的通知上报的流程就结束了。" +#: src\3-2-build.md:92 +msgid "vs" msgstr "" -#: src/5-1-syncd-and-sai.md:815 +#: src\3-2-build.md:94 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"3. [Github repo: Nvidia (Mellanox) SAI implementation][MnlxSAI]" +"After confirming the target platform, we can configure our build environment " +"with the following command:" msgstr "" -#: src/5-2-bgp.md:1 -msgid "# BGP" +#: src\3-2-build.md:97 +msgid "# e.g.: make PLATFORM=mellanox configure" msgstr "" -#: src/5-2-bgp.md:3 -msgid "[BGP][BGP]可能是交换机里面最常用,最重要,或者线上使用的最多的功能了。这一节,我们就来深入的看一下BGP相关的工作流。" +#: src\3-2-build.md:114 +msgid "Build the Code" msgstr "" -#: src/5-2-bgp.md:5 -msgid "## BGP相关进程" +#: src\3-2-build.md:116 +msgid "Build All Code" msgstr "" -#: src/5-2-bgp.md:7 -msgid "" -"SONiC使用[FRRouting][FRRouting]作为BGP的实现,用于负责BGP的协议处理。FRRouting是一个开源的路由软件,支持多种路由协议,包括BGP,OSPF,IS-IS,RIP,PIM,LDP等等。当FRR发布新版本后,SONiC会将其同步到[SONiC的FRR实现仓库:sonic-frr][SONiCFRR]中,每一个版本都对应这一个分支,比如`frr/8.2`。" +#: src\3-2-build.md:118 +msgid "After setting the platform, we can start compiling the code:" msgstr "" -#: src/5-2-bgp.md:9 -msgid "" -"FRR主要由两个大部分组成,第一个部分是各个协议的实现,这些进程的名字都叫做`*d`,而当它们收到路由更新的通知的时候,就会告诉第二个部分,也就是`zebra`进程,然后`zebra`进程会进行选路,并将最优的路由信息同步到kernel中,其主体结构如下图所示:" +#: src\3-2-build.md:121 +msgid "# The number of jobs can be the number of cores on your machine." msgstr "" -#: src/5-2-bgp.md:11 +#: src\3-2-build.md:121 msgid "" -"```\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -"|bgpd| |ripd| |ospfd| |ldpd| |pbrd| |pimd| |.....|\n" -"+----+ +----+ +-----+ +----+ +----+ +----+ +-----+\n" -" | | | | | | |\n" -"+----v-------v--------v-------v-------v-------v--------v\n" -"| |\n" -"| Zebra |\n" -"| |\n" -"+------------------------------------------------------+\n" -" | | |\n" -" | | |\n" -"+------v------+ +---------v--------+ +------v------+\n" -"| | | | | |\n" -"| *NIX Kernel | | Remote dataplane | | ........... |\n" -"| | | | | |\n" -"+-------------+ +------------------+ +-------------+\n" -"```" +"# Say, if you have 16 cores, then feel free to set it to 16 to speed up the " +"build." msgstr "" -#: src/5-2-bgp.md:30 -msgid "" -"在SONiC中,这些FRR的进程都跑在`bgp`的容器中。另外,为了将FRR和Redis连接起来,SONiC在`bgp`容器中还会运行一个叫做`fpgsyncd`的进程(Forwarding " -"Plane Manager " -"syncd),它的主要功能是监听kernel的路由更新,然后将其同步到APP_DB中。但是因为这个进程不是FRR的一部分,所以它的实现被放在了[sonic-swss][SONiCSWSS]仓库中。" +#: src\3-2-build.md:132 +msgid "Build Debug Image" msgstr "" -#: src/5-2-bgp.md:34 +#: src\3-2-build.md:134 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-frr][SONiCFRR]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" +"To improve the debug experience, SONiC also supports building debug image. " +"During build, SONiC will make sure the symbols are kept and debug tools are " +"installed inside all the containers, such as gdb. This will help us debug " +"the code more easily." msgstr "" -#: src/5-2-1-bgp-command-impl.md:1 -msgid "# BGP命令实现" +#: src\3-2-build.md:136 +msgid "To build the debug image, we can use `INSTALL_DEBUG_TOOLS` build option:" msgstr "" -#: src/5-2-1-bgp-command-impl.md:3 -msgid "由于BGP是使用FRR来实现的,所以自然而然的,`show`命令会将直接请求转发给FRR的`vtysh`,核心代码如下:" +#: src\3-2-build.md:139 +msgid "y" msgstr "" -#: src/5-2-1-bgp-command-impl.md:5 +#: src\3-2-build.md:142 +msgid "Build Specific Package" +msgstr "" + +#: src\3-2-build.md:144 msgid "" -"```python\n" -"# file: src/sonic-utilities/show/bgp_frr_v4.py\n" -"# 'summary' subcommand (\"show ip bgp summary\")\n" -"@bgp.command()\n" -"@multi_asic_util.multi_asic_click_options\n" -"def summary(namespace, display):\n" -" bgp_summary = bgp_util.get_bgp_summary_from_all_bgp_instances(\n" -" constants.IPV4, namespace, display)\n" -" bgp_util.display_bgp_summary(bgp_summary=bgp_summary, " -"af=constants.IPV4)\n" -"\n" -"# file: src/sonic-utilities/utilities_common/bgp_util.py\n" -"def get_bgp_summary_from_all_bgp_instances(af, namespace, display):\n" -" # IPv6 case is omitted here for simplicity\n" -" vtysh_cmd = \"show ip bgp summary json\"\n" -" \n" -" for ns in device.get_ns_list_based_on_options():\n" -" cmd_output = run_bgp_show_command(vtysh_cmd, ns)\n" -"\n" -"def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, " -"vtysh_shell_cmd=constants.VTYSH_COMMAND):\n" -" cmd = ['sudo', vtysh_shell_cmd] + bgp_instance_id + ['-c', vtysh_cmd]\n" -" output, ret = clicommon.run_command(cmd, return_cmd=True)\n" -"```" +"From SONiC's Build Pipeline, we can see that compiling the entire project is " +"very time-consuming. Most of the time, our code changes only affect a small " +"part of the code. So, is there a way to reduce our compilation workload? " +"Gladly, yes! We can specify the make target to build only the target or " +"package we need." msgstr "" -#: src/5-2-1-bgp-command-impl.md:28 -msgid "这里,我们也可以通过直接运行`vtysh`来进行验证:" +#: src\3-2-build.md:146 +msgid "" +"In SONiC, the files generated by each subproject can be found in the " +"`target` directory. For example:" msgstr "" -#: src/5-2-1-bgp-command-impl.md:30 +#: src\3-2-build.md:148 msgid "" -"```bash\n" -"root@7260cx3:/etc/sonic/frr# which vtysh\n" -"/usr/bin/vtysh\n" -"\n" -"root@7260cx3:/etc/sonic/frr# vtysh\n" -"\n" -"Hello, this is FRRouting (version 7.5.1-sonic).\n" -"Copyright 1996-2005 Kunihiro Ishiguro, et al.\n" -"\n" -"7260cx3# show ip bgp summary\n" -"\n" -"IPv4 Unicast Summary:\n" -"BGP router identifier 10.1.0.32, local AS number 65100 vrf-id 0\n" -"BGP table version 6410\n" -"RIB entries 12809, using 2402 KiB of memory\n" -"Peers 4, using 85 KiB of memory\n" -"Peer groups 4, using 256 bytes of memory\n" -"\n" -"Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down " -"State/PfxRcd PfxSnt\n" -"10.0.0.57 4 64600 3702 3704 0 0 0 08:15:03 " -" 6401 6406\n" -"10.0.0.59 4 64600 3702 3704 0 0 0 08:15:03 " -" 6401 6406\n" -"10.0.0.61 4 64600 3705 3702 0 0 0 08:15:03 " -" 6401 6406\n" -"10.0.0.63 4 64600 3702 3702 0 0 0 08:15:03 " -" 6401 6406\n" -"\n" -"Total number of neighbors 4\n" -"```" +"Docker containers: target/.gz, e.g., " +"`target/docker-orchagent.gz`" msgstr "" -#: src/5-2-1-bgp-command-impl.md:57 -msgid "而`config`命令则是通过直接操作CONFIG_DB来实现的,核心代码如下:" +#: src\3-2-build.md:149 +msgid "" +"Deb packages: target/debs//.deb, e.g., " +"`target/debs/bullseye/libswsscommon_1.0.0_amd64.deb`" msgstr "" -#: src/5-2-1-bgp-command-impl.md:59 +#: src\3-2-build.md:150 msgid "" -"```python\n" -"# file: src/sonic-utilities/config/main.py\n" -"\n" -"@bgp.group(cls=clicommon.AbbreviationGroup)\n" -"def remove():\n" -" \"Remove BGP neighbor configuration from the device\"\n" -" pass\n" -"\n" -"@remove.command('neighbor')\n" -"@click.argument('neighbor_ip_or_hostname', " -"metavar='', required=True)\n" -"def remove_neighbor(neighbor_ip_or_hostname):\n" -" \"\"\"Deletes BGP neighbor configuration of given hostname or ip from " -"devices\n" -" User can specify either internal or external BGP neighbor to remove\n" -" \"\"\"\n" -" namespaces = [DEFAULT_NAMESPACE]\n" -" removed_neighbor = False\n" -" ...\n" -"\n" -" # Connect to CONFIG_DB in linux host (in case of single ASIC) or " -"CONFIG_DB in all the\n" -" # namespaces (in case of multi ASIC) and do the sepcified \"action\" on " -"the BGP neighbor(s)\n" -" for namespace in namespaces:\n" -" config_db = ConfigDBConnector(use_unix_socket_path=True, " -"namespace=namespace)\n" -" config_db.connect()\n" -" if _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname):\n" -" removed_neighbor = True\n" -" ...\n" -"```" +"Python wheels: target/python-wheels//.whl, e.g., " +"`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl`" msgstr "" -#: src/5-2-1-bgp-command-impl.md:89 +#: src\3-2-build.md:152 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-frr][SONiCFRR]\n" -"3. [Github repo: sonic-utilities][SONiCUtil]\n" -"4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"5. [FRRouting][FRRouting]" +"After figuring out the package we need to build, we can delete its generated " +"files and then call the make command again. Here we use `libswsscommon` as " +"an example:" +msgstr "" + +#: src\3-2-build.md:155 +msgid "# Remove the deb package for bullseye" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1 -msgid "# BGP路由变更下发" +#: src\3-2-build.md:157 +msgid "# Build the deb package for bullseye" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:3 +#: src\3-2-build.md:162 +msgid "Check and Handle Build Errors" +msgstr "" + +#: src\3-2-build.md:164 msgid "" -"路由变更几乎是SONiC中最重要的工作流,它的整个流程从`bgpd`进程开始,到最终通过SAI到达ASIC芯片,中间参与的进程较多,流程也较为复杂,但是弄清楚之后,我们就可以很好的理解SONiC的设计思想,并且举一反三的理解其他配置下发的工作流了。所以这一节,我们就一起来深入的分析一下它的整体流程。" +"If an error occurs during the build process, we can check the log file of " +"the failed project to find the specific reason. In SONiC, each subproject " +"generates its related log file, which can be easily found in the `target` " +"directory, such as:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:5 +#: src\3-2-build.md:175 msgid "" -"为了方便我们理解和从代码层面来展示,我们把这个流程分成两个大块来介绍,分别是FRR是如何处理路由变化的,和SONiC的路由变更工作流以及它是如何与FRR进行整合的。" +"If we don't want to check the log files every time, then fix errors and " +"recompile in the root directory, SONiC provides another more convenient way " +"that allows us to stay in the docker builder after build. This way, we can " +"directly go to the corresponding directory to run the `make` command to " +"recompile the things you need:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:7 -msgid "## FRR处理路由变更" +#: src\3-2-build.md:178 +msgid "# KEEP_SLAVE_ON=yes make " msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:9 -msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant N as 邻居节点\n" -" box purple bgp容器\n" -" participant B as bgpd\n" -" participant ZH as zebra
(请求处理线程)\n" -" participant ZF as zebra
(路由处理线程)\n" -" participant ZD as zebra
(数据平面处理线程)\n" -" participant ZFPM as zebra
(FPM转发线程)\n" -" participant FPM as fpmsyncd\n" -" end\n" -" participant K as Linux Kernel\n" -"\n" -" N->>B: 建立BGP会话,
发送路由变更\n" -" B->>B: 选路,变更本地路由表(RIB)\n" -" alt 如果路由发生变化\n" -" B->>N: 通知其他邻居节点路由变化\n" -" end\n" -" B->>ZH: 通过zlient本地Socket
通知Zebra更新路由表\n" -" ZH->>ZH: 接受bgpd发送的请求\n" -" ZH->>ZF: 将路由请求放入
路由处理线程的队列中\n" -" ZF->>ZF: 更新本地路由表(RIB)\n" -" ZF->>ZD: 将路由表更新请求放入
数据平面处理线程
的消息队列中\n" -" ZF->>ZFPM: 请求FPM处理线程转发路由变更\n" -" ZFPM->>FPM: 通过FPM协议通知
fpmsyncd下发
路由变更\n" -" ZD->>K: 发送Netlink消息更新内核路由表\n" -"```" +#: src\3-2-build.md:179 src\3-2-build.md:180 +msgid "yes" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:38 +#: src\3-2-build.md:183 msgid "" "```admonish note\n" -"关于FRR的实现,这里更多的是从代码的角度来阐述其工作流的过程,而不是其对BGP的实现细节,如果想要了解FRR的BGP实现细节,可以参考[官方文档](https://docs.frrouting.org/en/latest/bgp.html)。\n" +"Some parts of the code in some repositories will not be build during full " +"build. For example, gtest in `sonic-swss-common`. So, when using this way to " +"recompile, please make sure to check the original repository's build " +"guidance to avoid errors, such as: " +".\n" "```" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:42 -msgid "### bgpd处理路由变更" +#: src\3-2-build.md:187 +msgid "Get the Correct Image File" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:44 +#: src\3-2-build.md:189 msgid "" -"`bgpd`是FRR中专门用来处理BGP会话的进程,它会开放TCP " -"179端口与邻居节点建立BGP连接,并处理路由表的更新请求。当路由发生变化后,FRR也会通过它来通知其他邻居节点。" +"After compilation, we can find the image files we need in the `target` " +"directory. However, there will be many different types of SONiC images, so " +"which one should we use? This mainly depends on what kind of BootLoader or " +"Installer the switch uses. The mapping is as below:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:46 -msgid "请求来到`bgpd`之后,它会首先来到它的io线程:`bgp_io`。顾名思义,`bgpd`中的网络读写工作都是在这个线程上完成的:" +#: src\3-2-build.md:191 +msgid "Bootloader" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:48 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_io.c\n" -"static int bgp_process_reads(struct thread *thread)\n" -"{\n" -" ...\n" -"\n" -" while (more) {\n" -" // Read packets here\n" -" ...\n" -" \n" -" // If we have more than 1 complete packet, mark it and process it " -"later.\n" -" if (ringbuf_remain(ibw) >= pktsize) {\n" -" ...\n" -" added_pkt = true;\n" -" } else break;\n" -" }\n" -" ...\n" -"\n" -" if (added_pkt)\n" -" thread_add_event(bm->master, bgp_process_packet, peer, 0, " -"&peer->t_process_packet);\n" -"\n" -" return 0;\n" -"}\n" -"```" +#: src\3-2-build.md:191 +msgid "Suffix" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:73 -msgid "" -"当数据包读完后,`bgpd`会将其发送到主线程进行路由处理。在这里,`bgpd`会根据数据包的类型进行分发,其中路由更新的请求会交给`bpg_update_receive`来进行解析:" +#: src\3-2-build.md:193 +msgid "Aboot" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:75 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_packet.c\n" -"int bgp_process_packet(struct thread *thread)\n" -"{\n" -" ...\n" -" unsigned int processed = 0;\n" -" while (processed < rpkt_quanta_old) {\n" -" uint8_t type = 0;\n" -" bgp_size_t size;\n" -" ...\n" -"\n" -" /* read in the packet length and type */\n" -" size = stream_getw(peer->curr);\n" -" type = stream_getc(peer->curr);\n" -" size -= BGP_HEADER_SIZE;\n" -"\n" -" switch (type) {\n" -" case BGP_MSG_OPEN:\n" -" ...\n" -" break;\n" -" case BGP_MSG_UPDATE:\n" -" ...\n" -" mprc = bgp_update_receive(peer, size);\n" -" ...\n" -" break;\n" -" ...\n" -"}\n" -"\n" -"// Process BGP UPDATE message for peer.\n" -"static int bgp_update_receive(struct peer *peer, bgp_size_t size)\n" -"{\n" -" struct stream *s;\n" -" struct attr attr;\n" -" struct bgp_nlri nlris[NLRI_TYPE_MAX];\n" -" ...\n" -"\n" -" // Parse attributes and NLRI\n" -" memset(&attr, 0, sizeof(struct attr));\n" -" attr.label_index = BGP_INVALID_LABEL_INDEX;\n" -" attr.label = MPLS_INVALID_LABEL;\n" -" ...\n" -"\n" -" memset(&nlris, 0, sizeof(nlris));\n" -" ...\n" -"\n" -" if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0)\n" -" || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) {\n" -" // More parsing here\n" -" ...\n" -"\n" -" if (afi && peer->afc[afi][safi]) {\n" -" struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);\n" -"\n" -" /* End-of-RIB received */\n" -" if (!CHECK_FLAG(peer->af_sflags[afi][safi], " -"PEER_STATUS_EOR_RECEIVED)) {\n" -" ...\n" -" if (gr_info->eor_required == gr_info->eor_received) {\n" -" ...\n" -" /* Best path selection */\n" -" if (bgp_best_path_select_defer( peer->bgp, afi, safi) < " -"0)\n" -" return BGP_Stop;\n" -" }\n" -" }\n" -" ...\n" -" }\n" -" }\n" -" ...\n" -"\n" -" return Receive_UPDATE_message;\n" -"}\n" -"```" +#: src\3-2-build.md:193 +msgid ".swi" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:147 -msgid "然后,`bgpd`会开始检查是否出现更优的路径,并更新自己的本地路由表(RIB,Routing Information Base):" +#: src\3-2-build.md:194 +msgid "ONIE" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:149 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_route.c\n" -"/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */\n" -"int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_dest *dest;\n" -" int cnt = 0;\n" -" struct afi_safi_info *thread_info;\n" -" ...\n" -"\n" -" /* Process the route list */\n" -" for (dest = bgp_table_top(bgp->rib[afi][safi]);\n" -" dest && bgp->gr_info[afi][safi].gr_deferred != 0;\n" -" dest = bgp_route_next(dest))\n" -" {\n" -" ...\n" -" bgp_process_main_one(bgp, dest, afi, safi);\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" return 0;\n" -"}\n" -"\n" -"static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, " -"afi_t afi, safi_t safi)\n" -"{\n" -" struct bgp_path_info *new_select;\n" -" struct bgp_path_info *old_select;\n" -" struct bgp_path_info_pair old_and_new;\n" -" ...\n" -"\n" -" const struct prefix *p = bgp_dest_get_prefix(dest);\n" -" ...\n" -"\n" -" /* Best path selection. */\n" -" bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, " -"afi, safi);\n" -" old_select = old_and_new.old;\n" -" new_select = old_and_new.new;\n" -" ...\n" -"\n" -" /* FIB update. */\n" -" if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)\n" -" && !bgp_option_check(BGP_OPT_NO_FIB)) {\n" -"\n" -" if (new_select && new_select->type == ZEBRA_ROUTE_BGP\n" -" && (new_select->sub_type == BGP_ROUTE_NORMAL\n" -" || new_select->sub_type == BGP_ROUTE_AGGREGATE\n" -" || new_select->sub_type == BGP_ROUTE_IMPORTED)) {\n" -" ...\n" -"\n" -" if (old_select && is_route_parent_evpn(old_select))\n" -" bgp_zebra_withdraw(p, old_select, bgp, safi);\n" -"\n" -" bgp_zebra_announce(dest, p, new_select, bgp, afi, safi);\n" -" } else {\n" -" /* Withdraw the route from the kernel. */\n" -" ...\n" -" }\n" -" }\n" -"\n" -" /* EVPN route injection and clean up */\n" -" ...\n" -"\n" -" UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);\n" -" return;\n" -"}\n" -"```" +#: src\3-2-build.md:194 +msgid ".bin" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:217 -msgid "最后,`bgp_zebra_announce`会通过`zclient`通知`zebra`更新内核路由表。" +#: src\3-2-build.md:195 +msgid "Grub" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:219 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct " -"bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi)\n" -"{\n" -" ...\n" -" zclient_route_send(valid_nh_count ? ZEBRA_ROUTE_ADD : " -"ZEBRA_ROUTE_DELETE, zclient, &api);\n" -"}\n" -"```" +#: src\3-2-build.md:195 +msgid ".img.gz" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:228 -msgid "`zclient`使用本地socket与`zebra`通信,并且提供一系列的回调函数用于接收`zebra`的通知,核心代码如下:" +#: src\3-2-build.md:197 +msgid "Partial Upgrade" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:230 +#: src\3-2-build.md:199 msgid "" -"```c\n" -"// File: src/sonic-frr/frr/bgpd/bgp_zebra.c\n" -"void bgp_zebra_init(struct thread_master *master, unsigned short instance)\n" -"{\n" -" zclient_num_connects = 0;\n" -"\n" -" /* Set default values. */\n" -" zclient = zclient_new(master, &zclient_options_default);\n" -" zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs);\n" -" zclient->zebra_connected = bgp_zebra_connected;\n" -" zclient->router_id_update = bgp_router_id_update;\n" -" zclient->interface_add = bgp_interface_add;\n" -" zclient->interface_delete = bgp_interface_delete;\n" -" zclient->interface_address_add = bgp_interface_address_add;\n" -" ...\n" -"}\n" -"\n" -"int zclient_socket_connect(struct zclient *zclient)\n" -"{\n" -" int sock;\n" -" int ret;\n" -"\n" -" sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0);\n" -" ...\n" -"\n" -" /* Connect to zebra. */\n" -" ret = connect(sock, (struct sockaddr *)&zclient_addr, " -"zclient_addr_len);\n" -" ...\n" -"\n" -" zclient->sock = sock;\n" -" return sock;\n" -"}\n" -"```" -msgstr "" - -#: src/5-2-2-bgp-route-update-workflow.md:264 -msgid "在`bgpd`容器中,我们可以在`/run/frr`目录下找到`zebra`通信使用的socket文件来进行简单的验证:" +"Obviously, during development, build the image and then performing a full " +"installation each time is very inefficient. So, we could choose not to " +"install the image but directly upgrading certain deb packages as partial " +"upgrade, which could improving our development efficiency." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:266 +#: src\3-2-build.md:201 msgid "" -"```bash\n" -"root@7260cx3:/run/frr# ls -l\n" -"total 12\n" -"...\n" -"srwx------ 1 frr frr 0 Jun 16 09:16 zserv.api\n" -"```" +"First, we can upload the deb package to the `/etc/sonic` directory of the " +"switch. The files in this directory will be mapped to the `/etc/sonic` " +"directory of all containers. Then, we can enter the container and use the " +"`dpkg` command to install the deb package, as follows:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:273 -msgid "### zebra更新路由表" +#: src\3-2-build.md:204 +msgid "# Enter the docker container" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:275 -msgid "" -"由于FRR支持的路由协议很多,如果每个路由协议处理进程都单独的对内核进行操作则必然会产生冲突,很难协调合作,所以FRR使用一个单独的进程用于和所有的路由协议处理进程进行沟通,整合好信息之后统一的进行内核的路由表更新,这个进程就是`zebra`。" +#: src\3-2-build.md:206 +msgid "# Install deb package" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:277 +#: src\3-2-build.md:213 msgid "" -"在`zebra`中,内核的更新发生在一个独立的数据面处理线程中:`dplane_thread`。所有的请求都会通过`zclient`发送给`zebra`,经过处理之后,最后转发给`dplane_thread`来处理,这样路由的处理就是有序的了,也就不会产生冲突了。" +"[SONiC Build " +"Guide](https://github.com/sonic-net/sonic-buildimage/blob/master/README.md)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:279 -msgid "`zebra`启动时,会将所有的请求处理函数进行注册,当请求到来时,就可以根据请求的类型调用相应的处理函数了,核心代码如下:" +#: src\3-2-build.md:214 +msgid "[Install Docker Engine](https://docs.docker.com/engine/install/)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:281 +#: src\3-2-build.md:215 msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {\n" -" [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,\n" -" [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,\n" -" [ZEBRA_INTERFACE_ADD] = zread_interface_add,\n" -" [ZEBRA_INTERFACE_DELETE] = zread_interface_delete,\n" -" [ZEBRA_ROUTE_ADD] = zread_route_add,\n" -" [ZEBRA_ROUTE_DELETE] = zread_route_del,\n" -" [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add,\n" -" [ZEBRA_REDISTRIBUTE_DELETE] = zebra_redistribute_delete,\n" -" ...\n" -"```" +"[Github repo: " +"sonic-buildimage](https://github.com/sonic-net/sonic-buildimage)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:295 +#: src\3-2-build.md:217 msgid "" -"我们这里拿添加路由`zread_route_add`作为例子,来继续分析后续的流程。从以下代码我们可以看到,当新的路由到来后,`zebra`会开始查看并更新自己内部的路由表:" +"[Wrapper for starting make inside sonic-slave " +"container](https://github.com/sonic-net/sonic-buildimage/blob/master/Makefile.work)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:297 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zapi_msg.c\n" -"static void zread_route_add(ZAPI_HANDLER_ARGS)\n" -"{\n" -" struct stream *s;\n" -" struct route_entry *re;\n" -" struct nexthop_group *ng = NULL;\n" -" struct nhg_hash_entry nhe;\n" -" ...\n" -"\n" -" // Decode zclient request\n" -" s = msg;\n" -" if (zapi_route_decode(s, &api) < 0) {\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" // Allocate new route entry.\n" -" re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));\n" -" re->type = api.type;\n" -" re->instance = api.instance;\n" -" ...\n" -" \n" -" // Init nexthop entry, if we have an id, then add route.\n" -" if (!re->nhe_id) {\n" -" zebra_nhe_init(&nhe, afi, ng->nexthop);\n" -" nhe.nhg.nexthop = ng->nexthop;\n" -" nhe.backup_info = bnhg;\n" -" }\n" -" ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, " -"&nhe);\n" -"\n" -" // Update stats. IPv6 is omitted here for simplicity.\n" -" if (ret > 0) client->v4_route_add_cnt++;\n" -" else if (ret < 0) client->v4_route_upd8_cnt++;\n" -"}\n" -"\n" -"// File: src/sonic-frr/frr/zebra/zebra_rib.c\n" -"int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p,\n" -" struct prefix_ipv6 *src_p, struct route_entry *re,\n" -" struct nhg_hash_entry *re_nhe)\n" -"{\n" -" struct nhg_hash_entry *nhe = NULL;\n" -" struct route_table *table;\n" -" struct route_node *rn;\n" -" int ret = 0;\n" -" ...\n" -"\n" -" /* Find table and nexthop entry */\n" -" table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, " -"re->table);\n" -" if (re->nhe_id > 0) nhe = zebra_nhg_lookup_id(re->nhe_id);\n" -" else nhe = zebra_nhg_rib_find_nhe(re_nhe, afi);\n" -"\n" -" /* Attach the re to the nhe's nexthop group. */\n" -" route_entry_update_nhe(re, nhe);\n" -"\n" -" /* Make it sure prefixlen is applied to the prefix. */\n" -" /* Set default distance by route type. */\n" -" ...\n" -"\n" -" /* Lookup route node.*/\n" -" rn = srcdest_rnode_get(table, p, src_p);\n" -" ...\n" -"\n" -" /* If this route is kernel/connected route, notify the dataplane to " -"update kernel route table. */\n" -" if (RIB_SYSTEM_ROUTE(re)) {\n" -" dplane_sys_route_add(rn, re);\n" -" }\n" -"\n" -" /* Link new re to node. */\n" -" SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" rib_addnode(rn, re, 1);\n" -"\n" -" /* Clean up */\n" -" ...\n" -" return ret;\n" -"}\n" -"```" +#: src\3-3-testing.md:1 +msgid "Testing" +msgstr "" + +#: src\3-4-debugging.md:1 +msgid "Debugging" +msgstr "" + +#: src\3-4-1-sai-debugging.md:1 +msgid "Debugging SAI" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:375 -msgid "`rib_addnode`会将这个路由添加请求转发给rib的处理线程,并由它顺序的进行处理:" +#: src\4-communications.md:1 +msgid "Communication" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:377 +#: src\4-communications.md:3 msgid "" -"```cpp\n" -"static void rib_addnode(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" ...\n" -" rib_link(rn, re, process);\n" -"}\n" -"\n" -"static void rib_link(struct route_node *rn, struct route_entry *re, int " -"process)\n" -"{\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" if (!dest) dest = zebra_rib_create_dest(rn);\n" -" re_list_add_head(&dest->routes, re);\n" -" ...\n" -"\n" -" if (process) rib_queue_add(rn);\n" -"}\n" -"```" +"There are three main communication mechanisms in SONiC: communication using " +"kernel, Redis-based inter-service communication, and ZMQ-based inter-service " +"communication." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:395 -msgid "请求会来到RIB的处理线程:`rib_process`,并由它来进行进一步的选路,然后将最优的路由添加到`zebra`的内部路由表(RIB)中:" +#: src\4-communications.md:5 +msgid "" +"There are two main methods for communication using kernel: command line " +"calls and Netlink messages." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:397 +#: src\4-communications.md:6 msgid "" -"```cpp\n" -"/* Core function for processing routing information base. */\n" -"static void rib_process(struct route_node *rn)\n" -"{\n" -" struct route_entry *re;\n" -" struct route_entry *next;\n" -" struct route_entry *old_selected = NULL;\n" -" struct route_entry *new_selected = NULL;\n" -" struct route_entry *old_fib = NULL;\n" -" struct route_entry *new_fib = NULL;\n" -" struct route_entry *best = NULL;\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" dest = rib_dest_from_rnode(rn);\n" -" old_fib = dest->selected_fib;\n" -" ...\n" -"\n" -" /* Check every route entry and select the best route. */\n" -" RNODE_FOREACH_RE_SAFE (rn, re, next) {\n" -" ...\n" -"\n" -" if (CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) {\n" -" best = rib_choose_best(new_fib, re);\n" -" if (new_fib && best != new_fib)\n" -" UNSET_FLAG(new_fib->status, ROUTE_ENTRY_CHANGED);\n" -" new_fib = best;\n" -" } else {\n" -" best = rib_choose_best(new_selected, re);\n" -" if (new_selected && best != new_selected)\n" -" UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED);\n" -" new_selected = best;\n" -" }\n" -"\n" -" if (best != re)\n" -" UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);\n" -" } /* RNODE_FOREACH_RE */\n" -" ...\n" -"\n" -" /* Update fib according to selection results */\n" -" if (new_fib && old_fib)\n" -" rib_process_update_fib(zvrf, rn, old_fib, new_fib);\n" -" else if (new_fib)\n" -" rib_process_add_fib(zvrf, rn, new_fib);\n" -" else if (old_fib)\n" -" rib_process_del_fib(zvrf, rn, old_fib);\n" -"\n" -" /* Remove all RE entries queued for removal */\n" -" /* Check if the dest can be deleted now. */\n" -" ...\n" -"}\n" -"```" +"Redis-based inter-service communication: There are 4 different communication " +"channel based on Redis - SubscriberStateTable, " +"NotificationProducer/Consumer, Producer/ConsumerTable, and " +"Producer/ConsumerStateTable. Although they are all based on Redis, their use " +"case can be very different." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:450 -msgid "对于新的路由,会调用`rib_process_add_fib`来将其添加到`zebra`的内部路由表中,然后通知dplane进行内核路由表的更新:" +#: src\4-communications.md:7 +msgid "" +"ZMQ-based inter-service communication: This communication mechanism is " +"currently only used in the communication between `orchagent` and `syncd`." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:452 +#: src\4-communications.md:15 msgid "" -"```cpp\n" -"static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node " -"*rn, struct route_entry *new)\n" -"{\n" -" hook_call(rib_update, rn, \"new route selected\");\n" -" ...\n" -"\n" -" /* If labeled-unicast route, install transit LSP. */\n" -" if (zebra_rib_labeled_unicast(new))\n" -" zebra_mpls_lsp_install(zvrf, rn, new);\n" -"\n" -" rib_install_kernel(rn, new, NULL);\n" -" UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);\n" -"}\n" -"\n" -"void rib_install_kernel(struct route_node *rn, struct route_entry *re,\n" -" struct route_entry *old)\n" -"{\n" -" struct rib_table_info *info = srcdest_rnode_table_info(rn);\n" -" enum zebra_dplane_result ret;\n" -" rib_dest_t *dest = rib_dest_from_rnode(rn);\n" -" ...\n" -"\n" -" /* Install the resolved nexthop object first. */\n" -" zebra_nhg_install_kernel(re->nhe);\n" -"\n" -" /* If this is a replace to a new RE let the originator of the RE know " -"that they've lost */\n" -" if (old && (old != re) && (old->type != re->type))\n" -" zsend_route_notify_owner(rn, old, ZAPI_ROUTE_BETTER_ADMIN_WON, " -"info->afi, info->safi);\n" -"\n" -" /* Update fib selection */\n" -" dest->selected_fib = re;\n" -"\n" -" /* Make sure we update the FPM any time we send new information to the " -"kernel. */\n" -" hook_call(rib_update, rn, \"installing in kernel\");\n" -"\n" -" /* Send add or update */\n" -" if (old) ret = dplane_route_update(rn, re, old);\n" -" else ret = dplane_route_add(rn, re);\n" -" ...\n" -"}\n" -"```" +"The implementation of all these basic communication mechanisms is in the " +"`common` directory of the " +"[sonic-swss-common](https://github.com/sonic-net/sonic-swss-common) repo. " +"Additionally, to facilitate the use of various services, SONiC has build a " +"wrapper layer called Orch in " +"[sonic-swss](https://github.com/sonic-net/sonic-swss), which helps simplify " +"the upper-layer services." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:494 +#: src\4-communications.md:17 msgid "" -"这里有两个重要的操作,一个自然是调用`dplane_route_*`函数来进行内核的路由表更新,另一个则是出现了两次的`hook_call`,fpm的钩子函数就是挂在这个地方,用来接收并转发路由表的更新通知。这里我们一个一个来看:" +"In this chapter, we will dive into the implementation of these communication " +"mechanisms!" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:496 -msgid "#### dplane更新内核路由表" +#: src\4-communications.md:22 src\4-2-1-redis-wrappers.md:35 +#: src\4-2-2-subscribe-state-table.md:74 +#: src\4-2-3-notification-producer-consumer.md:55 +#: src\4-2-4-producer-consumer-table.md:131 +#: src\4-2-5-producer-consumer-state-table.md:126 src\4-4-orch-layer.md:37 +#: src\4-5-event-polling-and-error-handling.md:124 src\5-2-bgp.md:35 +#: src\5-2-2-route-update-in-frr.md:738 src\5-2-3-route-update-in-sonic.md:737 +msgid "[Github repo: sonic-swss](https://github.com/sonic-net/sonic-swss)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:498 +#: src\4-communications.md:23 src\4-1-1-exec.md:38 src\4-1-2-netlink.md:78 +#: src\4-2-1-redis-wrappers.md:36 src\4-2-2-subscribe-state-table.md:75 +#: src\4-2-3-notification-producer-consumer.md:56 +#: src\4-2-4-producer-consumer-table.md:132 +#: src\4-2-5-producer-consumer-state-table.md:127 src\4-4-orch-layer.md:38 +#: src\4-5-event-polling-and-error-handling.md:125 +#: src\5-2-2-route-update-in-frr.md:739 src\5-2-3-route-update-in-sonic.md:738 msgid "" -"首先是dplane的`dplane_route_*`函数,它们的做的事情都一样:把请求打包,然后放入`dplane_thread`的消息队列中,并不会做任何实质的操作:" +"[Github repo: " +"sonic-swss-common](https://github.com/sonic-net/sonic-swss-common)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:500 +#: src\4-1-1-exec.md:3 msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"enum zebra_dplane_result dplane_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_ROUTE_INSTALL);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_route_update(struct route_node *rn, struct " -"route_entry *re, struct route_entry *old_re) {\n" -" return dplane_route_update_internal(rn, re, old_re, " -"DPLANE_OP_ROUTE_UPDATE);\n" -"}\n" -"\n" -"enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, struct " -"route_entry *re) {\n" -" return dplane_route_update_internal(rn, re, NULL, " -"DPLANE_OP_SYS_ROUTE_ADD);\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"dplane_route_update_internal(struct route_node *rn, struct route_entry *re, " -"struct route_entry *old_re, enum dplane_op_e op)\n" -"{\n" -" enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;\n" -" int ret = EINVAL;\n" -"\n" -" /* Create and init context */\n" -" struct zebra_dplane_ctx *ctx = ...;\n" -"\n" -" /* Enqueue context for processing */\n" -" ret = dplane_route_enqueue(ctx);\n" -"\n" -" /* Update counter */\n" -" atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, " -"memory_order_relaxed);\n" -"\n" -" if (ret == AOK)\n" -" result = ZEBRA_DPLANE_REQUEST_QUEUED;\n" -"\n" -" return result;\n" -"}\n" -"```" +"The simplest way SONiC communicates with the kernel is through command-line " +"calls, which are implemented in " +"[common/exec.h](https://github.com/sonic-net/sonic-swss-common/blob/master/common/exec.h). " +"The interface is straight-forward:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:536 -msgid "然后,我们就来到了数据面处理线程`dplane_thread`,其消息循环很简单,就是从队列中一个个取出消息,然后通过调用其处理函数:" +#: src\4-1-1-exec.md:6 +msgid "// File: common/exec.h" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:538 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/zebra_dplane.c\n" -"static int dplane_thread_loop(struct thread *event)\n" -"{\n" -" ...\n" -"\n" -" while (prov) {\n" -" ...\n" -"\n" -" /* Process work here */\n" -" (*prov->dp_fp)(prov);\n" -"\n" -" /* Check for zebra shutdown */\n" -" /* Dequeue completed work from the provider */\n" -" ...\n" -"\n" -" /* Locate next provider */\n" -" DPLANE_LOCK();\n" -" prov = TAILQ_NEXT(prov, dp_prov_link);\n" -" DPLANE_UNLOCK();\n" -" }\n" -"}\n" -"```" +#: src\4-1-1-exec.md:6 +msgid "// Namespace: swss" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:562 +#: src\4-1-1-exec.md:11 msgid "" -"默认情况下,`dplane_thread`会使用`kernel_dplane_process_func`来进行消息的处理,内部会根据请求的类型对内核的操作进行分发:" +"Here, `cmd` is the command to execute, and `stdout` captures the command " +"output. The `exec` function is a synchronous call that blocks until the " +"command finishes. Internally, it creates a child process via `popen` and " +"retrieves output via `fgets`. However, **although this function returns " +"output, it is rarely used in practice**. Most code only checks the return " +"value for success, and sometimes even error logs won't be logged in the " +"output." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:564 +#: src\4-1-1-exec.md:13 msgid "" -"```c\n" -"static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" struct zebra_dplane_ctx *ctx;\n" -" int counter, limit;\n" -" limit = dplane_provider_get_work_limit(prov);\n" -"\n" -" for (counter = 0; counter < limit; counter++) {\n" -" ctx = dplane_provider_dequeue_in_ctx(prov);\n" -" if (ctx == NULL) break;\n" -"\n" -" /* A previous provider plugin may have asked to skip the kernel " -"update. */\n" -" if (dplane_ctx_is_skip_kernel(ctx)) {\n" -" res = ZEBRA_DPLANE_REQUEST_SUCCESS;\n" -" goto skip_one;\n" -" }\n" -"\n" -" /* Dispatch to appropriate kernel-facing apis */\n" -" switch (dplane_ctx_get_op(ctx)) {\n" -" case DPLANE_OP_ROUTE_INSTALL:\n" -" case DPLANE_OP_ROUTE_UPDATE:\n" -" case DPLANE_OP_ROUTE_DELETE:\n" -" res = kernel_dplane_route_update(ctx);\n" -" break;\n" -" ...\n" -" }\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"\n" -"static enum zebra_dplane_result\n" -"kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" enum zebra_dplane_result res;\n" -" /* Call into the synchronous kernel-facing code here */\n" -" res = kernel_route_update(ctx);\n" -" return res;\n" -"}\n" -"```" +"Despite its simplicity, this function is widely used, especially in various " +"`*mgrd` services. For instance, `portmgrd` calls it to set each port's " +"status:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:606 -msgid "而`kernel_route_update`则是真正的内核操作了,它会通过netlink来通知内核路由更新:" +#: src\4-1-1-exec.md:16 +msgid "// File: sonic-swss - cfgmgr/portmgr.cpp" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:608 -msgid "" -"```c\n" -"// File: src/sonic-frr/frr/zebra/rt_netlink.c\n" -"// Update or delete a prefix from the kernel, using info from a dataplane " -"context.\n" -"enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)\n" -"{\n" -" int cmd, ret;\n" -" const struct prefix *p = dplane_ctx_get_dest(ctx);\n" -" struct nexthop *nexthop;\n" -"\n" -" if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {\n" -" cmd = RTM_DELROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) {\n" -" cmd = RTM_NEWROUTE;\n" -" } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) {\n" -" cmd = RTM_NEWROUTE;\n" -" }\n" -"\n" -" if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))\n" -" ret = netlink_route_multipath(cmd, ctx);\n" -" ...\n" -"\n" -" return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : " -"ZEBRA_DPLANE_REQUEST_FAILURE);\n" -"}\n" -"\n" -"// Routing table change via netlink interface, using a dataplane context " -"object\n" -"static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)\n" -"{\n" -" // Build netlink request.\n" -" struct {\n" -" struct nlmsghdr n;\n" -" struct rtmsg r;\n" -" char buf[NL_PKT_BUF_SIZE];\n" -" } req;\n" -"\n" -" req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\n" -"    req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;\n" -"    ...\n" -"\n" -" /* Talk to netlink socket. */\n" -" return netlink_talk_info(netlink_talk_filter, &req.n, " -"dplane_ctx_get_ns(ctx), 0);\n" -"}\n" -"```" +#: src\4-1-1-exec.md:22 +msgid "// ip link set dev [up|down]" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:651 -msgid "#### FPM路由更新转发" +#: src\4-1-1-exec.md:23 +msgid "\" link set dev \"" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:653 -msgid "" -"FPM(Forwarding Plane " -"Manager)是FRR中用于通知其他进程路由变更的协议,其主要逻辑代码在`src/sonic-frr/frr/zebra/zebra_fpm.c`中。它默认有两套协议实现:protobuf和netlink,SONiC就是使用的是netlink协议。" +#: src\4-1-1-exec.md:23 +msgid "\" up\"" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:655 -msgid "" -"上面我们已经提到,它通过钩子函数实现,监听RIB中的路由变化,并通过本地Socket转发给其他的进程。这个钩子会在启动的时候就注册好,其中和我们现在看的最相关的就是`rib_update`钩子了,如下所示:" +#: src\4-1-1-exec.md:23 +msgid "\" down\"" +msgstr "" + +#: src\4-1-1-exec.md:27 src\4-1-2-netlink.md:27 src\4-1-2-netlink.md:36 +#: src\4-1-2-netlink.md:43 src\4-1-2-netlink.md:59 src\4-1-2-netlink.md:65 +#: src\4-1-2-netlink.md:72 src\4-2-2-subscribe-state-table.md:28 +#: src\4-2-2-subscribe-state-table.md:42 src\4-2-2-subscribe-state-table.md:51 +#: src\4-2-2-subscribe-state-table.md:58 +#: src\4-2-3-notification-producer-consumer.md:28 +#: src\4-2-3-notification-producer-consumer.md:40 +#: src\4-2-3-notification-producer-consumer.md:44 +#: src\4-2-4-producer-consumer-table.md:42 +#: src\4-2-4-producer-consumer-table.md:90 +#: src\4-2-4-producer-consumer-table.md:108 +#: src\4-2-5-producer-consumer-state-table.md:32 +#: src\4-2-5-producer-consumer-state-table.md:60 +#: src\4-2-5-producer-consumer-state-table.md:63 +#: src\4-5-event-polling-and-error-handling.md:27 +#: src\4-5-event-polling-and-error-handling.md:29 +#: src\4-5-event-polling-and-error-handling.md:62 +#: src\4-5-event-polling-and-error-handling.md:83 +msgid "// ..." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:657 +#: src\4-1-2-netlink.md:3 msgid "" -"```c\n" -"static int zebra_fpm_module_init(void)\n" -"{\n" -" hook_register(rib_update, zfpm_trigger_update);\n" -" hook_register(zebra_rmac_update, zfpm_trigger_rmac_update);\n" -" hook_register(frr_late_init, zfpm_init);\n" -" hook_register(frr_early_fini, zfpm_fini);\n" -" return 0;\n" -"}\n" -"\n" -"FRR_MODULE_SETUP(.name = \"zebra_fpm\", .version = FRR_VERSION,\n" -" .description = \"zebra FPM (Forwarding Plane Manager) module\",\n" -" .init = zebra_fpm_module_init,\n" -");\n" -"```" +"Netlinkis the message-based communication mechanism provided by Linux kernel " +"and used between the kernel and user-space processes. It is implemented via " +"socket and custom protocol families. It can be used to deliver various types " +"of kernel messages, including network device status, routing table updates, " +"firewall rule changes, and system resource usage. SONiC's `*sync` services " +"heavily utilize Netlink to monitor changes of network devices in the system, " +"synchronize the latest status to Redis, and notify other services to make " +"corresponding updates." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:673 +#: src\4-1-2-netlink.md:5 msgid "" -"当`rib_update`钩子被调用时,`zfpm_trigger_update`函数会被调用,它会将路由变更信息再次放入fpm的转发队列中,并触发写操作:" +"The main implementation of netlink communication channel is done by these " +"files:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:675 +#: src\4-1-2-netlink.md:7 msgid "" -"```c\n" -"static int zfpm_trigger_update(struct route_node *rn, const char *reason)\n" -"{\n" -" rib_dest_t *dest;\n" -" ...\n" -"\n" -" // Queue the update request\n" -" dest = rib_dest_from_rnode(rn);\n" -" SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);\n" -" TAILQ_INSERT_TAIL(&zfpm_g->dest_q, dest, fpm_q_entries);\n" -" ...\n" -"\n" -" zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static inline void zfpm_write_on(void) {\n" -" thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, " -"&zfpm_g->t_write);\n" -"}\n" -"```" +"[common/netmsg.\\*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netmsg.h)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:696 -msgid "这个写操作的回调就会将其从队列中取出,并转换成FPM的消息格式,然后通过本地Socket转发给其他进程:" +#: src\4-1-2-netlink.md:8 +msgid "" +"[common/netlink.\\*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netlink.h)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:698 +#: src\4-1-2-netlink.md:9 msgid "" -"```c\n" -"static int zfpm_write_cb(struct thread *thread)\n" -"{\n" -" struct stream *s;\n" -"\n" -" do {\n" -" int bytes_to_write, bytes_written;\n" -" s = zfpm_g->obuf;\n" -"\n" -" // Convert route info to buffer here.\n" -" if (stream_empty(s)) zfpm_build_updates();\n" -"\n" -" // Write to socket until we don' have anything to write or cannot " -"write anymore (partial write).\n" -" bytes_to_write = stream_get_endp(s) - stream_get_getp(s);\n" -" bytes_written = write(zfpm_g->sock, stream_pnt(s), bytes_to_write);\n" -" ...\n" -" } while (1);\n" -"\n" -" if (zfpm_writes_pending()) zfpm_write_on();\n" -" return 0;\n" -"}\n" -"\n" -"static void zfpm_build_updates(void)\n" -"{\n" -" struct stream *s = zfpm_g->obuf;\n" -" do {\n" -" /* Stop processing the queues if zfpm_g->obuf is full or we do not " -"have more updates to process */\n" -" if (zfpm_build_mac_updates() == FPM_WRITE_STOP) break;\n" -" if (zfpm_build_route_updates() == FPM_WRITE_STOP) break;\n" -" } while (zfpm_updates_pending());\n" -"}\n" -"```" +"[common/netdispatcher.\\*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netdispatcher.h)." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:731 -msgid "到此,FRR的工作就完成了。" +#: src\4-1-2-netlink.md:11 +msgid "The class diagram is as follows:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:733 -msgid "## SONiC路由变更工作流" +#: src\4-1-2-netlink.md:13 +msgid "![](assets/chapter-4/netlink.png)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:735 -msgid "当FRR变更内核路由配置后,SONiC便会收到来自Netlink和FPM的通知,然后进行一系列操作将其下发给ASIC,其主要流程如下:" +#: src\4-1-2-netlink.md:15 +msgid "In this diagram:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:737 +#: src\4-1-2-netlink.md:17 msgid "" -"```mermaid\n" -"sequenceDiagram\n" -" autonumber\n" -" participant K as Linux Kernel\n" -" box purple bgp容器\n" -" participant Z as zebra\n" -" participant FPM as fpmsyncd\n" -" end\n" -" box darkred database容器\n" -" participant R as Redis\n" -" end\n" -" box darkblue swss容器\n" -" participant OA as orchagent\n" -" end\n" -" box darkgreen syncd容器\n" -" participant SD as syncd\n" -" end\n" -" participant A as ASIC\n" -"\n" -" K->>FPM: 内核路由变更时通过Netlink发送通知\n" -" Z->>FPM: 通过FPM接口和Netlink
消息格式发送路由变更通知\n" -"\n" -" FPM->>R: 通过ProducerStateTable
将路由变更信息写入
APPL_DB\n" -"\n" -" R->>OA: 通过ConsumerStateTable
接收路由变更信息\n" -" \n" -" OA->>OA: 处理路由变更信息
生成SAI路由对象\n" -" OA->>SD: 通过ProducerTable
或者ZMQ将SAI路由对象
发给syncd\n" -"\n" -" SD->>R: 接收SAI路由对象,写入ASIC_DB\n" -" SD->>A: 通过SAI接口
配置ASIC\n" -"```" +"**Netlink**: Wraps the netlink socket interface and provides an interface " +"for sending netlink messages and a callback for receiving messages." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:770 -msgid "### fpmsyncd更新Redis中的路由配置" +#: src\4-1-2-netlink.md:18 +msgid "" +"**NetDispatcher**: A singleton that provides an interface for registering " +"handlers. When a raw netlink message is received, it calls NetDispatcher to " +"parse them into `nl_object` objects and then dispatches them to the " +"corresponding handler based on the message type." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:772 -msgid "首先,我们从源头看起。`fpmsyncd`在启动的时候便会开始监听FPM和Netlink的事件,用于接收路由变更消息:" +#: src\4-1-2-netlink.md:19 +msgid "" +"**NetMsg**: The base class for netlink message handlers, which only provides " +"the `onMsg` interface without a default implementation." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:774 +#: src\4-1-2-netlink.md:21 msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp\n" -"int main(int argc, char **argv)\n" -"{\n" -" ...\n" -"\n" -" DBConnector db(\"APPL_DB\", 0);\n" -" RedisPipeline pipeline(&db);\n" -" RouteSync sync(&pipeline);\n" -" \n" -" // Register netlink message handler\n" -" NetLink netlink;\n" -" netlink.registerGroup(RTNLGRP_LINK);\n" -"\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, " -"&sync);\n" -" NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, " -"&sync);\n" -"\n" -" rtnl_route_read_protocol_names(DefaultRtProtoPath);\n" -" ...\n" -"\n" -" while (true) {\n" -" try {\n" -" // Launching FPM server and wait for zebra to connect.\n" -" FpmLink fpm(&sync);\n" -" ...\n" -"\n" -" fpm.accept();\n" -" ...\n" -" } catch (FpmLink::FpmConnectionClosedException &e) {\n" -" // If connection is closed, keep retrying until it succeeds, " -"before handling any other events.\n" -" cout << \"Connection lost, reconnecting...\" << endl;\n" -" }\n" -" ...\n" -" }\n" -"}\n" -"```" +"For example, when `portsyncd` starts, it creates a `Netlink` object to " +"listen for link-related status changes and implements the `NetMsg` interface " +"to handle the link messages. The specific implementation is as follows:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:813 -msgid "" -"这样,所有的路由变更消息都会以Netlink的形式发送给`RouteSync`,其中[EVPN Type " -"5][EVPN]必须以原始消息的形式进行处理,所以会发送给`onMsgRaw`,其他的消息都会统一的发给处理Netlink的`onMsg`回调:(关于Netlink如何接收和处理消息,请移步[4.1.2 " -"Netlink](./4-1-2-netlink.html))" +#: src\4-1-2-netlink.md:24 +msgid "// File: sonic-swss - portsyncd/portsyncd.cpp" +msgstr "" + +#: src\4-1-2-netlink.md:29 +msgid "// Create Netlink object to listen to link messages" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:815 +#: src\4-1-2-netlink.md:33 msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/fpmlink.cpp\n" -"// Called from: FpmLink::readData()\n" -"void FpmLink::processFpmMessage(fpm_msg_hdr_t* hdr)\n" -"{\n" -" size_t msg_len = fpm_msg_len(hdr);\n" -" nlmsghdr *nl_hdr = (nlmsghdr *)fpm_msg_data(hdr);\n" -" ...\n" -"\n" -" /* Read all netlink messages inside FPM message */\n" -" for (; NLMSG_OK (nl_hdr, msg_len); nl_hdr = NLMSG_NEXT(nl_hdr, " -"msg_len))\n" -" {\n" -" /*\n" -" * EVPN Type5 Add Routes need to be process in Raw mode as they " -"contain\n" -" * RMAC, VLAN and L3VNI information.\n" -" * Where as all other route will be using rtnl api to extract " -"information\n" -" * from the netlink msg.\n" -" */\n" -" bool isRaw = isRawProcessing(nl_hdr);\n" -" \n" -" nl_msg *msg = nlmsg_convert(nl_hdr);\n" -" ...\n" -" nlmsg_set_proto(msg, NETLINK_ROUTE);\n" -"\n" -" if (isRaw) {\n" -" /* EVPN Type5 Add route processing */\n" -" /* This will call into onRawMsg() */\n" -" processRawMsg(nl_hdr);\n" -" } else {\n" -" /* This will call into onMsg() */\n" -" NetDispatcher::getInstance().onNetlinkMessage(msg);\n" -" }\n" -"\n" -" nlmsg_free(msg);\n" -" }\n" -"}\n" -"\n" -"void FpmLink::processRawMsg(struct nlmsghdr *h)\n" -"{\n" -" m_routesync->onMsgRaw(h);\n" -"};\n" -"```" +"// Here SONiC requests a full dump of the current state to get the status of " +"all links" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:858 -msgid "接着,`RouteSync`收到路由变更的消息之后,会在`onMsg`和`onMsgRaw`中进行判断和分发:" +#: src\4-1-2-netlink.md:35 +msgid "\"Listen to link messages...\"" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:860 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onMsgRaw(struct nlmsghdr *h)\n" -"{\n" -" if ((h->nlmsg_type != RTM_NEWROUTE) && (h->nlmsg_type != RTM_DELROUTE))\n" -" return;\n" -" ...\n" -" onEvpnRouteMsg(h, len);\n" -"}\n" -"\n" -"void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj)\n" -"{\n" -" // Refill Netlink cache here\n" -" ...\n" -"\n" -" struct rtnl_route *route_obj = (struct rtnl_route *)obj;\n" -" auto family = rtnl_route_get_family(route_obj);\n" -" if (family == AF_MPLS) {\n" -" onLabelRouteMsg(nlmsg_type, obj);\n" -" return;\n" -" }\n" -" ...\n" -"\n" -" unsigned int master_index = rtnl_route_get_table(route_obj);\n" -" char master_name[IFNAMSIZ] = {0};\n" -" if (master_index) {\n" -" /* If the master device name starts with VNET_PREFIX, it is a VNET " -"route.\n" -" The VNET name is exactly the name of the associated master device. " -"*/\n" -" getIfName(master_index, master_name, IFNAMSIZ);\n" -" if (string(master_name).find(VNET_PREFIX) == 0) {\n" -" onVnetRouteMsg(nlmsg_type, obj, string(master_name));\n" -" }\n" -"\n" -" /* Otherwise, it is a regular route (include VRF route). */\n" -" else {\n" -" onRouteMsg(nlmsg_type, obj, master_name);\n" -" }\n" -" } else {\n" -" onRouteMsg(nlmsg_type, obj, NULL);\n" -" }\n" -"}\n" -"```" +#: src\4-1-2-netlink.md:38 +msgid "// Register handler for link messages" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:903 +#: src\4-1-2-netlink.md:47 msgid "" -"从上面的代码中,我们可以看到这里会有四种不同的路由处理入口,这些不同的路由会被最终通过各自的[ProducerStateTable](./4-2-2-redis-messaging-layer.html#producerstatetable--consumerstatetable)写入到`APPL_DB`中的不同的Table中:" +"The `LinkSync` class above is an implementation of `NetMsg`, providing the " +"`onMsg` interface for handling link messages:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:905 -msgid "" -"| 路由类型 | 处理函数 | Table |\n" -"| --- | --- | --- |\n" -"| MPLS | `onLabelRouteMsg` | LABLE_ROUTE_TABLE |\n" -"| Vnet VxLan Tunnel Route | `onVnetRouteMsg` | VNET_ROUTE_TUNNEL_TABLE |\n" -"| 其他Vnet路由 | `onVnetRouteMsg` | VNET_ROUTE_TABLE |\n" -"| EVPN Type 5 | `onEvpnRouteMsg` | ROUTE_TABLE |\n" -"| 普通路由 | `onRouteMsg` | ROUTE_TABLE |" +#: src\4-1-2-netlink.md:50 +msgid "// File: sonic-swss - portsyncd/linksync.h" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:913 -msgid "这里以普通路由来举例子,其他的函数的实现虽然有所不同,但是主体的思路是一样的:" +#: src\4-1-2-netlink.md:56 +msgid "// NetMsg interface" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:915 -msgid "" -"```cpp\n" -"// File: src/sonic-swss/fpmsyncd/routesync.cpp\n" -"void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char " -"*vrf)\n" -"{\n" -" // Parse route info from nl_object here.\n" -" ...\n" -" \n" -" // Get nexthop lists\n" -" string gw_list;\n" -" string intf_list;\n" -" string mpls_list;\n" -" getNextHopList(route_obj, gw_list, mpls_list, intf_list);\n" -" ...\n" -"\n" -" // Build route info here, including protocol, interface, next hops, " -"MPLS, weights etc.\n" -" vector fvVector;\n" -" FieldValueTuple proto(\"protocol\", proto_str);\n" -" FieldValueTuple gw(\"nexthop\", gw_list);\n" -" ...\n" -"\n" -" fvVector.push_back(proto);\n" -" fvVector.push_back(gw);\n" -" ...\n" -" \n" -" // Push to ROUTE_TABLE via ProducerStateTable.\n" -" m_routeTable.set(destipprefix, fvVector);\n" -" SWSS_LOG_DEBUG(\"RouteTable set msg: %s %s %s %s\", destipprefix, " -"gw_list.c_str(), intf_list.c_str(), mpls_list.c_str());\n" -" ...\n" -"}\n" -"```" +#: src\4-1-2-netlink.md:61 +msgid "// File: sonic-swss - portsyncd/linksync.cpp" +msgstr "" + +#: src\4-1-2-netlink.md:67 +msgid "// Write link state to Redis DB" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:946 -msgid "### orchagent处理路由配置变化" +#: src\4-1-2-netlink.md:68 +msgid "\"down\"" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:948 +#: src\4-2-redis-based-channels.md:3 msgid "" -"接下来,这些路由信息会来到orchagent。在orchagent启动的时候,它会创建好`VNetRouteOrch`和`RouteOrch`对象,这两个对象分别用来监听和处理Vnet相关路由和EVPN/普通路由:" +"To facilitate communication between services, SONiC provides a messaging " +"layer that is built on top of the Redis. On high-level, it contains 2 layers:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:950 +#: src\4-2-redis-based-channels.md:5 msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"bool OrchDaemon::init()\n" -"{\n" -" ...\n" -"\n" -" vector vnet_tables = { APP_VNET_RT_TABLE_NAME, " -"APP_VNET_RT_TUNNEL_TABLE_NAME };\n" -" VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, " -"vnet_orch);\n" -" ...\n" -"\n" -" const int routeorch_pri = 5;\n" -" vector route_tables = {\n" -" { APP_ROUTE_TABLE_NAME, routeorch_pri },\n" -" { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri }\n" -" };\n" -" gRouteOrch = new RouteOrch(m_applDb, route_tables, gSwitchOrch, " -"gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch, gSrv6Orch);\n" -" ...\n" -"}\n" -"```" +"First layer wraps frequenctly used redis operations and provide table " +"abstraction on top of it." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:970 +#: src\4-2-redis-based-channels.md:6 msgid "" -"所有Orch对象的消息处理入口都是`doTask`,这里`RouteOrch`和`VNetRouteOrch`也不例外,这里我们以`RouteOrch`为例子,看看它是如何处理路由变化的。" +"Second layer provides different channels for inter-service communication to " +"satisfy various communication channel requirements." +msgstr "" + +#: src\4-2-redis-based-channels.md:8 +msgid "Now, let's dive into them one by one." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:972 +#: src\4-2-1-redis-wrappers.md:3 +msgid "Redis Database Operation Layer" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:5 msgid "" -"```admonish note\n" -"从`RouteOrch`上,我们可以真切的感受到为什么这些类被命名为`Orch`。`RouteOrch`有2500多行,其中会有和很多其他Orch的交互,以及各种各样的细节…… " -"代码是相对难读,请大家读的时候一定保持耐心。\n" -"```" +"The first layer, which is also the lowest layer, is the Redis database " +"operation layer. It wraps various basic commands, such as DB connection, " +"command execution, event notification callback interfaces, etc. The specific " +"class diagram is as follows:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:976 -msgid "`RouteOrch`在处理路由消息的时候有几点需要注意:" +#: src\4-2-1-redis-wrappers.md:7 +msgid "![](assets/chapter-4/redis-ops.png)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:978 +#: src\4-2-1-redis-wrappers.md:9 +msgid "Among them:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:11 msgid "" -"- " -"从上面`init`函数,我们可以看到`RouteOrch`不仅会管理普通路由,还会管理MPLS路由,这两种路由的处理逻辑是不一样的,所以在下面的代码中,为了简化,我们只展示普通路由的处理逻辑。\n" -"- " -"因为`ProducerStateTable`在传递和接受消息的时候都是批量传输的,所以,`RouteOrch`在处理消息的时候,也是批量处理的。为了支持批量处理,`RouteOrch`会借用`EntityBulker " -"gRouteBulker`将需要改动的SAI路由对象缓存起来,然后在`doTask()`函数的最后,一次性将这些路由对象的改动应用到SAI中。\n" -"- " -"路由的操作会需要很多其他的信息,比如每个Port的状态,每个Neighbor的状态,每个VRF的状态等等。为了获取这些信息,`RouteOrch`会与其他的Orch对象进行交互,比如`PortOrch`,`NeighOrch`,`VRFOrch`等等。" +"**[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: " +"Wraps and maintains the connection to Redis, and closes the connection when " +"it is destroyed." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:982 +#: src\4-2-1-redis-wrappers.md:12 msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"void RouteOrch::doTask(Consumer& consumer)\n" -"{\n" -" // Calling PortOrch to make sure all ports are ready before processing " -"route messages.\n" -" if (!gPortsOrch->allPortsReady()) { return; }\n" -"\n" -" // Call doLabelTask() instead, if the incoming messages are from MPLS " -"messages. Otherwise, move on as regular routes.\n" -" ...\n" -"\n" -" /* Default handling is for ROUTE_TABLE (regular routes) */\n" -" auto it = consumer.m_toSync.begin();\n" -" while (it != consumer.m_toSync.end()) {\n" -" // Add or remove routes with a route bulker\n" -" while (it != consumer.m_toSync.end())\n" -" {\n" -" KeyOpFieldsValuesTuple t = it->second;\n" -"\n" -" // Parse route operation from the incoming message here.\n" -" string key = kfvKey(t);\n" -" string op = kfvOp(t);\n" -" ...\n" -"\n" -" // resync application:\n" -" // - When routeorch receives 'resync' message (key = \"resync\", " -"op = \"SET\"), it marks all current routes as dirty\n" -" // and waits for 'resync complete' message. For all newly " -"received routes, if they match current dirty routes,\n" -" // it unmarks them dirty.\n" -" // - After receiving 'resync complete' (key = \"resync\", op != " -"\"SET\") message, it creates all newly added routes\n" -" // and removes all dirty routes.\n" -" ...\n" -"\n" -" // Parsing VRF and IP prefix from the incoming message here.\n" -" ...\n" -"\n" -" // Process regular route operations.\n" -" if (op == SET_COMMAND)\n" -" {\n" -" // Parse and validate route attributes from the incoming " -"message here.\n" -" string ips;\n" -" string aliases;\n" -" ...\n" -"\n" -" // If the nexthop_group is empty, create the next hop group " -"key based on the IPs and aliases. \n" -" // Otherwise, get the key from the NhgOrch. The result will " -"be stored in the \"nhg\" variable below.\n" -" NextHopGroupKey& nhg = ctx.nhg;\n" -" ...\n" -" if (nhg_index.empty())\n" -" {\n" -" // Here the nexthop_group is empty, so we create the " -"next hop group key based on the IPs and aliases.\n" -" ...\n" -"\n" -" string nhg_str = \"\";\n" -" if (blackhole) {\n" -" nhg = NextHopGroupKey();\n" -" } else if (srv6_nh == true) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" } else if (overlay_nh == false) {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, weights);\n" -" } else {\n" -" ...\n" -" nhg = NextHopGroupKey(nhg_str, overlay_nh, " -"srv6_nh);\n" -" }\n" -" }\n" -" else\n" -" {\n" -" // Here we have a nexthop_group, so we get the key from " -"the NhgOrch.\n" -" const NhgBase& nh_group = getNhg(nhg_index);\n" -" nhg = nh_group.getNhgKey();\n" -" ...\n" -" }\n" -" ...\n" -"\n" -" // Now we start to create the SAI route entry.\n" -" if (nhg.getSize() == 1 && nhg.hasIntfNextHop())\n" -" {\n" -" // Skip certain routes, such as not valid, directly " -"routes to tun0, linklocal or multicast routes, etc.\n" -" ...\n" -"\n" -" // Create SAI route entry in addRoute function.\n" -" if (addRoute(ctx, nhg)) it = " -"consumer.m_toSync.erase(it);\n" -" else it++;\n" -" }\n" -"\n" -" /*\n" -" * Check if the route does not exist or needs to be updated " -"or\n" -" * if the route is using a temporary next hop group owned " -"by\n" -" * NhgOrch.\n" -" */\n" -" else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() " -"||\n" -" m_syncdRoutes.at(vrf_id).find(ip_prefix) == " -"m_syncdRoutes.at(vrf_id).end() ||\n" -" m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, " -"ctx.nhg_index) ||\n" -" gRouteBulker.bulk_entry_pending_removal(route_entry) ||\n" -" ctx.using_temp_nhg)\n" -" {\n" -" if (addRoute(ctx, nhg)) it = " -"consumer.m_toSync.erase(it);\n" -" else it++;\n" -" }\n" -" ...\n" -" }\n" -" // Handle other ops, like DEL_COMMAND for route deletion, etc.\n" -" ...\n" -" }\n" -"\n" -" // Flush the route bulker, so routes will be written to syncd and " -"ASIC\n" -" gRouteBulker.flush();\n" -"\n" -" // Go through the bulker results.\n" -" // Handle SAI failures, update neighbors, counters, send " -"notifications in add/removeRoutePost functions.\n" -" ... \n" -"\n" -" /* Remove next hop group if the reference count decreases to zero " -"*/\n" -" ...\n" -" }\n" -"}\n" -"```" +"**[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: " +"Wraps all the underlying Redis commands used, such as `SET`, `GET`, `DEL`, " +"etc." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1100 +#: src\4-2-1-redis-wrappers.md:13 msgid "" -"解析完路由操作后,`RouteOrch`会调用`addRoute`或者`removeRoute`函数来创建或者删除路由。这里以添加路由`addRoute`为例子来继续分析。它的逻辑主要分为几个大部分:" +"**[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redistran.h)**: " +"Wraps Redis transaction operations, used to execute multiple commands in a " +"transaction, such as `MULTI`, `EXEC`, etc." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1102 +#: src\4-2-1-redis-wrappers.md:14 msgid "" -"1. 从NeighOrch中获取下一跳信息,并检查下一跳是否真的可用。\n" -"2. 如果是新路由,或者是重新添加正在等待删除的路由,那么就会创建一个新的SAI路由对象\n" -"3. 如果是已有的路由,那么就更新已有的SAI路由对象" +"**[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redispipeline.h)**: " +"Wraps the hiredis redisAppendFormattedCommand API, providing an asynchronous " +"interface for executing Redis commands similar to a queue (although most " +"usage methods are still synchronous). It is also one of the few classes that " +"wraps the `SCRIPT LOAD` command, used to load Lua scripts in Redis to " +"implement stored procedures. Most classes in SONiC that need to execute Lua " +"scripts will use this class for loading and calling." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1106 +#: src\4-2-1-redis-wrappers.md:15 msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/routeorch.cpp\n" -"bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey " -"&nextHops)\n" -"{\n" -" // Get nexthop information from NeighOrch.\n" -" // We also need to check PortOrch for inband port, IntfsOrch to ensure " -"the related interface is created and etc.\n" -" ...\n" -" \n" -" // Start to sync the SAI route entry.\n" -" sai_route_entry_t route_entry;\n" -" route_entry.vr_id = vrf_id;\n" -" route_entry.switch_id = gSwitchId;\n" -" copy(route_entry.destination, ipPrefix);\n" -"\n" -" sai_attribute_t route_attr;\n" -" auto& object_statuses = ctx.object_statuses;\n" -" \n" -" // Create a new route entry in this case.\n" -" //\n" -" // In case the entry is already pending removal in the bulk, it would be " -"removed from m_syncdRoutes during the bulk call.\n" -" // Therefore, such entries need to be re-created rather than set " -"attribute.\n" -" if (it_route == m_syncdRoutes.at(vrf_id).end() || " -"gRouteBulker.bulk_entry_pending_removal(route_entry)) {\n" -" if (blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_DROP;\n" -" } else {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID;\n" -" route_attr.value.oid = next_hop_id;\n" -" }\n" -"\n" -" /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD " -"*/\n" -" object_statuses.emplace_back();\n" -" sai_status_t status = " -"gRouteBulker.create_entry(&object_statuses.back(), &route_entry, 1, " -"&route_attr);\n" -" if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) {\n" -" return false;\n" -" }\n" -" }\n" -" \n" -" // Update existing route entry in this case.\n" -" else {\n" -" // Set the packet action to forward when there was no next hop " -"(dropped) and not pointing to blackhole.\n" -" if (it_route->second.nhg_key.getSize() == 0 && !blackhole) {\n" -" route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;\n" -" route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD;\n" -"\n" -" object_statuses.emplace_back();\n" -" gRouteBulker.set_entry_attribute(&object_statuses.back(), " -"&route_entry, &route_attr);\n" -" }\n" -"\n" -" // Only 1 case is listed here as an example. Other cases are handled " -"with similar logic by calling set_entry_attributes as well.\n" -" ...\n" -" }\n" -" ...\n" -"}\n" -"```" +"**[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redisselect.h)**: " +"Implements the Selectable interface to support the epoll-based event " +"notification mechanism (Event Polling). Mainly used to trigger epoll " +"callbacks when we receive a reply from Redis (we will introduce this in more " +"detail later)." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1162 +#: src\4-2-1-redis-wrappers.md:16 msgid "" -"在创建和设置好所有的路由后,`RouteOrch`会调用`gRouteBulker.flush()`来将所有的路由写入到ASIC_DB中。`flush()`函数很简单,就是将所有的请求分批次进行处理,默认情况下每一批是1000个,这个定义在`OrchDaemon`中,并通过构造函数传入:" +"**[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: " +"This class is a \"static class\" that mainly implements the reading and " +"parsing of the SONiC DB configuration file. Other database operation classes " +"will use this class to obtain any configuration information if needed." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:18 +msgid "Table Abstraction Layer" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1164 +#: src\4-2-1-redis-wrappers.md:20 msgid "" -"```cpp\n" -"// File: src/sonic-swss/orchagent/orchdaemon.cpp\n" -"#define DEFAULT_MAX_BULK_SIZE 1000\n" -"size_t gMaxBulkSize = DEFAULT_MAX_BULK_SIZE;\n" -"\n" -"// File: src/sonic-swss/orchagent/bulker.h\n" -"template \n" -"class EntityBulker\n" -"{\n" -"public:\n" -" using Ts = SaiBulkerTraits;\n" -" using Te = typename Ts::entry_t;\n" -" ...\n" -"\n" -" void flush()\n" -" {\n" -" // Bulk remove entries\n" -" if (!removing_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" ...\n" -" flush_removing_entries(rs);\n" -" removing_entries.clear();\n" -" }\n" -"\n" -" // Bulk create entries\n" -" if (!creating_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call " -"flush_creating_entries to call SAI batch create API to create\n" -" // the objects in batch.\n" -" std::vector rs;\n" -" std::vector tss;\n" -" std::vector cs;\n" -" \n" -" for (auto const& i: creating_entries) {\n" -" sai_object_id_t *pid = std::get<0>(i);\n" -" auto const& attrs = std::get<1>(i);\n" -" if (*pid == SAI_NULL_OBJECT_ID) {\n" -" rs.push_back(pid);\n" -" tss.push_back(attrs.data());\n" -" cs.push_back((uint32_t)attrs.size());\n" -"\n" -" // Batch create here.\n" -" if (rs.size() >= max_bulk_size) {\n" -" flush_creating_entries(rs, tss, cs);\n" -" }\n" -" }\n" -" }\n" -"\n" -" flush_creating_entries(rs, tss, cs);\n" -" creating_entries.clear();\n" -" }\n" -"\n" -" // Bulk update existing entries\n" -" if (!setting_entries.empty()) {\n" -" // Split into batches of max_bulk_size, then call flush. Similar " -"to creating_entries, so details are omitted.\n" -" std::vector rs;\n" -" std::vector ts;\n" -" std::vector status_vector;\n" -" ...\n" -" flush_setting_entries(rs, ts, status_vector);\n" -" setting_entries.clear();\n" -" }\n" -" }\n" -"\n" -" sai_status_t flush_creating_entries(\n" -" _Inout_ std::vector &rs,\n" -" _Inout_ std::vector &tss,\n" -" _Inout_ std::vector &cs)\n" -" {\n" -" ...\n" -"\n" -" // Call SAI bulk create API\n" -" size_t count = rs.size();\n" -" std::vector statuses(count);\n" -" sai_status_t status = (*create_entries)((uint32_t)count, rs.data(), " -"cs.data(), tss.data()\n" -" , SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, statuses.data());\n" -"\n" -" // Set results back to input entries and clean up the batch below.\n" -" for (size_t ir = 0; ir < count; ir++) {\n" -" auto& entry = rs[ir];\n" -" sai_status_t *object_status = creating_entries[entry].second;\n" -" if (object_status) {\n" -" *object_status = statuses[ir];\n" -" }\n" -" }\n" -"\n" -" rs.clear(); tss.clear(); cs.clear();\n" -" return status;\n" -" }\n" -"\n" -" // flush_removing_entries and flush_setting_entries are similar to " -"flush_creating_entries, so we omit them here.\n" -" ...\n" -"};\n" -"```" +"Above the Redis database operation layer is the table abstraction layer " +"established by SONiC using the keys in Redis. Since the format of each Redis " +"key is ``, SONiC needs to craft or parse " +"it, when accessing the database. For more details on how the database is " +"designed, please refer to [the database section for more " +"information](/posts/sonic-2-key-components/#database)." +msgstr "" + +#: src\4-2-1-redis-wrappers.md:22 +msgid "The main class diagram of related classes is as follows:" +msgstr "" + +#: src\4-2-1-redis-wrappers.md:24 +msgid "![](assets/chapter-4/table-abstraction.png)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1259 -msgid "### orchagent中的SAI对象转发" +#: src\4-2-1-redis-wrappers.md:26 +msgid "In this diagram, we have three key classes:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1261 +#: src\4-2-1-redis-wrappers.md:28 msgid "" -"细心的小伙伴肯定已经发现了奇怪的地方,这里`EntityBulker`怎么看着像在直接调用SAI " -"API呢?难道它们不应该是在syncd中调用的吗?如果我们对传入`EntityBulker`的SAI " -"API对象进行跟踪,我们甚至会找到sai_route_api_t就是SAI的接口,而`orchagent`中还有SAI的初始化代码,如下:" +"**[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**: " +"This class is the base class for all tables. It mainly wraps the basic " +"information of the table, such as the table name, Redis key packaging, the " +"name of the channel used for communication when each table is modified, etc." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1263 +#: src\4-2-1-redis-wrappers.md:29 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h\n" -"/**\n" -" * @brief Router entry methods table retrieved with sai_api_query()\n" -" */\n" -"typedef struct _sai_route_api_t\n" -"{\n" -" sai_create_route_entry_fn create_route_entry;\n" -" sai_remove_route_entry_fn remove_route_entry;\n" -" sai_set_route_entry_attribute_fn set_route_entry_attribute;\n" -" sai_get_route_entry_attribute_fn get_route_entry_attribute;\n" -"\n" -" sai_bulk_create_route_entry_fn create_route_entries;\n" -" sai_bulk_remove_route_entry_fn remove_route_entries;\n" -" sai_bulk_set_route_entry_attribute_fn " -"set_route_entries_attribute;\n" -" sai_bulk_get_route_entry_attribute_fn " -"get_route_entries_attribute;\n" -"} sai_route_api_t;\n" -"\n" -"// File: src/sonic-swss/orchagent/saihelper.cpp\n" -"void initSaiApi()\n" -"{\n" -" SWSS_LOG_ENTER();\n" -"\n" -" if (ifstream(CONTEXT_CFG_FILE))\n" -" {\n" -" SWSS_LOG_NOTICE(\"Context config file %s exists\", " -"CONTEXT_CFG_FILE);\n" -" gProfileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = CONTEXT_CFG_FILE;\n" -" }\n" -"\n" -" sai_api_initialize(0, (const sai_service_method_table_t " -"*)&test_services);\n" -" sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api);\n" -" ...\n" -" sai_api_query(SAI_API_NEIGHBOR, (void " -"**)&sai_neighbor_api);\n" -" sai_api_query(SAI_API_NEXT_HOP, (void " -"**)&sai_next_hop_api);\n" -" sai_api_query(SAI_API_NEXT_HOP_GROUP, (void " -"**)&sai_next_hop_group_api);\n" -" sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api);\n" -" ...\n" -"\n" -" sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -" sai_log_set(SAI_API_NEIGHBOR, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_LEVEL_NOTICE);\n" -" sai_log_set(SAI_API_ROUTE, SAI_LOG_LEVEL_NOTICE);\n" -" ...\n" -"}\n" -"```" +"**[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**: " +"This class wraps the CRUD operations for each table. It contains the table " +"name and separator, so the final key can be constructed when called." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1311 -msgid "相信大家第一次看到这个代码会感觉到非常的困惑。不过别着急,这其实就是`orchagent`中SAI对象的转发机制。" +#: src\4-2-1-redis-wrappers.md:30 +msgid "" +"**[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertablebase.h)**: " +"This class is the base class for various SubscriptionTables. It mainly wraps " +"a simple queue and its pop operation (yes, only pop, no push, because it is " +"for consumers only), for upper layer calls." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1313 +#: src\4-2-2-subscribe-state-table.md:3 msgid "" -"熟悉RPC的小伙伴一定不会对`proxy-stub`模式感到陌生 —— " -"利用统一的接口来定义通信双方调用接口,在调用方实现序列化和发送,然后再接收方实现接收,反序列化与分发。这里SONiC的做法也是类似的:利用SAI " -"API本身作为统一的接口,并实现好序列化和发送功能给`orchagent`来调用,然后再`syncd`中实现接收,反序列化与分发功能。" +"The most straight-forward redis-based communication channel is " +"[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/subscriberstatetable.h)." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1315 +#: src\4-2-2-subscribe-state-table.md:5 msgid "" -"这里,发送端叫做`ClientSai`,实现在`src/sonic-sairedis/lib/ClientSai.*`中。而序列化与反序列化实现在SAI " -"metadata中:`src/sonic-sairedis/meta/sai_serialize.h`:" +"The idea is to use the built-in keyspace notification mechanism of the Redis " +"database [\\[4\\]](https://redis.io/docs/manual/keyspace-notifications/). " +"When any value in the Redis database changes, Redis sends two keyspace event " +"notifications: one is `` on `__keyspace@__:` and the other " +"is `` on `__keyevent@__:`. For example, deleting a key in " +"database `0` triggers:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1317 +#: src\4-2-2-subscribe-state-table.md:12 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.h\n" -"namespace sairedis\n" -"{\n" -" class ClientSai:\n" -" public sairedis::SaiInterface\n" -" {\n" -" ...\n" -" };\n" -"}\n" -"\n" -"// File: src/sonic-sairedis/meta/sai_serialize.h\n" -"// Serialize\n" -"std::string sai_serialize_route_entry(_In_ const sai_route_entry_t " -"&route_entry);\n" -"...\n" -"\n" -"// Deserialize\n" -"void sai_deserialize_route_entry(_In_ const std::string& s, _In_ " -"sai_route_entry_t &route_entry);\n" -"...\n" -"```" +"`SubscriberStateTable` listens for the first event notification and then " +"calls the corresponding callback function. The main classes related to it " +"are shown in this diagram, where we can see it inherits from " +"ConsumerTableBase because it is a consumer of Redis messages:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:14 +msgid "![](assets/chapter-4/subscriber-state-table.png)" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1338 -msgid "`orchagent`在编译的时候,会去链接`libsairedis`,从而实现调用SAI API时,对SAI对象进行序列化和发送:" +#: src\4-2-2-subscribe-state-table.md:16 +msgid "Initialization" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1340 +#: src\4-2-2-subscribe-state-table.md:18 msgid "" -"```makefile\n" -"# File: src/sonic-swss/orchagent/Makefile.am\n" -"orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis " -"-lsaimeta -lsaimetadata -lswsscommon -lzmq\n" -"```" +"From the initialization code, we can see how it subscribes to Redis event " +"notifications:" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:21 src\4-2-2-subscribe-state-table.md:39 +msgid "// File: sonic-swss-common - common/subscriberstatetable.cpp" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:25 +msgid "\"__keyspace@\"" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:26 +msgid "\"__:\"" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:26 +msgid "\"*\"" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1345 -msgid "我们这里用Bulk Create作为例子,来看看`ClientSai`是如何实现序列化和发送的:" +#: src\4-2-2-subscribe-state-table.md:31 +msgid "Event handling" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1347 +#: src\4-2-2-subscribe-state-table.md:33 msgid "" -"```cpp\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ sai_object_id_t switch_id,\n" -" _In_ uint32_t object_count,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Out_ sai_object_id_t *object_id,\n" -" _Out_ sai_status_t *object_statuses)\n" -"{\n" -" MUTEX();\n" -" REDIS_CHECK_API_INITIALIZED();\n" -"\n" -" std::vector serialized_object_ids;\n" -"\n" -" // Server is responsible for generate new OID but for that we need " -"switch ID\n" -" // to be sent to server as well, so instead of sending empty oids we " -"will\n" -" // send switch IDs\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" " -"serialized_object_ids.emplace_back(sai_serialize_object_id(switch_id));\n" -" }\n" -" auto status = bulkCreate(object_type, serialized_object_ids, attr_count, " -"attr_list, mode, object_statuses);\n" -"\n" -" // Since user requested create, OID value was created remotely and it " -"was returned in m_lastCreateOids\n" -" for (uint32_t idx = 0; idx < object_count; idx++) {\n" -" if (object_statuses[idx] == SAI_STATUS_SUCCESS) {\n" -" object_id[idx] = m_lastCreateOids.at(idx);\n" -" } else {\n" -" object_id[idx] = SAI_NULL_OBJECT_ID;\n" -" }\n" -" }\n" -"\n" -" return status;\n" -"}\n" -"\n" -"sai_status_t ClientSai::bulkCreate(\n" -" _In_ sai_object_type_t object_type,\n" -" _In_ const std::vector &serialized_object_ids,\n" -" _In_ const uint32_t *attr_count,\n" -" _In_ const sai_attribute_t **attr_list,\n" -" _In_ sai_bulk_op_error_mode_t mode,\n" -" _Inout_ sai_status_t *object_statuses)\n" -"{\n" -" ...\n" -"\n" -" // Calling SAI serialize APIs to serialize all objects\n" -" std::string str_object_type = sai_serialize_object_type(object_type);\n" -" std::vector entries;\n" -" for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) {\n" -" auto entry = SaiAttributeList::serialize_attr_list(object_type, " -"attr_count[idx], attr_list[idx], false);\n" -" if (entry.empty()) {\n" -" swss::FieldValueTuple null(\"NULL\", \"NULL\");\n" -" entry.push_back(null);\n" -" }\n" -"\n" -" std::string str_attr = Globals::joinFieldValues(entry);\n" -" swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , " -"str_attr);\n" -" entries.push_back(fvtNoStatus);\n" -" }\n" -" std::string key = str_object_type + \":\" + " -"std::to_string(entries.size());\n" -"\n" -" // Send to syncd via the communication channel.\n" -" m_communicationChannel->set(key, entries, " -"REDIS_ASIC_STATE_COMMAND_BULK_CREATE);\n" -"\n" -" // Wait for response from syncd.\n" -" return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, " -"(uint32_t)serialized_object_ids.size(), object_statuses);\n" -"}\n" -"```" +"`SubscriberStateTable` handles the event reception and distribution in two " +"main functions:" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1418 +#: src\4-2-2-subscribe-state-table.md:35 msgid "" -"最终,`ClientSai`会调用`m_communicationChannel->set()`,将序列化后的SAI对象发送给`syncd`。而这个Channel,在202106版本之前,就是[基于Redis的ProducerTable](https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h)了。可能是基于效率的考虑,从202111版本开始,这个Channel已经更改为[ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/ZeroMQChannel.h)了。" +"`readData()`: Reads pending events from Redis and puts them into the " +"ConsumerTableBase queue" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1420 +#: src\4-2-2-subscribe-state-table.md:36 msgid "" -"```cpp\n" -"// File: " -"https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h\n" -"class RedisChannel: public Channel\n" -"{\n" -" ...\n" -"\n" -" /**\n" -" * @brief Asic state channel.\n" -" *\n" -" * Used to sent commands like create/remove/set/get to syncd.\n" -" */\n" -" std::shared_ptr m_asicState;\n" -"\n" -" ...\n" -"};\n" -"\n" -"// File: src/sonic-sairedis/lib/ClientSai.cpp\n" -"sai_status_t ClientSai::initialize(\n" -" _In_ uint64_t flags,\n" -" _In_ const sai_service_method_table_t *service_method_table)\n" -"{\n" -" ...\n" -" \n" -" m_communicationChannel = std::make_shared(\n" -" cc->m_zmqEndpoint,\n" -" cc->m_zmqNtfEndpoint,\n" -" std::bind(&ClientSai::handleNotification, this, _1, _2, _3));\n" -"\n" -" m_apiInitialized = true;\n" -"\n" -" return SAI_STATUS_SUCCESS;\n" -"}\n" +"`pops()`: Retrieves the raw events from the queue, parses and passes them to " +"the caller via function parameters" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:55 +msgid "/*prefix*/" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:60 +msgid "// Pop from m_keyspace_event_buffer, which is filled by readData()" +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:63 +msgid "// Parsing here ..." +msgstr "" + +#: src\4-2-2-subscribe-state-table.md:76 +#: src\4-2-3-notification-producer-consumer.md:57 +#: src\4-2-4-producer-consumer-table.md:133 +#: src\4-2-5-producer-consumer-state-table.md:128 +msgid "" +"[Redis keyspace " +"notifications](https://redis.io/docs/manual/keyspace-notifications/)" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:1 +msgid "NotificationProducer / NotificationConsumer" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:3 +msgid "" +"When it comes to message communication, there is no way we can bypass " +"message queues. And this is the second communication channel in SONiC - " +"[NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationproducer.h) " +"and " +"[NotificationConsumer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationconsumer.h)." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:5 +msgid "" +"This communication channel is implemented using Redis's built-in PubSub " +"mechanism, wrapping the `PUBLISH` and `SUBSCRIBE` commands. However, because " +"`PUBLISH` needs everything being send to be serialized in the command, due " +"to API limitations [\\[5\\]](https://redis.io/docs/reference/clients/), " +"these commands are not suitable for passing large data. Hence, in SONiC, it " +"is only used in limited places, such as simple notification scenarios (e.g., " +"timeout checks or restart checks in orchagent), which won't have large " +"payload, such as user configurations or data:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:7 +msgid "![](assets/chapter-4/notification-producer-consumer.png)" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:9 +msgid "In this communication channel, the producer side performs two main tasks:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:11 +msgid "Package the message into JSON format." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:12 +msgid "Call Redis command `PUBLISH` to send it." +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:14 +msgid "" +"Because `PUBLISH` can only carry a single message, the \"op\" and \"data\" " +"fields are placed at the front of \"values\", then the `buildJson` function " +"is called to package them into a JSON array:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:19 +msgid "" +"// Pack the op and data into values array, then pack everything into a JSON " +"string as the message" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:25 +msgid "// Publish message to Redis channel" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:27 +msgid "\"PUBLISH %s %s\"" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:35 +msgid "" +"The consumer side uses the `SUBSCRIBE` command to receive all notifications:" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:46 +msgid "// Subscribe to Redis channel" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:47 +msgid "\"SUBSCRIBE \"" +msgstr "" + +#: src\4-2-3-notification-producer-consumer.md:58 +#: src\4-2-4-producer-consumer-table.md:137 +#: src\4-2-5-producer-consumer-state-table.md:132 +msgid "[Redis client handling](https://redis.io/docs/reference/clients/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:1 +msgid "ProducerTable / ConsumerTable" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:3 +msgid "" +"Although `NotificationProducer` and `NotificationConsumer` is " +"straight-forward, but they are not suitable for passing large data. " +"Therefore, SONiC provides another message-queue-based communication " +"mechanism that works in similar way - " +"[ProducerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producertable.h) " +"and " +"[ConsumerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertable.h)." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:5 +msgid "" +"This channel leverages the Redis list to pass the message. Unlike " +"Notification, which has limited message capacity, it stores all the message " +"data in a Redis list with a very slim custom messsage format. This solves " +"the message size limitation in Notification. In SONiC, it is mainly used in " +"FlexCounter, the `syncd` service, and `ASIC_DB`." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:7 +msgid "Message format" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:9 +msgid "" +"In this channel, a message is a triplet (`Key`, `FieldValuePairs`, `Op`) and " +"will be pushed into the Redis list (Key = `_KEY_VALUE_OP_QUEUE`) " +"as 3 list items:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:11 +msgid "" +"`Key` is table name and key (e.g., " +"`SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000`)." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:12 +msgid "" +"`FieldValuePairs` are the field that needs to be updated in the database and " +"their values, which is serialized into a JSON string: `\"[\\\"Field1\\\", " +"\\\"Value1\\\", \\\"Field2\\\", \\\"Value2\\\", ...]\"`." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:13 +msgid "`Op` is the operation to be performed (e.g., Set, Get, Del, etc.)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:15 +msgid "" +"Once the message is pushed into the Redis list, a notification will be " +"published to a specific channel (Key = `_CHANNEL`) with only a " +"single character \"G\" as payload, indicating that there is a new message in " +"the list." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:17 +msgid "" +"So, when using this channel, we can imaging the actual data stored in the " +"Redis:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:19 +msgid "In the channel: `[\"G\", \"G\", ...]`" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:20 +msgid "" +"In the list: `[\"Key1\", \"FieldValuePairs1\", \"Op1\", \"Key2\", " +"\"FieldValuePairs2\", \"Op2\", ...]`" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:22 +msgid "Queue operations" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:24 +msgid "" +"Using this message format, `ProducerTable` and `ConsumerTable` provides two " +"queue operations:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:26 +msgid "" +"Enqueue: `ProducerTable` uses a Lua script to atomically write the message " +"triplet into the Redis list and then publishes an update notification to a " +"specific channel." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:27 +msgid "" +"Pop: `ConsumerTable` also uses a Lua script to atomically read the message " +"triplet from the message queue and writes the requested changes to the " +"database during the read process." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:33 +msgid "" +"Its main class diagram is shown below. In ProducerTable, `m_shaEnqueue` and " +"in ConsumerTable, `m_shaPop` are the two Lua scripts we mentioned. After " +"they are loaded, you can call them atomically via `EVALSHA`:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:35 +msgid "![](assets/chapter-4/producer-consumer-table.png)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:37 +msgid "" +"The core logic of ProducerTable is as follows, showing how values are packed " +"into JSON and how `EVALSHA` is used to call Lua scripts:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:40 +msgid "// File: sonic-swss-common - common/producertable.cpp" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:45 +msgid "\"redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:46 +msgid "\"redis.call('PUBLISH', KEYS[2], ARGV[4]);\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:53 +msgid "\"S\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:58 +msgid "\"{}\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:58 +msgid "\"D\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:61 +msgid "/* prefix */" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:66 +msgid "\"EVALSHA %s 2 %s %s %s %s %s %s\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:73 +msgid "\"G\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:79 +msgid "" +"On the other side, ConsumerTable is slightly more complicated because it " +"supports many types of ops. The logic is written in a separate file " +"(`common/consumer_table_pops.lua`). Interested readers can explore it " +"further:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:82 +msgid "// File: sonic-swss-common - common/consumertable.cpp" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:88 +msgid "\"consumer_table_pops.lua\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:95 +msgid "" +"// Note that here we are processing the messages in bulk with POP_BATCH_SIZE!" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:98 +msgid "\"EVALSHA %s 2 %s %s %d %d\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:107 +msgid "// Parse and pack the messages in bulk" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:112 +msgid "Monitor" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:114 +msgid "" +"To monitor how the `ProducerTable` and `ConsumerTable` work, we can use the " +"`redis-cli monitor` command to see the actual Redis commands that being " +"executed." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:117 +msgid "# Filter to `LPUSH` and `PUBLISH` commands to help us reduce the noise." +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:118 +msgid "\"LPUSH|PUBLISH\"" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:121 +msgid "" +"And here is an example of the output showing a `ProducerTable` enqueue " +"operation:" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:123 +msgid "" +"```text\n" +"1735966216.139741 [1 lua] \"LPUSH\" \"ASIC_STATE_KEY_VALUE_OP_QUEUE\" " +"\"SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000\" " +"\"[\\\"SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY\\\",\\\"1\\\"]\" " +"\"Sget\" \n" +"1735966216.139774 [1 lua] \"PUBLISH\" \"ASIC_STATE_CHANNEL@1\" \"G\" \n" "```" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1454 +#: src\4-2-4-producer-consumer-table.md:134 +#: src\4-2-5-producer-consumer-state-table.md:129 +msgid "[Redis Transactions](https://redis.io/docs/manual/transactions/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:135 +#: src\4-2-5-producer-consumer-state-table.md:130 +msgid "" +"[Redis Atomicity with " +"Lua](https://developer.redis.com/develop/java/spring/rate-limiting/fixed-window/reactive-lua/)" +msgstr "" + +#: src\4-2-4-producer-consumer-table.md:136 +#: src\4-2-5-producer-consumer-state-table.md:131 +msgid "[Redis hashes](https://redis.io/docs/data-types/hashes/)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:1 +msgid "ProducerStateTable / ConsumerStateTable" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:3 +msgid "" +"Although `Producer/ConsumerTable` is straightforward and maintains the order " +"of the messages, each message can only update one table key and requires " +"JSON serialization. However, in many cases, we don't need strict ordering " +"but need higher throughput. To optimize performance, SONiC introduces the " +"fourth, and most frequently used, communication channel: " +"[ProducerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producerstatetable.h) " +"and " +"[ConsumerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumerstatetable.h)." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:5 +msgid "Overview" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:7 +msgid "" +"Unlike `ProducerTable`, `ProducerStateTable` uses a Hash to store messages " +"instead of a List. This means the order of messages will not be guranteed, " +"but it can significantly boosts performance:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:9 +msgid "First, no more JSON serialization, hence its overhead is gone." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:10 +msgid "Second, batch processing:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:11 msgid "" -"关于进程通信的方法,这里就不再赘述了,大家可以参考第四章描述的[进程间的通信机制](./4-2-2-redis-messaging-layer.html)。" +"Multiple table updates can be merged into one (single pending update key set " +"per table)." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1456 -msgid "### syncd更新ASIC" +#: src\4-2-5-producer-consumer-state-table.md:12 +msgid "" +"If the same Field under the same Key is changed multiple times, only the " +"latest change is preserved, merging all changes related to that Key into a " +"single message and reducing unnecessary handling." msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1458 +#: src\4-2-5-producer-consumer-state-table.md:14 msgid "" -"最后,当SAI对象生成好并发送给`syncd`后,`syncd`会接收,处理,更新ASIC_DB,最后更新ASIC。这一段的工作流,我们已经在[Syncd-SAI工作流](./5-1-syncd-sai-workflow.html)中详细介绍过了,这里就不再赘述了,大家可以移步去查看。" +"`Producer/ConsumerStateTable` is more complex under the hood than " +"`Producer/ConsumerTable`. The related classes are shown in the diagram " +"below, where `m_shaSet` and `m_shaDel` store the Lua scripts for modifying " +"and sending messages, while `m_shaPop` is used to retrieve messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:16 +msgid "![](assets/chapter-4/producer-consumer-state-table.png)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:18 +msgid "Sending messages" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:20 +msgid "When sending messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:22 +msgid "Each message is stored in two parts:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:23 +msgid "" +"KEY_SET: keeps track of which Keys have been modified (stored as a Set at " +"``)" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:24 +msgid "" +"A series of Hash: One Hash for each modified Key (stored at " +"`_`)." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:25 +msgid "" +"After storing a message, if the Producer finds out it's a new Key, it calls " +"`PUBLISH` to notify `_CHANNEL@` that a new Key has " +"appeared." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:28 +msgid "// File: sonic-swss-common - common/producerstatetable.cpp" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:35 +msgid "\"local added = redis.call('SADD', KEYS[2], ARGV[2])\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:36 +msgid "\"for i = 0, #KEYS - 3 do\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:37 +msgid "" +"\" redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i * 2])\\n" +"\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:38 +#: src\4-2-5-producer-consumer-state-table.md:41 +msgid "\"end\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:39 +msgid "\" if added > 0 then \\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:40 +msgid "\" redis.call('PUBLISH', KEYS[1], ARGV[1])\\n\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:47 +msgid "Receiving messages" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:49 +msgid "When receiving messages:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:51 +msgid "" +"The consumer uses `SUBSCRIBE` to listen on `_CHANNEL@`. " +"Once a new message arrives, it calls a Lua script to run `HGETALL`, fetch " +"all Keys, and write them into the database." +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:58 +msgid "\"consumer_state_table_pops.lua\"" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:67 +msgid "Example" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:69 +msgid "To illustrate, here is an example of enabling Port Ethernet0:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:71 +msgid "" +"First, we call `config interface startup Ethernet0` from the command line to " +"enable Ethernet0. This causes `portmgrd` to send a status update to APP_DB " +"via ProducerStateTable, as shown below:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:73 +msgid "" +"```redis\n" +"EVALSHA \"\" \"6\" \"PORT_TABLE_CHANNEL@0\" " +"\"PORT_TABLE_KEY_SET\" \n" +" \"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" " +"\"_PORT_TABLE:Ethernet0\" \"_PORT_TABLE:Ethernet0\" \"G\"\n" +" \"Ethernet0\" \"alias\" \"Ethernet5/1\" \"index\" \"5\" \"lanes\" " +"\"9,10,11,12\" \"speed\" \"40000\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:79 +msgid "This command triggers the following creation and broadcast:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:81 +msgid "" +"```redis\n" +"SADD \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" +"HSET \"_PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" +"PUBLISH \"PORT_TABLE_CHANNEL@0\" \"_PORT_TABLE:Ethernet0\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:90 +msgid "Thus, the message is ultimately stored in APPL_DB as follows:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:103 +msgid "" +"When ConsumerStateTable receives the message, it also calls `EVALSHA` to " +"execute a Lua script, such as:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:105 +msgid "" +"```redis\n" +"EVALSHA \"\" \"3\" \"PORT_TABLE_KEY_SET\" \"PORT_TABLE:\" " +"\"PORT_TABLE_DEL_SET\" \"8192\" \"_\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:109 +msgid "Similar to the Producer side, this script runs:" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:111 +msgid "" +"```redis\n" +"SPOP \"PORT_TABLE_KEY_SET\" \"_PORT_TABLE:Ethernet0\"\n" +"HGETALL \"_PORT_TABLE:Ethernet0\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"alias\" \"Ethernet5/1\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"index\" \"5\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"lanes\" \"9,10,11,12\"\n" +"HSET \"PORT_TABLE:Ethernet0\" \"speed\" \"40000\"\n" +"DEL \"_PORT_TABLE:Ethernet0\"\n" +"```" +msgstr "" + +#: src\4-2-5-producer-consumer-state-table.md:121 +msgid "At this point, the data update is complete." +msgstr "" + +#: src\4-3-zmq-based-channels.md:1 +msgid "ZMQ-based Channels" +msgstr "" + +#: src\4-4-orch-layer.md:1 +msgid "Service Layer - Orch" +msgstr "" + +#: src\4-4-orch-layer.md:3 +msgid "" +"Finally, to make it more convenient for building services, SONiC provides " +"another layer of abstraction on top of the communication layer, offering a " +"base class for services: " +"[Orch](https://github.com/sonic-net/sonic-swss/blob/master/src/orchagent/orch.h)." +msgstr "" + +#: src\4-4-orch-layer.md:5 +msgid "" +"With all the lower layers, adding message communication support in Orch is " +"relatively straightforward. The main class diagram is shown below:" +msgstr "" + +#: src\4-4-orch-layer.md:7 +msgid "![](assets/chapter-4/orch.png)" +msgstr "" + +#: src\4-4-orch-layer.md:13 +msgid "" +"We can see that Orch mainly wraps `SubscriberStateTable` and " +"`ConsumerStateTable` to simplify and unify the message subscription. The " +"core code is very simple and creates different Consumers based on the " +"database type:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:1 +msgid "Event Dispatching and Error Handling" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:3 +msgid "Epoll-based Event Dispatching" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:5 +msgid "" +"Just like many other Linux services, SONiC uses epoll at its core for event " +"dispatching:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:7 +msgid "" +"Any class that supports event dispatching should inherit from `Selectable` " +"and implement two key functions:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:8 +msgid "" +"`int getFd();`: Returns the fd for epoll to listen on. For most services, " +"this fd is the one used for Redis communication, so the call to `getFd()` " +"ultimately delegates to the Redis library." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:9 +msgid "`uint64_t readData()`: Reads data when an event arrives." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:10 +msgid "" +"Any objects that need to participate in event dispatching must register with " +"the `Select` class. This class registers all `Selectable` objects' fds with " +"epoll and calls `Selectable`'s `readData()` when an event arrives." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:12 +msgid "Here's the class diagram:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:14 +msgid "![](assets/chapter-4/event-polling.png)" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:16 +msgid "" +"The core logic lives in the `Select` class, which can be simplified as " +"follows:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:37 +msgid "// error handling here ..." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:47 +msgid "// After update callback ..." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:55 +msgid "" +"However, here comes the question... where is the callback? As mentioned, " +"`readData()` only reads the message and stores it in a pending queue for " +"processing. The real processing needs to call `pops()`. So at which point " +"does every upper-level message handler get called?" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:57 +msgid "" +"Here, let's look back again at `portmgrd`'s `main` function. From the " +"simplified code below, we can see - unlike a typical event loop, SONiC does " +"not handle events with callbacks; the outermost event loop directly calls " +"the actual handlers:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:64 +msgid "// Create PortMgr, which implements Orch interface." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:68 +msgid "// Create Select object for event loop and add PortMgr to it." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:74 +msgid "// Event loop" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:80 +msgid "// When anyone of the selectables gets signaled, select() will call" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:81 +msgid "// into readData() and fetch all events, then return." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:85 +msgid "// Then, we call into execute() explicitly to process all events." +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:93 +msgid "Error Handling" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:95 +msgid "" +"Another thing about event loops is error handling. For example, if a Redis " +"command fails, or the connection is broken, or any kind of failure happens, " +"what will happen to our services?" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:97 +msgid "" +"SONiC's error handling is very simple: it just throws exceptions (for " +"example, in the code that fetches command results). Then the event loop " +"catches the exceptions, logs them, and continues:" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:105 +msgid "// The only reason of error is REDIS_ERR_OOM (Out of memory)" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:106 +msgid "// ref: https://github.com/redis/hiredis/blob/master/hiredis.c" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:113 +msgid "\"Failed to redisGetReply with \"" +msgstr "" + +#: src\4-5-event-polling-and-error-handling.md:119 +msgid "" +"There is no specific code here for statistics or telemetry, so monitoring is " +"somewhat weak. We also need to consider data errors (for example, partial " +"writes leading to corrupted data), though simply restarting `*syncd` or " +"`*mgrd` services might fix such issues because many stored data in database " +"will be wiped out, such as APPL_DB, and the services will do a full sync on " +"startup." +msgstr "" + +#: src\5-core-components.md:1 +msgid "Core Components" +msgstr "" + +#: src\5-core-components.md:3 +msgid "" +"In this chapter, we take a deeper look at some of the representative core " +"components in SONiC and their workflows from a code perspective." +msgstr "" + +#: src\5-core-components.md:5 +msgid "" +"```admonish note\n" +"For helping us to read and understand, all the code shown here will be " +"simplified to its core part to illustrate the process. If you would like to " +"read the full code, please refer to [the original code in the " +"repository](./3-1-code-repos.html).\n" +"\n" +"Additionally, the relevant file path of the code will be shared in the " +"beginning of each code block, which is based on the SONiC's main repository: " +"[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage). If the " +"code is not imported by buildimage repo, the full URL will be provided.\n" +"```" +msgstr "" + +#: src\5-1-syncd-and-sai.md:3 +msgid "" +"[Syncd Container](./2-3-key-containers.html#asic-management-container-syncd) " +"is the container in SONiC dedicated to managing the ASIC. The key process " +"`syncd` is responsible for communicating with the Redis database, loading " +"SAI implementation, and interacting with it to handle ASIC initialization, " +"configuration, status reporting, and so on." +msgstr "" + +#: src\5-1-syncd-and-sai.md:5 +msgid "" +"Since many SONiC workflows ultimately need to interact with the ASIC through " +"Syncd and SAI, this part becomes common to all those workflows. Therefore, " +"before diving into other workflows, let's take a look at how Syncd and SAI " +"work first." +msgstr "" + +#: src\5-1-syncd-and-sai.md:7 +msgid "Syncd Startup Flow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:9 +msgid "" +"The entry point of the `syncd` process is the `syncd_main` function in " +"`syncd_main.cpp`. The startup flow can be roughly divided into two parts." +msgstr "" + +#: src\5-1-syncd-and-sai.md:11 +msgid "The first part creates and initializes various objects:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:29 +msgid "The second part starts the main loop and handles initialization events:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:64 +msgid "Now, let's dive into the code to see how Syncd and SAI are implemented." +msgstr "" + +#: src\5-1-syncd-and-sai.md:66 +msgid "The syncd_main Function" +msgstr "" + +#: src\5-1-syncd-and-sai.md:68 +msgid "" +"The `syncd_main` function itself is straightforward: it creates a `Syncd` " +"object and then calls its `run` method:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:71 +msgid "// File: src/sonic-sairedis/syncd/syncd_main.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:81 +msgid "" +"The Syncd constructor initializes each feature in Syncd, while the run " +"method starts the Syncd main loop." +msgstr "" + +#: src\5-1-syncd-and-sai.md:83 +msgid "The Syncd Constructor" +msgstr "" + +#: src\5-1-syncd-and-sai.md:85 +msgid "" +"The `Syncd` constructor creates or initializes the key components in " +"`Syncd`, such as database connection objects, statistics management, and " +"ASIC notification handler. The key code looks like below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:88 src\5-1-syncd-and-sai.md:229 +#: src\5-1-syncd-and-sai.md:319 src\5-1-syncd-and-sai.md:370 +#: src\5-1-syncd-and-sai.md:466 src\5-1-syncd-and-sai.md:730 +msgid "// File: src/sonic-sairedis/syncd/Syncd.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:98 +msgid "// Load context config" +msgstr "" + +#: src\5-1-syncd-and-sai.md:103 +msgid "// Create FlexCounter manager" +msgstr "" + +#: src\5-1-syncd-and-sai.md:106 +msgid "// Create DB related objects" +msgstr "" + +#: src\5-1-syncd-and-sai.md:111 +msgid "// Create notification processor and handler" +msgstr "" + +#: src\5-1-syncd-and-sai.md:119 +msgid "// Init many other event handlers here" +msgstr "" + +#: src\5-1-syncd-and-sai.md:123 src\5-1-syncd-and-sai.md:143 +msgid "// Initialize SAI" +msgstr "" + +#: src\5-1-syncd-and-sai.md:129 +msgid "SAI Initialization and VendorSai" +msgstr "" + +#: src\5-1-syncd-and-sai.md:131 +msgid "" +"The last and most important step in `Syncd` initialization is to initialize " +"SAI. [In the core component introduction to SAI](./2-4-sai-intro.html), we " +"briefly described how SAI is initialized and implemented, and how it " +"provides support for different platforms in SONiC. And here, we will focus " +"more on how Syncd wraps SAI and uses it." +msgstr "" + +#: src\5-1-syncd-and-sai.md:133 +msgid "" +"`Syncd` uses `VendorSai` to wrap all SAI APIs to simplify upper-level calls. " +"The initialization looks like below, essentially just calling the sai " +"initialize and api query functions, and handling errors:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:136 src\5-1-syncd-and-sai.md:168 +msgid "// File: src/sonic-sairedis/syncd/VendorSai.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:147 +msgid "// If SAI is initialized successfully, query all SAI API methods." +msgstr "" + +#: src\5-1-syncd-and-sai.md:148 +msgid "" +"// sai_metadata_api_query will also update all extern global sai_*_api " +"variables, so we can also use" +msgstr "" + +#: src\5-1-syncd-and-sai.md:149 +msgid "" +"// sai_metadata_get_object_type_info to get methods for a specific SAI " +"object type." +msgstr "" + +#: src\5-1-syncd-and-sai.md:161 +msgid "" +"Once all the SAI APIs have been acquired, we can call into the SAI " +"implementation using the `VendorSai` object." +msgstr "" + +#: src\5-1-syncd-and-sai.md:163 +msgid "" +"Currently, `VendorSai` internally has two different ways to call the SAI " +"APIs:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:165 +msgid "" +"Using `sai_object_type_info_t` from SAI metadata, which essentially acts " +"like a virtual table for all SAI Objects:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:182 +msgid "" +"Using `m_apis` stored in the `VendorSai` object. This approach needs us to " +"check the object type and then call the corresponding APIs, so the code " +"becomes more verbose:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:214 +msgid "\"not implemented, FIXME\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:222 +msgid "The first approach is more succinct." +msgstr "" + +#: src\5-1-syncd-and-sai.md:224 +msgid "Main Event Loop" +msgstr "" + +#: src\5-1-syncd-and-sai.md:226 +msgid "" +"`Syncd`'s main event loop follows SONiC's standard [event " +"dispatching](./4-5-event-polling-and-error-handling.html) pattern. On " +"startup, Syncd registers all Selectable objects handling events with a " +"Select object that waits for events. The main loop calls \"select\" to wait " +"for events:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:236 +msgid "// Start notification processing thread" +msgstr "" + +#: src\5-1-syncd-and-sai.md:239 +msgid "// Start MDIO threads" +msgstr "" + +#: src\5-1-syncd-and-sai.md:243 +msgid "// Registering selectable for event polling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:249 +msgid "// Main event loop" +msgstr "" + +#: src\5-1-syncd-and-sai.md:257 +msgid "// Handling switch restart event and restart switch here." +msgstr "" + +#: src\5-1-syncd-and-sai.md:263 +msgid "// Handle redis updates here." +msgstr "" + +#: src\5-1-syncd-and-sai.md:266 +msgid "\"select failed: %d\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:274 +msgid "" +"Here, `m_selectableChannel` handles Redis database events. It interacts with " +"Redis [ProducerTable / ConsumerTable](./4-2-4-producer-consumer-table.html). " +"Hence, all operations from `orchagent` will be stored in Redis lists, " +"waiting for `Syncd` to consume." +msgstr "" + +#: src\5-1-syncd-and-sai.md:277 +msgid "// File: src/sonic-sairedis/meta/RedisSelectableChannel.h" +msgstr "" + +#: src\5-1-syncd-and-sai.md:288 +msgid "// SelectableChannel overrides" +msgstr "" + +#: src\5-1-syncd-and-sai.md:292 +msgid "// Selectable overrides" +msgstr "" + +#: src\5-1-syncd-and-sai.md:305 +msgid "During the main loop startup, `Syncd` also launches two threads:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:307 +msgid "" +"A notification processing thread for receiving ASIC-reported notifications: " +"`m_processor->startNotificationsProcessingThread()`" +msgstr "" + +#: src\5-1-syncd-and-sai.md:308 +msgid "" +"A thread for handling MDIO communication: " +"`m_mdioIpcServer->startMdioThread()`" +msgstr "" + +#: src\5-1-syncd-and-sai.md:310 +msgid "" +"We'll discuss their details more thoroughly when introducing related " +"workflows." +msgstr "" + +#: src\5-1-syncd-and-sai.md:312 +msgid "Initialize SAI Switch and Notifications" +msgstr "" + +#: src\5-1-syncd-and-sai.md:314 +msgid "" +"Once the main event loop is started, `Syncd` will call into SAI to create " +"the Switch object. There are two main entry points: either a create switch " +"request from ASIC_DB (called by swss) or `Syncd` directlly calls it for the " +"Warm Boot process. Either way, the internal flow is similar." +msgstr "" + +#: src\5-1-syncd-and-sai.md:316 +msgid "" +"A crucial step here is initializing the notification callbacks in the SAI " +"implementation, such as FDB events. These callback functions are passed to " +"SAI as Switch attributes in `create_switch`. The SAI implementation stores " +"them so it can call back into `Syncd` whenever these events occur:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:324 +msgid "// Parse event into SAI object" +msgstr "" + +#: src\5-1-syncd-and-sai.md:332 +msgid "// Update notifications pointers in attribute list" +msgstr "" + +#: src\5-1-syncd-and-sai.md:340 +msgid "" +"// ProcessQuadEventInInitViewMode will eventually call into VendorSai, which " +"calls create_swtich function in SAI." +msgstr "" + +#: src\5-1-syncd-and-sai.md:347 src\5-1-syncd-and-sai.md:702 +msgid "// File: src/sonic-sairedis/syncd/NotificationHandler.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:371 +msgid "// Call stack: processQuadEvent" +msgstr "" + +#: src\5-1-syncd-and-sai.md:372 +msgid "// -> processQuadEventInInitViewMode" +msgstr "" + +#: src\5-1-syncd-and-sai.md:373 +msgid "// -> processQuadInInitViewModeCreate" +msgstr "" + +#: src\5-1-syncd-and-sai.md:374 +msgid "// -> onSwitchCreateInInitViewMode" +msgstr "" + +#: src\5-1-syncd-and-sai.md:392 +msgid "" +"From the open-sourced Mellanox's implementation, we can see how the SAI " +"switch is created and the notification callbacks are set:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:431 +msgid "ASIC Programming Workflow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:433 +msgid "" +"ASIC programming workflow is the most important workflow in `Syncd`. When " +"`orchagent` discovers any configuration changes, it sends ASIC programming " +"request via `ASIC_DB`, which triggers this workflow and uses SAI to update " +"the ASIC. After understanding Syncd's main event loop and the communication " +"channels, the workflow will become easier to follow." +msgstr "" + +#: src\5-1-syncd-and-sai.md:435 +msgid "All steps happen sequentially on the main thread:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:461 +msgid "" +"First, `orchagent` sends operations through Redis, which will be received by " +"the `RedisSelectableChannel.` When the main event loop processes " +"`m_selectableChannel`, it calls `processEvent` to process it, just like what " +"we have discussed in the main event loop section." +msgstr "" + +#: src\5-1-syncd-and-sai.md:463 +msgid "" +"Then, `processEvent` calls the relevant SAI API to update the ASIC. The " +"logic is a giant switch-case statement that dispatches the operations:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:469 +msgid "// Loop all operations in the queue" +msgstr "" + +#: src\5-1-syncd-and-sai.md:496 +msgid "// Parse operation" +msgstr "" + +#: src\5-1-syncd-and-sai.md:511 +msgid "// Process the operation" +msgstr "" + +#: src\5-1-syncd-and-sai.md:519 +msgid "// Send response" +msgstr "" + +#: src\5-1-syncd-and-sai.md:547 +msgid "\"api %s not supported\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:552 +msgid "ASIC State Change Notification Workflow" +msgstr "" + +#: src\5-1-syncd-and-sai.md:554 +msgid "" +"On the other hand, when the ASIC state is changed or needs to report certain " +"status, it notifies us through SAI. `Syncd` listens for these notifications, " +"then reports them back to `orchagent` through our communication channel on " +"top of `ASIC_DB`." +msgstr "" + +#: src\5-1-syncd-and-sai.md:556 +msgid "The workflow shows as below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:587 +msgid "" +"Here, let's look into a real implementation. For better understanding, we " +"still use Mellanox's open-sourced SAI implementation as an example." +msgstr "" + +#: src\5-1-syncd-and-sai.md:589 +msgid "" +"First of all, SAI implementation needs to be able to receive notification " +"from ASIC. This is done by calling into the ASIC SDK. In Mellanox's SAI, it " +"sets up an event thread to hook into ASIC, then use `select` to handle the " +"events from ASIC SDK:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:608 +msgid "// Init SDK API" +msgstr "" + +#: src\5-1-syncd-and-sai.md:621 +msgid "// Register for port and channel notifications" +msgstr "" + +#: src\5-1-syncd-and-sai.md:639 +msgid "// Port state change event" +msgstr "" + +#: src\5-1-syncd-and-sai.md:641 +msgid "// Parse port state event here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:648 +msgid "// Receive notification event." +msgstr "" + +#: src\5-1-syncd-and-sai.md:654 +msgid "// BFD packet event" +msgstr "" + +#: src\5-1-syncd-and-sai.md:657 +msgid "// Parse and check event valid here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:662 +msgid "" +"// Same way to handle BFD timeout event, Bulk counter ready event. Emiited." +msgstr "" + +#: src\5-1-syncd-and-sai.md:664 +msgid "// FDB event and packet event handling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:666 +msgid "\"FDB event\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:672 +msgid "// Parse FDB events here ..." +msgstr "" + +#: src\5-1-syncd-and-sai.md:681 +msgid "// Packet event handling" +msgstr "" + +#: src\5-1-syncd-and-sai.md:695 +msgid "Using FDB event as an example:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:697 +msgid "" +"When ASIC sends the FDB events, it will be received by the event loop above." +msgstr "" + +#: src\5-1-syncd-and-sai.md:698 +msgid "" +"The callback `g_notification_callbacks.on_fdb_event` stored in SAI " +"implementation will be called to handle this event." +msgstr "" + +#: src\5-1-syncd-and-sai.md:699 +msgid "" +"It then calls `NotificationHandler::onFdbEvent` in Syncd to serialize the " +"event and put it into the notification queue:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:710 +msgid "" +"Then the notification thread is signaled to pick up this event from the " +"queue, then process it under the syncd lock:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:713 src\5-1-syncd-and-sai.md:743 +#: src\5-1-syncd-and-sai.md:763 src\5-1-syncd-and-sai.md:797 +msgid "// File: src/sonic-sairedis/syncd/NotificationProcessor.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:720 +msgid "// When notification arrives, it will signal this condition variable." +msgstr "" + +#: src\5-1-syncd-and-sai.md:723 +msgid "// Process notifications in the queue." +msgstr "" + +#: src\5-1-syncd-and-sai.md:731 +msgid "// Call from NotificationProcessor::processNotification" +msgstr "" + +#: src\5-1-syncd-and-sai.md:740 +msgid "" +"Now, it goes into the event dispatching and handling logic. " +"`syncProcessNotification` function is essentially a series of `if-else` " +"statements, which calls the corresponding handling function based on the " +"event type:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:755 +msgid "\"unknown notification: %s\"" +msgstr "" + +#: src\5-1-syncd-and-sai.md:760 +msgid "" +"For each event, the handling function deserializes the event and processes " +"it, such as `handle_fdb_event` and `process_on_fdb_event`:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:779 +msgid "// Check FDB event notification data here" +msgstr "" + +#: src\5-1-syncd-and-sai.md:788 +msgid "// Send notification" +msgstr "" + +#: src\5-1-syncd-and-sai.md:794 +msgid "" +"Finally, it's written to ASIC_DB via " +"[NotificationProducer](./4-2-3-notification-producer-consumer.md) to notify " +"`orchagent`:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:808 +msgid "// File: src/sonic-sairedis/syncd/RedisNotificationProducer.cpp" +msgstr "" + +#: src\5-1-syncd-and-sai.md:814 +msgid "" +"// The m_notificationProducer is created in the ctor of " +"RedisNotificationProducer as below:" +msgstr "" + +#: src\5-1-syncd-and-sai.md:815 +msgid "" +"// m_notificationProducer = " +"std::make_shared(m_db.get(), " +"REDIS_TABLE_NOTIFICATIONS_PER_DB(dbName));" +msgstr "" + +#: src\5-1-syncd-and-sai.md:820 +msgid "That's it! This is basically how things work in high level in `Syncd`!" +msgstr "" + +#: src\5-1-syncd-and-sai.md:825 src\5-2-2-route-update-in-frr.md:742 +#: src\5-2-3-route-update-in-sonic.md:741 +msgid "" +"[Github repo: sonic-sairedis](https://github.com/sonic-net/sonic-sairedis/)" +msgstr "" + +#: src\5-2-bgp.md:3 +msgid "" +"[BGP](https://datatracker.ietf.org/doc/html/rfc4271) might be the most " +"commonly used and important feature in switches. In this section, we take a " +"deeper look at BGP-related workflows." +msgstr "" + +#: src\5-2-bgp.md:5 +msgid "BGP Processes" +msgstr "" + +#: src\5-2-bgp.md:7 +msgid "" +"SONiC uses [FRRouting](https://frrouting.org/) as its BGP implementation, " +"responsible for handling the BGP protocol. FRRouting is an open-source " +"routing software that supports multiple routing protocols, including BGP, " +"OSPF, IS-IS, RIP, PIM, LDP, etc. When a new version of FRR is released, " +"SONiC synchronizes it to the [SONiC FRR repository: " +"sonic-frr](https://github.com/sonic-net/sonic-frr), with each version " +"corresponding to a branch such as `frr/8.2`." +msgstr "" + +#: src\5-2-bgp.md:9 +msgid "" +"FRR mainly consists of two major parts. The first part includes the " +"implementations of each protocol, where processes are named `*d.` When they " +"receive routing update notifications, they inform the second part, the " +"\"zebra\" process. The `zebra` process performs route selection and " +"synchronizes the best routing information to the kernel. Its main structure " +"is shown below:" +msgstr "" + +#: src\5-2-bgp.md:30 +msgid "" +"In SONiC, these FRR processes all run inside the `bgp` container. In " +"addition, to integrate FRR with Redis, SONiC runs a process called " +"`fpmsyncd` (Forwarding Plane Manager syncd) within the `bgp` container. Its " +"main function is to listen to kernel routing updates and synchronize them to " +"the `APPL_DB`. Because it is not part of FRR, its implementation is located " +"in the [sonic-swss](https://github.com/sonic-net/sonic-swss) repository." +msgstr "" + +#: src\5-2-bgp.md:36 src\5-2-1-bgp-cli.md:92 +#: src\5-2-2-route-update-in-frr.md:740 src\5-2-3-route-update-in-sonic.md:739 +msgid "[Github repo: sonic-frr](https://github.com/sonic-net/sonic-frr)" +msgstr "" + +#: src\5-2-bgp.md:37 src\5-2-1-bgp-cli.md:94 +#: src\5-2-2-route-update-in-frr.md:743 src\5-2-3-route-update-in-sonic.md:742 +msgid "" +"[RFC 4271: A Border Gateway Protocol 4 " +"(BGP-4)](https://datatracker.ietf.org/doc/html/rfc4271)" +msgstr "" + +#: src\5-2-bgp.md:38 src\5-2-1-bgp-cli.md:95 +#: src\5-2-2-route-update-in-frr.md:744 src\5-2-3-route-update-in-sonic.md:743 +msgid "[FRRouting](https://frrouting.org/)" +msgstr "" + +#: src\5-2-1-bgp-cli.md:3 +msgid "`show` Command" +msgstr "" + +#: src\5-2-1-bgp-cli.md:5 +msgid "" +"Since BGP is implemented using FRR, naturally, the `show` command will " +"forward the direct request to FRR's `vtysh`. The key code is as follows:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:8 +msgid "# file: src/sonic-utilities/show/bgp_frr_v4.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:8 +msgid "# 'summary' subcommand (\"show ip bgp summary\")" +msgstr "" + +#: src\5-2-1-bgp-cli.md:16 +msgid "# file: src/sonic-utilities/utilities_common/bgp_util.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:19 +msgid "# The IPv6 case is omitted here for simplicity" +msgstr "" + +#: src\5-2-1-bgp-cli.md:20 +msgid "\"show ip bgp summary json\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:26 +msgid "'sudo'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:26 +msgid "'-c'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:30 +msgid "We can also verify by running `vtysh` directly:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:59 +msgid "`config` Command" +msgstr "" + +#: src\5-2-1-bgp-cli.md:61 +msgid "" +"Meanwhile, the `config` command directly operates on `CONFIG_DB` to achieve " +"configuration changes." +msgstr "" + +#: src\5-2-1-bgp-cli.md:63 +msgid "Take remove neighbor as an example. The key code is shown as follows:" +msgstr "" + +#: src\5-2-1-bgp-cli.md:66 +msgid "# file: src/sonic-utilities/config/main.py" +msgstr "" + +#: src\5-2-1-bgp-cli.md:70 +msgid "\"Remove BGP neighbor configuration from the device\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:73 +msgid "'neighbor'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:74 +msgid "'neighbor_ip_or_hostname'" +msgstr "" + +#: src\5-2-1-bgp-cli.md:74 +msgid "''" +msgstr "" + +#: src\5-2-1-bgp-cli.md:76 +msgid "" +"\"\"\"Removes BGP neighbor configuration (internal or external) from the " +"device\"\"\"" +msgstr "" + +#: src\5-2-1-bgp-cli.md:93 src\5-2-2-route-update-in-frr.md:741 +#: src\5-2-3-route-update-in-sonic.md:740 +msgid "" +"[Github repo: sonic-utilities](https://github.com/sonic-net/sonic-utilities)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:3 +msgid "" +"Route update is almost the most important workflow in SONiC. The entire " +"process starts from the `bgpd` process and eventually reaches the ASIC chip " +"through SAI. Many processes are involved in between, and the workflow is " +"quite complex. However, once we understand it, we can understand the design " +"of SONiC and many other configuration workflows much better. Therefore, in " +"this section, we will deeply dive into its overall process." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:5 +msgid "" +"To help us understand the workflow on the code level, we divide this " +"workflow into two major parts: how FRR handles route changes in this " +"chapter, and how the SONiC updates the routes and integrates with FRR in the " +"next chapter." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:7 +msgid "FRR Handling Route Changes" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:38 +msgid "" +"```admonish note\n" +"Regarding the implementation of FRR, this section focuses more on explaining " +"its workflow from the code perspective rather than the details of its BGP " +"implementation. If you want to learn about the details of FRR's BGP " +"implementation, you can refer to the [official " +"documentation](https://docs.frrouting.org/en/latest/bgp.html).\n" +"```" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:42 +msgid "`bgpd` Handling Route Changes" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:44 +msgid "" +"`bgpd` is the process in FRR specifically used to handle BGP sessions. It " +"opens TCP port 179 to establish BGP connections with neighbors and handles " +"routing table update requests. When a route changes, FRR also uses this " +"session to notify other neighbors." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:46 +msgid "" +"When a request arrives at `bgpd`, it will land on the io thread first: " +"`bgp_io`. As the name suggests, this thread is responsible for network read " +"and write operations in `bgpd`:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:49 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_io.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:55 +msgid "// Read packets here" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:58 +msgid "// If we have more than 1 complete packet, mark it and process it later." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:73 +msgid "" +"After the packet is read, `bgpd` sends it to the main thread for processing. " +"Here, `bgpd` dispatches the packet based on its type. And the route update " +"requests will be handed over to `bpg_update_receive` for processing:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:76 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_packet.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:86 +msgid "/* read in the packet length and type */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:102 +msgid "// Process BGP UPDATE message for peer." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:111 +msgid "// Parse attributes and NLRI" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:122 +msgid "// More parsing here" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:128 +msgid "/* End-of-RIB received */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:133 +msgid "/* Best path selection */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:147 +msgid "" +"Then, `bgpd` starts checking for better paths and updates its local routing " +"table (RIB, Routing Information Base):" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:150 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_route.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:150 +msgid "/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:159 +msgid "/* Process the route list */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:183 +msgid "/* Best path selection. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:189 +msgid "/* FIB update. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:204 +msgid "/* Withdraw the route from the kernel. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:209 +msgid "/* EVPN route injection and clean up */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:217 +msgid "" +"Finally, `bgp_zebra_announce` notifies `zebra` to update the kernel routing " +"table through `zclient`." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:220 src\5-2-2-route-update-in-frr.md:231 +msgid "// File: src/sonic-frr/frr/bgpd/bgp_zebra.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:228 +msgid "" +"`zclient` communicates with `zebra` using a local socket and provides a " +"series of callback functions to receive notifications from `zebra`. The key " +"code is shown as follows:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:236 +msgid "/* Set default values. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:255 +msgid "/* Connect to zebra. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:264 +msgid "" +"In the `bgpd` container, we can find the socket file used for `zebra` " +"communication in the `/run/frr` directory for simple verification:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:273 +msgid "`zebra` Updating Routing Table" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:275 +msgid "" +"Since FRR supports many routing protocols, if each routing protocol updates " +"kernel independently, conflicts will inevitably arise, because it is " +"difficult to coordinate. Therefore, FRR uses a separate process to " +"communicate with all routing protocol handling processes, merges the " +"information, and then update the kernel routing table. This process is " +"`zebra`." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:277 +msgid "" +"In `zebra`, kernel updates occur in a separate data plane handling thread: " +"`dplane_thread`. All requests are sent to `zebra` through `zclient`, then " +"get processed, and finally get forwarded to `dplane_thread` for handling. In " +"whis way, the route update will always be in order, which avoids any " +"conflicts to happen." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:279 +msgid "" +"When `zebra` starts, it registers all request handlers. When a request " +"arrives, the corresponding handler will be called based on the request type. " +"And here is the key code:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:282 src\5-2-2-route-update-in-frr.md:298 +msgid "// File: src/sonic-frr/frr/zebra/zapi_msg.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:295 +msgid "" +"Take adding a route (`zread_route_add`) as an example to explain the later " +"workflow. From the following code, we can see that when a new route arrives, " +"`zebra` will start checking and updating its internal routing table:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:307 +msgid "// Decode zclient request" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:314 +msgid "// Allocate new route entry." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:320 +msgid "// Init nexthop entry, if we have an id, then add route." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:328 +msgid "// Update stats. IPv6 is omitted here for simplicity." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:332 +msgid "// File: src/sonic-frr/frr/zebra/zebra_rib.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:344 +msgid "/* Find table and nexthop entry */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:349 +msgid "/* Attach the re to the nhe's nexthop group. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:352 +msgid "/* Make it sure prefixlen is applied to the prefix. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:353 +msgid "/* Set default distance by route type. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:356 +msgid "/* Lookup route node.*/" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:360 +msgid "" +"/* If this route is kernel/connected route, notify the dataplane to update " +"kernel route table. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:365 +msgid "/* Link new re to node. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:369 +msgid "/* Clean up */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:375 +msgid "" +"Here, `rib_addnode` will forward this route add request to the rib " +"processing thread, where the requests are being processed sequentially:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:395 +msgid "" +"Then, the request arrives at the RIB processing thread: `rib_process`, which " +"further selects the best route and adds it to `zebra`'s internal routing " +"table (RIB):" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:398 +msgid "/* Core function for processing routing information base. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:415 +msgid "/* Check every route entry and select the best route. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:433 +msgid "/* RNODE_FOREACH_RE */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:436 +msgid "/* Update fib according to selection results */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:444 +msgid "/* Remove all RE entries queued for removal */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:445 +msgid "/* Check if the dest can be deleted now. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:450 +msgid "" +"For new routes, `rib_process_add_fib` is called to add them to `zebra`'s " +"internal routing table and notify the dplane to update the kernel routing " +"table:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:455 +msgid "\"new route selected\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:458 +msgid "/* If labeled-unicast route, install transit LSP. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:474 +msgid "/* Install the resolved nexthop object first. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:477 +msgid "" +"/* If this is a replace to a new RE let the originator of the RE know that " +"they've lost */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:481 +msgid "/* Update fib selection */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:484 +msgid "" +"/* Make sure we update the FPM any time we send new information to the " +"kernel. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:485 +msgid "\"installing in kernel\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:487 +msgid "/* Send add or update */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:494 +msgid "" +"There are two important operations here: one is to call the `dplane_route_*` " +"functions to update the kernel routing table, and the other is the " +"`hook_call` that appears twice here. The FPM hook function is hooked here to " +"receive and forward routing table update notifications." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:496 +msgid "Here, let's look at them one by one:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:498 +msgid "`dplane` Updating Kernel Routing Table" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:500 +msgid "" +"Let's look at the dplane `dplane_route_*` functions first. They are " +"essentially do the same thing: simply pack the request and put it into the " +"`dplane_thread` message queue:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:503 src\5-2-2-route-update-in-frr.md:541 +msgid "// File: src/sonic-frr/frr/zebra/zebra_dplane.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:522 +msgid "/* Create and init context */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:525 +msgid "/* Enqueue context for processing */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:528 +msgid "/* Update counter */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:538 +msgid "" +"Then, on the data plane handling thread `dplane_thread`, in its message " +"loop, it take messages from the queue one by one and call their handling " +"functions:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:549 +msgid "/* Process work here */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:552 +msgid "/* Check for zebra shutdown */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:553 +msgid "/* Dequeue completed work from the provider */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:556 +msgid "/* Locate next provider */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:564 +msgid "" +"By default, `dplane_thread` uses `kernel_dplane_process_func` to process the " +"messages. Inside this function, different kernel operations will be invoked " +"based on the request type:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:578 +msgid "" +"/* A previous provider plugin may have asked to skip the kernel update. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:584 +msgid "/* Dispatch to appropriate kernel-facing apis */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:602 +msgid "/* Call into the synchronous kernel-facing code here */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:608 +msgid "" +"And `kernel_route_update` is the real kernel operation. It notifies the " +"kernel of route updates through netlink:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:611 +msgid "// File: src/sonic-frr/frr/zebra/rt_netlink.c" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:611 +msgid "" +"// Update or delete a prefix from the kernel, using info from a dataplane " +"context." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:633 +msgid "" +"// Routing table change via netlink interface, using a dataplane context " +"object" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:637 +msgid "// Build netlink request." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:648 +msgid "/* Talk to netlink socket. */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:653 +msgid "FPM Route Update Forwarding" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:655 +msgid "" +"FPM (Forwarding Plane Manager) is the protocol in FRR used to notify other " +"processes of route changes. Its main logic code is in " +"`src/sonic-frr/frr/zebra/zebra_fpm.c`. It supports two protocols by default: " +"`protobuf` and `netlink`. The one used in SONiC is the `netlink` protocol." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:657 +msgid "" +"As mentioned earlier, it is implemented through hook functions. By listening " +"for route changes in the RIB, the updates are forwarded to other processes " +"through a local socket. This hook is registered at startup. And the most " +"relevant one to us is the `rib_update` hook, as shown below:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:669 +msgid "\"zebra_fpm\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:670 +msgid "\"zebra FPM (Forwarding Plane Manager) module\"" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:675 +msgid "" +"When the `rib_update` hook is called, the `zfpm_trigger_update` function " +"will be called, which puts the route update info into the fpm forwarding " +"queue and triggers a write operation:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:683 +msgid "// Queue the update request" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:698 +msgid "" +"The write callback takes the update from the queue, converts it into the FPM " +"message format, and forwards it to other processes through a local socket:" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:709 +msgid "// Convert route info to buffer here." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:712 +msgid "" +"// Write to socket until we don' have anything to write or cannot write " +"anymore (partial write)." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:726 +msgid "" +"/* Stop processing the queues if zfpm_g->obuf is full or we do not have more " +"updates to process */" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:733 +msgid "At this point, FRR's work is done." +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:745 src\5-2-3-route-update-in-sonic.md:744 +msgid "[FRRouting - BGP](https://datatracker.ietf.org/doc/html/rfc4271)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:746 src\5-2-3-route-update-in-sonic.md:745 +msgid "" +"[FRRouting - " +"FPM](https://docs.frrouting.org/projects/dev-guide/en/latest/fpm.html)" +msgstr "" + +#: src\5-2-2-route-update-in-frr.md:747 src\5-2-3-route-update-in-sonic.md:746 +msgid "" +"[Understanding EVPN Pure Type 5 " +"Routes](https://www.juniper.net/documentation/us/en/software/junos/evpn-vxlan/topics/concept/evpn-route-type5-understanding.html)" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:3 +msgid "" +"After the work of FRR is done, the route update information is forwarded to " +"SONiC, either via Netlink or FPM. This causes a series of operations in " +"SONiC, and eventually updates the route table in the ASIC." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:5 +msgid "The main workflow is shown as below:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:40 +msgid "`fpmsyncd` Updating Route Configuration in Redis" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:42 +msgid "" +"First, let's start from the source. When `fpmsyncd` launches, it starts " +"listening for FPM and Netlink events to receive route change messages and " +"forward to `RouteSync` for processing:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:45 +msgid "// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:54 +msgid "// Register netlink message handler" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:68 +msgid "// Launching FPM server and wait for zebra to connect." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:75 +msgid "" +"// If connection is closed, keep retrying until it succeeds, before handling " +"any other events." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:76 +msgid "\"Connection lost, reconnecting...\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:83 +msgid "" +"In `FpmLink`, the FPM events will be converted into Netlink messages. This " +"unifies the message that being sent to `RouteSync` to Netlink. And " +"`RouteSync::onMsg` will be called for processing them (for how Netlink " +"receives and processes messages, please refer to [4.1.2 " +"Netlink](./4-1-2-netlink.html)):" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:85 +msgid "" +"One small thing to notice is that - EVPN Type 5 messages must be processed " +"in raw message form, so `RouteSync::onMsgRaw` will be called." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:88 +msgid "// File: src/sonic-swss/fpmsyncd/fpmlink.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:88 +msgid "// Called from: FpmLink::readData()" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:96 +msgid "/* Read all netlink messages inside FPM message */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:99 src\5-2-3-route-update-in-sonic.md:341 +msgid "/*" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:112 +msgid "/* EVPN Type5 Add route processing */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:113 +msgid "/* This will call into onRawMsg() */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:116 +msgid "/* This will call into onMsg() */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:130 +msgid "" +"Next, when `RouteSync` receives a route change message, it makes judgments " +"and dispatches in `onMsg` and `onMsgRaw`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:133 +#: src\5-2-3-route-update-in-sonic.md:188 +msgid "// File: src/sonic-swss/fpmsyncd/routesync.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:144 +msgid "// Refill Netlink cache here" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:158 +msgid "/* If the master device name starts with VNET_PREFIX, it is a VNET route." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:165 +msgid "/* Otherwise, it is a regular route (include VRF route). */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:175 +msgid "" +"From the code above, we can see that there are four different route " +"processing entry points. These different routes will be finally written to " +"different tables in `APPL_DB` through their respective " +"[ProducerStateTable](./4-2-5-producer-consumer-state-table.md):" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Route Type" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Entry Point" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:177 +msgid "Table" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "MPLS" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "`onLabelRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:179 +msgid "LABLE_ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +msgid "Vnet VxLan Tunnel Route" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "`onVnetRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:180 +msgid "VNET_ROUTE_TUNNEL_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "Other Vnet Routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:181 +msgid "VNET_ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +msgid "EVPN Type 5" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +msgid "`onEvpnRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:182 +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "ROUTE_TABLE" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "Regular Routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:183 +msgid "`onRouteMsg`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:185 +msgid "" +"Here we take regular routes as an example. The implementation of other " +"functions is different, but the basic idea is the same:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:191 +msgid "// Parse route info from nl_object here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:194 +msgid "// Get nexthop lists" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:201 +msgid "" +"// Build route info here, including protocol, interface, next hops, MPLS, " +"weights etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:203 +msgid "\"protocol\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:204 +msgid "\"nexthop\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:211 +msgid "// Push to ROUTE_TABLE via ProducerStateTable." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:213 +msgid "\"RouteTable set msg: %s %s %s %s\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:218 +msgid "`orchagent` Processing Route Configuration Changes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:220 +msgid "" +"Next, these route information will come to `orchagent`. When `orchagent` " +"starts, it creates `VNetRouteOrch` and `RouteOrch` objects, which are used " +"to listen and process Vnet-related routes and EVPN/regular routes " +"respectively:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:223 +#: src\5-2-3-route-update-in-sonic.md:439 +msgid "// File: src/sonic-swss/orchagent/orchdaemon.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:242 +msgid "" +"The entry function that process the incoming messages for all Orch objects " +"is `doTask`. `RouteOrch` and `VNetRouteOrch` are the same. Here we take " +"`RouteOrch` as an example to see how it handles route changes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:248 +msgid "" +"Before we dive into the code, we have a few things to note for `RouteOrch`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:250 +msgid "" +"From the above `init` function, we can see that `RouteOrch` not only manages " +"regular routes but also manages MPLS routes. The logic for handling these " +"two types of routes is different. Therefore, in the following code, to " +"simplify, we only show the logic for handling the regular routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:251 +msgid "" +"Since `ProducerStateTable` transmits and receives messages in batches, " +"`RouteOrch` also processes the route updates in batches. To support batch " +"processing, `RouteOrch` uses `EntityBulker gRouteBulker` to " +"cache the SAI route objects that need to be changed, and then applies these " +"route object changes to SAI at the end of the `doTask()` function." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:252 +msgid "" +"Route operations require a lot of other information, such as the status of " +"each port, the status of each neighbor, the status of each VRF, etc. To " +"obtain this information, `RouteOrch` interacts with other Orch objects, such " +"as `PortOrch`, `NeighOrch`, `VRFOrch`, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:254 +msgid "" +"Let's start with the `RouteOrch::doTask` function. It parses the incoming " +"route operation messages, then calls the `addRoute` or `removeRoute` " +"function to create or delete routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:257 +#: src\5-2-3-route-update-in-sonic.md:381 +msgid "// File: src/sonic-swss/orchagent/routeorch.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:260 +msgid "" +"// Calling PortOrch to make sure all ports are ready before processing route " +"messages." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:263 +msgid "" +"// Call doLabelTask() instead, if the incoming messages are from MPLS " +"messages. Otherwise, move on as regular routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:266 +msgid "/* Default handling is for ROUTE_TABLE (regular routes) */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:269 +msgid "// Add or remove routes with a route bulker" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:274 +msgid "// Parse route operation from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:279 +msgid "// resync application:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:280 +msgid "" +"// - When routeorch receives 'resync' message (key = \"resync\", op = " +"\"SET\"), it marks all current routes as dirty" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:281 +msgid "" +"// and waits for 'resync complete' message. For all newly received routes, " +"if they match current dirty routes," +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:282 +msgid "// it unmarks them dirty." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:283 +msgid "" +"// - After receiving 'resync complete' (key = \"resync\", op != \"SET\") " +"message, it creates all newly added routes" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:284 +msgid "// and removes all dirty routes." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:287 +msgid "// Parsing VRF and IP prefix from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:290 +msgid "// Process regular route operations." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:293 +msgid "// Parse and validate route attributes from the incoming message here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:298 +msgid "" +"// If the nexthop_group is empty, create the next hop group key based on the " +"IPs and aliases." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:299 +msgid "" +"// Otherwise, get the key from the NhgOrch. The result will be stored in the " +"\"nhg\" variable below." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:304 +msgid "" +"// Here the nexthop_group is empty, so we create the next hop group key " +"based on the IPs and aliases." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:323 +msgid "// Here we have a nexthop_group, so we get the key from the NhgOrch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:330 +msgid "// Now we start to create the SAI route entry." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:333 +msgid "" +"// Skip certain routes, such as not valid, directly routes to tun0, " +"linklocal or multicast routes, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:336 +msgid "// Create SAI route entry in addRoute function." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:357 +msgid "// Handle other ops, like DEL_COMMAND for route deletion, etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:361 +msgid "// Flush the route bulker, so routes will be written to syncd and ASIC" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:364 +msgid "// Go through the bulker results." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:365 +msgid "" +"// Handle SAI failures, update neighbors, counters, send notifications in " +"add/removeRoutePost functions." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:368 +msgid "/* Remove next hop group if the reference count decreases to zero */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:374 +msgid "Here we take `addRoute` as an example. It mainly does a few things below:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:376 +msgid "" +"Get next hop information from `NeighOrch` and check if the next hop is " +"really available." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:377 +msgid "" +"If the route is a new or re-added back while waiting to be deleted, a new " +"SAI route object will be created." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:378 +msgid "If it is an existing route, the existing SAI route object is updated." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:384 +msgid "// Get nexthop information from NeighOrch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:385 +msgid "" +"// We also need to check PortOrch for inband port, IntfsOrch to ensure the " +"related interface is created and etc." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:388 +msgid "// Start to sync the SAI route entry." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:397 +msgid "// Create a new route entry in this case." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:398 +msgid "//" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:399 +msgid "" +"// In case the entry is already pending removal in the bulk, it would be " +"removed from m_syncdRoutes during the bulk call." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:400 +msgid "" +"// Therefore, such entries need to be re-created rather than set attribute." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:410 +msgid "/* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:418 +msgid "// Update existing route entry in this case." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:420 +msgid "" +"// Set the packet action to forward when there was no next hop (dropped) and " +"not pointing to blackhole." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:429 +msgid "" +"// Only 1 case is listed here as an example. Other cases are handled with " +"similar logic by calling set_entry_attributes as well." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:436 +msgid "" +"After creating and setting up all the routes, `RouteOrch` calls " +"`gRouteBulker.flush()` to write all the routes to `ASIC_DB`. The `flush()` " +"function is straightforward: it processes all requests in batches, with each " +"batch being 1000 by default, defined in `OrchDaemon` and passed through the " +"constructor:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:442 +msgid "// File: src/sonic-swss/orchagent/bulker.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:454 +msgid "// Bulk remove entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:456 +#: src\5-2-3-route-update-in-sonic.md:492 +msgid "" +"// Split into batches of max_bulk_size, then call flush. Similar to " +"creating_entries, so details are omitted." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:463 +msgid "// Bulk create entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:465 +msgid "" +"// Split into batches of max_bulk_size, then call flush_creating_entries to " +"call SAI batch create API to create" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:466 +msgid "// the objects in batch." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:479 +msgid "// Batch create here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:490 +msgid "// Bulk update existing entries" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:509 +msgid "// Call SAI bulk create API" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:515 +msgid "// Set results back to input entries and clean up the batch below." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:528 +msgid "" +"// flush_removing_entries and flush_setting_entries are similar to " +"flush_creating_entries, so we omit them here." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:533 +msgid "SAI Object Forwarding in `orchagent`" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:535 +msgid "" +"At this point, you might have noticed something strange - The `EntityBulker` " +"seems to be directly calling the SAI API. Shouldn't they be called in " +"`syncd`? If we follow the SAI API objects passed to `EntityBulker`, we will " +"even find that `sai_route_api_t` is indeed the SAI interface, and there is " +"SAI initialization code in `orchagent`, as follows:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:538 +msgid "" +"// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:538 +#: src\5-2-3-route-update-in-sonic.md:700 +msgid "/**" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:554 +msgid "// File: src/sonic-swss/orchagent/saihelper.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:562 +msgid "\"Context config file %s exists\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:585 +msgid "" +"I believe whoever saw this code for the first time will definitely feel " +"confused. But don't worry, this is actually the SAI object forwarding " +"mechanism in `orchagent`." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:587 +msgid "" +"If you are familiar with RPC, the `proxy-stub` pattern might sounds very " +"familar to you - using a unified way to define the interfaces called by both " +"parties in communication, implementing message serialization and sending on " +"the client side, and implementing message receiving, deserialization, and " +"dispatching on the server side. Here, SONiC does something similar: using " +"the SAI API itself as a unified interface, implementing message " +"serialization and sending for `orchagent` to call, and implementing message " +"receiving, deserialization, and dispatch functions in `syncd`." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:589 +msgid "" +"Here, the sending end is called `ClientSai`, implemented in " +"`src/sonic-sairedis/lib/ClientSai.*`. Serialization and deserialization are " +"implemented in SAI metadata: `src/sonic-sairedis/meta/sai_serialize.h`:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:592 +msgid "// File: src/sonic-sairedis/lib/ClientSai.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:601 +msgid "// File: src/sonic-sairedis/meta/sai_serialize.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:602 +msgid "// Serialize" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:606 +msgid "// Deserialize" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:612 +msgid "" +"When `orchagent` is compiled, it links to `libsairedis`, which implements " +"the SAI client and handles the serialization and message sending:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:615 +msgid "# File: src/sonic-swss/orchagent/Makefile.am" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:616 +msgid "" +"$(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta " +"-lsaimetadata -lswsscommon -lzmq" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:619 +msgid "" +"Here, we use Bulk Create as an example to see how `ClientSai` serializes and " +"sends the SAI API call:" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:622 +#: src\5-2-3-route-update-in-sonic.md:709 +msgid "// File: src/sonic-sairedis/lib/ClientSai.cpp" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:638 +msgid "" +"// Server is responsible for generate new OID but for that we need switch ID" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:639 +msgid "// to be sent to server as well, so instead of sending empty oids we will" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:640 +msgid "// send switch IDs" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:646 +msgid "" +"// Since user requested create, OID value was created remotely and it was " +"returned in m_lastCreateOids" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:668 +msgid "// Calling SAI serialize APIs to serialize all objects" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:674 +msgid "\"NULL\"" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:684 +msgid "// Send to syncd via the communication channel." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:687 +msgid "// Wait for response from syncd." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:692 +msgid "" +"Finally, `ClientSai` calls `m_communicationChannel->set()` to send the " +"serialized SAI objects to `syncd`. This channel, before the 202106 version, " +"was the [ProducerTable based on " +"Redis](https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h). " +"Possibly for efficiency reasons, starting from the 202111 version, this " +"channel has been changed to " +"[ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/ZeroMQChannel.h)." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:695 +msgid "" +"// File: " +"https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h" +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:728 +msgid "" +"For the inter-process communication, we are going to skip the details here. " +"Please feel free to refer to the [Redis-based " +"channels](./4-2-redis-based-channels.md) described in Chapter 4." +msgstr "" + +#: src\5-2-3-route-update-in-sonic.md:730 +msgid "`syncd` Updating ASIC" msgstr "" -#: src/5-2-2-bgp-route-update-workflow.md:1462 +#: src\5-2-3-route-update-in-sonic.md:732 msgid "" -"1. [SONiC Architecture][SONiCArch]\n" -"2. [Github repo: sonic-swss][SONiCSWSS]\n" -"3. [Github repo: sonic-swss-common][SONiCSWSSCommon]\n" -"4. [Github repo: sonic-frr][SONiCFRR]\n" -"5. [Github repo: sonic-utilities][SONiCUtil]\n" -"6. [Github repo: sonic-sairedis][SONiCSAIRedis]\n" -"7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP]\n" -"8. [FRRouting][FRRouting]\n" -"9. [FRRouting - BGP][BGP]\n" -"10. [FRRouting - FPM][FPM]\n" -"11. [Understanding EVPN Pure Type 5 Routes][EVPN]" +"Finally, when the SAI objects are generated and sent to `syncd`, `syncd` " +"will receive, processa and updates `ASIC_DB`, then finally updates the ASIC. " +"We have already described this workflow in detail in the [Syncd-SAI " +"Workflow](./5-1-syncd-and-sai.md), so we will skip them here. For more " +"details, please refer to the [Syncd-SAI Workflow " +"chapter](./5-1-syncd-and-sai.md)." msgstr "" -#: src/6-boot.md:1 -msgid "# 启动流程" +#: src\6-boot.md:1 +msgid "Boot" msgstr "" -#: src/6-1-cold-boot.md:1 -msgid "# 冷启动" +#: src\6-1-cold-boot.md:1 +msgid "Cold Boot" msgstr "" -#: src/6-2-fast-boot.md:1 -msgid "# 快速启动" +#: src\6-2-fast-boot.md:1 +msgid "Fast Boot" msgstr "" -#: src/6-3-warm-boot.md:1 -msgid "# 热启动" +#: src\6-3-warm-boot.md:1 +msgid "Warm Boot" msgstr "" diff --git a/src/1-1-install.md b/src/1-1-install.md index 67f1854..dfa7a01 100644 --- a/src/1-1-install.md +++ b/src/1-1-install.md @@ -1,63 +1,63 @@ -# 安装 +# Installation -如果你自己就拥有一台交换机,或者想购买一台交换机,在上面安装SONiC,那么请认真阅读这一小节,否则可以自行跳过。:D +If you already own a switch or are planning to purchase one and install SONiC on it, please read this section carefully. Otherwise, feel free to skip it. :D -## 交换机选择和SONiC安装 +## Switch Selection and SONiC Installation -首先,请确认你的交换机是否支持SONiC,SONiC目前支持的交换机型号可以在[这里][SONiCDevices]找到,如果你的交换机型号不在列表中,那么就需要联系厂商,看看是否有支持SONiC的计划。有很多交换机是不支持SONiC的,比如: +First, please confirm if your switch supports SONiC. The list of currently supported switch models can be found [here][SONiCDevices]. If your switch model is not on the list, you will need to contact the manufacturer to see if they have plans to support SONiC. There are many switches that do not support SONiC, such as: -1. 普通针对家用的交换机,这些交换机的硬件配置都比较低(即便支持的带宽很高,比如[MikroTik CRS504-4XQ-IN][MikroTik100G],虽然它支持100GbE网络,但是它只有16MB的Flash存储和64MB的RAM,所以基本只能跑它自己的RouterOS了)。 -2. 有些虽然是数据中心用的交换机,但是可能由于型号老旧,厂商并没有计划支持SONiC。 +1. Regular switches for home use. These switches have relatively low hardware configurations (even if they support high bandwidth, such as [MikroTik CRS504-4XQ-IN][MikroTik100G], which supports 100GbE networks but only has 16MB of flash storage and 64MB of RAM, so it can basically only run its own RouterOS). +2. Some data center switches may not support SONiC due to their outdated models and lack of manufacturer plans. -对于安装过程,由于每一家厂商的交换机设计不同,其底层接口各有差别,所以,其安装方法也都有所差别,这些差别主要集中在两个地方: +Regarding the installation process, since each manufacturer's switch design is different, the underlying interfaces are also different, so the installation methods vary. These differences mainly focus on two areas: -1. 每个厂商都会有自己的[SONiC Build][SONiCDevices],还有的厂商会在SONiC的基础之上进行扩展开发,为自己的交换机支持更多的功能,比如:[Dell Enterprise SONiC][DellSonic],[EdgeCore Enterprise SONiC][EdgeCoreSONiC],所以需要根据自己的交换机选择对应的版本。 -2. 每个厂商的交换机也会支持不同的安装方式,有一些是直接使用USB对ROM进行Flash,有一些是通过ONIE进行安装,这也需要根据自己的交换机来进行配置。 +1. Each manufacturer will have their own [SONiC Build][SONiCDevices], and some manufacturers will extend development on top of SONiC to support more features for their switches, such as [Dell Enterprise SONiC][DellSONiC] and [EdgeCore Enterprise SONiC][EdgeCoreSONiC]. Therefore, you need to choose the corresponding version based on your switch model. +2. Each manufacturer's switch will also support different installation methods, some using USB to flash the ROM directly, and some using ONIE for installation. This configuration needs to be done according to your specific switch. -所以,虽然安装方法各有差别,但是总体而言,安装的步骤都是差不多的。请联系自己的厂商,获取对应的安装文档,然后按照文档进行安装即可。 +Although the installation methods may vary, the overall steps are similar. Please contact your manufacturer to obtain the corresponding installation documentation and follow the instructions to complete the installation. -## 配置交换机 +## Configure the Switch -安装好之后,我们需要进行一些基础设置,部分设置是通用的,我们在这里简单总结一下。 +After installation, we need to perform some basic settings. Some settings are common, and we will summarize them here. -### 设置admin密码 +### Set the admin password -默认SONiC的账号密码是admin:YourPaSsWoRd,使用默认密码显然不安全: +The default SONiC account and password is `admin` and `YourPaSsWoRd`. Using default password is obviously not secure. To change the password, we can run the following command: ```bash sudo passwd admin ``` -### 设置风扇转速 +### Set fan speed -数据中心用的交换机风扇声音都特别的大!比如,我用的交换机是Arista 7050QX-32S,上面有4个风扇,最高能到每分钟17000转,放在车库中,高频的啸叫即便是在二楼隔着3面墙还是能听得到,所以如果你是在家使用的话,建议对其进行一些设置,将转速调低。 +Data center switches are usually very noisy! For example, the switch I use is Arista 7050QX-32S, which has 4 fans that can spin up to 17000 RPM. Even if it is placed in the garage, the high-frequency whining can still be heard behind 3 walls on the second floor. Therefore, if you are using it at home, it is recommended to adjust the fan speed. -可惜,[由于SONiC并没有cli对风扇转速的规则进行控制][SONiCThermal],所以我们需要通过手动修改pmon容器中的配置文件的方式来进行设置。 +Unfortunately, [SONiC does not have CLI control over fan speed][SONiCThermal], so we need to manually modify the configuration file in the pmon container to adjust the fan speed. ```bash -# Enter pmon container +# Enter the pmon container sudo docker exec -it pmon bash -# Use pwmconfig to detect all pwm fans and create configuration file. The configuration file will be created at /etc/fancontrol. +# Use pwmconfig to detect all PWM fans and create a configuration file. The configuration file will be created at /etc/fancontrol. pwmconfig # Start fancontrol and make sure it works. If it doesn't work, you can run fancontrol directly to see what's wrong. VERBOSE=1 /etc/init.d/fancontrol start VERBOSE=1 /etc/init.d/fancontrol status -# Exit pmon container +# Exit the pmon container exit # Copy the configuration file from the container to the host, so that the configuration will not be lost after reboot. -# This command needs to know what is the model of your switch, for example, the command I need to run here is as follows. If your switch model is different, please modify it yourself. +# This command needs to know what is the model of your switch. For example, the command I need to run here is as follows. If your switch model is different, please modify it accordingly. sudo docker cp pmon:/etc/fancontrol /usr/share/sonic/device/x86_64-arista_7050_qx32s/fancontrol ``` -### 设置交换机Management Port IP +### Set the Switch Management Port IP -一般的数据中心用的交换机都提供了Serial Console连接的方式,但是其速度实在是太慢了,所以我们在安装完成之后,都会尽快的把Management Port给设置好,然后通过SSH的方式来进行管理。 +Data center switches usually can be connected via Serial Console, but its speed is very slow. Therefore, after installation, it is better to set up the Management Port as soon as possible, then use SSH connection. -一般来说,management port的设备名是eth0,所以我们可以通过SONiC的配置命令来进行设置: +Generally, the management port is named eth0, so we can use SONiC's configuration command to set it up: ```bash # sudo config interface ip add eth0 @@ -68,9 +68,9 @@ sudo config interface ip add eth0 192.168.1.2/24 192.168.1.1 sudo config interface ip add eth0 2001::8/64 2001::1 ``` -### 创建网络配置 +### Create Network Configuration -新安装完的SONiC交换机会有一个默认的网络配置,这个配置有很多问题,比如对于10.0.0.0的IP的使用,如下: +A newly installed SONiC switch will have a default network configuration, which has many issues, such as using 10.0.0.0 IP on Ethernet0, as shown below: ```bash admin@sonic:~$ show ip interfaces @@ -81,31 +81,31 @@ Ethernet4 10.0.0.2/31 up/up ARISTA02T2 10.0.0 Ethernet8 10.0.0.4/31 up/up ARISTA03T2 10.0.0.5 ``` -所以我们需要创建一个新的网络配置,然后将我们使用的Port都放入到这个网络配置中。这里简单的方法就是创建一个VLAN,使用VLAN Routing: +Therefore, we need to update the ports with a new network configuration. A simple method is to create a VLAN and use VLAN Routing: ```bash -# Create untagged vlan +# Create untagged VLAN sudo config vlan add 2 -# Add IP to vlan +# Add IP to VLAN sudo config interface ip add Vlan2 10.2.0.0/24 # Remove all default IP settings show ip interfaces | tail -n +3 | grep Ethernet | awk '{print "sudo config interface ip remove", $1, $2}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh -# Add all ports to the new vlan +# Add all ports to the new VLAN show interfaces status | tail -n +3 | grep Ethernet | awk '{print "sudo config vlan member add -u 2", $1}' > oobe.sh; chmod +x oobe.sh; ./oobe.sh -# Enable proxy arp, so switch can respond to arp requests from hosts +# Enable proxy ARP, so the switch can respond to ARP requests from hosts sudo config vlan proxy_arp 2 enabled -# Save config, so it will be persistent after reboot +# Save the config, so it will be persistent after reboot sudo config save -y ``` -这样就完成了,我们可以通过show vlan brief来查看一下: +That's it! Now we can use `show vlan brief` to check it: -``` +```text admin@sonic:~$ show vlan brief +-----------+--------------+-------------+----------------+-------------+-----------------------+ | VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | DHCP Helper Address | @@ -116,13 +116,13 @@ admin@sonic:~$ show vlan brief +-----------+--------------+-------------+----------------+-------------+-----------------------+ ``` -### 配置主机 +### Configure the Host -如果你家里只有一台主机使用多网口连接交换机进行测试,那么我们还需要在主机上进行一些配置,以保证流量会通过网卡,流经交换机,否则,请跳过这一步。 +If you only have one host at home using multiple NICs to connect to the switch for testing, we need to update some settings on the host to ensure that traffic flows through the NIC and the switch. Otherwise, feel free to skip this step. -这里网上的攻略很多,比如使用iptables中的DNAT和SNAT创建一个虚拟地址,但是过程非常繁琐,经过一些实验,我发现最简单的办法就是将其中一个网口移动到一个新的网络命名空间中,就可以了,即便使用的是同一个网段的IP,也不会有问题。 +There are many online guides for this, such as using DNAT and SNAT in iptables to create a virtual address. However, after some experiments, I found that the simplest way is to move one of the NICs to a new network namespace, even if it uses the same IP subnet, it will still work. -比如,我家使用的是Netronome Agilio CX 2x40GbE,它会创建两个interface:`enp66s0np0`和`enp66s0np1`,我们这里可以将`enp66s0np1`移动到一个新的网络命名空间中,再配置好ip地址就可以了: +For example, if I use Netronome Agilio CX 2x40GbE at home, it will create two interfaces: `enp66s0np0` and `enp66s0np1`. Here, we can move `enp66s0np1` to a new network namespace and configure the IP address: ```bash # Create a new network namespace @@ -137,10 +137,10 @@ sudo ip netns exec toy-ns-1 ip link set enp66s0np1 up sudo ip netns exec toy-ns-1 ip route add default via 10.2.0.1 ``` -这样就可以了,我们可以通过iperf来测试一下,并在交换机上进行确认: +That's it! We can start testing it using iperf and confirm on the switch: ```bash -# On the host (enp66s0np0 has ip 10.2.0.10 assigned) +# On the host (enp66s0np0 has IP 10.2.0.10 assigned) $ iperf -s --bind 10.2.0.10 # Test within the new network namespace @@ -153,7 +153,7 @@ TCP window size: 85.0 KByte (default) [SUM] 0.0000-10.0301 sec 30.7 GBytes 26.3 Gbits/sec [ CT] final connect times (min/avg/max/stdev) = 0.288/0.465/0.647/0.095 ms (tot/err) = 16/0 -# Confirm on switch +# Confirm on the switch admin@sonic:~$ show interfaces counters IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP TX_OVR ----------- ------- ---------- ------------ --------- -------- -------- -------- ---------- ------------ --------- -------- -------- -------- @@ -161,7 +161,7 @@ admin@sonic:~$ show interfaces counters Ethernet12 U 51,261,888 2086.79 MB/s 41.74% 0 1 0 2,580,317 6191.00 KB/s 0.12% 0 0 0 ``` -# 参考资料 +# References 1. [SONiC Supported Devices and Platforms][SONiCDevices] 2. [SONiC Thermal Control Design][SONiCThermal] diff --git a/src/1-2-hello-world-virtually.md b/src/1-2-hello-world-virtually.md index ef85301..00e3387 100644 --- a/src/1-2-hello-world-virtually.md +++ b/src/1-2-hello-world-virtually.md @@ -1,32 +1,33 @@ -# 虚拟测试环境 +# Hello, World! Virtually -虽然SONiC功能强大,但是大部分时候一台能够支持SONiC系统的交换机价格并不便宜,如果你只是想试一试SONiC,但是又不想花钱买一台SONiC的硬件设备,那么这一章一定不能错过,这一章会总结一下如何通过GNS3在本地搭建一个虚拟的SONiC的Lab,让你可以很快的在本地体验一把SONiC的基本功能。 +Although SONiC is powerful, most of the time the price of a switch that supports SONiC OS is not cheap. If you just want to try SONiC without spending money on a hardware device, then this chapter is a must-read. In this chapter, we will summarize how to build a virtual SONiC lab using GNS3 locally, allowing you to quickly experience the basic functionality of SONiC. -在本地运行SONiC的方法很好几种,比如docker + vswitch,p4软交换机等等,对于初次使用而言,用GNS3可能是最方便快捷的了,所以本文就以GNS3为例,介绍一下如何在本地搭建一个SONiC的Lab。那么,我们就开始吧! +There are several ways to run SONiC locally, such as docker + vswitch, p4 software switch, etc. For first-time users, using GNS3 may be the most convenient way, so here, we will use GNS3 as an example to explain how to build a SONiC lab locally. So, let's get started! -## 安装GNS3 +## Prepare GNS3 -首先,为了让我们方便而且直观的建立测试用的虚拟网络,我们需要先来安装一下GNS3。 +First, in order to easily and intuitively establish a virtual network for testing, we need to install GNS3. -[GNS3,全称为Graphical Network Simulator 3,是一个图形化的网络仿真软件][GNS3]。它支持多种不同的虚拟化技术,比如:QEMU、VMware、VirtualBox等等。这样,我们在等会搭建虚拟网络的时候,就不需要手动的运行很多命令,或者写脚本了,大部分的工作都可以通过图形界面来完成了。 +[GNS3, short for Graphical Network Simulator 3, is (obviously) a graphical network simulation software][GNS3]. It supports various virtualization technologies such as QEMU, VMware, VirtualBox, etc. With it, when we build a virtual network, we don't have to run many commands manually or write scripts. Most of the work can be done through its GUI, which is very convenient. -### 安装依赖 +### Install Dependencies -安装它之前,我们需要先安装几个其他的软件:docker, wireshark, putty, qemu, ubridge, libvirt和bridge-utils,已经装好的小伙伴可以自行跳过。 +Before installing it, we need to install several other software: docker, wireshark, putty, qemu, ubridge, libvirt, and bridge-utils. If you have already installed them, you can skip this step. -首先是Docker,它们的安装过程,大家可以自己通过下面的传送门去安装:[https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/) +First is Docker. You can install it by following the instructions in this link: [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/) -其他的在ubuntu上安装都非常简单,只需要执行下面的命令就可以了。这里安装时要注意,ubridge和Wireshark的安装过程中会询问是不是要创建wireshark用户组来bypass sudo,这里一定要选择Yes。 +Installing the others on Ubuntu is very simple, just execute the following command. Note that during the installation of ubridge and Wireshark, you will be asked if you want to create the wireshark user group to bypass sudo. Be sure to choose Yes. -``` +```bash sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils wireshark putty ubridge ``` -安装好了之后,我们就可以来安装GNS3了。 +Once completed, we can proceed to install GNS3. + +### Install GNS3 -### 安装GNS3 -在Ubuntu上,GNS3的安装非常简单,只需要执行下面的命令就可以了。 +On Ubuntu, the installation of GNS3 is very simple, just execute the following commands: ```bash sudo add-apt-repository ppa:gns3/ppa @@ -34,7 +35,7 @@ sudo apt update sudo apt install gns3-gui gns3-server ``` -然后把你的用户加入到如下的组中,这样GNS3就可以去访问docker,wireshark等功能而不用sudo了。 +Then add your user to the following groups, so that GNS3 can access docker, wireshark, and other functionalities without using sudo. ```bash for g in ubridge libvirt kvm wireshark docker; do @@ -42,78 +43,78 @@ for g in ubridge libvirt kvm wireshark docker; do done ``` -如果你使用的不是Ubuntu,更详细的安装文档可以参考[他们的官方文档][GNS3Install]。 +If you are not using Ubuntu, you can refer to [their official documentation][GNS3Install] for more detailed installation instructions. -## 准备SONiC的镜像 +## Prepare the SONiC Image -在测试之前,我们还需要一个SONiC的镜像。由于需要支持大量不同的厂商,而每个厂商的底层实现都不一样,所以最后每个厂商都会编译一个自己的镜像。这里因为我们在创建虚拟的环境,所以我们需要使用基于VSwitch的镜像来创建虚拟交换机:sonic-vs.img.gz。 +Before testing, we need a SONiC image. Since SONiC supports various vendors with different underlying implementations, each vendor has their own image. In our case, since we are creating a virtual environment, we can use the VSwitch-based image to create virtual switches: sonic-vs.img.gz. -[SONiC镜像的项目在这里](https://github.com/sonic-net/sonic-buildimage),虽然我们可以自己去编译,但是速度实在有点慢,所以为了节省时间,我们可以直接[去这里下载最新的镜像](https://sonic-build.azurewebsites.net/ui/sonic/pipelines/142/builds?branchName=master)。只要找一个最新的成功的Build就行,在Artifacts中找到sonic-vs.img.gz,下载就可以了。 +[The SONiC image project is located here](https://github.com/sonic-net/sonic-buildimage). Although we can compile it ourselves, the process can be slow. To save time, we can directly [download the latest image from here](https://sonic-build.azurewebsites.net/ui/sonic/pipelines/142/builds?branchName=master). Just find the latest successful build and download the sonic-vs.img.gz file from the Artifacts section. -然后,我们来准备一下项目: +Next, let's prepare the project: ```bash git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git cd sonic-buildimage/platform/vs -# 将下载的镜像放在这个目录下,然后运行下面这个命令进行解压缩。 +# Place the downloaded image in this directory and then run the following command to extract it. gzip -d sonic-vs.img.gz -# 下面这个命令会生成GNS3的镜像配置文件 +# The following command will generate the GNS3 image configuration file. ./sonic-gns3a.sh ``` -执行完成之后,我们运行`ls`命令就可以看到我们需要的镜像文件了。 +After executing the above commands, you can run the `ls` command to see the required image file. ```bash r12f@r12f-svr:~/code/sonic/sonic-buildimage/platform/vs $ l total 2.8G ... --rw-rw-r-- 1 r12f r12f 1.1K Apr 18 16:36 SONiC-latest.gns3a # <= 这个是GNS3的镜像配置文件 --rw-rw-r-- 1 r12f r12f 2.8G Apr 18 16:32 sonic-vs.img # <= 这个是我们解压出来的镜像 +-rw-rw-r-- 1 r12f r12f 1.1K Apr 18 16:36 SONiC-latest.gns3a # <= This is the GNS3 image configuration file +-rw-rw-r-- 1 r12f r12f 2.8G Apr 18 16:32 sonic-vs.img # <= This is the image we extracted ... ``` -## 导入镜像 +## Import the Image -现在,在命令行里面输入`gns3`,就可以启动GNS3了。如果你是ssh到另外一台机器上,可以试着启用X11转发,这样就可以在远程运行GNS3,但是图形界面显示在本地了。我就是这样,将GNS3运行在了远程的服务器上,但是图形界面通过MobaXterm显示在了本地的Windows机器上。 +Now, run `gns3` in the command line to start GNS3. If you are SSHed into another machine, you can try enabling X11 forwarding so that you can run GNS3 remotely, with the GUI displayed locally on your machine. And I made this working using MobaXterm on my local machine. -运行起来之后,GNS3会让我们创建一个项目,很简单,填个目录地址就好。如果你是使用的X11转发,请注意,这个目录是在你远程服务器上,而不是本地。 +Once it's up and running, GNS3 will prompt us to create a project. It's simple, just enter a directory. If you are using X11 forwarding, please note that this directory is on your remote server, not local. ![](assets/chapter-1/gns3-new-project.png) -然后,我们就可以通过`File -> Import appliance`来导入我们刚刚生成的镜像了。 +Next, we can import the image we just generated by going to `File -> Import appliance`. ![](assets/chapter-1/gns3-import-appliance-menu.png) -选择我们刚刚生成的`SONiC-latest.gns3a`镜像配置文件,然后点击`Next`。 +Select the `SONiC-latest.gns3a` image configuration file we just generated and click `Next`. ![](assets/chapter-1/gns3-import-appliance-select-image.png) -这个时候就可以看到我们的镜像了,点击`Next`。 +Now you can see our image, click `Next`. ![](assets/chapter-1/gns3-import-appliance-image.png) -这个时候会开始导入镜像,这个过程可能会比较慢,因为GNS3需要将镜像转换成qcow2格式,放入我们的项目目录中。导入完成之后,我们就可以看到我们的镜像了。 +At this point, the image import process will start, which may be slow because GNS3 needs to convert the image to qcow2 format and place it in our project directory. Once completed, we can see our image. ![](assets/chapter-1/gns3-import-appliance-done.png) -好的!完成! +Great! We're done! -## 创建网络 +## Create the Network -好了!现在一切就绪,我们还是创建一个虚拟的网络吧! +Alright! Now that everything is set up, let's create a virtual network! -GNS3的图形界面非常的好用,基本上就是打开侧边栏,把交换机拖进来,把VPC拖进来,然后把线连起来就可以了。连接好之后记得点上面的Play按钮开始网络模拟。这里我们就不多说了,直接上图。 +The GNS3 graphical interface is very user-friendly. Basically, open the sidebar, drag in the switch, drag in the VPC, and then connect the ports. After connecting, remember to click the Play button at the top to start the network simulation. We won't go into much detail here, let's just look at the pictures. ![](assets/chapter-1/gns3-console.png) -接着,在交换机上点击右键,选择`Custom Console`,再选择Putty,就可以打开我们的上面看到的交换机的Console了。这里,SONiC的默认用户名和密码是`admin`和`YourPaSsWoRd`。登录进去之后,我们就可以运行熟悉的命令,用`show interfaces status`或者`show ip interface`来查看网络的状态了。我们这里也可以看到,前面两个我们连接好了的Interface的状态都是`up`的了。 +Next, right-click on the switch, select `Custom Console`, then select Putty to open the console of the switch we saw earlier. Here, the default username and password for SONiC are `admin` and `YourPaSsWoRd`. Once logged in, we can run familiar commands like `show interfaces status` or `show ip interface` to check the network status. Here, we can also see that the status of the first two interfaces we connected is `up`. -## 配置网络 +## Configure the Network -SONiC软交换机下,默认的端口使用的是10.0.0.x的子网(如下),而且都是eth pair: +In SONiC software switches, the default ports use the 10.0.0.x subnet (as shown below) with neighbor paired. ```bash admin@sonic:~$ show ip interfaces @@ -124,7 +125,7 @@ Ethernet4 10.0.0.2/31 up/up ARISTA02T2 10.0.0 Ethernet8 10.0.0.4/31 up/up ARISTA03T2 10.0.0.5 ``` -这里,我们比较方便的做法是创建一个小的vlan,把我们的端口都包在里面(我们这里用的是Ethernet4和Ethernet8): +Similar to what we mentioned in [installation](./1-1-install.md), we are going to create a simple network by creating a small VLAN and including our ports in it (in this case, Ethernet4 and Ethernet8): ```bash # Remove old config @@ -142,7 +143,7 @@ sudo config vlan member add -u 2 Ethernet8 sudo config interface ip add Vlan2 10.0.0.0/24 ``` -这样,我们的vlan就创建好了,我们可以通过`show vlan brief`来查看一下: +Now, our VLAN is created, and we can use `show vlan brief` to check: ```bash admin@sonic:~$ show vlan brief @@ -154,7 +155,7 @@ admin@sonic:~$ show vlan brief +-----------+--------------+-----------+----------------+-------------+-----------------------+ ``` -然后,我们就可以给所有的主机配置一个10.0.0.x的IP地址了。 +Now, let's assign a 10.0.0.x IP address to each host. ```bash # VPC1 @@ -164,27 +165,27 @@ ip 10.0.0.2 255.0.0.0 10.0.0.1 ip 10.0.0.3 255.0.0.0 10.0.0.1 ``` -好的,现在我们来Ping一下吧! +Alright, let's start the ping! ![](assets/chapter-1/gns3-ping.png) -通了! +It works! -## 抓包 +## Packet Capture -上面,我们安装GNS3前,我们特意安装了Wireshark,这样我们就可以在GNS3里面抓包了。我们只需要右键点击图中我们想抓包的Link上,然后选择`Start capture`,就可以开始抓包了。 +Before installing GNS3, we installed Wireshark so that we can capture packets within the virtual network created by GNS3. To start capturing, simply right-click on the link you want to capture on and select `Start capture`. ![](assets/chapter-1/gns3-capture.png) -稍等一下,Wireshark就会自动打开,实时的显示所有的包,非常的方便: +After a moment, Wireshark will automatically open and display all the packets in real-time. Very convenient! ![](assets/chapter-1/gns3-capture-live.png) -## 更多的网络 +## More Networks -除了上面这种最简单的网络搭建,我们其实可以用GNS3搭建很多非常复杂的网络来进行测试,比如多层ECMP + eBGP等等。XFlow Research发布了一篇非常详细的文档来介绍这些内容,感兴趣的小伙伴可以去传送到这篇文档去看看:[SONiC Deployment and Testing Using GNS3][SONiCWithGNS3]。 +In addition to the simplest network setup we discussed above, we can actually use GNS3 to build much more complex networks for testing, such as multi-layer ECMP + eBGP, and more. XFlow Research has published a very detailed document that covers these topics. Interested folks can refer to the document: [SONiC Deployment and Testing Using GNS3][SONiCWithGNS3]. -# 参考资料 +# References 1. [GNS3][GNS3] 2. [GNS3 Linux Install][GNS3Install] diff --git a/src/1-3-command-cheatsheet.md b/src/1-3-command-cheatsheet.md index 49104e9..dab64bf 100644 --- a/src/1-3-command-cheatsheet.md +++ b/src/1-3-command-cheatsheet.md @@ -1,28 +1,29 @@ -# 常用命令 -为了帮助我们查看和配置SONiC的状态,SONiC提供了大量的CLI命令供我们调用。这些命令大多分为两类:`show`和`config`,他们的格式基本类似,大多都符合下面的格式: +# Common Commands + +To help us check and configure the state of SONiC, SONiC provides a large number of CLI commands for us to use. These commands are mostly divided into two categories: `show` and `config`. Their formats are generally similar, mostly following the format below: ```bash show [options] config [options] ``` -SONiC的文档提供了非常详细的命令列表:[SONiC Command Line Interface Guide][SONiCCommands],但是由于其命令众多,不便于我们初期的学习和使用,所以列出了一些平时最常用的命令和解释,供大家参考。 +The SONiC documentation provides a very detailed list of commands: [SONiC Command Line Interface Guide][SONiCCommands], but due to the large number of commands, it is not very convenient for us to ramp up, so we listed some of the most commonly used commands and explanations for reference. ```admonish info -SONiC中的所有命令的子命令都可以只打前三个字母,来帮助我们有效的节约输入命令的时间,比如: +All subcommands in SONiC can be abbreviated to the first three letters to help us save time when entering commands. For example: show interface transceiver error-status -和下面这条命令是等价的: +is equivalent to: show int tra err -为了帮助大家记忆和查找,下面的命令列表都用的全名,但是大家在实际使用的时候,可以大胆的使用缩写来减少工作量。 +To help with memory and lookup, the following command list uses full names, but in actual use, you can boldly use abbreviations to reduce workload. ``` ```admonish info -如果遇到不熟悉的命令,都可以通过输入`-h`或者`--help`来查看帮助信息,比如: +If you encounter unfamiliar commands, you can view the help information by entering `-h` or `--help`, for example: show -h show interface --help @@ -30,39 +31,49 @@ SONiC中的所有命令的子命令都可以只打前三个字母,来帮助我 ``` -## General +## Basic system information ```bash +# Show system version, platform info and docker containers show version +# Show system uptime show uptime +# Show platform information, such as HWSKU show platform summary ``` ## Config ```bash +# Reload all config. +# WARNING: This will restart almost all services and will cause network interruption. sudo config reload -sudo config load_minigraph + +# Save the current config from redis DB to disk, which makes the config persistent across reboots. +# NOTE: The config file is saved to `/etc/sonic/config_db.json` sudo config save -y ``` -## Docker相关 +## Docker Related ```bash +# Show all docker containers docker ps -``` -```bash +# Show processes running in a container docker top | + +# Enter the container +docker exec -it | bash ``` ```admonish note -如果我们想对所有的docker container进行某个操作,我们可以通过`docker ps`命令来获取所有的container id,然后pipe到`tail -n +2`来去掉第一行的标题,从而实现批量调用。 +If we want to perform an operation on all docker containers, we can use the `docker ps` command to get all container IDs, then pipe to `tail -n +2` to remove the first line of the header, thus achieving batch calls. -比如,我们可以通过如下命令来查看所有container中正在运行的所有线程: +For example, we can use the following command to view all threads running in all containers: $ for id in `docker ps | tail -n +2 | awk '{print $1}'`; do docker top $id; done UID PID PPID C STIME TTY TIME CMD @@ -78,7 +89,7 @@ show interface status show interface counters show interface portchannel show interface transceiver info -show interface transceiver error-status +show interface transceiver eeprom sonic-clear counters TODO: config @@ -130,7 +141,7 @@ show lldp neighbors show vlan brief ``` -## QoS相关 +## QoS Related ```bash # Show PFC watchdog stats @@ -181,7 +192,7 @@ sudo config muxcable firmware download AEC_WYOMING_B52Yb0_MS_0.6_20201218.bin Et sudo config muxcable firmware rollback Ethernet0 ``` -# 参考资料 +# References 1. [SONiC Command Line Interface Guide][SONiCCommands] diff --git a/src/1-intro.md b/src/1-intro.md index 68ec98b..7bed406 100644 --- a/src/1-intro.md +++ b/src/1-intro.md @@ -1,49 +1,49 @@ -# SONiC入门指南 +# Getting Started with SONiC -## 为什么要做SONiC +## Why SONiC? -我们知道交换机内部都有一套可大可小的操作系统,用于配置和查看交换机的状态。但是,从1986年第一台交换机面世开始,虽然各个厂商都在进行着相关的开发,到现在为止种类也相当的多,但是依然存在一些问题,比如: +We know that switches have their own operating systems for configuration, monitoring and so on. However, since the first switch was introduced in 1986, despite ongoing development by various vendors, there are still some issues, such as: -1. 生态封闭,不开源,主要是为了支持自家的硬件,无法很好的兼容其他厂商的设备 -2. 支持的场景很有限,难以使用同一套系统去支撑大规模的数据中心中复杂多变的场景 -3. 升级可能会导致网络中断,难以实现无缝升级,这对于云提供商来说有时候是致命的 -4. 设备功能升级缓慢,难以很好的支持快速的产品迭代 +1. Closed ecosystem: Non-open source systems primarily support proprietary hardware and are not compatible with devices from other vendors. +2. Limited use cases: It is difficult to use the same system to support complex and diverse scenarios in large-scale data centers. +3. Disruptive upgrades: Upgrades can cause network interruptions, which can be fatal for cloud providers. +4. Slow feature upgrades: It is challenging to support rapid product iterations due to slow device feature upgrades. -所以,微软在2016年发起了开源项目SONiC,希望能够通过开源的方式,让SONiC能够成为一个通用的网络操作系统,从而解决上面的问题。而且,由于微软在Azure中大范围的使用SONiC,也保证了SONiC的实现确实能够承受大规模的生产环境的考验,这也是SONiC的一个优势。 +To address these issues, Microsoft initiated the SONiC open-source project in 2016. The goal was to create a universal network operating system that solves the aforementioned problems. Additionally, Microsoft's extensive use of SONiC in Azure ensures its suitability for large-scale production environments, which is another significant advantage. -## 主体架构 +## Architecture -SONiC是微软开发的基于debian的开源的网络操作系统,它的设计核心思想有三个: +SONiC is an open-source network operating system (NOS) developed by Microsoft based on Debian. It is designed with three core principles: -1. **硬件和软件解耦**:通过SAI(Switch Abstraction Interface)将硬件的操作抽象出来,从而使得SONiC能够支持多种硬件平台。这一层抽象层由SONiC定义,由各个厂商来实现。 -2. **使用docker容器将软件微服务化**:SONiC上的主要功能都被拆分成了一个个的docker容器,和传统的网络操作系统不同,升级系统可以只对其中的某个容器进行升级,而不需要整体升级和重启,这样就可以很方便的进行升级和维护,支持快速的开发和迭代。 -3. **使用redis作为中心数据库对服务进行解耦**:绝大部分服务的配置和状态最后都被存储到中心的redis数据库中,这样不仅使得所有的服务可以很轻松的进行协作(数据存储和pubsub),也可以让我们很方便的在上面开发工具,使用统一的方法对各个服务进行操作和查询,而不用担心状态丢失和协议兼容问题,最后还可以很方便的进行状态的备份和恢复。 +1. **Hardware and software decoupling**: SONiC abstracts hardware operations through the Switch Abstraction Interface (SAI), enabling SONiC to support multiple hardware platforms. SAI defines this abstraction layer, which is implemented by various vendors. +2. **Microservices with Docker containers**: The main functionalities of SONiC are divided into individual Docker containers. Unlike traditional network operating systems, upgrading the system can be done by upgrading specific containers without the need for a complete system upgrade or restart. This allows for easy upgrades, maintenance, and supports rapid development and iteration. +3. **Redis as a central database for service decoupling**: The configuration and status of most services are stored in a central Redis database. This enables seamless collaboration between services (data storage and pub/sub) and provides a unified method for operating and querying various services without concerns about data loss or protocol compatibility. It also facilitates easy backup and recovery of states. -这让SONiC拥有了非常开放的生态([Community][SONiCLanding],[Workgroups][SONiCWG],[Devices][SONiCDevices]),总体而言,SONiC的架构如下图所示: +These design choices gives SONiC a great open ecosystem ([Community][SONiCLanding], [Workgroups][SONiCWG], [Devices][SONiCDevices]). Overall, the architecture of SONiC is illustrated in the following diagram: ![](assets/chapter-1/sonic-arch.png) _(Source: [SONiC Wiki - Architecture][SONiCArch])_ -当然,这样的设计也有一些缺点,比如:对磁盘的占用会变大,不过,现在一点点存储空间并不是什么很大的问题,而且这个问题也都可以通过一些方法来解决。 +Of course, this design has some drawbacks, such as relative large disk usage. However, with the availability of storage space and various methods to address this issue, it is not a significant concern. -## 发展方向 +## Future Direction -虽然交换机已经发展很多很多年了,但是随着现在云的发展,对网络的要求也越来越高,不管是直观的需求,比如更大的带宽,更大的容量,还是最新的研究,比如,带内计算,端网融合等等,都对交换机的发展提出了更高的要求和挑战,也促使着各大厂商和研究机构不断的进行创新。SONiC也一样,随着时间的发展,需求一点没有减少。 +Although switches have been around for many years, the development of cloud has raised higher demands and challenges for networks. These include intuitive requirements like increased bandwidth and capacity, as well as cutting-edge research such as in-band computing and edge-network convergence. These factors drive innovation among major vendors and research institutions. SONiC is no exception and continues to evolve to meet the growing demands. -关于SONiC的发展方向,我们可以在它的[Roadmap][SONiCPlanning]中看到。如果大家对最新的动态感兴趣,也可以关注它的Workshop,比如,最近的[OCP Global Summit 2022 - SONiC Workshop][SONiCWorkshop]。这里就不展开了。 +To learn more about the future direction of SONiC, you can refer to its [Roadmap][SONiCPlanning]. If you are interested in the latest updates, you can also follow its workshops, such as the recent [OCP Global Summit 2022 - SONiC Workshop][SONiCWorkshop]. However, I won't go into detail here. -## 感谢 +## Acknowledgments -感谢以下朋友的帮助和贡献,没有你们也就没有这本入门指南! +Special thanks to the following individuals for their help and contributions. Without them, this introductory guide would not have been possible! [@bingwang-ms](https://github.com/bingwang-ms) # License -本书使用 [署名-非商业性使用-相同方式共享(CC BY-NC-SA)4.0 许可协议](https://creativecommons.org/licenses/by-nc-sa/4.0/)。 +This book is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). -# 参考资料 +# References 1. [SONiC Wiki - Architecture][SONiCArch] 2. [SONiC Wiki - Roadmap Planning][SONiCPlanning] diff --git a/src/2-1-database.md b/src/2-1-database.md index d6d4b58..f3f3241 100644 --- a/src/2-1-database.md +++ b/src/2-1-database.md @@ -1,8 +1,8 @@ -# Redis数据库 +# Redis database -首先,在SONiC里面最核心的服务,自然是当之无愧的中心数据库Redis了!它的主要目的有两个:存储所有服务的配置和状态,并且为各个服务提供通信的媒介。 +First and foremost, the core service in SONiC is undoubtedly the central database - Redis! It has two major purposes: storing the configuration and state of all services, and providing a communication channel for these services. -为了提供这些功能,SONiC会在Redis中创建一个名为`sonic-db`的数据库实例,其配置和分库信息我们可以在`/var/run/redis/sonic-db/database_config.json`中找到: +To provide these functionalities, SONiC creates a database instance in Redis named `sonic-db`. The configuration and database partitioning information can be found in `/var/run/redis/sonic-db/database_config.json`: ```bash admin@sonic:~$ cat /var/run/redis/sonic-db/database_config.json @@ -35,16 +35,18 @@ admin@sonic:~$ cat /var/run/redis/sonic-db/database_config.json } ``` -虽然我们可以看到SONiC中的数据库有十来个,但是我们大部分时候只需要关注以下几个最重要的数据库就可以了: +Although we can see that there are about a dozen databases in SONiC, most of the time we only need to focus on the following most important ones: -- **CONFIG_DB(ID = 4)**:存储所有服务的**配置信息**,比如端口配置,VLAN配置等等。它代表着**用户想要交换机达到的状态**的数据模型,这也是所有CLI和外部应用程序修改配置时的主要操作对象。 -- **APPL_DB(Application DB, ID = 0)**:存储**所有服务的内部状态信息**。这些信息有两种:一种是各个服务在读取了CONFIG_DB的配置信息后,自己计算出来的。我们可以理解为**各个服务想要交换机达到的状态**(Goal State),还有一种是当最终硬件状态发生变化被写回时,有些服务会直接写回到APPL_DB,而不是我们下面马上要介绍的STATE_DB。这些信息我们可以理解为**各个服务认为交换机当前的状态**(Current State)。 -- **STATE_DB(ID = 6)**:存储着交换机**各个部件当前的状态**(Current State)。当SONiC中的服务收到了STATE_DB的状态变化,但是发现和Goal State不一致的时候,SONiC就会重新下发配置,直到两者一致。(当然,对于那些回写到APPL_DB状态,服务就会监听APPL_DB的变化,而不是STATE_DB了。) -- **ASIC_DB(ID = 1)**:存储着**SONiC想要交换机ASIC达到状态信息**,比如,ACL,路由等等。和APPL_DB不同,这个数据库里面的数据模型是面向ASIC设计的,而不是面向服务抽象的。这样做的目的是为了方便各个厂商进行SAI和ASIC驱动的开发。 +- **CONFIG_DB (ID = 4)**: Stores the **configuration** of all services, such as port configuration, VLAN configuration, etc. It represents the data model of the **desired state of the switch** as intended by the user. This is also the main object of operation when all CLI and external applications modify the configuration. +- **APPL_DB (Application DB, ID = 0)**: Stores **internal state information of all services**. It contains two types of information: + - One is calculated by each service after reading the configuration information from CONFIG_DB, which can be understood as the **desired state of the switch** (Goal State) but from the perspective of each service. + - The other is when the ASIC state changes and is written back, some services write directly to APPL_DB instead of the STATE_DB we will introduce next. This information can be understood as the **current state of the switch** as perceived by each service. +- **STATE_DB (ID = 6)**: Stores the **current state** of various components of the switch. When a service in SONiC receives a state change from STATE_DB and finds it inconsistent with the Goal State, SONiC will reapply the configuration until the two states are consistent. (Of course, for those states written back to APPL_DB, the service will monitor changes in APPL_DB instead of STATE_DB.) +- **ASIC_DB (ID = 1)**: Stores the **desired state information** of the switch ASIC in SONiC, such as ACL, routing, etc. Unlike APPL_DB, the data model in this database is designed for ASIC rather than service abstraction. This design facilitates the development of SAI and ASIC drivers by various vendors. -这里,我们会发现一个很直观的问题:交换机里面这么多服务,难道所有的配置和状态都放在一个数据库里面没有隔离的么?如果两个服务用了同一个Redis Key怎么办呢?这个问题非常的好,SONiC的解决也很直接,那就是在每个数据库里面继续分表! +Now, we have an intuitive question: with so many services in the switch, are all configurations and states stored in a single database without isolation? What if two services use the same Redis Key? This is a very good question, and SONiC's solution is straightforward: continue to partition each database into tables! -我们知道Redis在每个数据库里面并没有表的概念,而是使用key-value的方式来存储数据。所以,为了进一步分表,SONiC的解决方法是将表的名字放入key中,并且使用分隔符将表和key隔开。上面的配置文件中`separator`字段就是做这个了。比如:`APPL_DB`中的`PORT_TABLE`表中的`Ethernet4`端口的状态,我们可以通过`PORT_TABLE:Ethernet4`来获取,如下: +We know that Redis does not have the concept of tables within each database but uses key-value pairs to store data. Therefore, to further partition tables, SONiC's solution is to include the table name in the key and separate the table and key with a delimiter. The `separator` field in the configuration file above serves this purpose. For example, the state of the `Ethernet4` port in the `PORT_TABLE` table in `APPL_DB` can be accessed using `PORT_TABLE:Ethernet4` as follows: ```bash 127.0.0.1:6379> select 0 @@ -60,18 +62,18 @@ OK 7) "lanes" 8) "13,14,15,16" 9) "mtu" -10) "9100" -11) "speed" -12) "40000" -13) "description" -14) "" -15) "oper_status" -16) "up" +1) "9100" +2) "speed" +3) "40000" +4) "description" +5) "" +6) "oper_status" +7) "up" ``` -当然在SONiC中,不仅仅是数据模型,包括通信机制,都是使用类似的方法来实现“表”级别的隔离的。 +Of course, in SONiC, not only the data model but also the communication mechanism uses a similar method to achieve "table" level isolation. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] diff --git a/src/2-2-services-intro.md b/src/2-2-services-intro.md index 58a64e2..4137c0d 100644 --- a/src/2-2-services-intro.md +++ b/src/2-2-services-intro.md @@ -1,79 +1,79 @@ -# 服务与工作流简介 -SONiC里面的服务(常驻进程)非常的多,有二三十种,它们会在随着交换机启动而启动,并一直保持运行,直到交换机关机。如果我们想快速掌握SONiC,一个一个服务的去了解,会很容易陷入细节的泥潭,所以,我们最好把这些服务和控制流进行一个大的分类,以帮助我们建立一个宏观的概念。 +# Introduction to Services and Workflows + +There are many services (daemon processes) in SONiC, around twenty to thirty. They start with the switch and keep running until the switch is shut down. If we want to quickly understand how SONiC works, diving into each service one by one is obviously not a good option. Therefore, it is better to categorize these services and control flows on high level to help us build a big picture. ```admonish note -我们这里不会深入到某一个具体的服务中去,而是先从整体上来看看SONiC中的服务的结构,帮助我们建立一个整体的认识。关于具体的服务,我们会在工作流一章中,对常用的工作流进行介绍,而关于详细的技术细节,大家也可以查阅每个服务相关的设计文档。 +We will not delve into any specific service here. Instead, we will first look at the overall structure of services in SONiC to help us build a comprehensive understanding. For specific services, we will introduce its workflows in the workflow chapter. For detailed information, we can also refer to the design documents related to each service. ``` -## 服务分类 - -总体而言,SONiC中的服务可以分为以下几类:`*syncd`, `*mgrd`,feature实现,`orchagent`和`syncd`。 +## Service Categories -### `*syncd`服务 +Generally speaking, the services in SONiC can be divided into the following categories: `*syncd`, `*mgrd`, feature implementations, `orchagent`, and `syncd`. -这类服务名字中都以`syncd`结尾。它们做的事情都很类似:它们负责将硬件状态同步到Redis中,一般目标都以APPL_DB或者STATE_DB为主。 +### `*syncd` Services -比如,`portsyncd`就是通过监听netlink的事件,将交换机中所有Port的状态同步到STATE_DB中,而`natsyncd`则是监听netlink的事件,将交换机中所有的NAT状态同步到APPL_DB中。 +These services have names ending with `syncd`. They perform similar tasks: synchronizing hardware states to Redis, usually into APPL_DB or STATE_DB. -### `*mgrd`服务 +For example, `portsyncd` listens to netlink events and synchronizes the status of all ports in the switch to STATE_DB, while `natsyncd` listens to netlink events and synchronizes all NAT statuses in the switch to APPL_DB. -这类服务名字中都以`mgrd`结尾。顾名思义,这些服务是所谓的“Manager”服务,也就是说它们负责各个硬件的配置,和`*syncd`完全相反。它们的逻辑主要有两个部分: +### `*mgrd` Services -1. **配置下发**:负责读取配置文件和监听Redis中的配置和状态改变(主要是CONFIG_DB,APPL_DB和STATE_DB),然后将这些修改推送到交换机硬件中去。推送的方法有多种,取决于更新的目标是什么,可以通过更新APPL_DB并发布更新消息,或者是直接调用linux下的命令行,对系统进行修改。比如:`nbrmgr`就是监听CONFIG_DB,APPL_DB和STATE_DB中neighbor的变化,并调用netlink和command line来对neighbor和route进行修改,而`intfmgr`除了调用command line还会将一些状态更新到APPL_DB中去。 -2. **状态同步**:对于需要Reconcile的服务,`*mgrd`还会监听STATE_DB中的状态变化,如果发现硬件状态和当前期望状态不一致,就会重新发起配置流程,将硬件状态设置为期望状态。这些STATE_DB中的状态变化一般都是`*syncd`服务推送的。比如:`intfmgr`就会监听STATE_DB中,由`portsyncd`推送的,端口的Up/Down状态和MTU变化,一旦发现和其内存中保存的期望状态不一致,就会重新下发配置。 +These services have names ending with `mgrd`. As the name suggests, these are "Manager" services responsible for configuring various hardware, opposite to `*syncd`. Their logic mainly consists of two parts: -### 功能实现服务 +1. **Configuration Deployment**: Responsible for reading configuration files and listening to configuration and state changes in Redis (mainly CONFIG_DB, APPL_DB, and STATE_DB), then pushing these changes to the switch hardware. The method of pushing varies depending on the target, either by updating APPL_DB and publishing update messages or directly calling Linux command lines to modify the system. For example, `nbrmgr` listens to changes in CONFIG_DB, APPL_DB, and STATE_DB for neighbors and modifies neighbors and routes using netlink and command lines, while `intfmgr` not only calls command lines but also updates some states to APPL_DB. +2. **State Synchronization**: For services that need reconciliation, `*mgrd` also listens to state changes in STATE_DB. If it finds that the hardware state is inconsistent with the expected state, it will re-initiate the configuration process to set the hardware state to the expected state. These state changes in STATE_DB are usually pushed by `*syncd` services. For example, `intfmgr` listens to port up/down status and MTU changes pushed by `portsyncd` in STATE_DB. If it finds inconsistencies with the expected state stored in its memory, it will re-deploy the configuration. -有一些功能并不是依靠OS本身来完成的,而是由一些特定的进程来实现的,比如BGP,或者一些外部接口。这些服务名字中经常以`d`结尾,表示deamon,比如:`bgpd`,`lldpd`,`snmpd`,`teamd`等,或者干脆就是这个功能的名字,比如:`fancontrol`。 +### `orchagent` Service -### `orchagent`服务 +This is the most important service in SONiC. Unlike other services that are responsible for one or two specific functions, `orchagent`, as the orchestrator of the switch ASIC state, checks all states from `*syncd` services in the database, integrates them, and deploys them to ASIC_DB, which is used to store the switch ASIC configuration. These states are eventually received by `syncd`, which calls the SAI API through the SAI implementation and ASIC SDK provided by various vendors to interact with the ASIC, ultimately deploying the configuration to the switch hardware. -这个是SONiC中最重要的一个服务,不像其他的服务只负责一两个特定的功能,`orchagent`作为交换机ASIC状态的编排者(orchestrator),会检查数据库中所有来自`*syncd`服务的状态,整合起来并下发给用于保存交换机ASIC配置的数据库:ASIC_DB。这些状态最后会被`syncd`接收,并调用SAI API经过各个厂商提供的SAI实现和ASIC SDK和ASIC进行交互,最终将配置下发到交换机硬件中。 +### Feature Implementation Services -### `syncd`服务 +Some features are not implemented by the OS itself but by specific processes, such as BGP or some external-facing interfaces. These services often have names ending with `d`, indicating daemon, such as `bgpd`, `lldpd`, `snmpd`, `teamd`, etc., or simply the name of the feature, such as `fancontrol`. -`syncd`服务是`orchagent`的下游,它虽然名字叫`syncd`,但是它却同时肩负着ASIC的`*mgrd`和`*syncd`的工作。 +### `syncd` Service -- 首先,作为`*mgrd`,它会监听ASIC_DB的状态变化,一旦发现,就会获取其新的状态并调用SAI API,将配置下发到交换机硬件中。 -- 然后,作为`*syncd`,如果ASIC发送了任何的通知给SONiC,它也会将这些通知通过消息的方式发送到Redis中,以便`orchagent`和`*mgrd`服务获取到这些变化,并进行处理。这些通知的类型我们可以在[SwitchNotifications.h][SAISwitchNotify]中找到。 +The `syncd` service is downstream of `orchagent`. Although its name is `syncd`, it shoulders the work of both `*mgrd` and `*syncd` for the ASIC. -## 服务间控制流分类 +- First, as `*mgrd`, it listens to state changes in ASIC_DB. Once detected, it retrieves the new state and calls the SAI API to deploy the configuration to the switch hardware. +- Then, as `*syncd`, if the ASIC sends any notifications to SONiC, it will send these notifications to Redis as messages, allowing `orchagent` and `*mgrd` services to obtain these changes and process them. The types of these notifications can be found in [SwitchNotifications.h][SAISwitchNotify]. -有了这些分类,我们就可以更加清晰的来理解SONiC中的服务了,而其中非常重要的就是理解服务之间的控制流。有了上面的分类,我们这里也可以把主要的控制流有分为两类:配置下发和状态同步。 +## Control Flow Between Services -### 配置下发 +With service categories, we can now better understand the services in SONiC. To get started, it is crucial to understand the control flow between services. Based on the above categories, we can divide the main control flows into two categories: configuration deployment and state synchronization. -配置下发的流程一般是这样的: +### Configuration Deployment -1. **修改配置**:用户可以通过CLI或者REST API修改配置,这些配置会被写入到CONFIG_DB中并通过Redis发送更新通知。或者外部程序可以通过特定的接口,比如BGP的API,来修改配置,这种配置会通过内部的TCP Socket发送给`*mgrd`服务。 -2. **`*mgrd`下发配置**:服务监听到CONFIG_DB中的配置变化,然后将这些配置推送到交换机硬件中。这里由两种主要情况(并且可以同时存在): - 1. **直接下发**: - 1. `*mgrd`服务直接调用linux下的命令行,或者是通过netlink来修改系统配置 - 2. `*syncd`服务会通过netlink或者其他方式监听到系统配置的变化,并将这些变化推送到STATE_DB或者APPL_DB中。 - 3. `*mgrd`服务监听到STATE_DB或者APPL_DB中的配置变化,然后将这些配置和其内存中存储的配置进行比较,如果发现不一致,就会重新调用命令行或者netlink来修改系统配置,直到它们一致为止。 - 2. **间接下发**: - 1. `*mgrd`将状态推送到APPL_DB并通过Redis发送更新通知。 - 2. `orchagent`服务监听到配置变化,然后根据所有相关的状态,计算出此时ASIC应该达到的状态,并下发到ASIC_DB中。 - 3. `syncd`服务监听到ASIC_DB的变化,然后将这些新的配置通过统一的SAI API接口,调用ASIC Driver更新交换机ASIC中的配置。 +The configuration deployment process generally follows these steps: -配置初始化和配置下发类似,不过是在服务启动的时候读取配置文件,这里就不展开了。 +1. **Modify Configuration**: Users can modify configurations through CLI or REST API. These configurations are written to CONFIG_DB and send update notifications through Redis. Alternatively, external programs can modify configurations through specific interfaces, such as the BGP API. These configurations are sent to `*mgrd` services through internal TCP sockets. +2. **`*mgrd` Deploys Configuration**: Services listen to configuration changes in CONFIG_DB and then push these configurations to the switch hardware. There are two main scenarios (which can coexist): + 1. **Direct Deployment**: + 1. `*mgrd` services directly call Linux command lines or modify system configurations through netlink. + 2. `*syncd` services listen to system configuration changes through netlink or other methods and push these changes to STATE_DB or APPL_DB. + 3. `*mgrd` services listen to configuration changes in STATE_DB or APPL_DB, compare these configurations with those stored in their memory, and if inconsistencies are found, they re-call command lines or netlink to modify system configurations until they are consistent. + 2. **Indirect Deployment**: + 1. `*mgrd` pushes states to APPL_DB and sends update notifications through Redis. + 2. `orchagent` listens to configuration changes, calculates the state the ASIC should achieve based on all related states, and deploys it to ASIC_DB. + 3. `syncd` listens to changes in ASIC_DB and updates the switch ASIC configuration through the unified SAI API interface by calling the ASIC Driver. -### 状态同步 +Configuration initialization is similar to configuration deployment but involves reading configuration files when services start, which will not be expanded here. -如果这个时候,出现了一些情况,比如网口坏了,ASIC中的状态变了等等,这个时候我们就需要进行状态更新和同步了。这个流程一般是这样的: +### State Synchronization -1. **检测状态变化**:这个状态变化主要来源于`*syncd`服务(netlink等等)和`syncd`服务([SAI Switch Notification][SAISwitchNotify]),这些服务在检测到变化后,会将它们发送给STATE_DB或者APPL_DB。 -2. **处理状态变化**:`orchagent`和`*mgrd`服务会监听到这些变化,然后开始处理,将新的配置重新通过命令行和netlink下发给系统,或者下发到ASIC_DB中,让`syncd`服务再次对ASIC进行更新。 +If situations arise, such as a port failure or changes in the ASIC state, state updates and synchronization are needed. The process generally follows these steps: -### 具体例子 +1. **Detect State Changes**: These state changes mainly come from `*syncd` services (netlink, etc.) and `syncd` services ([SAI Switch Notification][SAISwitchNotify]). After detecting changes, these services send them to STATE_DB or APPL_DB. +2. **Process State Changes**: `orchagent` and `*mgrd` services listen to these changes, process them, and re-deploy new configurations to the system through command lines and netlink or to ASIC_DB for `syncd` services to update the ASIC again. -SONiC的官方文档中给出了几个典型的控制流流转的例子,这里就不过多的展开了,有兴趣的朋友可以去这里看看:[SONiC Subsystem Interactions](https://github.com/sonic-net/SONiC/wiki/Architecture#sonic-subsystems-interactions)。我们在后面工作流一章中,也会选择一些非常常用的工作流进行展开。 +### Specific Examples +The official SONiC documentation provides several typical examples of control flow. Interested readers can refer to [SONiC Subsystem Interactions](https://github.com/sonic-net/SONiC/wiki/Architecture#sonic-subsystems-interactions). In the workflow chapter, we will also expand on some very common workflows. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] [SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture -[SAISwitchNotify]: https://github.com/sonic-net/sonic-sairedis/blob/master/syncd/SwitchNotifications.h \ No newline at end of file +[SAISwitchNotify]: https://github.com/sonic-net/sonic-sairedis/blob/master/syncd/SwitchNotifications.h diff --git a/src/2-3-key-containers.md b/src/2-3-key-containers.md index a8cd25d..9306eed 100644 --- a/src/2-3-key-containers.md +++ b/src/2-3-key-containers.md @@ -1,8 +1,8 @@ -# 核心容器 +# Key containers -SONiC的设计中最具特色的地方:容器化。 +One of the most distinctive features of SONiC's design is containerization. -从SONiC的上面的设计图中,我们可以看出来,SONiC中,所有的服务都是以容器的形式存在的。在登录进交换机之后,我们可以通过`docker ps`命令来查看当前运行的容器: +From the design diagram of SONiC, we can see that all services in SONiC run in the form of containers. After logging into the switch, we can use the `docker ps` command to see all containers that are currently running: ```bash admin@sonic:~$ docker ps @@ -20,13 +20,13 @@ bd20649228c8 docker-eventd:latest "/usr/local/bin/supe…" 2 b2f58447febb docker-database:latest "/usr/local/bin/dock…" 2 days ago Up 32 hours database ``` -这里我们来简单介绍一下这些容器。 +Here we will briefly introduce these containers. -## 数据库容器:database +## Database Container: `database` -这个容器中运行的就是我们多次提到的SONiC中的中心数据库Redis了,它里面存放着所有交换机的配置和状态信息,SONiC也是主要通过它来向各个服务提供底层的通信机制。 +This container contains the central database - Redis, which we have mentioned multiple times. It stores all the configuration and status of the switch, and SONiC also uses it to provide the underlying communication mechanism to various services. -我们通过docker进入这个容器,就可以看到里面正在运行的redis进程了: +By entering this container via Docker, we can see the running Redis process: ```bash admin@sonic:~$ sudo docker exec -it database bash @@ -41,7 +41,9 @@ root@sonic:/# cat /var/run/redis/redis.pid 82 ``` -那么别的容器是如何来访问这个Redis数据库的呢?答案是通过Unix Socket。我们可以在database容器中看到这个Unix Socket,它将交换机上的`/var/run/redis`目录map进database容器,让database容器可以创建这个socket: +How does other container access this Redis database? + +The answer is through Unix Socket. We can see this Unix Socket in the database container, which is mapped from the `/var/run/redis` directory on the switch. ```bash # In database container @@ -53,25 +55,25 @@ admin@sonic:~$ ls /var/run/redis redis.pid redis.sock sonic-db ``` -然后再将这个socket给map到其他的容器中,这样所有容器就都可以来访问这个中心数据库啦,比如,swss容器: +Then SONiC maps `/var/run/redis` folder into all relavent containers, allowing other services to access the central database. For example, the swss container: ```bash admin@sonic:~$ docker inspect swss ... - "HostConfig": { - "Binds": [ - ... - "/var/run/redis:/var/run/redis:rw", - ... - ], + "HostConfig": { + "Binds": [ + ... + "/var/run/redis:/var/run/redis:rw", + ... + ], ... ``` -## 交换机状态管理容器:swss(Switch State Service) +## SWitch State Service Container: `swss` -这个容器可以说是SONiC中最关键的容器了,**它是SONiC的大脑**,里面运行着大量的`*syncd`和`*mgrd`服务,用来管理交换机方方面面的配置,比如Port,neighbor,ARP,VLAN,Tunnel等等等等。另外里面还运行着上面提到的`orchagent`,用来统一处理和ASIC相关的配置和状态变化。 +This container can be considered the most critical container in SONiC. **It is the brain of SONiC**, running numerous `*syncd` and `*mgrd` services to manage various configurations of the switch, such as Port, neighbor, ARP, VLAN, Tunnel, etc. Additionally, it runs the `orchagent`, which handles many configurations and state changes related to the ASIC. -这些服务大概的功能和流程我们上面已经提过了,所以就不再赘述了。这里我们可以通过`ps`命令来看一下这个容器中运行的服务: +We have already discussed the general functions and processes of these services, so we won't repeat them here. We can use the `ps` command to see the services running in this container: ```bash admin@sonic:~$ docker exec -it swss bash @@ -96,11 +98,11 @@ root 208 0.0 0.0 5772 1636 pts/0 S Apr26 0:07 /usr/sbin/ndp ... ``` -## ASIC管理容器:syncd +## ASIC Management Container: `syncd` -这个容器中主要是用于管理交换机上的ASIC的,里面运行着`syncd`服务。我们之前提到的各个厂商提供的SAI(Switch Abstraction Interface)和ASIC Driver都是放在这个容器中的。正是因为这个容器的存在,才使得SONiC可以支持多种不同的ASIC,而不需要修改上层的服务。换句话说,如果没有这个容器,那SONiC就是一个缸中大脑,除了一些基本的配置,其他只能靠想的,什么都干不了。 +This container is mainly used for managing the ASIC on the switch, running the `syncd` service. The SAI (Switch Abstraction Interface) implementation and ASIC Driver provided by various vendors are placed in this container. It allows SONiC to support multiple different ASICs without modifying the upper-layer services. In other words, without this container, SONiC would be a brain in a jar, capable of only thinking but nothing else. -在syncd容器中运行的服务并不多,就是syncd,我们可以通过`ps`命令来查看,而在`/usr/lib`目录下,我们也可以找到这个为了支持ASIC而编译出来的巨大无比的SAI文件: +We don't have too many services running in the syncd container, mainly syncd. We can check them using the `ps` command, and in the `/usr/lib` directory, we can find the enormous SAI file compiled to support the ASIC: ```bash admin@sonic:~$ docker exec -it syncd bash @@ -120,24 +122,24 @@ lrwxrwxrwx 1 root root 13 Apr 25 04:38 libsai.so.1 -> libsai.so.1.0 ... ``` -## 各种实现特定功能的容器 +## Feature Containers -SONiC中还有很多的容器是为了实现一些特定功能而存在的。这些容器一般都有着特殊的外部接口(非SONiC CLI和REST API)和实现(非OS或ASIC),比如: +There are many containers in SONiC designed to implement specific features. These containers usually have special external interfaces (non-SONiC CLI and REST API) and implementations (non-OS or ASIC), such as: -- bgp:用来实现BGP协议(Border Gateway Protocol,边界网关协议)的容器 -- lldp:用来实现LLDP协议(Link Layer Discovery Protocol,链路层发现协议)的容器 -- teamd:用来实现Link Aggregation(链路聚合)的容器 -- snmp:用来实现SNMP协议(Simple Network Management Protocol,简单网络管理协议)的容器 +- `bgp`: Container for implementing the BGP and other routing protocol (Border Gateway Protocol) +- `lldp`: Container for implementing the LLDP protocol (Link Layer Discovery Protocol) +- `teamd`: Container for implementing Link Aggregation +- `snmp`: Container for implementing the SNMP protocol (Simple Network Management Protocol) -和SWSS类似,为了适应SONiC的架构,它们中间也都会运行着上面我们提到的那几种服务: +Similar to SWSS, these containers also run the services we mentioned earlier to adapt to SONiC's architecture: -- 配置管理和下发(类似`*mgrd`):`lldpmgrd`,`zebra`(bgp) -- 状态同步(类似`*syncd`):`lldpsyncd`,`fpmsyncd`(bgp),`teamsyncd` -- 服务实现或者外部接口(`*d`):`lldpd`,`bgpd`,`teamd`,`snmpd` +- Configuration management and deployment (similar to `*mgrd`): `lldpmgrd`, `zebra` (bgp) +- State synchronization (similar to `*syncd`): `lldpsyncd`, `fpmsyncd` (bgp), `teamsyncd` +- Service implementation or external interface (`*d`): `lldpd`, `bgpd`, `teamd`, `snmpd` -## 管理服务容器:mgmt-framework +## Management Service Container: `mgmt-framework` -我们在之前的章节中已经看过如何使用SONiC的CLI来进行一些交换机的配置,但是在实际生产环境中,手动登录交换机使用CLI来配置所有的交换机是不现实的,所以SONiC提供了一个REST API来解决这个问题。这个REST API的实现就是在`mgmt-framework`容器中。我们可以通过`ps`命令来查看: +In previous chapters, we have seen how to use SONiC's CLI to configure some aspects of the switch. However, in a production environment, manually logging into the switch and using the CLI to configure all switches is unrealistic. Therefore, SONiC provides a REST API to solve this problem. This REST API is implemented in the `mgmt-framework` container. We can check it using the `ps` command: ```bash admin@sonic:~$ docker exec -it mgmt-framework bash @@ -148,15 +150,15 @@ root 16 0.3 1.2 1472804 52036 pts/0 Sl 16:20 0:02 /usr/sbin/res ... ``` -其实除了REST API,SONiC还可以通过其他方式来进行管理,如gNMI,这些也都是运行在这个容器中的。其整体架构如下图所示 [\[2\]][SONiCMgmtFramework]: +In addition to the REST API, SONiC can also be managed through other methods such as gNMI, all of which run in this container. The overall architecture is shown in the figure below [\[2\]][SONiCMgmtFramework]: ![](assets/chapter-2/sonic-mgmt-framework.jpg) -这里我们也可以发现,其实我们使用的CLI,底层也是通过调用这个REST API来实现的~ +Here we can also see that the CLI we use can be implemented by calling this REST API at the bottom layer. -## 平台监控容器:pmon(Platform Monitor) +## Platform Monitor Container: `pmon` -这个容器里面的服务基本都是用来监控交换机一些基础硬件的运行状态的,比如温度,电源,风扇,SFP事件等等。同样,我们可以用`ps`命令来查看这个容器中运行的服务: +The services in this container are mainly used to monitor the basic hardware status of the switch, such as temperature, power supply, fans, SFP events, etc. Similarly, we can use the `ps` command to check the services running in this container: ```bash admin@sonic:~$ docker exec -it pmon bash @@ -174,9 +176,9 @@ root 45 0.0 0.8 58648 32220 pts/0 S Apr26 2:45 python3 /usr/ ... ``` -其中大部分的服务从名字我们就能猜出来是做什么的了,中间只有xcvrd不是那么明显,这里xcvr是transceiver的缩写,它是用来监控交换机的光模块的,比如SFP,QSFP等等。 +The purpose of most of these services can be told from their names. The only one that is not so obvious is `xcvrd`, where xcv stands for transceiver. It is used to monitor the optical modules of the switch, such as SFP, QSFP, etc. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [SONiC Management Framework][SONiCMgmtFramework] diff --git a/src/2-4-sai-intro.md b/src/2-4-sai-intro.md index dc5c6c5..cc5bd5f 100644 --- a/src/2-4-sai-intro.md +++ b/src/2-4-sai-intro.md @@ -1,12 +1,12 @@ # SAI -SAI(Switch Abstraction Interface,交换机抽象接口)是SONiC的基石,正因为有了它,SONiC才能支持多种硬件平台。我们在[这个SAI API的文档][SAIAPI]中,可以看到它定义的所有接口。 +SAI (Switch Abstraction Interface) is the cornerstone of SONiC, while enables it to support multiple hardware platforms. In [this SAI API document][SAIAPI], we can see all the interfaces it defines. -[在核心容器一节中我们提到,SAI运行在`syncd`容器中](./2-3-key-containers.html)。不过和其他组件不同,它并不是一个服务,而是一组公共的头文件和动态链接库(.so)。其中,所有的抽象接口都以c语言头文件的方式定义在了[OCP的SAI仓库][OCPSAI]中,而.so文件则由各个硬件厂商提供,用于实现SAI的接口。 +[In the core container section, we mentioned that SAI runs in the `syncd` container](./2-3-key-containers.html). However, unlike other components, it is not a service but a set of common header files and dynamic link libraries (.so). All abstract interfaces are defined as C language header files in the [OCP SAI repository][OCPSAI], and the hardware vendors provides the .so files that implement the SAI interfaces. -## SAI接口 +## SAI Interface -为了有一个更加直观的理解,我们拿一小部分代码来展示一下SAI的接口定义和初始化的方法,如下: +To make things more intuitive, let's take a small portion of the code to show how SAI interfaces look like and how it works, as follows: ```cpp // File: meta/saimetadata.h @@ -37,23 +37,23 @@ typedef struct _sai_port_api_t } sai_port_api_t; ``` -其中,`sai_apis_t`结构体是SAI所有模块的接口的集合,其中每个成员都是一个特定模块的接口列表的指针。我们用`sai_switch_api_t`来举例,它定义了SAI Switch模块的所有接口,我们在`inc/saiswitch.h`中可以看到它的定义。同样的,我们在`inc/saiport.h`中可以看到SAI Port模块的接口定义。 +The `sai_apis_t` structure is a collection of interfaces for all SAI modules, with each member being a pointer to a specific module's interface list. For example, `sai_switch_api_t` defines all the interfaces for the SAI Switch module, and its definition can be found in `inc/saiswitch.h`. Similarly, the interface definitions for the SAI Port module can be found in `inc/saiport.h`. -## SAI初始化 +## SAI Initialization -SAI的初始化其实就是想办法获取上面这些函数指针,这样我们就可以通过SAI的接口来操作ASIC了。 +SAI initialization is essentially about obtaining these function pointers so that we can operate the ASIC through the SAI interfaces. -参与SAI初始化的主要函数有两个,他们都定义在`inc/sai.h`中: +The main functions involved in SAI initialization are defined in `inc/sai.h`: -- `sai_api_initialize`:初始化SAI -- `sai_api_query`:传入SAI的API的类型,获取对应的接口列表 +- `sai_api_initialize`: Initialize SAI +- `sai_api_query`: Pass in the type of SAI API to get the corresponding interface list -虽然大部分厂商的SAI实现是闭源的,但是mellanox却开源了自己的SAI实现,所以这里我们可以借助其更加深入的理解SAI是如何工作的。 +Although most vendors' SAI implementations are closed-source, Mellanox has open-sourced its SAI implementation, allowing us to gain a deeper understanding of how SAI works. -比如,`sai_api_initialize`函数其实就是简单的设置设置两个全局变量,然后返回`SAI_STATUS_SUCCESS`: +For example, the `sai_api_initialize` function simply sets two global variables and returns `SAI_STATUS_SUCCESS`: ```cpp -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_interfacequery.c sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const sai_service_method_table_t* services) { if (g_initialized) { @@ -67,10 +67,10 @@ sai_status_t sai_api_initialize(_In_ uint64_t flags, _In_ const sai_service_meth } ``` -初始化完成后,我们就可以使用`sai_api_query`函数,通过传入API的类型来查询对应的接口列表,而每一个接口列表其实都是一个全局变量: +After initialization, we can use the `sai_api_query` function to query the corresponding interface list by passing in the type of API, where each interface list is actually a global variable: ```cpp -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_interfacequery.c sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** api_method_table) { if (!g_initialized) { @@ -81,7 +81,7 @@ sai_status_t sai_api_query(_In_ sai_api_t sai_api_id, _Out_ void** api_method_ta return sai_api_query_eth(sai_api_id, api_method_table); } -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_interfacequery_eth.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_interfacequery_eth.c sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** api_method_table) { switch (sai_api_id) { @@ -101,7 +101,7 @@ sai_status_t sai_api_query_eth(_In_ sai_api_t sai_api_id, _Out_ void** api_metho } } -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_bridge.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_bridge.c const sai_bridge_api_t mlnx_bridge_api = { mlnx_create_bridge, mlnx_remove_bridge, @@ -111,7 +111,7 @@ const sai_bridge_api_t mlnx_bridge_api = { }; -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_switch.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_switch.c const sai_switch_api_t mlnx_switch_api = { mlnx_create_switch, mlnx_remove_switch, @@ -121,11 +121,11 @@ const sai_switch_api_t mlnx_switch_api = { }; ``` -## SAI的使用 +## Using SAI -在`syncd`容器中,SONiC会在启动时启动`syncd`服务,而`syncd`服务会加载当前系统中的SAI组件。这个组件由各个厂商提供,它们会根据自己的硬件平台来实现上面展现的SAI的接口,从而让SONiC使用统一的上层逻辑来控制多种不同的硬件平台。 +In the `syncd` container, SONiC starts the `syncd` service at startup, which loads the SAI component present in the system. This component is provided by various vendors, who implement the SAI interfaces based on their hardware platforms, allowing SONiC to use a unified upper-layer logic to control various hardware platforms. -我们可以通过`ps`, `ls`和`nm`命令来简单的对这个进行验证: +We can verify this using the `ps`, `ls`, and `nm` commands: ```bash # Enter into syncd container @@ -161,7 +161,7 @@ $ vim sai-exports.txt ... ``` -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [SAI API][SAIAPI] diff --git a/src/2-core-components-intro.md b/src/2-core-components-intro.md index accac92..576c211 100644 --- a/src/2-core-components-intro.md +++ b/src/2-core-components-intro.md @@ -1,22 +1,22 @@ -# 核心组件简介 +# Core components -我们也许会觉得交换机是一个很简单的网络设备,但是实际上交换机上的组件非常的多,而且由于SONiC中Redis的解耦,我们很难简单的对代码进行跟踪来理解服务之间的关系,这就需要我们先建立一个比较抽象的整体模型,然后再去深入的学习每个组件的细节。所以在深入其他部分之前,我们这里先对每个组件都做一个点到为止的介绍,帮助大家建立一个大概的整体模型。 +We might feel that a switch is a simple network device, but in fact, there could be many components running on the switch. -```admonish info -在阅读本章之前,有两个名词会经常在本章和SONiC的官方文档中出现:ASIC(Application-Specific Integrated Circuit)和ASIC状态(State)。它们指的是交换机中用来进行包处理的Pipeline的状态,比如,ACL,转发方式等等,这个和其他交换机的硬件状态,比如,端口状态(端口速度,接口类型),IP信息等等硬件状态是非常不同的。 +Since SONiC decoupled all its services using Redis, it can be difficult to understand the relationships between services by simpling tracking the code. To get started on SONiC quickly, it is better to first establish a high-level model, and then delve into the details of each component. Therefore, before diving into other parts, we will first give a brief introduction to each component to help everyone build a rough overall model. -如果大家有兴趣了解更深入的细节,可以先移步阅读两个相关资料:[SAI (Switch Abstraction Interface) API][SAIAPI]和一篇RMT(Reprogrammable Match Table)的相关论文:[Forwarding Metamorphosis: Fast Programmable Match-Action Processing in Hardware for SDN][PISA]。 +```admonish info +Before reading this chapter, there are two terms that will frequently appear in this chapter and in SONiC's official documentation: ASIC (Application-Specific Integrated Circuit) and ASIC state. They refer to the state of the pipeline used for packet processing in the switch, such as ACL or other packet forwarding methods. -这些都会对我们阅读SONiC的文档有很大的帮助。 +If you are interested in learning more details, you can first read two related materials: [SAI (Switch Abstraction Interface) API][SAIAPI] and a related paper on RMT (Reprogrammable Match Table): [Forwarding Metamorphosis: Fast Programmable Match-Action Processing in Hardware for SDN][PISA]. ``` -另外为了方便我们的理解和阅读,我们也把SONiC架构图在这里放在这一章的开头,作为引用: +In addition, to help us get started, we placed the SONiC architecture diagram here again as a reference: ![](assets/chapter-2/sonic-arch.png) _(Source: [SONiC Wiki - Architecture][SONiCArch])_ -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [SAI API][SAIAPI] diff --git a/src/3-1-code-repos.md b/src/3-1-code-repos.md index ed55053..17729c4 100644 --- a/src/3-1-code-repos.md +++ b/src/3-1-code-repos.md @@ -1,134 +1,136 @@ -# 代码仓库 +# Code Repositories -SONiC的代码都托管在[GitHub的sonic-net账号][SONiCGitHub]上,仓库数量有30几个之多,所以刚开始看SONiC的代码时,肯定是会有点懵的,不过不用担心,我们这里就来一起看看~ +The code of SONiC is hosted on the [sonic-net account on GitHub][SONiCGitHub], with over 30 repositories. It can be a bit overwhelming at first, but don't worry, we'll go through them together here. -## 核心仓库 +## Core Repositories -首先是SONiC中最重要的两个核心仓库:SONiC和sonic-buildimage。 +First, let's look at the two most important core repositories in SONiC: SONiC and sonic-buildimage. -### Landing仓库:SONiC +### Landing Repository: `SONiC` -这个仓库里面存储着SONiC的Landing Page和大量的文档,Wiki,教程,以往的Talk的Slides,等等等等。这个仓库可以说是每个新人上手最常用的仓库了,但是注意,这个仓库里面**没有任何的代码**,只有文档。 +This repository contains the SONiC Landing Page and a large number of documents, Wiki, tutorials, slides from past talks, and so on. This repository is the most commonly used by newcomers, but note that there is **no code** in this repository, only documentation. -### 镜像构建仓库:sonic-buildimage +### Image Build Repository: `sonic-buildimage` -这个构建仓库为什么对于我们十分重要?和其他项目不同,**SONiC的构建仓库其实才是它的主仓库**!这个仓库里面包含: +Why is this build repository so important to us? Unlike other projects, **the build repository of SONiC is actually its main repository**! This repository contains: -- 所有的功能实现仓库,它们都以submodule的形式被加入到了这个仓库中(`src`目录) -- 所有设备厂商的支持文件(`device`目录),比如每个型号的交换机的配置文件,用来访问硬件的支持脚本,等等等等,比如:我的交换机是Arista 7050 QX-32S,那么我就可以在`device/arista/x86_64-arista_7050_qx32s`目录中找到它的支持文件。 -- 所有ASIC芯片厂商提供的支持文件(`platform`目录),比如每个平台的驱动程序,BSP,底层支持的脚本等等。这里我们可以看到几乎所有的主流芯片厂商的支持文件,比如:Broadcom,Mellanox,等等,也有用来做模拟软交换机的实现,比如vs和p4。 -- SONiC用来构建所有容器镜像的Dockerfile(`dockers`目录) -- 各种各样通用的配置文件和脚本(`files`目录) -- 用来做构建的编译容器的dockerfile(`sonic-slave-*`目录) -- 等等…… +- All the feature implementation repositories, in the form of git submodules (under the `src` directory). +- Support files for each device from switch manufactures (under the `device` directory), such as device configuration files for each model of switch, scripts, and so on. For example, my switch is an Arista 7050QX-32S, so I can find its support files in the `device/arista/x86_64-arista_7050_qx32s` directory. +- Support files provided by all ASIC chip manufacturers (in the `platform` directory), such as drivers, BSP, and low-level support scripts for each platform. Here we can see support files from almost all major chip manufacturers, such as Broadcom, Mellanox, etc., as well as implementations for simulated software switches, such as vs and p4. But for protecting IPs from each vendor, most of the time, the repo only contains the Makefiles that downloads these things for build purpose. +- Dockerfiles for building all container images used by SONiC (in the `dockers` directory). +- Various general configuration files and scripts (in the `files` directory). +- Dockerfiles for the build containers used for building (in the `sonic-slave-*` directories). +- And more... -正因为这个仓库里面将所有相关的资源全都放在了一起,所以我们学习SONiC的代码时,基本只需要下载这一个源码仓库就可以了,不管是搜索还是跳转都非常方便! +Because this repository brings all related resources together, we basically only need to checkout this single repository to get all SONiC's code. It makes searching and navigating the code much more convenient than checking out the repos one by one! -## 功能实现仓库 +## Feature Repositories -除了核心仓库,SONiC下还有很多功能实现仓库,里面都是各个容器和子服务的实现,这些仓库都被以submodule的形式放在了sonic-buildimage的`src`目录下,如果我们想对SONiC进行修改和贡献,我们也需要了解一下。 +In addition to the core repositories, SONiC also has many feature repositories, which contain the implementations of various containers and services. These repositories are imported as submodules in the `src` directory of sonic-buildimage. If we would like to modify and contribute to SONiC, we also need to understand them. -### SWSS(Switch State Service)相关仓库 +### SWSS (Switch State Service) Related Repositories -在上一篇中我们介绍过,SWSS容器是SONiC的大脑,在SONiC下,它由两个repo组成:[sonic-swss-common](https://github.com/sonic-net/sonic-swss-common)和[sonic-swss](https://github.com/sonic-net/sonic-swss)。 +As introduced in the previous section, the SWSS container is the brain of SONiC. In SONiC, it consists of two repositories: [sonic-swss-common](https://github.com/sonic-net/sonic-swss-common) and [sonic-swss](https://github.com/sonic-net/sonic-swss). -#### SWSS公共库:sonic-swss-common +#### SWSS Common Library: `sonic-swss-common` -首先是公共库:sonic-swss-common()。 +The first one is the common library: `sonic-swss-common` (). -这个仓库里面包含了所有`*mgrd`和`*syncd`服务所需要的公共功能,比如,logger,json,netlink的封装,Redis操作和基于Redis的各种服务间通讯机制的封装等等。虽然能看出来这个仓库一开始的目标是专门给swss服务使用的,但是也正因为功能多,很多其他的仓库都有它的引用,比如`swss-sairedis`和`swss-restapi`。 +This repository contains all the common functionalities needed by `*mgrd` and `*syncd` services, such as logger, JSON, netlink encapsulation, Redis operations, and various inter-service communication mechanisms based on Redis. Although it was initially intended for swss services, its extensive functionalities have led to its use in many other repositories, such as `swss-sairedis` and `swss-restapi`. -#### SWSS主仓库:sonic-swss +#### Main SWSS Repository: `sonic-swss` -然后就是SWSS的主仓库sonic-swss了:。 +Next is the main SWSS repository: `sonic-swss` (). -我们可以在这个仓库中找到: +In this repository, we can find: -- 绝大部分的`*mgrd`和`*syncd`服务:`orchagent`, `portsyncd/portmgrd/intfmgrd`,`neighsyncd/nbrmgrd`,`natsyncd/natmgrd`,`buffermgrd`,`coppmgrd`,`macsecmgrd`,`sflowmgrd`,`tunnelmgrd`,`vlanmgrd`,`vrfmgrd`,`vxlanmgrd`,等等。 -- `swssconfig`:在`swssconfig`目录下,用于在快速重启时(fast reboot)恢复FDB和ARP表。 -- `swssplayer`:也在`swssconfig`目录下,用来记录所有通过SWSS进行的配置下发操作,这样我们就可以利用它来做replay,从而对问题进行重现和调试。 -- 甚至一些不在SWSS容器中的服务,比如`fpmsyncd`(bgp容器)和`teamsyncd/teammgrd`(teamd容器)。 +- Most of the `*mgrd` and `*syncd` services: `orchagent`, `portsyncd/portmgrd/intfmgrd`, `neighsyncd/nbrmgrd`, `natsyncd/natmgrd`, `buffermgrd`, `coppmgrd`, `macsecmgrd`, `sflowmgrd`, `tunnelmgrd`, `vlanmgrd`, `vrfmgrd`, `vxlanmgrd`, and more. +- `swssconfig`: Located in the `swssconfig` directory, used to restore FDB and ARP tables during fast reboot. +- `swssplayer`: Also in the `swssconfig` directory, used to record all configuration operations performed through SWSS, allowing us to replay them for troubleshooting and debugging. +- Even some services not in the SWSS container, such as `fpmsyncd` (BGP container) and `teamsyncd/teammgrd` (teamd container). -### SAI/平台相关仓库 +### SAI/Platform Related Repositories -接下来就是作为交换机抽象接口的SAI了,[虽然SAI是微软提出来并在2015年3月份发布了0.1版本](https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf),但是[在2015年9月份,SONiC都还没有发布第一个版本的时候,就已经被OCP接收并作为一个公共的标准了](https://azure.microsoft.com/en-us/blog/switch-abstraction-interface-sai-officially-accepted-by-the-open-compute-project-ocp/),这也是SONiC能够在这么短的时间内就得到了这么多厂商的支持的原因之一。而也因为如此,SAI的代码仓库也被分成了两部分: +Next is the Switch Abstraction Interface (SAI). [Although SAI was proposed by Microsoft and released version 0.1 in March 2015](https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf), [by September 2015, before SONiC had even released its first version, it was already accepted by OCP as a public standard](https://azure.microsoft.com/en-us/blog/switch-abstraction-interface-sai-officially-accepted-by-the-open-compute-project-ocp/). This shows how quickly SONiC and SAI was getting supports from the community and vendors. -- OCP下的OpenComputeProject/SAI:。里面包含了有关SAI标准的所有代码,包括SAI的头文件,behavior model,测试用例,文档等等。 -- SONiC下的sonic-sairedis:。里面包含了SONiC中用来和SAI交互的所有代码,比如syncd服务,和各种调试统计,比如用来做replay的`saiplayer`和用来导出asic状态的`saidump`。 +Overall, the SAI code is divided into two parts: -除了这两个仓库之外,还有一个平台相关的仓库,比如:[sonic-platform-vpp](https://github.com/sonic-net/sonic-platform-vpp),它的作用是通过SAI的接口,利用vpp来实现数据平面的功能,相当于一个高性能的软交换机,个人感觉未来可能会被合并到buildimage仓库中,作为platform目录下的一部分。 +- OpenComputeProject/SAI under OCP: . This repository contains all the code related to the SAI standard, including SAI header files, behavior models, test cases, documentation, and more. +- sonic-sairedis under SONiC: . This repository contains all the code used by SONiC to interact with SAI, such as the syncd service and various debugging tools like `saiplayer` for replay and `saidump` for exporting ASIC states. -### 管理服务(mgmt)相关仓库 +In addition to these two repositories, there is another platform-related repository, such as [sonic-platform-vpp](https://github.com/sonic-net/sonic-platform-vpp), which uses SAI interfaces to implement data plane functionalities through VPP, essentially acting as a high-performance soft switch. I personally feel it might be merged into the buildimage repository in the future as part of the platform directory. -然后是SONiC中所有和[管理服务][SONiCMgmtFramework]相关的仓库: +### Management Service (mgmt) Related Repositories -| 名称 | 说明 | +Next are all the repositories related to [management services][SONiCMgmtFramework] in SONiC: + +| Name | Description | | --- | --- | -| [sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common) | 管理服务的基础库,里面包含着`translib`,yang model相关的代码 | -| [sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework) | 使用Go来实现的REST Server,是下方架构图中的REST Gateway(进程名:`rest_server`) | -| [sonic-gnmi](https://github.com/sonic-net/sonic-gnmi) | 和sonic-mgmt-framework类似,是下方架构图中,基于gRPC的gNMI(gRPC Network Management Interface)Server | -| [sonic-restapi](https://github.com/sonic-net/sonic-restapi) | 这是SONiC使用go来实现的另一个配置管理的REST Server,和mgmt-framework不同,这个server在收到消息后会直接对CONFIG_DB进行操作,而不是走translib(下图中没有,进程名:`go-server-server`) | -| [sonic-mgmt](https://github.com/sonic-net/sonic-mgmt) | 各种自动化脚本(`ansible`目录),测试(`tests`目录),用来搭建test bed和测试上报(`test_reporting`目录)之类的, | +| [sonic-mgmt-common](https://github.com/sonic-net/sonic-mgmt-common) | Base library for management services, containing `translib`, YANG model-related code | +| [sonic-mgmt-framework](https://github.com/sonic-net/sonic-mgmt-framework) | REST Server implemented in Go, acting as the REST Gateway in the architecture diagram below (process name: `rest_server`) | +| [sonic-gnmi](https://github.com/sonic-net/sonic-gnmi) | Similar to sonic-mgmt-framework, this is the gNMI (gRPC Network Management Interface) Server based on gRPC in the architecture diagram below | +| [sonic-restapi](https://github.com/sonic-net/sonic-restapi) | Another configuration management REST Server implemented in Go. Unlike mgmt-framework, this server directly operates on CONFIG_DB upon receiving messages, instead of using translib (not shown in the diagram, process name: `go-server-server`) | +| [sonic-mgmt](https://github.com/sonic-net/sonic-mgmt) | Various automation scripts (in the `ansible` directory), tests (in the `tests` directory), test bed setup and test reporting (in the `test_reporting` directory), and more | -这里还是附上SONiC管理服务的架构图,方便大家配合食用 [\[4\]][SONiCMgmtFramework]: +Here is the architecture diagram of SONiC management services for reference [\[4\]][SONiCMgmtFramework]: ![](assets/chapter-3/sonic-mgmt-framework.jpg) -### 平台监控相关仓库:sonic-platform-common和sonic-platform-daemons +### Platform Monitoring Related Repositories: `sonic-platform-common` and `sonic-platform-daemons` -以下两个仓库都和平台监控和控制相关,比如LED,风扇,电源,温控等等: +The following two repositories are related to platform monitoring and control, such as LEDs, fans, power supplies, thermal control, and more: -| 名称 | 说明 | +| Name | Description | | --- | --- | -| [sonic-platform-common](https://github.com/sonic-net/sonic-platform-common) | 这是给厂商们提供的基础包,用来定义访问风扇,LED,电源管理,温控等等模块的接口定义,这些接口都是用python来实现的 | -| [sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-daemons) | 这里包含了SONiC中pmon容器中运行的各种监控服务:`chassisd`,`ledd`,`pcied`,`psud`,`syseepromd`,`thermalctld`,`xcvrd`,`ycabled`,它们都使用python实现,通过和中心数据库Redis进行连接,和加载并调用各个厂商提供的接口实现来对各个模块进行监控和控制 | +| [sonic-platform-common](https://github.com/sonic-net/sonic-platform-common) | A base package provided to manufacturers, defining interfaces for accessing fans, LEDs, power management, thermal control, and other modules, all implemented in Python | +| [sonic-platform-daemons](https://github.com/sonic-net/sonic-platform-daemons) | Contains various monitoring services running in the pmon container in SONiC, such as `chassisd`, `ledd`, `pcied`, `psud`, `syseepromd`, `thermalctld`, `xcvrd`, `ycabled`. All these services are implemented in Python, and used for monitoring and controlling the platform modules, by calling the interface implementations provided by manufacturers. | -### 其他功能实现仓库 +### Other Feature Repositories -除了上面这些仓库以外,SONiC还有很多实现其方方面面功能的仓库,有些是一个或多个进程,有些是一些库,它们的作用如下表所示: +In addition to the repositories above, SONiC has many repositories implementing various functionalities. They can be services or libraries described in the table below: -| 仓库 | 介绍 | +| Repository | Description | | --- | --- | -| [sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent) | [AgentX](https://www.ietf.org/rfc/rfc2741.txt) SNMP subagent的实现(`sonic_ax_impl`),用于连接Redis数据库,给snmpd提供所需要的各种信息,可以把它理解成snmpd的控制面,而snmpd是数据面,用于响应外部SNMP的请求 | -| [sonic-frr](https://github.com/sonic-net/sonic-frr) | FRRouting,各种路由协议的实现,所以这个仓库中我们可以找到如`bgpd`,`zebra`这类的路由相关的进程实现 | -| [sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd) | Dual ToR support,检查Link的状态,并且控制ToR的连接 | +| [sonic-frr](https://github.com/sonic-net/sonic-frr) | FRRouting, implementing various routing protocols, so in this repository, we can find implementations of routing-related processes like `bgpd`, `zebra`, etc. | +| [sonic-snmpagent](https://github.com/sonic-net/sonic-snmpagent) | Implementation of [AgentX](https://www.ietf.org/rfc/rfc2741.txt) SNMP subagent (`sonic_ax_impl`), used to connect to the Redis database and provide various information needed by snmpd. It can be understood as the control plane of snmpd, while snmpd is the data plane, responding to external SNMP requests | +| [sonic-linkmgrd](https://github.com/sonic-net/sonic-linkmgrd) | Dual ToR support, checking the status of links and controlling ToR connections | | [sonic-dhcp-relay](https://github.com/sonic-net/sonic-dhcp-relay) | DHCP relay agent | -| [sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon) | 监控DHCP的状态,并报告给中心数据库Redis | -| [sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd) | `lldp_syncd`服务,但是repo的名字没取好,叫做dbsyncd | -| [sonic-pins](https://github.com/sonic-net/sonic-pins) | Google开发的基于P4的网络栈支持(P4 Integrated Network Stack,PINS),更多信息可以参看[PINS的官网][SONiCPINS]。 | -| [sonic-stp](https://github.com/sonic-net/sonic-stp) | STP(Spanning Tree Protocol)的支持 | +| [sonic-dhcpmon](https://github.com/sonic-net/sonic-dhcpmon) | Monitors the status of DHCP and reports to the central Redis database | +| [sonic-dbsyncd](https://github.com/sonic-net/sonic-dbsyncd) | `lldp_syncd` service, but the repository name is not well-chosen, called dbsyncd | +| [sonic-pins](https://github.com/sonic-net/sonic-pins) | Google's P4-based network stack support (P4 Integrated Network Stack, PINS). More information can be found on the [PINS website][SONiCPINS] | +| [sonic-stp](https://github.com/sonic-net/sonic-stp) | STP (Spanning Tree Protocol) support | | [sonic-ztp](https://github.com/sonic-net/sonic-ztp) | [Zero Touch Provisioning][SONiCZTP] | | [DASH](https://github.com/sonic-net/DASH) | [Disaggregated API for SONiC Hosts][SONiCDASH] | -| [sonic-host-services](https://github.com/sonic-net/sonic-host-services) | 运行在host上通过dbus用来为容器中的服务提供支持的服务,比如保存和重新加载配置,保存dump之类的非常有限的功能,类似一个host broker | -| [sonic-fips](https://github.com/sonic-net/sonic-fips) | FIPS(Federal Information Processing Standards)的支持,里面有很多为了支持FIPS标准而加入的各种补丁文件 | -| [sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant) | 各种无线网络协议的支持 | +| [sonic-host-services](https://github.com/sonic-net/sonic-host-services) | Services running on the host, providing support to services in containers via dbus, such as saving and reloading configurations, saving dumps, etc., similar to a host broker | +| [sonic-fips](https://github.com/sonic-net/sonic-fips) | FIPS (Federal Information Processing Standards) support, containing various patch files added to support FIPS standards | +| [sonic-wpa-supplicant](https://github.com/sonic-net/sonic-wpa-supplicant) | Support for various wireless network protocols | -## 工具仓库:sonic-utilities +## Tooling Repository: `sonic-utilities` -这个仓库存放着SONiC所有的命令行下的工具: +This repository contains all the command-line tools for SONiC: -- `config`,`show`,`clear`目录:这是三个SONiC CLI的主命令的实现。需要注意的是,具体的命令实现并不一定在这几个目录里面,大量的命令是通过调用其他命令来实现的,这几个命令只是提供了一个入口。 -- `scripts`,`sfputil`,`psuutil`,`pcieutil`,`fwutil`,`ssdutil`,`acl_loader`目录:这些目录下提供了大量的工具命令,但是它们大多并不是直接给用户使用的,而是被`config`,`show`和`clear`目录下的命令调用的,比如:`show platform fan`命令,就是通过调用`scripts`目录下的`fanshow`命令来实现的。 -- `utilities_common`,`flow_counter_util`,`syslog_util`目录:这些目录和上面类似,但是提供的是基础类,可以直接在python中import调用。 -- 另外还有很多其他的命令:`fdbutil`,`pddf_fanutil`,`pddf_ledutil`,`pddf_psuutil`,`pddf_thermalutil`,等等,用于查看和控制各个模块的状态。 -- `connect`和`consutil`目录:这两个目录下的命令是用来连接到其他SONiC设备并对其进行管理的。 -- `crm`目录:用来配置和查看SONiC中的[CRM(Critical Resource Monitoring)][SONiCCRM]。这个命令并没有被包含在`config`和`show`命令中,所以用户可以直接使用。 -- `pfc`目录:用来配置和查看SONiC中的[PFC(Priority-based Flow Control)][SONiCPFC]。 -- `pfcwd`目录:用来配置和查看SONiC中的[PFC Watch Dog][SONiCPFCWD],比如启动,停止,修改polling interval之类的操作。 +- `config`, `show`, `clear` directories: These are the implementations of the three main SONiC CLI commands. Note that the specific command implementations may not necessarily be in these directories; many commands are implemented by calling other commands, with these directories providing an entry point. +- `scripts`, `sfputil`, `psuutil`, `pcieutil`, `fwutil`, `ssdutil`, `acl_loader` directories: These directories provide many tool commands, but most are not directly used by users; instead, they are called by commands in the `config`, `show`, and `clear` directories. For example, the `show platform fan` command is implemented by calling the `fanshow` command in the `scripts` directory. +- `utilities_common`, `flow_counter_util`, `syslog_util` directories: Similar to the above, but they provide base classes that can be directly imported and called in Python. +- There are also many other commands: `fdbutil`, `pddf_fanutil`, `pddf_ledutil`, `pddf_psuutil`, `pddf_thermalutil`, etc., used to view and control the status of various modules. +- `connect` and `consutil` directories: Commands in these directories are used to connect to and manage other SONiC devices. +- `crm` directory: Used to configure and view [CRM (Critical Resource Monitoring)][SONiCCRM] in SONiC. This command is not included in the `config` and `show` commands, so users can use it directly. +- `pfc` directory: Used to configure and view PFC (Priority-based Flow Control) in SONiC. +- `pfcwd` directory: Used to configure and view [PFC Watch Dog][SONiCPFCWD] in SONiC, such as starting, stopping, modifying polling intervals, and more. -## 内核补丁:sonic-linux-kernel +## Kernel Patches: sonic-linux-kernel -虽然SONiC是基于debian的,但是默认的debian内核却不一定能运行SONiC,比如某个模块默认没有启动,或者某些老版本的驱动有问题,所以SONiC需要或多或少有一些修改的Linux内核。而这个仓库就是用来存放所有的内核补丁的。 +Although SONiC is based on Debian, the default Debian kernel may not necessarily run SONiC, such as certain modules not being enabled by default or issues with older drivers. Therefore, SONiC requires some modifications to the Linux kernel. This repository is used to store all the kernel patches. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [SONiC Source Repositories][SONiCRepo] @@ -140,6 +142,7 @@ SONiC的代码都托管在[GitHub的sonic-net账号][SONiCGitHub]上,仓库数 8. [SONiC P4 Integrated Network Stack][SONiCPINS] 9. [SONiC Disaggregated API for Switch Hosts][SONiCDash] 10. [SAI spec for OCP][SAISpec] +11. [PFC Watchdog][SONiCPFCWD] [SONiCIntro]: /posts/sonic-1-intro/ [SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture @@ -152,4 +155,5 @@ SONiC的代码都托管在[GitHub的sonic-net账号][SONiCGitHub]上,仓库数 [SONiCPINS]: https://opennetworking.org/pins/ [SONiCZTP]: https://github.com/sonic-net/SONiC/blob/master/doc/ztp/ztp.md [SONiCDASH]: https://github.com/sonic-net/DASH/blob/main/documentation/general/dash-high-level-design.md -[SAISpec]: https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf \ No newline at end of file +[SAISpec]: https://www.opencompute.org/documents/switch-abstraction-interface-ocp-specification-v0-2-pdf +[SONiCPFCWD]: https://github.com/sonic-net/SONiC/wiki/PFC-Watchdog \ No newline at end of file diff --git a/src/3-2-build.md b/src/3-2-build.md new file mode 100644 index 0000000..e29d252 --- /dev/null +++ b/src/3-2-build.md @@ -0,0 +1,223 @@ +# Build + +To ensure that we can successfully build SONiC on any platform as well, SONiC leverages docker to build its build environment. It installs all tools and dependencies in a docker container of the corresponding Debian version, mounts its code into the container, and then start the build process inside the container. This way, we can easily build SONiC on any platform without worrying about dependency mismatches. For example, some packages in Debian have higher versions than in Ubuntu, which might cause unexpected errors during build time or runtime. + +## Setup the Build Environment + +### Install Docker + +To support the containerized build environment, the first step is to ensure that Docker is installed on our machine. + +You can refer to the [official documentation][DockerInstall] for Docker installation methods. Here, we briefly introduce the installation method for Ubuntu. + +First, we need to add docker's source and certificate to the apt source list: + +```bash +sudo apt-get update +sudo apt-get install ca-certificates curl gnupg + +sudo install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg +sudo chmod a+r /etc/apt/keyrings/docker.gpg + +echo \ + "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ + "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +``` + +Then, we can quickly install docker via apt: + +```bash +sudo apt-get update +sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +``` + +After installing docker, we need to add the current user to the docker user group and **log out and log back in**. This way, we can run any docker commands without sudo! **This is very important** because subsequent SONiC builds do not allow the use of sudo. + +```bash +sudo gpasswd -a ${USER} docker +``` + +After installation, don't forget to verify the installation with the following command (note, no sudo is needed here!): + +```bash +docker run hello-world +``` + +### Install Other Dependencies + +```bash +sudo apt install -y python3-pip +pip3 install --user j2cli +``` + +### Pull the Code + +In [Chapter 3.1 Code Repositories](./3-1-code-repos), we mentioned that the main repository of SONiC is [sonic-buildimage][SonicBuildimageRepo]. It is also the only repo we need to focus on for now. + +Since this repository includes all other build-related repositories as submodules, we need to use the `--recurse-submodules` option when pulling the code with git: + +```bash +git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git +``` + +If you forget to pull the submodules when pulling the code, you can make up for it with the following command: + +```bash +git submodule update --init --recursive +``` + +After the code is downloaded, or for an existing repo, we can initialize the compilation environment with the following command. This command updates all current submodules to the required versions to help us successfully compile: + +```bash +sudo modprobe overlay +make init +``` + +## Set Your Target Platform + +[Although SONiC supports many different types of switches][SONiCDevices], different models of switches use different ASICs, which means different drivers and SDKs. Although SONiC uses SAI to hide these differences and provide a unified interface for the upper layers. However, we need to set target platform correctly to ensure that the right SAI will be used, so the SONiC we build can run on our target devices. + +Currently, SONiC mainly supports the following platforms: + +- broadcom +- mellanox +- marvell +- barefoot +- cavium +- centec +- nephos +- innovium +- vs + +After confirming the target platform, we can configure our build environment with the following command: + +```bash +make PLATFORM= configure +# e.g.: make PLATFORM=mellanox configure +``` + +```admonish note +All make commands (except `make init`) will first check and create all Debian version docker builders: `bookwarm`, `bullseye`, `stretch`, `jessie`, `buster`. Each builder takes tens of minutes to create, which is unnecessary for our daily development. Generally, we only need to create the latest version (currently `bookwarm`). The specific command is as follows: + + NO_BULLSEYE=1 NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make PLATFORM= configure + +To make future development more convenient and avoid entering these every time, we can set these environment variables in `~/.bashrc`, so that every time we open the terminal, they will be set automatically. + + export NOBULLSEYE=1 + export NOJESSIE=1 + export NOSTRETCH=1 + export NOBUSTER=1 +``` + +## Build the Code + +### Build All Code + +After setting the platform, we can start compiling the code: + +```bash +# The number of jobs can be the number of cores on your machine. +# Say, if you have 16 cores, then feel free to set it to 16 to speed up the build. +make SONIC_BUILD_JOBS=4 all +``` + +```admonish note +For daily development, we can also add SONIC_BUILD_JOBS and other variables above to `~/.bashrc`: + + export SONIC_BUILD_JOBS= +``` + +### Build Debug Image + +To improve the debug experience, SONiC also supports building debug image. During build, SONiC will make sure the symbols are kept and debug tools are installed inside all the containers, such as gdb. This will help us debug the code more easily. + +To build the debug image, we can use `INSTALL_DEBUG_TOOLS` build option: + +```bash +INSTALL_DEBUG_TOOLS=y make all +``` + +### Build Specific Package + +From SONiC's Build Pipeline, we can see that compiling the entire project is very time-consuming. Most of the time, our code changes only affect a small part of the code. So, is there a way to reduce our compilation workload? Gladly, yes! We can specify the make target to build only the target or package we need. + +In SONiC, the files generated by each subproject can be found in the `target` directory. For example: + +- Docker containers: target/.gz, e.g., `target/docker-orchagent.gz` +- Deb packages: target/debs//.deb, e.g., `target/debs/bullseye/libswsscommon_1.0.0_amd64.deb` +- Python wheels: target/python-wheels//.whl, e.g., `target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl` + +After figuring out the package we need to build, we can delete its generated files and then call the make command again. Here we use `libswsscommon` as an example: + +```bash +# Remove the deb package for bullseye +rm target/debs/bullseye/libswsscommon_1.0.0_amd64.deb + +# Build the deb package for bullseye +make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb +``` + +### Check and Handle Build Errors + +If an error occurs during the build process, we can check the log file of the failed project to find the specific reason. In SONiC, each subproject generates its related log file, which can be easily found in the `target` directory, such as: + +```bash +$ ls -l +... +-rw-r--r-- 1 r12f r12f 103M Jun 8 22:35 docker-database.gz +-rw-r--r-- 1 r12f r12f 26K Jun 8 22:35 docker-database.gz.log // Log file for docker-database.gz +-rw-r--r-- 1 r12f r12f 106M Jun 8 22:44 docker-dhcp-relay.gz +-rw-r--r-- 1 r12f r12f 106K Jun 8 22:44 docker-dhcp-relay.gz.log // Log file for docker-dhcp-relay.gz +``` + +If we don't want to check the log files every time, then fix errors and recompile in the root directory, SONiC provides another more convenient way that allows us to stay in the docker builder after build. This way, we can directly go to the corresponding directory to run the `make` command to recompile the things you need: + +```bash +# KEEP_SLAVE_ON=yes make +KEEP_SLAVE_ON=yes make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb +KEEP_SLAVE_ON=yes make all +``` + +```admonish note +Some parts of the code in some repositories will not be build during full build. For example, gtest in `sonic-swss-common`. So, when using this way to recompile, please make sure to check the original repository's build guidance to avoid errors, such as: . +``` + +## Get the Correct Image File + +After compilation, we can find the image files we need in the `target` directory. However, there will be many different types of SONiC images, so which one should we use? This mainly depends on what kind of BootLoader or Installer the switch uses. The mapping is as below: + +| Bootloader | Suffix | +| --- | --- | +| Aboot | .swi | +| ONIE | .bin | +| Grub | .img.gz | + +## Partial Upgrade + +Obviously, during development, build the image and then performing a full installation each time is very inefficient. So, we could choose not to install the image but directly upgrading certain deb packages as partial upgrade, which could improving our development efficiency. + +First, we can upload the deb package to the `/etc/sonic` directory of the switch. The files in this directory will be mapped to the `/etc/sonic` directory of all containers. Then, we can enter the container and use the `dpkg` command to install the deb package, as follows: + +```bash +# Enter the docker container +docker exec -it bash + +# Install deb package +dpkg -i +``` + +# References + +1. [SONiC Build Guide][SONiCBuild] +2. [Install Docker Engine][DockerInstall] +3. [Github repo: sonic-buildimage][SonicBuildimageRepo] +4. [SONiC Supported Devices and Platforms][SONiCDevices] +5. [Wrapper for starting make inside sonic-slave container][SONiCBuildImageMakeFile] + +[SONiCBuild]: https://github.com/sonic-net/sonic-buildimage/blob/master/README.md +[DockerInstall]: https://docs.docker.com/engine/install/ +[SonicBuildimageRepo]: https://github.com/sonic-net/sonic-buildimage +[SONiCDevices]: https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html +[SONiCBuildImageMakeFile]: https://github.com/sonic-net/sonic-buildimage/blob/master/Makefile.work \ No newline at end of file diff --git a/src/3-2-compile.md b/src/3-2-compile.md deleted file mode 100644 index ea57ece..0000000 --- a/src/3-2-compile.md +++ /dev/null @@ -1,214 +0,0 @@ -# 编译 - -## 编译环境 - -由于SONiC是基于debian开发的,为了保证我们无论在什么平台下都可以成功的编译SONiC,并且编译出来的程序能在对应的平台上运行,SONiC使用了容器化的编译环境 —— 它将所有的工具和依赖都安装在对应debian版本的docker容器中,然后将我们的代码挂载进容器,最后在容器内部进行编译工作,这样我们就可以很轻松的在任何平台上编译SONiC,而不用担心依赖不匹配的问题,比如有一些包在debian里的版本比ubuntu更高,这样就可能导致最后的程序在debian上运行的时候出现一些意外的错误。 - -## 初始化编译环境 - -### 安装Docker - -为了支持容器化的编译环境,第一步,我们需要保证我们的机器上安装了docker。 - -Docker的安装方法可以参考[官方文档][DockerInstall],这里我们以Ubuntu为例,简单介绍一下安装方法。 - -首先,我们需要把docker的源和证书加入到apt的源列表中: - -```bash -sudo apt-get update -sudo apt-get install ca-certificates curl gnupg - -sudo install -m 0755 -d /etc/apt/keyrings -curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg -sudo chmod a+r /etc/apt/keyrings/docker.gpg - -echo \ - "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ - "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ - sudo tee /etc/apt/sources.list.d/docker.list > /dev/null -``` - -然后,我们就可以通过apt来快速安装啦: - -```bash -sudo apt-get update -sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -``` - -安装完docker的程序之后,我们还需要把当前的账户添加到docker的用户组中,然后**退出并重新登录当前用户**,这样我们就可以不用sudo来运行docker命令了!**这一点非常重要**,因为后续SONiC的build是不允许使用sudo的。 - -```bash -sudo gpasswd -a ${USER} docker -``` - -安装完成之后,别忘了通过以下命令来验证一下是否安装成功(注意,此处不需要sudo!): - -```bash -docker run hello-world -``` - -### 安装其他依赖 - -```bash -sudo apt install -y python3-pip -pip3 install --user j2cli -``` - -### 拉取代码 - -在[3.1 代码仓库](./3-1-code-repos)一章中,我们提到了SONiC的主仓库是[sonic-buildimage][SonicBuildimageRepo]。它也是我们目前为止唯一需要安装关注的repo。 - -因为这个仓库通过submodule的形式将其他所有和编译相关的仓库包含在内,我们通过git命令拉取代码时需要注意加上`--recuse-submodules`的选项: - -```bash -git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git -``` - -如果在拉取代码的时候忘记拉取submodule,可以通过以下命令来补上: - -```bash -git submodule update --init --recursive -``` - -当代码下载完毕之后,或者对于已有的repo,我们就可以通过以下命令来初始化编译环境了。这个命令更新当前所有的submodule到需要的版本,以帮助我们成功编译: - -```bash -sudo modprobe overlay -make init -``` - -## 了解并设置你的目标平台 - -[SONiC虽然支持非常多种不同的交换机][SONiCDevices],但是由于不同型号的交换机使用的ASIC不同,所使用的驱动和SDK也会不同。SONiC通过SAI来封装这些变化,为上层提供统一的配置接口,但是在编译的时候,我们需要正确的设置好,这样才能保证我们编译出来的SONiC可以在我们的目标平台上运行。 - -现在,SONiC主要支持如下几个平台: - -- barefoot -- broadcom -- marvell -- mellanox -- cavium -- centec -- nephos -- innovium -- vs - -在确认好平台之后,我们就可以运行如下命令来配置我们的编译环境了: - -```bash -make PLATFORM= configure -# e.g.: make PLATFORM=mellanox configure -``` - -```admonish note -所有的make命令(除了`make init`)一开始都会检查并创建所有debian版本的docker builder:bullseye,stretch,jessie,buster。每个builder都需要几十分钟的时间才能创建完成,这对于我们平时开发而言实在完全没有必要,一般来说,我们只需要创建最新的版本即可(当前为bullseye,bookwarm暂时还没有支持),具体命令如下: - - NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make PLATFORM= configure - -当然,为了以后开发更加方便,避免重复输入,我们可以将这个命令写入到`~/.bashrc`中,这样每次打开终端的时候,就会设置好这些环境变量了。 - - export NOJESSIE=1 - export NOSTRETCH=1 - export NOBUSTER=1 -``` - -## 编译代码 - -### 编译全部代码 - -设置好平台之后,我们就可以开始编译代码了: - -```bash -# The number of jobs can be the number of cores on your machine. -# Say, if you have 16 cores, then feel free to set it to 16 to speed up the build. -make SONIC_BUILD_JOBS=4 all -``` - -```admonish note -当然,对于开发而言,我们可以把SONIC_BUILD_JOBS和上面其他变量一起也加入`~/.bashrc`中,减少我们的输入。 - - export SONIC_BUILD_JOBS= -``` - -### 编译子项目代码 - -我们从SONiC的Build Pipeline中就会发现,编译整个项目是非常耗时的,而绝大部分时候,我们的代码改动只会影响很小部分的代码,所以有没有办法减少我们编译的工作量呢?答案是肯定的,我们可以通过指定make target来仅编译我们需要的子项目。 - -SONiC中每个子项目生成的文件都可以在`target`目录中找到,比如: - -- Docker containers: target/.gz,比如:`target/docker-orchagent.gz` -- Deb packages: target/debs//.deb,比如:`target/debs/bullseye/libswsscommon_1.0.0_amd64.deb` -- Python wheels: target/python-wheels//.whl,比如:`target/python-wheels/bullseye/sonic_utilities-1.2-py3-none-any.whl` - -当我们找到了我们需要的子项目之后,我们便可以将其生成的文件删除,然后重新调用make命令,这里我们用`libswsscommon`来举例子,如下: - -```bash -# Remove the deb package for bullseye -rm target/debs/bullseye/libswsscommon_1.0.0_amd64.deb - -# Build the deb package for bullseye -NOJESSIE=1 NOSTRETCH=1 NOBUSTER=1 make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb -``` - -### 检查和处理编译错误 - -如果不巧在编译的时候发生了错误,我们可以通过检查失败项目的日志文件来查看具体的原因。在SONiC中,每一个子编译项目都会生成其相关的日志文件,我们可以很容易的在`target`目录中找到,如下: - -```bash -$ ls -l -... --rw-r--r-- 1 r12f r12f 103M Jun 8 22:35 docker-database.gz --rw-r--r-- 1 r12f r12f 26K Jun 8 22:35 docker-database.gz.log // Log file for docker-database.gz --rw-r--r-- 1 r12f r12f 106M Jun 8 22:44 docker-dhcp-relay.gz --rw-r--r-- 1 r12f r12f 106K Jun 8 22:44 docker-dhcp-relay.gz.log // Log file for docker-dhcp-relay.gz -``` - -如果我们不想每次在更新代码之后都去代码的根目录下重新编译,然后查看日志文件,SONiC还提供了一个更加方便的方法,能让我们在编译完成之后停在docker builder中,这样我们就可以直接去对应的目录运行`make`命令来重新编译了: - -```bash -# KEEP_SLAVE_ON=yes make -KEEP_SLAVE_ON=yes make target/debs/bullseye/libswsscommon_1.0.0_amd64.deb -KEEP_SLAVE_ON=yes make all -``` - -```admonish note -有些仓库中的部分代码在全量编译的时候是不会编译的,比如,`sonic-swss-common`中的gtest,所以使用这种方法重编译的时候,请一定注意查看原仓库的编译指南,以避免出错,如:。 -``` - -## 获取正确的镜像文件 - -编译完成之后,我们就可以在`target`目录中找到我们需要的镜像文件了,但是这里有一个问题:我们到底要用哪一种镜像来把SONiC安装到我们的交换机上呢?这里主要取决于交换机使用什么样的BootLoader或者安装程序,其映射关系如下: - -| Bootloader | 后缀 | -| --- | --- | -| Aboot | .swi | -| ONIE | .bin | -| Grub | .img.gz | - -## 部分升级 - -显然,在开发的时候,每次都编译安装镜像然后进行全量安装的效率是相当低下的,所以我们可以选择不安装镜像而使用直接升级deb包的方式来进行部分升级,从而提高我们的开发效率。 - -我们可以将deb包上传到交换机的`/etc/sonic`目录下,这个目录下的文件会被map到所有容器的`/etc/sonic`目录下,接着我们可以进入到容器中,然后使用`dpkg`命令来安装deb包,如下: - -```bash -# Enter the docker container -docker exec -it bash - -# Install deb package -dpkg -i -``` - -# 参考资料 - -1. [SONiC Build Guide][SONiCBuild] -2. [Install Docker Engine][DockerInstall] -3. [Github repo: sonic-buildimage][SonicBuildimageRepo] -4. [SONiC Supported Devices and Platforms][SONiCDevices] -5. [Wrapper for starting make inside sonic-slave container][SONiCBuildImageMakeFile] - -[SONiCBuild]: https://github.com/sonic-net/sonic-buildimage/blob/master/README.md -[DockerInstall]: https://docs.docker.com/engine/install/ -[SonicBuildimageRepo]: https://github.com/sonic-net/sonic-buildimage -[SONiCDevices]: https://sonic-net.github.io/SONiC/Supported-Devices-and-Platforms.html -[SONiCBuildImageMakeFile]: https://github.com/sonic-net/sonic-buildimage/blob/master/Makefile.work \ No newline at end of file diff --git a/src/3-3-testing.md b/src/3-3-testing.md index 0961fd6..f00b526 100644 --- a/src/3-3-testing.md +++ b/src/3-3-testing.md @@ -1 +1 @@ -# 测试 +# Testing diff --git a/src/3-4-1-sai-debugging.md b/src/3-4-1-sai-debugging.md new file mode 100644 index 0000000..0d63e79 --- /dev/null +++ b/src/3-4-1-sai-debugging.md @@ -0,0 +1 @@ +# Debugging SAI diff --git a/src/3-4-debugging.md b/src/3-4-debugging.md index 801e971..1e584c9 100644 --- a/src/3-4-debugging.md +++ b/src/3-4-debugging.md @@ -1 +1 @@ -# 调试 +# Debugging diff --git a/src/3-4-sai-debugging.md b/src/3-4-sai-debugging.md deleted file mode 100644 index c9b47ee..0000000 --- a/src/3-4-sai-debugging.md +++ /dev/null @@ -1 +0,0 @@ -# SAI调试 diff --git a/src/3-dev-guide.md b/src/3-dev-guide.md index f8f6b58..6f8d214 100644 --- a/src/3-dev-guide.md +++ b/src/3-dev-guide.md @@ -1 +1 @@ -# 开发上手指南 \ No newline at end of file +# Developer Guide \ No newline at end of file diff --git a/src/4-1-1-exec.md b/src/4-1-1-exec.md index e47d0bc..2ba8d99 100644 --- a/src/4-1-1-exec.md +++ b/src/4-1-1-exec.md @@ -1,6 +1,6 @@ -# 命令行调用 +# Command Line Invocation -SONiC中的与内核通信最简单的方式就是命令行调用了,其实现放在[common/exec.h](https://github.com/sonic-net/sonic-swss-common/blob/master/common/exec.h)文件下,且十分简单,接口如下: +The simplest way SONiC communicates with the kernel is through command-line calls, which are implemented in [common/exec.h](https://github.com/sonic-net/sonic-swss-common/blob/master/common/exec.h). The interface is straight-forward: ```cpp // File: common/exec.h @@ -8,9 +8,9 @@ SONiC中的与内核通信最简单的方式就是命令行调用了,其实现 int exec(const std::string &cmd, std::string &stdout); ``` -其中,`cmd`是要执行的命令,`stdout`是命令执行的输出。这里的`exec`函数是一个同步调用,调用者会一直阻塞,直到命令执行完毕。其内部通过调用`popen`函数来创建子进程,并且通过`fgets`函数来获取输出。不过,**虽然这个函数返回了输出,但是基本上并没有人使用**,而只是通过返回值来判断是否成功,甚至连错误log中都不会写入输出的结果。 +Here, `cmd` is the command to execute, and `stdout` captures the command output. The `exec` function is a synchronous call that blocks until the command finishes. Internally, it creates a child process via `popen` and retrieves output via `fgets`. However, **although this function returns output, it is rarely used in practice**. Most code only checks the return value for success, and sometimes even error logs won't be logged in the output. -这个函数虽然粗暴,但是使用广泛,特别是在各个`*mgrd`服务中,比如`portmgrd`中就用它来设置每一个Port的状态等等。 +Despite its simplicity, this function is widely used, especially in various `*mgrd` services. For instance, `portmgrd` calls it to set each port's status: ```cpp // File: sonic-swss - cfgmgr/portmgr.cpp @@ -28,12 +28,12 @@ bool PortMgr::setPortAdminStatus(const string &alias, const bool up) ``` ```admonish note -**为什么说命令行调用是一种通信机制呢**? +**Why is a command-line call considered a communication mechanism?** -原因是当`*mgrd`服务调用`exec`函数对系统进行的修改,会触发下面马上会提到的netlink事件,从而通知其他服务进行相应的修改,比如`*syncd`,这样就间接的构成了一种通信。所以这里我们把命令行调用看作一种通信机制能帮助我们以后更好的理解SONiC的各种工作流。 +Because when a `*mgrd` service modifies the system using `exec`, it triggers netlink events (which will be mentioned in later chapters), notifying other services like `*syncd` to take corresponding actions. This indirect communication helps us better understand SONiC's workflows. ``` -# 参考资料 +# References 1. [Github repo: sonic-swss-common][SONiCSWSSCommon] diff --git a/src/4-1-2-netlink.md b/src/4-1-2-netlink.md index 0c4e38d..8dbe38a 100644 --- a/src/4-1-2-netlink.md +++ b/src/4-1-2-netlink.md @@ -1,18 +1,24 @@ # Netlink -Netlink是Linux内核中用于内核与用户空间进程之间的一种基于消息的通信机制。它通过套接字接口和自定义的协议族来实现,可以用来传递各种类型的内核消息,包括网络设备状态、路由表更新、防火墙规则变化、系统资源使用情况等等。而SONiC的`*sync`服务就大量使用了Netlink的机制来监听系统中网络设备的变化,并将最新的状态同步到Redis中,并通知其他服务进行相应的修改。 +Netlinkis the message-based communication mechanism provided by Linux kernel and used between the kernel and user-space processes. It is implemented via socket and custom protocol families. It can be used to deliver various types of kernel messages, including network device status, routing table updates, firewall rule changes, and system resource usage. SONiC's `*sync` services heavily utilize Netlink to monitor changes of network devices in the system, synchronize the latest status to Redis, and notify other services to make corresponding updates. -Netlink的实现主要在这几个文件中:[common/netmsg.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netmsg.h)、[common/netlink.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netlink.h) 和 [common/netdispatcher.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netdispatcher.h),具体类图如下: +The main implementation of netlink communication channel is done by these files: + +- [common/netmsg.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netmsg.h) +- [common/netlink.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netlink.h) +- [common/netdispatcher.*](https://github.com/sonic-net/sonic-swss-common/blob/master/common/netdispatcher.h). + +The class diagram is as follows: ![](assets/chapter-4/netlink.png) -其中: +In this diagram: -- **Netlink**:封装了Netlink的套接字接口,提供了Netlink消息的接口和接收消息的回调。 -- **NetDispatcher**:它是一个单例,提供了Handler注册的接口。当Netlink类接收到原始的消息后,就会调用NetDispatcher将其解析成nl_onject,并根据消息的类型调用相应的Handler。 -- **NetMsg**:Netlink消息Handler的基类,仅提供了onMsg的接口,其中没有实现。 +- **Netlink**: Wraps the netlink socket interface and provides an interface for sending netlink messages and a callback for receiving messages. +- **NetDispatcher**: A singleton that provides an interface for registering handlers. When a raw netlink message is received, it calls NetDispatcher to parse them into `nl_object` objects and then dispatches them to the corresponding handler based on the message type. +- **NetMsg**: The base class for netlink message handlers, which only provides the `onMsg` interface without a default implementation. -举一个例子,当`portsyncd`启动的时候,它会创建一个Netlink对象,用来监听Link相关的状态变化,并且会实现NetMsg的接口,对Link相关的消息进行处理。具体的实现如下: +For example, when `portsyncd` starts, it creates a `Netlink` object to listen for link-related status changes and implements the `NetMsg` interface to handle the link messages. The specific implementation is as follows: ```cpp // File: sonic-swss - portsyncd/portsyncd.cpp @@ -24,8 +30,8 @@ int main(int argc, char **argv) NetLink netlink; netlink.registerGroup(RTNLGRP_LINK); - // Here SONiC request a fulldump of current state, so that it can get the current state of all links - netlink.dumpRequest(RTM_GETLINK); + // Here SONiC requests a full dump of the current state to get the status of all links + netlink.dumpRequest(RTM_GETLINK); cout << "Listen to link messages..." << endl; // ... @@ -38,7 +44,7 @@ int main(int argc, char **argv) } ``` -上面的LinkSync,就是一个NetMsg的实现,它实现了onMsg接口,用来处理Link相关的消息: +The `LinkSync` class above is an implementation of `NetMsg`, providing the `onMsg` interface for handling link messages: ```cpp // File: sonic-swss - portsyncd/linksync.h @@ -67,7 +73,7 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) } ``` -# 参考资料 +# References 1. [Github repo: sonic-swss-common][SONiCSWSSCommon] diff --git a/src/4-1-communicate-via-kernel.md b/src/4-1-communicate-via-kernel.md new file mode 100644 index 0000000..cbf9677 --- /dev/null +++ b/src/4-1-communicate-via-kernel.md @@ -0,0 +1 @@ +# Communicate via Kernel diff --git a/src/4-1-communication-with-kernel.md b/src/4-1-communication-with-kernel.md deleted file mode 100644 index 443c057..0000000 --- a/src/4-1-communication-with-kernel.md +++ /dev/null @@ -1 +0,0 @@ -# 与内核的通信 diff --git a/src/4-2-1-redis-wrappers.md b/src/4-2-1-redis-wrappers.md index 2fac79b..e9f05e0 100644 --- a/src/4-2-1-redis-wrappers.md +++ b/src/4-2-1-redis-wrappers.md @@ -1,36 +1,35 @@ -# Redis封装 +# Redis Wrappers -## Redis数据库操作层 +## Redis Database Operation Layer -第一层,也是最底层,是Redis的数据库操作层,封装了各种基本命令,比如,DB的连接,命令的执行,事件通知的回调接口等等。具体的类图如下: +The first layer, which is also the lowest layer, is the Redis database operation layer. It wraps various basic commands, such as DB connection, command execution, event notification callback interfaces, etc. The specific class diagram is as follows: ![](assets/chapter-4/redis-ops.png) -其中: +Among them: -- **[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:封装并保持着与Redis的连接,当其销毁时会将其连接关闭。 -- **[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:封装了所有的底层使用到的Redis的命令,比如`SET`、`GET`、`DEL`等等。 -- **[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redistran.h)**:封装了Redis的事务操作,用于在一个事务中执行多个命令,比如`MULTI`、`EXEC`等等。 -- **[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redispipeline.h)**:封装了hiredis的redisAppendFormattedCommand API,提供了一个类似队列的异步的执行Redis命令的接口(虽然大部分使用方法依然是同步的)。它也是少有的对`SCRIPT LOAD`命令进行了封装的类,用于在Redis中加载Lua脚本实现存储过程。SONiC中绝大部分需要执行Lua脚本的类,都会使用这个类来进行加载和调用。 -- **[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redisselect.h)**:它实现了Selectable的接口,用来支持基于epoll的事件通知机制(Event Polling)。主要是在我们收到了Redis的回复,用来触发epoll进行回调(我们最后会更详细的介绍)。 -- **[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**:这个类是一个“静态类”,它主要实现了SONiC DB的配置文件的读取和解析。其他的数据库操作类,如果需要任何的配置信息,都会通过这个类来获取。 +- **[RedisContext](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: Wraps and maintains the connection to Redis, and closes the connection when it is destroyed. +- **[DBConnector](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: Wraps all the underlying Redis commands used, such as `SET`, `GET`, `DEL`, etc. +- **[RedisTransactioner](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redistran.h)**: Wraps Redis transaction operations, used to execute multiple commands in a transaction, such as `MULTI`, `EXEC`, etc. +- **[RedisPipeline](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redispipeline.h)**: Wraps the hiredis redisAppendFormattedCommand API, providing an asynchronous interface for executing Redis commands similar to a queue (although most usage methods are still synchronous). It is also one of the few classes that wraps the `SCRIPT LOAD` command, used to load Lua scripts in Redis to implement stored procedures. Most classes in SONiC that need to execute Lua scripts will use this class for loading and calling. +- **[RedisSelect](https://github.com/sonic-net/sonic-swss-common/blob/master/common/redisselect.h)**: Implements the Selectable interface to support the epoll-based event notification mechanism (Event Polling). Mainly used to trigger epoll callbacks when we receive a reply from Redis (we will introduce this in more detail later). +- **[SonicDBConfig](https://github.com/sonic-net/sonic-swss-common/blob/master/common/dbconnector.h)**: This class is a "static class" that mainly implements the reading and parsing of the SONiC DB configuration file. Other database operation classes will use this class to obtain any configuration information if needed. +## Table Abstraction Layer -## 表(Table)抽象层 +Above the Redis database operation layer is the table abstraction layer established by SONiC using the keys in Redis. Since the format of each Redis key is ``, SONiC needs to craft or parse it, when accessing the database. For more details on how the database is designed, please refer to [the database section for more information](/posts/sonic-2-key-components/#database). -在Redis数据库操作层之上,便是SONiC自己利用Redis中间的Key建立的表(Table)的抽象了,因为每一个Redis的Key的格式都是``,所以SONiC在访问数据库时需要对其进行一次转换(没有印象的小伙伴可以移步[我之前的博客了解更多的信息](/posts/sonic-2-key-components/#数据库))。 - -相关类的主要类图如下: +The main class diagram of related classes is as follows: ![](assets/chapter-4/table-abstraction.png) -其中关键的类有三个: +In this diagram, we have three key classes: -- **[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**:这个类是所有表的基类,它主要封装了表的基本信息,如表的名字,Redis Key的打包,每个表发生修改时用于通信的Channel的名字,等等。 -- **[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**:这个类就是对于每个表增删改查的封装了,里面包含了表的名称和分隔符,这样就可以在调用时构造最终的key了。 -- **[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertablebase.h)**:这个类是各种SubscriptionTable的基类,里面主要是封装了一个简单的队列和其pop操作(对,只有pop,没有push),用来给上层调用。 +- **[TableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**: This class is the base class for all tables. It mainly wraps the basic information of the table, such as the table name, Redis key packaging, the name of the channel used for communication when each table is modified, etc. +- **[Table](https://github.com/sonic-net/sonic-swss-common/blob/master/common/table.h)**: This class wraps the CRUD operations for each table. It contains the table name and separator, so the final key can be constructed when called. +- **[ConsumerTableBase](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertablebase.h)**: This class is the base class for various SubscriptionTables. It mainly wraps a simple queue and its pop operation (yes, only pop, no push, because it is for consumers only), for upper layer calls. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [Github repo: sonic-swss][SONiCSWSS] diff --git a/src/4-2-2-redis-messaging-layer.md b/src/4-2-2-redis-messaging-layer.md deleted file mode 100644 index 4cbf226..0000000 --- a/src/4-2-2-redis-messaging-layer.md +++ /dev/null @@ -1,336 +0,0 @@ -# 通信层 - -在Redis的封装和表抽象之上,便是SONiC的通信层了,由于需求的不同,这一层中提供了四种不同的PubSub的封装,用于服务间的通信。 - -## SubscribeStateTable - -最直接的就是[SubscriberStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/subscriberstatetable.h)了。 - -它的原理是利用Redis数据库中自带的keyspace消息通知机制 [\[4\]][RedisKeyspace] —— 当数据库中的任何一个key对应的值发生了变化,就会触发Redis发送两个keyspace的事件通知,一个是`__keyspace@__:`下的``事件,一个是`__keyspace@__:`下的`>`事件,比如,在数据库0中删除了一个key,那么就会触发两个事件通知: - -```redis -PUBLISH __keyspace@0__:foo del -PUBLISH __keyevent@0__:del foo -``` - -而SubscriberStateTable就是监听了第一个事件通知,然后调用相应的回调函数。和其直接相关的主要的类的类图如下,这里可以看到它继承了ConsumerTableBase,因为它是Redis的消息的Consumer: - -![](assets/chapter-4/subscriber-state-table.png) - -在初始化时,我们可以看到它是如何订阅Redis的事件通知的: - -```cpp -// File: sonic-swss-common - common/subscriberstatetable.cpp -SubscriberStateTable::SubscriberStateTable(DBConnector *db, const string &tableName, int popBatchSize, int pri) - : ConsumerTableBase(db, tableName, popBatchSize, pri), m_table(db, tableName) -{ - m_keyspace = "__keyspace@"; - m_keyspace += to_string(db->getDbId()) + "__:" + tableName + m_table.getTableNameSeparator() + "*"; - psubscribe(m_db, m_keyspace); - // ... -``` - -其事件接收和分发主要由两个函数负责: - -- `readData()`负责将redis中待读取的事件读取出来,并放入ConsumerTableBase中的队列中 -- `pops()`:负责将队列中的原始事件取出来,并且进行解析,然后通过函数参数传递给调用方 - -```cpp -// File: sonic-swss-common - common/subscriberstatetable.cpp -uint64_t SubscriberStateTable::readData() -{ - // ... - reply = nullptr; - int status; - do { - status = redisGetReplyFromReader(m_subscribe->getContext(), reinterpret_cast(&reply)); - if(reply != nullptr && status == REDIS_OK) { - m_keyspace_event_buffer.emplace_back(make_shared(reply)); - } - } while(reply != nullptr && status == REDIS_OK); - // ... - return 0; -} - -void SubscriberStateTable::pops(deque &vkco, const string& /*prefix*/) -{ - vkco.clear(); - // ... - - // Pop from m_keyspace_event_buffer, which is filled by readData() - while (auto event = popEventBuffer()) { - KeyOpFieldsValuesTuple kco; - // Parsing here ... - vkco.push_back(kco); - } - - m_keyspace_event_buffer.clear(); -} -``` - -## NotificationProducer / NotificationConsumer - -说到消息通信,我们很容易就会联想到消息队列,这就是我们的第二种通信方式 —— [NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationproducer.h)和[NotificationConsumer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationconsumer.h)。 - -这种通信方式通过Redis的自带的PubSub来实现,主要是对`PUBLISH`和`SUBSCRIBE`命令的包装,很有限的应用在最简单的通知型的场景中,比如orchagent中的timeout check, restart check之类,非传递用户配置和数据的场景: - -![](assets/chapter-4/notification-producer-consumer.png) - -这种通信模式下,消息的发送方Producer,主要会做两件事情:一是将消息打包成JSON格式,二是调用Redis的`PUBLISH`命令将消息发送出去。而且由于`PUBLISH`命令只能携带一个消息,所以请求中的`op`和`data`字段会被放在`values`的最前面,然后再调用`buildJson`函数将其打包成一个JSON数组的格式: - -```cpp -int64_t swss::NotificationProducer::send(const std::string &op, const std::string &data, std::vector &values) -{ - // Pack the op and data into values array, then pack everything into a JSON string as the message - FieldValueTuple opdata(op, data); - values.insert(values.begin(), opdata); - std::string msg = JSon::buildJson(values); - values.erase(values.begin()); - - // Publish message to Redis channel - RedisCommand command; - command.format("PUBLISH %s %s", m_channel.c_str(), msg.c_str()); - // ... - RedisReply reply = m_pipe->push(command); - reply.checkReplyType(REDIS_REPLY_INTEGER); - return reply.getReply(); -} -``` - -接收方则是利用`SUBSCRIBE`命令来接收所有的通知: - -```cpp -void swss::NotificationConsumer::subscribe() -{ - // ... - m_subscribe = new DBConnector(m_db->getDbId(), - m_db->getContext()->unix_sock.path, - NOTIFICATION_SUBSCRIBE_TIMEOUT); - // ... - - // Subscribe to Redis channel - std::string s = "SUBSCRIBE " + m_channel; - RedisReply r(m_subscribe, s, REDIS_REPLY_ARRAY); -} -``` - -## ProducerTable / ConsumerTable - -我们可以看到NotificationProducer/Consumer实现简单粗暴,但是由于API的限制 [\[8\]][RedisClientHandling],它并不适合用来传递数据,所以,SONiC中提供了一种和它非常接近的另外一种基于消息队列的通信机制 —— [ProducerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producertable.h)和[ConsumerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertable.h)。 - -这种通信方式通过Redis的List来实现,和Notification不同的地方在于,发布给Channel中的消息非常的简单(单字符"G"),所有的数据都存储在List中,从而解决了Notification中消息大小限制的问题。在SONiC中,它主要用在FlexCounter,`syncd`服务和`ASIC_DB`中: - -1. **消息格式**:每条消息都是一个(Key, FieldValuePairs, Op)的三元组,如果用JSON来表达这个消息,那么它的格式如下:(这里的Key是Table中数据的Key,被操作的数据是[Hash][RedisHash],所以Field就是Hash中的Field,Value就是Hash中的Value了,也就是说一个消息可以对很多个Field进行操作) - - ```json - [ "Key", "[\"Field1\", \"Value1\", \"Field2", \"Value2\", ...]", "Op" ] - ``` - -2. **Enqueue**:ProducerTable通过Lua脚本将消息三元组原子的写入消息队列中(Key = `_KEY_VALUE_OP_QUEUE`,并且发布更新通知到特定的Channel(Key = `_CHANNEL`)中。 -3. **Pop**:ConsumerTable也通过Lua脚本从消息队列中原子的读取消息三元组,并**在读取过程中**将其中请求的改动真正的写入到数据库中。 - -```admonish note -**注意**:Redis中Lua脚本和MULTI/EXEC的原子性和通常说的数据库ACID中的原子性(Atomicity)不同,Redis中的原子性其实更接近于ACID中的隔离性(Isolation),他保证Lua脚本中所有的命令在执行的时候不会有其他的命令执行,但是并不保证Lua脚本中的所有命令都会执行成功,比如,如果Lua脚本中的第二个命令执行失败了,那么第一个命令依然会被提交,只是后面的命令就不会继续执行了。更多的细节可以参考Redis官方文档 [\[5\]][RedisTx] [\[6\]][RedisLuaAtomicity]。 -``` - -其主要类图如下,这里我们可以看到在ProducerTable中的`m_shaEnqueue`和ConsumerTable中的`m_shaPop`,它们就是上面我们提到的这两个Lua脚本在加载时获得的SHA了,而之后我们就可以使用Redis的`EVALSHA`命令对他们进行原子的调用了: - -![](assets/chapter-4/producer-consumer-table.png) - -ProducerTable的核心逻辑如下,我们可以看到对Values的JSON打包,和使用`EVALSHA`来进行Lua脚本的调用: - -```cpp -// File: sonic-swss-common - common/producertable.cpp -ProducerTable::ProducerTable(RedisPipeline *pipeline, const string &tableName, bool buffered) - // ... -{ - string luaEnque = - "redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);" - "redis.call('PUBLISH', KEYS[2], ARGV[4]);"; - - m_shaEnque = m_pipe->loadRedisScript(luaEnque); -} - -void ProducerTable::set(const string &key, const vector &values, const string &op, const string &prefix) -{ - enqueueDbChange(key, JSon::buildJson(values), "S" + op, prefix); -} - -void ProducerTable::del(const string &key, const string &op, const string &prefix) -{ - enqueueDbChange(key, "{}", "D" + op, prefix); -} - -void ProducerTable::enqueueDbChange(const string &key, const string &value, const string &op, const string& /* prefix */) -{ - RedisCommand command; - - command.format( - "EVALSHA %s 2 %s %s %s %s %s %s", - m_shaEnque.c_str(), - getKeyValueOpQueueTableName().c_str(), - getChannelName(m_pipe->getDbId()).c_str(), - key.c_str(), - value.c_str(), - op.c_str(), - "G"); - - m_pipe->push(command, REDIS_REPLY_NIL); -} -``` - -而另一侧的ConsumerTable就稍稍复杂一点,因为其支持的op类型很多,所以逻辑都写在了一个单独的文件中(`common/consumer_table_pops.lua`),我们这里就不贴代码了,有兴趣的同学可以自己去看看。 - -```cpp -// File: sonic-swss-common - common/consumertable.cpp -ConsumerTable::ConsumerTable(DBConnector *db, const string &tableName, int popBatchSize, int pri) - : ConsumerTableBase(db, tableName, popBatchSize, pri) - , TableName_KeyValueOpQueues(tableName) - , m_modifyRedis(true) -{ - std::string luaScript = loadLuaScript("consumer_table_pops.lua"); - m_shaPop = loadRedisScript(db, luaScript); - // ... -} - -void ConsumerTable::pops(deque &vkco, const string &prefix) -{ - // Note that here we are processing the messages in bulk with POP_BATCH_SIZE! - RedisCommand command; - command.format( - "EVALSHA %s 2 %s %s %d %d", - m_shaPop.c_str(), - getKeyValueOpQueueTableName().c_str(), - (prefix+getTableName()).c_str(), - POP_BATCH_SIZE, - - RedisReply r(m_db, command, REDIS_REPLY_ARRAY); - vkco.clear(); - - // Parse and pack the messages in bulk - // ... -} -``` - -## ProducerStateTable / ConsumerStateTable - -Producer/ConsumerTable虽然直观,而且保序,但是它一个消息只能处理一个Key,并且还需要JSON的序列化,然而很多时候我们并用不到保序的功能,反而更需要更大的吞吐量,所以为了优化性能,SONiC就引入了第四种通信方式,也是最常用的通信方式:[ProducerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producerstatetable.h)和[ConsumerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertatetable.h)。 - -与ProducerTable不同,ProducerStateTable使用Hash的方式来存储消息,而不是List。这样虽然不能保证消息的顺序,但是却可以很好的提升性能!首先,我们省下了JSON的序列化的开销,其次,对于同一个Key下的相同的Field如果被变更多次,那么只需要保留最后一次的变更,这样就将关于这个Key的所有变更消息就合并成了一条,减少了很多不必要的消息处理。 - -Producer/ConsumerStateTable的底层实现相比于Producer/ConsumerTable也更加复杂一些。其相关联的类的主要类图如下,这里我们依然可以看到它的实现是通过`EVALSHA`调用Lua脚本来实现的,`m_shaSet`和`m_shaDel`就是用来存放修改和发送消息的,而另一边`m_shaPop`就是用来获取消息的: - -![](assets/chapter-4/producer-consumer-state-table.png) - -在传递消息时: - -- 首先,每个消息会被存放成两个部分:一个是KEY_SET,用来保存当前有哪些Key发生了修改,它以Set的形式存放在``的key下,另一个是所有被修改的Key的内容,它以Hash的形式存放在`_`的key下。 -- 然后,消息存放之后Producer如果发现是新的Key,那么就是调用`PUBLISH`命令,来通知`_CHANNEL@`Channel,有新的Key出现了。 - - ```cpp - // File: sonic-swss-common - common/producerstatetable.cpp - ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const string &tableName, bool buffered) - : TableBase(tableName, SonicDBConfig::getSeparator(pipeline->getDBConnector())) - , TableName_KeySet(tableName) - // ... - { - string luaSet = - "local added = redis.call('SADD', KEYS[2], ARGV[2])\n" - "for i = 0, #KEYS - 3 do\n" - " redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i * 2])\n" - "end\n" - " if added > 0 then \n" - " redis.call('PUBLISH', KEYS[1], ARGV[1])\n" - "end\n"; - - m_shaSet = m_pipe->loadRedisScript(luaSet); - ``` - -- 最后,Consumer会通过`SUBSCRIBE`命令来订阅`_CHANNEL@`Channel,一旦有新的消息到来,就会使用Lua脚本调用`HGETALL`命令来获取所有的Key,并将其中的值读取出来并真正的写入到数据库中去。 - - ```cpp - ConsumerStateTable::ConsumerStateTable(DBConnector *db, const std::string &tableName, int popBatchSize, int pri) - : ConsumerTableBase(db, tableName, popBatchSize, pri) - , TableName_KeySet(tableName) - { - std::string luaScript = loadLuaScript("consumer_state_table_pops.lua"); - m_shaPop = loadRedisScript(db, luaScript); - // ... - - subscribe(m_db, getChannelName(m_db->getDbId())); - // ... - ``` - -为了方便理解,我们这里举一个例子:启用Port Ethernet0: - -- 首先,我们在命令行下调用`config interface startup Ethernet0`来启用Ethernet0,这会导致`portmgrd`通过ProducerStateTable向APP_DB发送状态更新消息,如下: - - ```redis - EVALSHA "" "6" "PORT_TABLE_CHANNEL@0" "PORT_TABLE_KEY_SET" - "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "G" - "Ethernet0" "alias" "Ethernet5/1" "index" "5" "lanes" "9,10,11,12" "speed" "40000" - ``` - - 这个命令会在其中调用如下的命令来创建和发布消息: - - ```redis - SADD "PORT_TABLE_KEY_SET" "_PORT_TABLE:Ethernet0" - HSET "_PORT_TABLE:Ethernet0" "alias" "Ethernet5/1" - HSET "_PORT_TABLE:Ethernet0" "index" "5" - HSET "_PORT_TABLE:Ethernet0" "lanes" "9,10,11,12" - HSET "_PORT_TABLE:Ethernet0" "speed" "40000" - PUBLISH "PORT_TABLE_CHANNEL@0" "_PORT_TABLE:Ethernet0" - ``` - - 所以最终这个消息会在APPL_DB中被存放成如下的形式: - - ```redis - PORT_TABLE_KEY_SET: - _PORT_TABLE:Ethernet0 - - _PORT_TABLE:Ethernet0: - alias: Ethernet5/1 - index: 5 - lanes: 9,10,11,12 - speed: 40000 - ``` - -- 当ConsumerStateTable收到消息后,也会调用`EVALSHA`命令来执行Lua脚本,如下: - - ```redis - EVALSHA "" "3" "PORT_TABLE_KEY_SET" "PORT_TABLE:" "PORT_TABLE_DEL_SET" "8192" "_" - ``` - - 和Producer类似,这个脚本会执行如下命令,将`PORT_TABLE_KEY_SET`中的key,也就是`_PORT_TABLE:Ethernet0`读取出来,然后再将其对应的Hash读取出来,并更新到`PORT_TABLE:Ethernet0`去,同时将`_PORT_TABLE:Ethernet0`从数据库和`PORT_TABLE_KEY_SET`中删除。 - - ```redis - SPOP "PORT_TABLE_KEY_SET" "_PORT_TABLE:Ethernet0" - HGETALL "_PORT_TABLE:Ethernet0" - HSET "PORT_TABLE:Ethernet0" "alias" "Ethernet5/1" - HSET "PORT_TABLE:Ethernet0" "index" "5" - HSET "PORT_TABLE:Ethernet0" "lanes" "9,10,11,12" - HSET "PORT_TABLE:Ethernet0" "speed" "40000" - DEL "_PORT_TABLE:Ethernet0" - ``` - - 到这里,数据的更新才算是完成了。 - -# 参考资料 - -1. [SONiC Architecture][SONiCArch] -2. [Github repo: sonic-swss][SONiCSWSS] -3. [Github repo: sonic-swss-common][SONiCSWSSCommon] -4. [Redis keyspace notifications][RedisKeyspace] -5. [Redis Transactions][RedisTx] -6. [Redis Atomicity with Lua][RedisLuaAtomicity] -7. [Redis hashes][RedisHash] -8. [Redis client handling][RedisClientHandling] - -[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture -[SONiCSWSS]: https://github.com/sonic-net/sonic-swss -[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common -[RedisKeyspace]: https://redis.io/docs/manual/keyspace-notifications/ -[RedisTx]: https://redis.io/docs/manual/transactions/ -[RedisLuaAtomicity]: https://developer.redis.com/develop/java/spring/rate-limiting/fixed-window/reactive-lua/ -[RedisHash]: https://redis.io/docs/data-types/hashes/ -[RedisClientHandling]: https://redis.io/docs/reference/clients/ \ No newline at end of file diff --git a/src/4-2-2-subscribe-state-table.md b/src/4-2-2-subscribe-state-table.md new file mode 100644 index 0000000..adfd37c --- /dev/null +++ b/src/4-2-2-subscribe-state-table.md @@ -0,0 +1,81 @@ +# SubscribeStateTable + +The most straight-forward redis-based communication channel is [SubscriberStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/subscriberstatetable.h). + +The idea is to use the built-in keyspace notification mechanism of the Redis database [\[4\]][RedisKeyspace]. When any value in the Redis database changes, Redis sends two keyspace event notifications: one is `` on `__keyspace@__:` and the other is `` on `__keyevent@__:`. For example, deleting a key in database `0` triggers: + +```redis +PUBLISH __keyspace@0__:foo del +PUBLISH __keyevent@0__:del foo +``` + +`SubscriberStateTable` listens for the first event notification and then calls the corresponding callback function. The main classes related to it are shown in this diagram, where we can see it inherits from ConsumerTableBase because it is a consumer of Redis messages: + +![](assets/chapter-4/subscriber-state-table.png) + +## Initialization + +From the initialization code, we can see how it subscribes to Redis event notifications: + +```cpp +// File: sonic-swss-common - common/subscriberstatetable.cpp +SubscriberStateTable::SubscriberStateTable(DBConnector *db, const string &tableName, int popBatchSize, int pri) + : ConsumerTableBase(db, tableName, popBatchSize, pri), m_table(db, tableName) +{ + m_keyspace = "__keyspace@"; + m_keyspace += to_string(db->getDbId()) + "__:" + tableName + m_table.getTableNameSeparator() + "*"; + psubscribe(m_db, m_keyspace); + // ... +``` + +## Event handling + +`SubscriberStateTable` handles the event reception and distribution in two main functions: + +- `readData()`: Reads pending events from Redis and puts them into the ConsumerTableBase queue +- `pops()`: Retrieves the raw events from the queue, parses and passes them to the caller via function parameters + +```cpp +// File: sonic-swss-common - common/subscriberstatetable.cpp +uint6464_t SubscriberStateTable::readData() +{ + // ... + reply = nullptr; + int status; + do { + status = redisGetReplyFromReader(m_subscribe->getContext(), reinterpret_cast(&reply)); + if(reply != nullptr && status == REDIS_OK) { + m_keyspace_event_buffer.emplace_back(make_shared(reply)); + } + } while(reply != nullptr && status == REDIS_OK); + // ... + return 0; +} + +void SubscriberStateTable::pops(deque &vkco, const string& /*prefix*/) +{ + vkco.clear(); + // ... + + // Pop from m_keyspace_event_buffer, which is filled by readData() + while (auto event = popEventBuffer()) { + KeyOpFieldsValuesTuple kco; + // Parsing here ... + vkco.push_back(kco); + } + + m_keyspace_event_buffer.clear(); +} +``` + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Redis keyspace notifications][RedisKeyspace] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[RedisKeyspace]: https://redis.io/docs/manual/keyspace-notifications/ diff --git a/src/4-2-3-notification-producer-consumer.md b/src/4-2-3-notification-producer-consumer.md new file mode 100644 index 0000000..6129a73 --- /dev/null +++ b/src/4-2-3-notification-producer-consumer.md @@ -0,0 +1,64 @@ +# NotificationProducer / NotificationConsumer + +When it comes to message communication, there is no way we can bypass message queues. And this is the second communication channel in SONiC - [NotificationProducer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationproducer.h) and [NotificationConsumer](https://github.com/sonic-net/sonic-swss-common/blob/master/common/notificationconsumer.h). + +This communication channel is implemented using Redis's built-in PubSub mechanism, wrapping the `PUBLISH` and `SUBSCRIBE` commands. However, because `PUBLISH` needs everything being send to be serialized in the command, due to API limitations [\[5\]][RedisClientHandling], these commands are not suitable for passing large data. Hence, in SONiC, it is only used in limited places, such as simple notification scenarios (e.g., timeout checks or restart checks in orchagent), which won't have large payload, such as user configurations or data: + +![](assets/chapter-4/notification-producer-consumer.png) + +In this communication channel, the producer side performs two main tasks: + +1. Package the message into JSON format. +2. Call Redis command `PUBLISH` to send it. + +Because `PUBLISH` can only carry a single message, the "op" and "data" fields are placed at the front of "values", then the `buildJson` function is called to package them into a JSON array: + +```cpp +int64_t swss::NotificationProducer::send(const std::string &op, const std::string &data, std::vector &values) +{ + // Pack the op and data into values array, then pack everything into a JSON string as the message + FieldValueTuple opdata(op, data); + values.insert(values.begin(), opdata); + std::string msg = JSon::buildJson(values); + values.erase(values.begin()); + + // Publish message to Redis channel + RedisCommand command; + command.format("PUBLISH %s %s", m_channel.c_str(), msg.c_str()); + // ... + RedisReply reply = m_pipe->push(command); + reply.checkReplyType(REDIS_REPLY_INTEGER); + return reply.getReply(); +} +``` + +The consumer side uses the `SUBSCRIBE` command to receive all notifications: + +```cpp +void swss::NotificationConsumer::subscribe() +{ + // ... + m_subscribe = new DBConnector(m_db->getDbId(), + m_db->getContext()->unix_sock.path, + NOTIFICATION_SUBSCRIBE_TIMEOUT); + // ... + + // Subscribe to Redis channel + std::string s = "SUBSCRIBE " + m_channel; + RedisReply r(m_subscribe, s, REDIS_REPLY_ARRAY); +} +``` + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Redis keyspace notifications][RedisKeyspace] +5. [Redis client handling][RedisClientHandling] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[RedisKeyspace]: https://redis.io/docs/manual/keyspace-notifications/ +[RedisClientHandling]: https://redis.io/docs/reference/clients/ \ No newline at end of file diff --git a/src/4-2-4-producer-consumer-table.md b/src/4-2-4-producer-consumer-table.md new file mode 100644 index 0000000..cc9e347 --- /dev/null +++ b/src/4-2-4-producer-consumer-table.md @@ -0,0 +1,146 @@ +# ProducerTable / ConsumerTable + +Although `NotificationProducer` and `NotificationConsumer` is straight-forward, but they are not suitable for passing large data. Therefore, SONiC provides another message-queue-based communication mechanism that works in similar way - [ProducerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producertable.h) and [ConsumerTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumertable.h). + +This channel leverages the Redis list to pass the message. Unlike Notification, which has limited message capacity, it stores all the message data in a Redis list with a very slim custom messsage format. This solves the message size limitation in Notification. In SONiC, it is mainly used in FlexCounter, the `syncd` service, and `ASIC_DB`. + +## Message format + +In this channel, a message is a triplet (`Key`, `FieldValuePairs`, `Op`) and will be pushed into the Redis list (Key = `_KEY_VALUE_OP_QUEUE`) as 3 list items: + +- `Key` is table name and key (e.g., `SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000`). +- `FieldValuePairs` are the field that needs to be updated in the database and their values, which is serialized into a JSON string: `"[\"Field1\", \"Value1\", \"Field2\", \"Value2\", ...]"`. +- `Op` is the operation to be performed (e.g., Set, Get, Del, etc.) + +Once the message is pushed into the Redis list, a notification will be published to a specific channel (Key = `_CHANNEL`) with only a single character "G" as payload, indicating that there is a new message in the list. + +So, when using this channel, we can imaging the actual data stored in the Redis: + +- In the channel: `["G", "G", ...]` +- In the list: `["Key1", "FieldValuePairs1", "Op1", "Key2", "FieldValuePairs2", "Op2", ...]` + +## Queue operations + +Using this message format, `ProducerTable` and `ConsumerTable` provides two queue operations: + +1. Enqueue: `ProducerTable` uses a Lua script to atomically write the message triplet into the Redis list and then publishes an update notification to a specific channel. +2. Pop: `ConsumerTable` also uses a Lua script to atomically read the message triplet from the message queue and writes the requested changes to the database during the read process. + +```admonish note +**Note**: The atomicity of Lua scripts and MULTI/EXEC in Redis differs from the usual database ACID notion of Atomicity. Redis's atomicity is closer to Isolation in ACID: it ensures that no other command interleaves while a Lua script is running, but it does not guarantee that all commands in the script will successfully execute. For example, if the second command fails, the first one is still committed, and the subsequent commands are not executed. Refer to [\[5\]][RedisTx] and [\[6\]][RedisLuaAtomicity] for more details. +``` + +Its main class diagram is shown below. In ProducerTable, `m_shaEnqueue` and in ConsumerTable, `m_shaPop` are the two Lua scripts we mentioned. After they are loaded, you can call them atomically via `EVALSHA`: + +![](assets/chapter-4/producer-consumer-table.png) + +The core logic of ProducerTable is as follows, showing how values are packed into JSON and how `EVALSHA` is used to call Lua scripts: + +```cpp +// File: sonic-swss-common - common/producertable.cpp +ProducerTable::ProducerTable(RedisPipeline *pipeline, const string &tableName, bool buffered) + // ... +{ + string luaEnque = + "redis.call('LPUSH', KEYS[1], ARGV[1], ARGV[2], ARGV[3]);" + "redis.call('PUBLISH', KEYS[2], ARGV[4]);"; + + m_shaEnque = m_pipe->loadRedisScript(luaEnque); +} + +void ProducerTable::set(const string &key, const vector &values, const string &op, const string &prefix) +{ + enqueueDbChange(key, JSon::buildJson(values), "S" + op, prefix); +} + +void ProducerTable::del(const string &key, const string &op, const string &prefix) +{ + enqueueDbChange(key, "{}", "D" + op, prefix); +} + +void ProducerTable::enqueueDbChange(const string &key, const string &value, const string &op, const string& /* prefix */) +{ + RedisCommand command; + + command.format( + "EVALSHA %s 2 %s %s %s %s %s %s", + m_shaEnque.c_str(), + getKeyValueOpQueueTableName().c_str(), + getChannelName(m_pipe->getDbId()).c_str(), + key.c_str(), + value.c_str(), + op.c_str(), + "G"); + + m_pipe->push(command, REDIS_REPLY_NIL); +} +``` + +On the other side, ConsumerTable is slightly more complicated because it supports many types of ops. The logic is written in a separate file (`common/consumer_table_pops.lua`). Interested readers can explore it further: + +```cpp +// File: sonic-swss-common - common/consumertable.cpp +ConsumerTable::ConsumerTable(DBConnector *db, const string &tableName, int popBatchSize, int pri) + : ConsumerTableBase(db, tableName, popBatchSize, pri) + , TableName_KeyValueOpQueues(tableName) + , m_modifyRedis(true) +{ + std::string luaScript = loadLuaScript("consumer_table_pops.lua"); + m_shaPop = loadRedisScript(db, luaScript); + // ... +} + +void ConsumerTable::pops(deque &vkco, const string &prefix) +{ + // Note that here we are processing the messages in bulk with POP_BATCH_SIZE! + RedisCommand command; + command.format( + "EVALSHA %s 2 %s %s %d %d", + m_shaPop.c_str(), + getKeyValueOpQueueTableName().c_str(), + (prefix+getTableName()).c_str(), + POP_BATCH_SIZE, + + RedisReply r(m_db, command, REDIS_REPLY_ARRAY); + vkco.clear(); + + // Parse and pack the messages in bulk + // ... +} +``` + +## Monitor + +To monitor how the `ProducerTable` and `ConsumerTable` work, we can use the `redis-cli monitor` command to see the actual Redis commands that being executed. + +```bash +# Filter to `LPUSH` and `PUBLISH` commands to help us reduce the noise. +redis-cli monitor | grep -E "LPUSH|PUBLISH" +``` + +And here is an example of the output showing a `ProducerTable` enqueue operation: + +```text +1735966216.139741 [1 lua] "LPUSH" "ASIC_STATE_KEY_VALUE_OP_QUEUE" "SAI_OBJECT_TYPE_SWITCH:oid:0x21000000000000" "[\"SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY\",\"1\"]" "Sget" +1735966216.139774 [1 lua] "PUBLISH" "ASIC_STATE_CHANNEL@1" "G" +``` + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Redis keyspace notifications][RedisKeyspace] +5. [Redis Transactions][RedisTx] +6. [Redis Atomicity with Lua][RedisLuaAtomicity] +7. [Redis hashes][RedisHash] +8. [Redis client handling][RedisClientHandling] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[RedisKeyspace]: https://redis.io/docs/manual/keyspace-notifications/ +[RedisTx]: https://redis.io/docs/manual/transactions/ +[RedisLuaAtomicity]: https://developer.redis.com/develop/java/spring/rate-limiting/fixed-window/reactive-lua/ +[RedisHash]: https://redis.io/docs/data-types/hashes/ +[RedisClientHandling]: https://redis.io/docs/reference/clients/ \ No newline at end of file diff --git a/src/4-2-5-producer-consumer-state-table.md b/src/4-2-5-producer-consumer-state-table.md new file mode 100644 index 0000000..cc4136a --- /dev/null +++ b/src/4-2-5-producer-consumer-state-table.md @@ -0,0 +1,141 @@ +# ProducerStateTable / ConsumerStateTable + +Although `Producer/ConsumerTable` is straightforward and maintains the order of the messages, each message can only update one table key and requires JSON serialization. However, in many cases, we don't need strict ordering but need higher throughput. To optimize performance, SONiC introduces the fourth, and most frequently used, communication channel: [ProducerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/producerstatetable.h) and [ConsumerStateTable](https://github.com/sonic-net/sonic-swss-common/blob/master/common/consumerstatetable.h). + +## Overview + +Unlike `ProducerTable`, `ProducerStateTable` uses a Hash to store messages instead of a List. This means the order of messages will not be guranteed, but it can significantly boosts performance: + +- First, no more JSON serialization, hence its overhead is gone. +- Second, batch processing: + - Multiple table updates can be merged into one (single pending update key set per table). + - If the same Field under the same Key is changed multiple times, only the latest change is preserved, merging all changes related to that Key into a single message and reducing unnecessary handling. + +`Producer/ConsumerStateTable` is more complex under the hood than `Producer/ConsumerTable`. The related classes are shown in the diagram below, where `m_shaSet` and `m_shaDel` store the Lua scripts for modifying and sending messages, while `m_shaPop` is used to retrieve messages: + +![](assets/chapter-4/producer-consumer-state-table.png) + +## Sending messages + +When sending messages: + +1. Each message is stored in two parts: + 1. KEY_SET: keeps track of which Keys have been modified (stored as a Set at ``) + 2. A series of Hash: One Hash for each modified Key (stored at `_`). +2. After storing a message, if the Producer finds out it's a new Key, it calls `PUBLISH` to notify `_CHANNEL@` that a new Key has appeared. + + ```cpp + // File: sonic-swss-common - common/producerstatetable.cpp + ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const string &tableName, bool buffered) + : TableBase(tableName, SonicDBConfig::getSeparator(pipeline->getDBConnector())) + , TableName_KeySet(tableName) + // ... + { + string luaSet = + "local added = redis.call('SADD', KEYS[2], ARGV[2])\n" + "for i = 0, #KEYS - 3 do\n" + " redis.call('HSET', KEYS[3 + i], ARGV[3 + i * 2], ARGV[4 + i * 2])\n" + "end\n" + " if added > 0 then \n" + " redis.call('PUBLISH', KEYS[1], ARGV[1])\n" + "end\n"; + + m_shaSet = m_pipe->loadRedisScript(luaSet); + } + ``` + +## Receiving messages + +When receiving messages: + +The consumer uses `SUBSCRIBE` to listen on `_CHANNEL@`. Once a new message arrives, it calls a Lua script to run `HGETALL`, fetch all Keys, and write them into the database. + +```cpp +ConsumerStateTable::ConsumerStateTable(DBConnector *db, const std::string &tableName, int popBatchSize, int pri) + : ConsumerTableBase(db, tableName, popBatchSize, pri) + , TableName_KeySet(tableName) +{ + std::string luaScript = loadLuaScript("consumer_state_table_pops.lua"); + m_shaPop = loadRedisScript(db, luaScript); + // ... + + subscribe(m_db, getChannelName(m_db->getDbId())); + // ... +} +``` + +## Example + +To illustrate, here is an example of enabling Port Ethernet0: + +1. First, we call `config interface startup Ethernet0` from the command line to enable Ethernet0. This causes `portmgrd` to send a status update to APP_DB via ProducerStateTable, as shown below: + + ```redis + EVALSHA "" "6" "PORT_TABLE_CHANNEL@0" "PORT_TABLE_KEY_SET" + "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "_PORT_TABLE:Ethernet0" "G" + "Ethernet0" "alias" "Ethernet5/1" "index" "5" "lanes" "9,10,11,12" "speed" "40000" + ``` + + This command triggers the following creation and broadcast: + + ```redis + SADD "PORT_TABLE_KEY_SET" "_PORT_TABLE:Ethernet0" + HSET "_PORT_TABLE:Ethernet0" "alias" "Ethernet5/1" + HSET "_PORT_TABLE:Ethernet0" "index" "5" + HSET "_PORT_TABLE:Ethernet0" "lanes" "9,10,11,12" + HSET "_PORT_TABLE:Ethernet0" "speed" "40000" + PUBLISH "PORT_TABLE_CHANNEL@0" "_PORT_TABLE:Ethernet0" + ``` + + Thus, the message is ultimately stored in APPL_DB as follows: + + ```redis + PORT_TABLE_KEY_SET: + _PORT_TABLE:Ethernet0 + + _PORT_TABLE:Ethernet0: + alias: Ethernet5/1 + index: 5 + lanes: 9,10,11,12 + speed: 40000 + ``` + +2. When ConsumerStateTable receives the message, it also calls `EVALSHA` to execute a Lua script, such as: + + ```redis + EVALSHA "" "3" "PORT_TABLE_KEY_SET" "PORT_TABLE:" "PORT_TABLE_DEL_SET" "8192" "_" + ``` + + Similar to the Producer side, this script runs: + + ```redis + SPOP "PORT_TABLE_KEY_SET" "_PORT_TABLE:Ethernet0" + HGETALL "_PORT_TABLE:Ethernet0" + HSET "PORT_TABLE:Ethernet0" "alias" "Ethernet5/1" + HSET "PORT_TABLE:Ethernet0" "index" "5" + HSET "PORT_TABLE:Ethernet0" "lanes" "9,10,11,12" + HSET "PORT_TABLE:Ethernet0" "speed" "40000" + DEL "_PORT_TABLE:Ethernet0" + ``` + + At this point, the data update is complete. + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Redis keyspace notifications][RedisKeyspace] +5. [Redis Transactions][RedisTx] +6. [Redis Atomicity with Lua][RedisLuaAtomicity] +7. [Redis hashes][RedisHash] +8. [Redis client handling][RedisClientHandling] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[RedisKeyspace]: https://redis.io/docs/manual/keyspace-notifications/ +[RedisTx]: https://redis.io/docs/manual/transactions/ +[RedisLuaAtomicity]: https://developer.redis.com/develop/java/spring/rate-limiting/fixed-window/reactive-lua/ +[RedisHash]: https://redis.io/docs/data-types/hashes/ +[RedisClientHandling]: https://redis.io/docs/reference/clients/ \ No newline at end of file diff --git a/src/4-2-communication-thru-redis.md b/src/4-2-communication-thru-redis.md deleted file mode 100644 index 434ea6d..0000000 --- a/src/4-2-communication-thru-redis.md +++ /dev/null @@ -1 +0,0 @@ -# 基于Redis的通信 diff --git a/src/4-2-redis-based-channels.md b/src/4-2-redis-based-channels.md new file mode 100644 index 0000000..4a6c9b4 --- /dev/null +++ b/src/4-2-redis-based-channels.md @@ -0,0 +1,8 @@ +# Redis-based Channels + +To facilitate communication between services, SONiC provides a messaging layer that is built on top of the Redis. On high-level, it contains 2 layers: + +1. First layer wraps frequenctly used redis operations and provide table abstraction on top of it. +2. Second layer provides different channels for inter-service communication to satisfy various communication channel requirements. + +Now, let's dive into them one by one. diff --git a/src/4-3-zmq-based-channels.md b/src/4-3-zmq-based-channels.md new file mode 100644 index 0000000..6289816 --- /dev/null +++ b/src/4-3-zmq-based-channels.md @@ -0,0 +1 @@ +# ZMQ-based Channels diff --git a/src/4-3-zmq-messaging.md b/src/4-3-zmq-messaging.md deleted file mode 100644 index 7cfc1fa..0000000 --- a/src/4-3-zmq-messaging.md +++ /dev/null @@ -1 +0,0 @@ -# 基于ZMQ的通信 diff --git a/src/4-4-orch-layer.md b/src/4-4-orch-layer.md index 5ddba6d..d96dfe2 100644 --- a/src/4-4-orch-layer.md +++ b/src/4-4-orch-layer.md @@ -1,16 +1,16 @@ -# 服务层 - Orch +# Service Layer - Orch -最后,为了方便各个服务使用,SONiC还在通信层上进行了更进一步的封装,为各个服务提供了一个基类:[Orch](https://github.com/sonic-net/sonic-swss/blob/master/src/orchagent/orch.hcommon/consumertatetable.h)。 +Finally, to make it more convenient for building services, SONiC provides another layer of abstraction on top of the communication layer, offering a base class for services: [Orch](https://github.com/sonic-net/sonic-swss/blob/master/src/orchagent/orch.h). -由于有了上面这些封装,Orch中关于消息通信的封装就相对简单了,主要的类图如下: +With all the lower layers, adding message communication support in Orch is relatively straightforward. The main class diagram is shown below: ![](assets/chapter-4/orch.png) ```admonish note -注意:由于这一层是服务层,所以其代码是在`sonic-swss`的仓库中,而不是`sonic-swss`。这个类中除了消息通信的封装以外,还提供了很多和服务实现相关的公共函数,比如,日志文件等等。 +Note: Since this layer is part of the service layer, the code lives in the sonic-swss repository, not in sonic-swss-common. In addition to message communication, this class also provides many other utility functions related to service implementation (for example, log files, etc.). ``` -可以看到,Orch主要是封装了`SubscriberStateTable`和`ConsumerStateTable`来简化和统一消息的订阅,核心代码非常简单,就是根据不同的数据库类型来创建不同的Consumer,如下: +We can see that Orch mainly wraps `SubscriberStateTable` and `ConsumerStateTable` to simplify and unify the message subscription. The core code is very simple and creates different Consumers based on the database type: ```cpp void Orch::addConsumer(DBConnector *db, string tableName, int pri) @@ -31,10 +31,10 @@ void Orch::addConsumer(DBConnector *db, string tableName, int pri) } ``` -# 参考资料 +# References -1. [SONiC Architecture][SONiCArch] -2. [Github repo: sonic-swss][SONiCSWSS] +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] 3. [Github repo: sonic-swss-common][SONiCSWSSCommon] [SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture diff --git a/src/4-5-event-polling-and-error-handling.md b/src/4-5-event-polling-and-error-handling.md index 8582a40..56ea0bd 100644 --- a/src/4-5-event-polling-and-error-handling.md +++ b/src/4-5-event-polling-and-error-handling.md @@ -1,17 +1,19 @@ -# 事件分发和错误处理 +# Event Dispatching and Error Handling -## 基于epoll的事件分发机制 +## Epoll-based Event Dispatching -和很多的Linux服务一样,SONiC底层使用了epoll作为事件分发机制: +Just like many other Linux services, SONiC uses epoll at its core for event dispatching: -- 所有需要支持事件分发的类都需要继承`Selectable`类,并实现两个最核心的函数:`int getFd();`(用于返回epoll能用来监听事件的fd)和`uint64_t readData()`(用于在监听到事件到来之后进行读取)。而对于一般服务而言,这个fd就是redis通信使用的fd,所以`getFd()`函数的调用,都会被最终转发到Redis的库中。 -- 所有需要参与事件分发的对象,都需要注册到`Select`类中,这个类会将所有的`Selectable`对象的fd注册到epoll中,并在事件到来时调用`Selectable`的`readData()`函数。 +- Any class that supports event dispatching should inherit from `Selectable` and implement two key functions: + - `int getFd();`: Returns the fd for epoll to listen on. For most services, this fd is the one used for Redis communication, so the call to `getFd()` ultimately delegates to the Redis library. + - `uint64_t readData()`: Reads data when an event arrives. +- Any objects that need to participate in event dispatching must register with the `Select` class. This class registers all `Selectable` objects' fds with epoll and calls `Selectable`'s `readData()` when an event arrives. -其类图如下: +Here's the class diagram: ![](assets/chapter-4/event-polling.png) -在Select类中,我们可以很容易的找到其最核心的代码,实现也非常的简单: +The core logic lives in the `Select` class, which can be simplified as follows: ```cpp int Select::poll_descriptors(Selectable **c, unsigned int timeout, bool interrupt_on_signal = false) @@ -50,9 +52,9 @@ int Select::poll_descriptors(Selectable **c, unsigned int timeout, bool interrup } ``` -然而,问题来了…… 回调呢?我们上面提过,`readData()`只是把消息读出来放在一个待处理队列中,并不会真正的处理消息,真正的消息处理需要调用`pops()`函数,将消息拿出来处理,所以什么地方会调用每一个上层封装的消息处理呢? +However, here comes the question... where is the callback? As mentioned, `readData()` only reads the message and stores it in a pending queue for processing. The real processing needs to call `pops()`. So at which point does every upper-level message handler get called? -这里我们还是找到我们的老朋友`portmgrd`的`main`函数,从下面简化的代码中,我们可以看到和一般的Event Loop实现不同,SONiC中,最后的事件处理不是通过回调来实现的,而是需要最外层的Event Loop来主动调用完成: +Here, let's look back again at `portmgrd`'s `main` function. From the simplified code below, we can see - unlike a typical event loop, SONiC does not handle events with callbacks; the outermost event loop directly calls the actual handlers: ```cpp int main(int argc, char **argv) @@ -88,11 +90,11 @@ int main(int argc, char **argv) } ``` -## 错误处理 +## Error Handling -关于Event Loop我们还有一个问题,那就是错误处理,比如,如果Redis的命令执行出错了,连接断开了,故障了等等的情况下,我们的服务会发生什么呢? +Another thing about event loops is error handling. For example, if a Redis command fails, or the connection is broken, or any kind of failure happens, what will happen to our services? -从代码上来看,SONiC中的错误处理是非常简单的,就是直接抛出异常(比如,获取命令执行结果的代码,如下),然后在Event Loop中捕获异常,打印日志,接着继续执行。 +SONiC's error handling is very simple: it just throws exceptions (for example, in the code that fetches command results). Then the event loop catches the exceptions, logs them, and continues: ```cpp RedisReply::RedisReply(RedisContext *ctx, const RedisCommand& command) @@ -114,14 +116,14 @@ RedisReply::RedisReply(RedisContext *ctx, const RedisCommand& command) } ``` -关于异常和错误的种类及其原因,在代码里面并没有看到用于统计和Telemetry的代码,所以监控上说是比较薄弱的。另外还需要考虑数据出错的场景,比如数据库写到一半突然断开导致的脏数据,不过简单的重启相关的`*syncd`和`*mgrd`服务可能可以解决此类问题,因为启动时会进行全量同步。 +There is no specific code here for statistics or telemetry, so monitoring is somewhat weak. We also need to consider data errors (for example, partial writes leading to corrupted data), though simply restarting `*syncd` or `*mgrd` services might fix such issues because many stored data in database will be wiped out, such as APPL_DB, and the services will do a full sync on startup. -# 参考资料 +# References -1. [SONiC Architecture][SONiCArch] -2. [Github repo: sonic-swss][SONiCSWSS] +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] 3. [Github repo: sonic-swss-common][SONiCSWSSCommon] -[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture -[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss [SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common \ No newline at end of file diff --git a/src/4-communications.md b/src/4-communications.md index 81c429b..efca0f4 100644 --- a/src/4-communications.md +++ b/src/4-communications.md @@ -1,22 +1,22 @@ -# 通信机制 +# Communication -SONiC中主要的通信机制有三种:与内核的通信,基于Redis和基于ZMQ的服务间的通信。 +There are three main communication mechanisms in SONiC: communication using kernel, Redis-based inter-service communication, and ZMQ-based inter-service communication. -- 与内核通信主要有两种方法:命令行调用和Netlink消息。 -- 基于Redis的服务间通信主要有四种方法:SubscriberStateTable,NotificationProducer/Consumer,Producer/ConsumerTable,Producer/ConsumerStateTable。虽然它们都是基于Redis的,但是它们解决的问题和方法却非常不同。 -- 基于ZMQ的服务间通信:现在只在`orchagent`和`syncd`的通信中使用了这种通信机制。 +- There are two main methods for communication using kernel: command line calls and Netlink messages. +- Redis-based inter-service communication: There are 4 different communication channel based on Redis - SubscriberStateTable, NotificationProducer/Consumer, Producer/ConsumerTable, and Producer/ConsumerStateTable. Although they are all based on Redis, their use case can be very different. +- ZMQ-based inter-service communication: This communication mechanism is currently only used in the communication between `orchagent` and `syncd`. ```admonish note -虽然大部分的通信机制都支持多消费者的PubSub的模式,但是请特别注意:在SONiC中,所有的通信都是点对点的,即一个生产者对应一个消费者,绝对不会出现一个生产者对应多个消费者的情况! +Although most communication mechanisms support multi-consumer PubSub mode, please note: in SONiC, majority of communication (except some config table or state table via SubscriberStateTable) is point-to-point, meaning one producer will only send the message to one consumer. It is very rare to have a situation where one producer sending data to multiple consumers! -一旦多消费者出现,那么一个消息的处理逻辑将可能发生在多个进程中,这将导致很大的问题,因为对于任何一种特定的消息,SONiC中只有一个地方来处理,所以这会导致部分消息不可避免的出错或者丢失。 +Channels like Producer/ConsumerStateTable essentually only support point-to-point communication. If multiple consumers appear, the message will only be delivered to one of the customers, causing all other consumer missing updates. ``` -所有这些基础的通信机制的实现都在[sonic-swss-common][SONiCSWSSCommon]这个repo中的`common`目录下。另外在其之上,为了方便各个服务使用,SONiC还在[sonic-swss][SONiCSWSS]中封装了一层Orch,将常用的表放在其中。 +The implementation of all these basic communication mechanisms is in the `common` directory of the [sonic-swss-common][SONiCSWSSCommon] repo. Additionally, to facilitate the use of various services, SONiC has build a wrapper layer called Orch in [sonic-swss][SONiCSWSS], which helps simplify the upper-layer services. -这一章,我们就主要来看看这些通信机制的实现吧! +In this chapter, we will dive into the implementation of these communication mechanisms! -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [Github repo: sonic-swss][SONiCSWSS] diff --git a/src/5-1-syncd-and-sai.md b/src/5-1-syncd-and-sai.md index 7255a2b..1264df2 100644 --- a/src/5-1-syncd-and-sai.md +++ b/src/5-1-syncd-and-sai.md @@ -1,14 +1,14 @@ -# Syncd和SAI +# Syncd and SAI -[Syncd容器](./2-3-key-containers.html#asic管理容器syncd)是SONiC中专门负责管理ASIC的容器,其中核心进程`syncd`负责与Redis数据库沟通,加载SAI并与其交互,以完成ASIC的初始化,配置和状态上报的处理等等。 +[Syncd Container](./2-3-key-containers.html#asic-management-container-syncd) is the container in SONiC dedicated to managing the ASIC. The key process `syncd` is responsible for communicating with the Redis database, loading SAI implementation, and interacting with it to handle ASIC initialization, configuration, status reporting, and so on. -由于SONiC中大量的工作流最后都需要通过Syncd和SAI来和ASIC进行交互,所以这一部分也就成为了这些工作流的公共部分,所以,在展开其他工作流之前,我们先来看一下Syncd和SAI是如何工作的。 +Since many SONiC workflows ultimately need to interact with the ASIC through Syncd and SAI, this part becomes common to all those workflows. Therefore, before diving into other workflows, let's take a look at how Syncd and SAI work first. -## Syncd启动流程 +## Syncd Startup Flow -`syncd`进程的入口在`syncd_main.cpp`中的`syncd_main`函数,其启动的整体流程大致分为两部分。 +The entry point of the `syncd` process is the `syncd_main` function in `syncd_main.cpp`. The startup flow can be roughly divided into two parts. -第一部分是创建各个对象,并进行初始化: +The first part creates and initializes various objects: ```mermaid sequenceDiagram @@ -17,55 +17,55 @@ sequenceDiagram participant SD as Syncd participant SAI as VendorSai - SDM->>+SD: 调用构造函数 - SD->>SD: 加载和解析命令行参数和配置文件 - SD->>SD: 创建数据库相关对象,如:
ASIC_DB Connector和FlexCounterManager - SD->>SD: 创建MDIO IPC服务器 - SD->>SD: 创建SAI上报处理逻辑 - SD->>SD: 创建RedisSelectableChannel用于接收Redis通知 - SD->>-SAI: 初始化SAI + SDM->>+SD: Call constructor + SD->>SD: Load and parse command line
arguments and config files + SD->>SD: Create database objects, e.g.:
ASIC_DB Connector and FlexCounterManager + SD->>SD: Create MDIO IPC server + SD->>SD: Create SAI event reporting logic + SD->>SD: Create RedisSelectableChannel
to receive Redis notifications + SD->>-SAI: Initialize SAI ``` -第二个部分是启动主循环,并且处理初始化事件: +The second part starts the main loop and handles initialization events: ```mermaid sequenceDiagram autonumber - box purple 主线程 + box purple Main Thread participant SDM as syncd_main participant SD as Syncd participant SAI as VendorSai end - box darkblue 通知处理线程 + box darkblue Notification Handler Thread participant NP as NotificationProcessor end - box darkgreen MDIO IPC服务器线程 + box darkgreen MDIO IPC Server Thread participant MIS as MdioIpcServer end - SDM->>+SD: 启动主线程循环 - SD->>NP: 启动SAI上报处理线程 - NP->>NP: 开始通知处理循环 - SD->>MIS: 启动MDIO IPC服务器线程 - MIS->>MIS: 开始MDIO IPC服务器事件循环 - SD->>SD: 初始化并启动事件分发机制,开始主循环 - - loop 处理事件 - alt 如果是创建Switch的事件或者是WarmBoot - SD->>SAI: 创建Switch对象,设置通知回调 - else 如果是其他事件 - SD->>SD: 处理事件 + SDM->>+SD: Start main thread loop + SD->>NP: Start SAI event reporting thread + NP->>NP: Begin notification processing loop + SD->>MIS: Start MDIO IPC server thread + MIS->>MIS: Begin MDIO IPC server event loop + SD->>SD: Initialize and start event dispatching,
then begin main loop + + loop Process events + alt If it's the create-Switch event or WarmBoot + SD->>SAI: Create Switch object, set notification callbacks + else If it's other events + SD->>SD: Handle events end end - SD->>-SDM: 退出主循环返回 + SD->>-SDM: Exit main loop and return ``` -然后我们再从代码的角度来更加仔细的看一下这个流程。 +Now, let's dive into the code to see how Syncd and SAI are implemented. -### syncd_main函数 +### The syncd_main Function -`syncd_main`函数本身非常简单,主要逻辑就是创建Syncd对象,然后调用其`run`方法: +The `syncd_main` function itself is straightforward: it creates a `Syncd` object and then calls its `run` method: ```cpp // File: src/sonic-sairedis/syncd/syncd_main.cpp @@ -78,11 +78,11 @@ int syncd_main(int argc, char **argv) } ``` -其中,`Syncd`对象的构造函数负责初始化`Syncd`中的各个功能,而`run`方法则负责启动Syncd的主循环。 +The Syncd constructor initializes each feature in Syncd, while the run method starts the Syncd main loop. -### Syncd构造函数 +### The Syncd Constructor -`Syncd`对象的构造函数负责创建或初始化`Syncd`中的各个功能,比如用于连接数据库的对象,统计管理,和ASIC通知的处理逻辑等等,其主要代码如下: +The `Syncd` constructor creates or initializes the key components in `Syncd`, such as database connection objects, statistics management, and ASIC notification handler. The key code looks like below: ```cpp // File: src/sonic-sairedis/syncd/Syncd.cpp @@ -126,11 +126,11 @@ Syncd::Syncd( } ``` -### SAI的初始化与VendorSai +### SAI Initialization and VendorSai -`Syncd`初始化的最后也是最重要的一步,就是对SAI进行初始化。[在核心组件的SAI介绍中,我们简单的展示了SAI的初始化,实现,以及它是如何为SONiC提供不同平台的支持](./2-4-sai-intro.html),所以这里我们主要来看看`Syncd`是如何对SAI进行封装和调用的。 +The last and most important step in `Syncd` initialization is to initialize SAI. [In the core component introduction to SAI](./2-4-sai-intro.html), we briefly described how SAI is initialized and implemented, and how it provides support for different platforms in SONiC. And here, we will focus more on how Syncd wraps SAI and uses it. -`Syncd`使用`VendorSai`来对SAI的所有API进行封装,方便上层调用。其初始化过程也非常直接,基本就是对上面两个函数的直接调用和错误处理,如下: +`Syncd` uses `VendorSai` to wrap all SAI APIs to simplify upper-level calls. The initialization looks like below, essentially just calling the sai initialize and api query functions, and handling errors: ```cpp // File: src/sonic-sairedis/syncd/VendorSai.cpp @@ -158,70 +158,72 @@ sai_status_t VendorSai::initialize( } ``` -当获取好所有的SAI API之后,我们就可以通过`VendorSai`对象来调用SAI的API了。当前调用SAI的API方式主要有两种。 - -第一种是通过`sai_object_type_into_t`来调用,它类似于为所有的SAI Object实现了一个虚表,如下: - -```cpp -// File: src/sonic-sairedis/syncd/VendorSai.cpp -sai_status_t VendorSai::set( - _In_ sai_object_type_t objectType, - _In_ sai_object_id_t objectId, - _In_ const sai_attribute_t *attr) -{ - ... - - auto info = sai_metadata_get_object_type_info(objectType); - sai_object_meta_key_t mk = { .objecttype = objectType, .objectkey = { .key = { .object_id = objectId } } }; - return info->set(&mk, attr); -} -``` - -另外一种是通过保存在`VendorSai`对象中的`m_apis`来调用,这种方式更加直接,但是调用前需要先根据SAI Object的类型来调用不同的API。 - -```cpp -sai_status_t VendorSai::getStatsExt( - _In_ sai_object_type_t object_type, - _In_ sai_object_id_t object_id, - _In_ uint32_t number_of_counters, - _In_ const sai_stat_id_t *counter_ids, - _In_ sai_stats_mode_t mode, - _Out_ uint64_t *counters) -{ - sai_status_t (*ptr)( - _In_ sai_object_id_t port_id, - _In_ uint32_t number_of_counters, - _In_ const sai_stat_id_t *counter_ids, - _In_ sai_stats_mode_t mode, - _Out_ uint64_t *counters); - - switch ((int)object_type) - { - case SAI_OBJECT_TYPE_PORT: - ptr = m_apis.port_api->get_port_stats_ext; - break; - case SAI_OBJECT_TYPE_ROUTER_INTERFACE: - ptr = m_apis.router_interface_api->get_router_interface_stats_ext; - break; - case SAI_OBJECT_TYPE_POLICER: - ptr = m_apis.policer_api->get_policer_stats_ext; - break; - ... - - default: - SWSS_LOG_ERROR("not implemented, FIXME"); - return SAI_STATUS_FAILURE; - } - - return ptr(object_id, number_of_counters, counter_ids, mode, counters); -} -``` - -可以明显看出,第一种调用方式代码要精炼和直观许多。 - -### Syncd主循环 - -`Syncd`的主循环也是使用的SONiC中标准的[事件分发](./4-3-event-polling-and-error-handling.html)机制:在启动时,`Syncd`会将所有用于事件处理的`Selectable`对象注册到用于获取事件的`Select`对象中,然后在主循环中调用`Select`的`select`方法,等待事件的发生。核心代码如下: +Once all the SAI APIs have been acquired, we can call into the SAI implementation using the `VendorSai` object. + +Currently, `VendorSai` internally has two different ways to call the SAI APIs: + +1. Using `sai_object_type_info_t` from SAI metadata, which essentially acts like a virtual table for all SAI Objects: + + ```cpp + // File: src/sonic-sairedis/syncd/VendorSai.cpp + sai_status_t VendorSai::set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) + { + ... + + auto info = sai_metadata_get_object_type_info(objectType); + sai_object_meta_key_t mk = { .objecttype = objectType, .objectkey = { .key = { .object_id = objectId } } }; + return info->set(&mk, attr); + } + ``` + +2. Using `m_apis` stored in the `VendorSai` object. This approach needs us to check the object type and then call the corresponding APIs, so the code becomes more verbose: + + ```cpp + sai_status_t VendorSai::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) + { + sai_status_t (*ptr)( + _In_ sai_object_id_t port_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters); + + switch ((int)object_type) + { + case SAI_OBJECT_TYPE_PORT: + ptr = m_apis.port_api->get_port_stats_ext; + break; + case SAI_OBJECT_TYPE_ROUTER_INTERFACE: + ptr = m_apis.router_interface_api->get_router_interface_stats_ext; + break; + case SAI_OBJECT_TYPE_POLICER: + ptr = m_apis.policer_api->get_policer_stats_ext; + break; + ... + + default: + SWSS_LOG_ERROR("not implemented, FIXME"); + return SAI_STATUS_FAILURE; + } + + return ptr(object_id, number_of_counters, counter_ids, mode, counters); + } + ``` + +The first approach is more succinct. + +### Main Event Loop + +`Syncd`'s main event loop follows SONiC's standard [event dispatching](./4-5-event-polling-and-error-handling.html) pattern. On startup, Syncd registers all Selectable objects handling events with a Select object that waits for events. The main loop calls "select" to wait for events: ```c // File: src/sonic-sairedis/syncd/Syncd.cpp @@ -269,7 +271,7 @@ void Syncd::run() } ``` -其中,`m_selectableChannel`就是主要负责处理Redis数据库中的事件的对象。它使用[ProducerTable / ConsumerTable](./4-2-2-redis-messaging-layer.md#producertable--consumertable)的方式与Redis数据库进行交互,所以,所有`orchagent`发送过来的操作都会以三元组的形式保存在Redis中的list中,等待`Syncd`的处理。其核心定义如下: +Here, `m_selectableChannel` handles Redis database events. It interacts with Redis [ProducerTable / ConsumerTable](./4-2-4-producer-consumer-table.html). Hence, all operations from `orchagent` will be stored in Redis lists, waiting for `Syncd` to consume. ```cpp // File: src/sonic-sairedis/meta/RedisSelectableChannel.h @@ -300,18 +302,18 @@ class RedisSelectableChannel: public SelectableChannel }; ``` -另外,在主循环启动时,`Syncd`还会额外启动两个线程: +During the main loop startup, `Syncd` also launches two threads: -- 用于接收ASIC上报通知的通知处理线程:`m_processor->startNotificationsProcessingThread();` -- 用于处理MDIO通信的MDIO IPC处理线程:`m_mdioIpcServer->startMdioThread();` +- A notification processing thread for receiving ASIC-reported notifications: `m_processor->startNotificationsProcessingThread()` +- A thread for handling MDIO communication: `m_mdioIpcServer->startMdioThread()` -它们的细节我们在初始化的部分不做过多展开,等后面介绍相关工作流时再来详细介绍。 +We'll discuss their details more thoroughly when introducing related workflows. -### 创建Switch对象,初始化通知机制 +### Initialize SAI Switch and Notifications -在主循环启动后,`Syncd`就会开始调用SAI的API来创建Switch对象,这里的入口有两个,一个是ASIC_DB收到创建Switch的通知,另外一个是Warm Boot时,`Syncd`来主动调用,但是创建Switch这一步的内部流程都类似。 +Once the main event loop is started, `Syncd` will call into SAI to create the Switch object. There are two main entry points: either a create switch request from ASIC_DB (called by swss) or `Syncd` directlly calls it for the Warm Boot process. Either way, the internal flow is similar. -在这一步中间,有一个很重要的步骤,就是初始化SAI内部实现中的通知回调,将我们之前已经创建好的通知处理逻辑传递给SAI的实现,比如FDB的事件等等。这些回调函数会被当做Switch的属性(Attributes)通过参数的形式传给SAI的`create_switch`方法,SAI的实现会将其保存起来,这样就可以在事件发生时调用回调函数,来通知`Syncd`了。这里的核心代码如下: +A crucial step here is initializing the notification callbacks in the SAI implementation, such as FDB events. These callback functions are passed to SAI as Switch attributes in `create_switch`. The SAI implementation stores them so it can call back into `Syncd` whenever these events occur: ```cpp // File: src/sonic-sairedis/syncd/Syncd.cpp @@ -387,9 +389,10 @@ void Syncd::onSwitchCreateInInitViewMode(_In_ sai_object_id_t switchVid, _In_ ui } ``` -从Mellanox的SAI实现,我们可以看到其具体的保存的方法: +From the open-sourced Mellanox's implementation, we can see how the SAI switch is created and the notification callbacks are set: ```cpp +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_switch.c static sai_status_t mlnx_create_switch(_Out_ sai_object_id_t * switch_id, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) @@ -425,11 +428,11 @@ static sai_status_t mlnx_create_switch(_Out_ sai_object_id_t * switch_id, } ``` -## ASIC状态更新 +## ASIC Programming Workflow -ASIC状态更新是`Syncd`中最重要的工作流之一,当`orchagent`发现任何变化并开始修改ASIC_DB时,就会触发该工作流,通过SAI来对ASIC进行更新。在了解了`Syncd`的主循环之后,理解ASIC状态更新的工作流就很简单了。 +ASIC programming workflow is the most important workflow in `Syncd`. When `orchagent` discovers any configuration changes, it sends ASIC programming request via `ASIC_DB`, which triggers this workflow and uses SAI to update the ASIC. After understanding Syncd's main event loop and the communication channels, the workflow will become easier to follow. -所有的步骤都发生在主线程一个线程中,顺序执行,总结成时序图如下: +All steps happen sequentially on the main thread: ```mermaid sequenceDiagram @@ -439,25 +442,25 @@ sequenceDiagram participant SAI as VendorSai participant R as Redis - loop 主线程循环 - SD->>RSC: 收到epoll通知,通知获取所有到来的消息 - RSC->>R: 通过ConsumerTable获取所有到来的消息 - - critical 给Syncd加锁 - loop 所有收到的消息 - SD->>RSC: 获取一个消息 - SD->>SD: 解析消息,获取操作类型和操作对象 - SD->>SAI: 调用对应的SAI API,更新ASIC - SD->>RSC: 发送调用结果给Redis - RSC->>R: 将调用结果写入Redis + loop Main thread loop + SD->>RSC: epoll notifies arrival of new messages + RSC->>R: Fetch all new messages from ConsumerTable + + critical Lock Syncd + loop For each message + SD->>RSC: Get the message + SD->>SD: Parse message, get operation type and object + SD->>SAI: Call the corresponding SAI API to update the ASIC + SD->>RSC: Send the operation result to Redis + RSC->>R: Write the result into Redis end end end ``` -首先,`orchagent`通过Redis发送过来的操作会被`RedisSelectableChannel`对象接收,然后在主循环中被处理。当`Syncd`处理到`m_selectableChannel`时,就会调用`processEvent`方法来处理该操作。这几步的核心代码我们上面介绍主循环时已经介绍过了,这里就不再赘述。 +First, `orchagent` sends operations through Redis, which will be received by the `RedisSelectableChannel.` When the main event loop processes `m_selectableChannel`, it calls `processEvent` to process it, just like what we have discussed in the main event loop section. -然后,`processEvent`会根据其中的操作类型,调用对应的SAI的API来对ASIC进行更新。其逻辑是一个巨大的switch-case语句,如下: +Then, `processEvent` calls the relevant SAI API to update the ASIC. The logic is a giant switch-case statement that dispatches the operations: ```cpp // File: src/sonic-sairedis/syncd/Syncd.cpp @@ -546,45 +549,47 @@ sai_status_t Syncd::processEntry(_In_ sai_object_meta_key_t metaKey, _In_ sai_co } ``` -## ASIC状态变更上报 +## ASIC State Change Notification Workflow + +On the other hand, when the ASIC state is changed or needs to report certain status, it notifies us through SAI. `Syncd` listens for these notifications, then reports them back to `orchagent` through our communication channel on top of `ASIC_DB`. -反过来,当ASIC状态发生任何变化,或者需要上报数据,它也会通过SAI来通知我们,此时Syncd会监听这些通知,然后通过ASIC_DB上报给orchagent。其主要工作流如下: +The workflow shows as below: ```mermaid sequenceDiagram - box purple SAI实现事件处理线程 + box purple SAI Implementation Event Thread participant SAI as SAI Impl end - box darkblue 通知处理线程 + box darkblue Notification Processing Thread participant NP as NotificationProcessor participant SD as Syncd participant RNP as RedisNotificationProducer participant R as Redis end - loop SAI实现事件处理消息循环 - SAI->>SAI: 通过ASIC SDK获取事件 - SAI->>SAI: 解析事件,并转换成SAI通知对象 - SAI->>NP: 将通知对象序列化,
并发送给通知处理线程的队列中 + loop SAI Implementation Event Loop + SAI->>SAI: Get events from ASIC SDK + SAI->>SAI: Parse events, convert to SAI notifications + SAI->>NP: Serialize notifications
and add to the notification thread queue end - loop 通知处理线程消息循环 - NP->>NP: 从队列中获取通知 - NP->>SD: 获取Syncd锁 - critical 给Syncd加锁 - NP->>NP: 反序列化通知对象,并做一些处理 - NP->>RNP: 重新序列化通知对象,并请求发送 - RNP->>R: 将通知以NotificationProducer
的形式写入ASIC_DB + loop Notification Thread Loop + NP->>NP: Fetch notification from queue + NP->>SD: Acquire Syncd lock + critical Lock Syncd + NP->>NP: Deserialize notification, handle it + NP->>RNP: Re-serialize notification and send to Redis + RNP->>R: Write the notification to ASIC_DB via NotificationProducer end end ``` -这里我们也来看一下具体的实现。为了更加深入的理解,我们还是借助开源的Mellanox的SAI实现来进行分析。 +Here, let's look into a real implementation. For better understanding, we still use Mellanox's open-sourced SAI implementation as an example. -最开始,SAI的实现需要接受到ASIC的通知,这一步是通过ASIC的SDK来实现的,Mellanox的SAI会创建一个事件处理线程(event_thread),然后使用`select`函数来获取并处理ASIC发送过来的通知,核心代码如下: +First of all, SAI implementation needs to be able to receive notification from ASIC. This is done by calling into the ASIC SDK. In Mellanox's SAI, it sets up an event thread to hook into ASIC, then use `select` to handle the events from ASIC SDK: ```cpp -// File: platform/mellanox/mlnx-sai/SAI-Implementation/mlnx_sai/src/mlnx_sai_switch.c +// File: https://github.com/Mellanox/SAI-Implementation/blob/master/mlnx_sai/src/mlnx_sai_switch.c static void event_thread_func(void *context) { #define MAX_PACKET_SIZE MAX(g_resource_limits.port_mtu_max, SX_HOST_EVENT_BUFFER_SIZE_MAX) @@ -687,7 +692,11 @@ out: } ``` -接下来,我们用FDB事件来举例,当ASIC收到FDB事件,就会被上面的事件处理循环获取到,并调用`g_notification_callbacks.on_fdb_event`函数来处理。这个函数接下来就会调用到`Syncd`初始化时设置好的`NotificationHandler::onFdbEvent`函数,这个函数会将该事件序列化后,通过消息队列转发给通知处理线程来进行处理: +Using FDB event as an example: + +1. When ASIC sends the FDB events, it will be received by the event loop above. +2. The callback `g_notification_callbacks.on_fdb_event` stored in SAI implementation will be called to handle this event. +3. It then calls `NotificationHandler::onFdbEvent` in Syncd to serialize the event and put it into the notification queue: ```cpp // File: src/sonic-sairedis/syncd/NotificationHandler.cpp @@ -698,7 +707,7 @@ void NotificationHandler::onFdbEvent(_In_ uint32_t count, _In_ const sai_fdb_eve } ``` -而此时通知处理线程会被唤醒,从消息队列中取出该事件,然后通过`Syncd`获取到`Syncd`的锁,再开始处理该通知: +Then the notification thread is signaled to pick up this event from the queue, then process it under the syncd lock: ```cpp // File: src/sonic-sairedis/syncd/NotificationProcessor.cpp @@ -728,7 +737,7 @@ void Syncd::syncProcessNotification(_In_ const swss::KeyOpFieldsValuesTuple& ite } ``` -接下来就是事件的分发和处理了,`syncProcessNotification`函数是一系列的`if-else`语句,根据事件的类型,调用不同的处理函数来处理该事件: +Now, it goes into the event dispatching and handling logic. `syncProcessNotification` function is essentially a series of `if-else` statements, which calls the corresponding handling function based on the event type: ```cpp // File: src/sonic-sairedis/syncd/NotificationProcessor.cpp @@ -748,7 +757,7 @@ void NotificationProcessor::syncProcessNotification( _In_ const swss::KeyOpField } ``` -而每个事件处理函数都类似,他们会对发送过来的事件进行反序列化,然后调用真正的处理逻辑发送通知,比如,fdb事件对应的`handle_fdb_event`函数和`process_on_fdb_event`: +For each event, the handling function deserializes the event and processes it, such as `handle_fdb_event` and `process_on_fdb_event`: ```cpp // File: src/sonic-sairedis/syncd/NotificationProcessor.cpp @@ -782,7 +791,7 @@ void NotificationProcessor::process_on_fdb_event( _In_ uint32_t count, _In_ sai_ } ``` -具体发送事件的逻辑就非常直接了,最终就是通过[NotificationProducer](./4-2-2-redis-messaging-layer.html#notificationproducer--notificationconsumer)来发送通知到ASIC_DB中: +Finally, it's written to ASIC_DB via [NotificationProducer](./4-2-3-notification-producer-consumer.md) to notify `orchagent`: ```cpp // File: src/sonic-sairedis/syncd/NotificationProcessor.cpp @@ -808,9 +817,9 @@ void RedisNotificationProducer::send(_In_ const std::string& op, _In_ const std: } ``` -到此,`Syncd`中的通知上报的流程就结束了。 +That's it! This is basically how things work in high level in `Syncd`! -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [Github repo: sonic-sairedis][SONiCSAIRedis] diff --git a/src/5-2-1-bgp-command-impl.md b/src/5-2-1-bgp-cli.md similarity index 71% rename from src/5-2-1-bgp-command-impl.md rename to src/5-2-1-bgp-cli.md index 43944f0..15efae9 100644 --- a/src/5-2-1-bgp-command-impl.md +++ b/src/5-2-1-bgp-cli.md @@ -1,6 +1,8 @@ -# BGP命令实现 +# BGP CLI and vtysh -由于BGP是使用FRR来实现的,所以自然而然的,`show`命令会将直接请求转发给FRR的`vtysh`,核心代码如下: +## `show` Command + +Since BGP is implemented using FRR, naturally, the `show` command will forward the direct request to FRR's `vtysh`. The key code is as follows: ```python # file: src/sonic-utilities/show/bgp_frr_v4.py @@ -14,7 +16,7 @@ def summary(namespace, display): # file: src/sonic-utilities/utilities_common/bgp_util.py def get_bgp_summary_from_all_bgp_instances(af, namespace, display): - # IPv6 case is omitted here for simplicity + # The IPv6 case is omitted here for simplicity vtysh_cmd = "show ip bgp summary json" for ns in device.get_ns_list_based_on_options(): @@ -25,7 +27,7 @@ def run_bgp_command(vtysh_cmd, bgp_namespace=multi_asic.DEFAULT_NAMESPACE, vtysh output, ret = clicommon.run_command(cmd, return_cmd=True) ``` -这里,我们也可以通过直接运行`vtysh`来进行验证: +We can also verify by running `vtysh` directly: ```bash root@7260cx3:/etc/sonic/frr# which vtysh @@ -54,7 +56,11 @@ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down Sta Total number of neighbors 4 ``` -而`config`命令则是通过直接操作CONFIG_DB来实现的,核心代码如下: +## `config` Command + +Meanwhile, the `config` command directly operates on `CONFIG_DB` to achieve configuration changes. + +Take remove neighbor as an example. The key code is shown as follows: ```python # file: src/sonic-utilities/config/main.py @@ -67,33 +73,29 @@ def remove(): @remove.command('neighbor') @click.argument('neighbor_ip_or_hostname', metavar='', required=True) def remove_neighbor(neighbor_ip_or_hostname): - """Deletes BGP neighbor configuration of given hostname or ip from devices - User can specify either internal or external BGP neighbor to remove - """ + """Removes BGP neighbor configuration (internal or external) from the device""" namespaces = [DEFAULT_NAMESPACE] removed_neighbor = False - ... + // ...existing code... - # Connect to CONFIG_DB in linux host (in case of single ASIC) or CONFIG_DB in all the - # namespaces (in case of multi ASIC) and do the sepcified "action" on the BGP neighbor(s) for namespace in namespaces: config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) config_db.connect() if _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname): removed_neighbor = True - ... + // ...existing code... ``` -# 参考资料 +# References -1. [SONiC Architecture][SONiCArch] -2. [Github repo: sonic-frr][SONiCFRR] -3. [Github repo: sonic-utilities][SONiCUtil] -4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP] -5. [FRRouting][FRRouting] +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-frr][SONiCFRR] +3. [Github repo: sonic-utilities][SONiCUtil] +4. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP] +5. [FRRouting][FRRouting] -[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture -[SONiCFRR]: https://github.com/sonic-net/sonic-frr -[SONiCUtil]: https://github.com/sonic-net/sonic-utilities -[BGP]: https://datatracker.ietf.org/doc/html/rfc4271 +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCFRR]: https://github.com/sonic-net/sonic-frr +[SONiCUtil]: https://github.com/sonic-net/sonic-utilities +[BGP]: https://datatracker.ietf.org/doc/html/rfc4271 [FRRouting]: https://frrouting.org/ \ No newline at end of file diff --git a/src/5-2-1-bgp-commands.md b/src/5-2-1-bgp-commands.md new file mode 100644 index 0000000..1a360a4 --- /dev/null +++ b/src/5-2-1-bgp-commands.md @@ -0,0 +1 @@ +# BGP Command Implementation diff --git a/src/5-2-2-bgp-route-update-workflow.md b/src/5-2-2-bgp-route-update-workflow.md deleted file mode 100644 index d4136ce..0000000 --- a/src/5-2-2-bgp-route-update-workflow.md +++ /dev/null @@ -1,1484 +0,0 @@ -# BGP路由变更下发 - -路由变更几乎是SONiC中最重要的工作流,它的整个流程从`bgpd`进程开始,到最终通过SAI到达ASIC芯片,中间参与的进程较多,流程也较为复杂,但是弄清楚之后,我们就可以很好的理解SONiC的设计思想,并且举一反三的理解其他配置下发的工作流了。所以这一节,我们就一起来深入的分析一下它的整体流程。 - -为了方便我们理解和从代码层面来展示,我们把这个流程分成两个大块来介绍,分别是FRR是如何处理路由变化的,和SONiC的路由变更工作流以及它是如何与FRR进行整合的。 - -## FRR处理路由变更 - -```mermaid -sequenceDiagram - autonumber - participant N as 邻居节点 - box purple bgp容器 - participant B as bgpd - participant ZH as zebra
(请求处理线程) - participant ZF as zebra
(路由处理线程) - participant ZD as zebra
(数据平面处理线程) - participant ZFPM as zebra
(FPM转发线程) - participant FPM as fpmsyncd - end - participant K as Linux Kernel - - N->>B: 建立BGP会话,
发送路由变更 - B->>B: 选路,变更本地路由表(RIB) - alt 如果路由发生变化 - B->>N: 通知其他邻居节点路由变化 - end - B->>ZH: 通过zlient本地Socket
通知Zebra更新路由表 - ZH->>ZH: 接受bgpd发送的请求 - ZH->>ZF: 将路由请求放入
路由处理线程的队列中 - ZF->>ZF: 更新本地路由表(RIB) - ZF->>ZD: 将路由表更新请求放入
数据平面处理线程
的消息队列中 - ZF->>ZFPM: 请求FPM处理线程转发路由变更 - ZFPM->>FPM: 通过FPM协议通知
fpmsyncd下发
路由变更 - ZD->>K: 发送Netlink消息更新内核路由表 -``` - -```admonish note -关于FRR的实现,这里更多的是从代码的角度来阐述其工作流的过程,而不是其对BGP的实现细节,如果想要了解FRR的BGP实现细节,可以参考[官方文档](https://docs.frrouting.org/en/latest/bgp.html)。 -``` - -### bgpd处理路由变更 - -`bgpd`是FRR中专门用来处理BGP会话的进程,它会开放TCP 179端口与邻居节点建立BGP连接,并处理路由表的更新请求。当路由发生变化后,FRR也会通过它来通知其他邻居节点。 - -请求来到`bgpd`之后,它会首先来到它的io线程:`bgp_io`。顾名思义,`bgpd`中的网络读写工作都是在这个线程上完成的: - -```c -// File: src/sonic-frr/frr/bgpd/bgp_io.c -static int bgp_process_reads(struct thread *thread) -{ - ... - - while (more) { - // Read packets here - ... - - // If we have more than 1 complete packet, mark it and process it later. - if (ringbuf_remain(ibw) >= pktsize) { - ... - added_pkt = true; - } else break; - } - ... - - if (added_pkt) - thread_add_event(bm->master, bgp_process_packet, peer, 0, &peer->t_process_packet); - - return 0; -} -``` - -当数据包读完后,`bgpd`会将其发送到主线程进行路由处理。在这里,`bgpd`会根据数据包的类型进行分发,其中路由更新的请求会交给`bpg_update_receive`来进行解析: - -```c -// File: src/sonic-frr/frr/bgpd/bgp_packet.c -int bgp_process_packet(struct thread *thread) -{ - ... - unsigned int processed = 0; - while (processed < rpkt_quanta_old) { - uint8_t type = 0; - bgp_size_t size; - ... - - /* read in the packet length and type */ - size = stream_getw(peer->curr); - type = stream_getc(peer->curr); - size -= BGP_HEADER_SIZE; - - switch (type) { - case BGP_MSG_OPEN: - ... - break; - case BGP_MSG_UPDATE: - ... - mprc = bgp_update_receive(peer, size); - ... - break; - ... -} - -// Process BGP UPDATE message for peer. -static int bgp_update_receive(struct peer *peer, bgp_size_t size) -{ - struct stream *s; - struct attr attr; - struct bgp_nlri nlris[NLRI_TYPE_MAX]; - ... - - // Parse attributes and NLRI - memset(&attr, 0, sizeof(struct attr)); - attr.label_index = BGP_INVALID_LABEL_INDEX; - attr.label = MPLS_INVALID_LABEL; - ... - - memset(&nlris, 0, sizeof(nlris)); - ... - - if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0) - || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { - // More parsing here - ... - - if (afi && peer->afc[afi][safi]) { - struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); - - /* End-of-RIB received */ - if (!CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED)) { - ... - if (gr_info->eor_required == gr_info->eor_received) { - ... - /* Best path selection */ - if (bgp_best_path_select_defer( peer->bgp, afi, safi) < 0) - return BGP_Stop; - } - } - ... - } - } - ... - - return Receive_UPDATE_message; -} -``` - -然后,`bgpd`会开始检查是否出现更优的路径,并更新自己的本地路由表(RIB,Routing Information Base): - -```c -// File: src/sonic-frr/frr/bgpd/bgp_route.c -/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */ -int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) -{ - struct bgp_dest *dest; - int cnt = 0; - struct afi_safi_info *thread_info; - ... - - /* Process the route list */ - for (dest = bgp_table_top(bgp->rib[afi][safi]); - dest && bgp->gr_info[afi][safi].gr_deferred != 0; - dest = bgp_route_next(dest)) - { - ... - bgp_process_main_one(bgp, dest, afi, safi); - ... - } - ... - - return 0; -} - -static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi) -{ - struct bgp_path_info *new_select; - struct bgp_path_info *old_select; - struct bgp_path_info_pair old_and_new; - ... - - const struct prefix *p = bgp_dest_get_prefix(dest); - ... - - /* Best path selection. */ - bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); - old_select = old_and_new.old; - new_select = old_and_new.new; - ... - - /* FIB update. */ - if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) - && !bgp_option_check(BGP_OPT_NO_FIB)) { - - if (new_select && new_select->type == ZEBRA_ROUTE_BGP - && (new_select->sub_type == BGP_ROUTE_NORMAL - || new_select->sub_type == BGP_ROUTE_AGGREGATE - || new_select->sub_type == BGP_ROUTE_IMPORTED)) { - ... - - if (old_select && is_route_parent_evpn(old_select)) - bgp_zebra_withdraw(p, old_select, bgp, safi); - - bgp_zebra_announce(dest, p, new_select, bgp, afi, safi); - } else { - /* Withdraw the route from the kernel. */ - ... - } - } - - /* EVPN route injection and clean up */ - ... - - UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED); - return; -} -``` - -最后,`bgp_zebra_announce`会通过`zclient`通知`zebra`更新内核路由表。 - -```c -// File: src/sonic-frr/frr/bgpd/bgp_zebra.c -void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi) -{ - ... - zclient_route_send(valid_nh_count ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, zclient, &api); -} -``` - -`zclient`使用本地socket与`zebra`通信,并且提供一系列的回调函数用于接收`zebra`的通知,核心代码如下: - -```c -// File: src/sonic-frr/frr/bgpd/bgp_zebra.c -void bgp_zebra_init(struct thread_master *master, unsigned short instance) -{ - zclient_num_connects = 0; - - /* Set default values. */ - zclient = zclient_new(master, &zclient_options_default); - zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs); - zclient->zebra_connected = bgp_zebra_connected; - zclient->router_id_update = bgp_router_id_update; - zclient->interface_add = bgp_interface_add; - zclient->interface_delete = bgp_interface_delete; - zclient->interface_address_add = bgp_interface_address_add; - ... -} - -int zclient_socket_connect(struct zclient *zclient) -{ - int sock; - int ret; - - sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0); - ... - - /* Connect to zebra. */ - ret = connect(sock, (struct sockaddr *)&zclient_addr, zclient_addr_len); - ... - - zclient->sock = sock; - return sock; -} -``` - -在`bgpd`容器中,我们可以在`/run/frr`目录下找到`zebra`通信使用的socket文件来进行简单的验证: - -```bash -root@7260cx3:/run/frr# ls -l -total 12 -... -srwx------ 1 frr frr 0 Jun 16 09:16 zserv.api -``` - -### zebra更新路由表 - -由于FRR支持的路由协议很多,如果每个路由协议处理进程都单独的对内核进行操作则必然会产生冲突,很难协调合作,所以FRR使用一个单独的进程用于和所有的路由协议处理进程进行沟通,整合好信息之后统一的进行内核的路由表更新,这个进程就是`zebra`。 - -在`zebra`中,内核的更新发生在一个独立的数据面处理线程中:`dplane_thread`。所有的请求都会通过`zclient`发送给`zebra`,经过处理之后,最后转发给`dplane_thread`来处理,这样路由的处理就是有序的了,也就不会产生冲突了。 - -`zebra`启动时,会将所有的请求处理函数进行注册,当请求到来时,就可以根据请求的类型调用相应的处理函数了,核心代码如下: - -```c -// File: src/sonic-frr/frr/zebra/zapi_msg.c -void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { - [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add, - [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, - [ZEBRA_INTERFACE_ADD] = zread_interface_add, - [ZEBRA_INTERFACE_DELETE] = zread_interface_delete, - [ZEBRA_ROUTE_ADD] = zread_route_add, - [ZEBRA_ROUTE_DELETE] = zread_route_del, - [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add, - [ZEBRA_REDISTRIBUTE_DELETE] = zebra_redistribute_delete, - ... -``` - -我们这里拿添加路由`zread_route_add`作为例子,来继续分析后续的流程。从以下代码我们可以看到,当新的路由到来后,`zebra`会开始查看并更新自己内部的路由表: - -```c -// File: src/sonic-frr/frr/zebra/zapi_msg.c -static void zread_route_add(ZAPI_HANDLER_ARGS) -{ - struct stream *s; - struct route_entry *re; - struct nexthop_group *ng = NULL; - struct nhg_hash_entry nhe; - ... - - // Decode zclient request - s = msg; - if (zapi_route_decode(s, &api) < 0) { - return; - } - ... - - // Allocate new route entry. - re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); - re->type = api.type; - re->instance = api.instance; - ... - - // Init nexthop entry, if we have an id, then add route. - if (!re->nhe_id) { - zebra_nhe_init(&nhe, afi, ng->nexthop); - nhe.nhg.nexthop = ng->nexthop; - nhe.backup_info = bnhg; - } - ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, &nhe); - - // Update stats. IPv6 is omitted here for simplicity. - if (ret > 0) client->v4_route_add_cnt++; - else if (ret < 0) client->v4_route_upd8_cnt++; -} - -// File: src/sonic-frr/frr/zebra/zebra_rib.c -int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re, - struct nhg_hash_entry *re_nhe) -{ - struct nhg_hash_entry *nhe = NULL; - struct route_table *table; - struct route_node *rn; - int ret = 0; - ... - - /* Find table and nexthop entry */ - table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re->table); - if (re->nhe_id > 0) nhe = zebra_nhg_lookup_id(re->nhe_id); - else nhe = zebra_nhg_rib_find_nhe(re_nhe, afi); - - /* Attach the re to the nhe's nexthop group. */ - route_entry_update_nhe(re, nhe); - - /* Make it sure prefixlen is applied to the prefix. */ - /* Set default distance by route type. */ - ... - - /* Lookup route node.*/ - rn = srcdest_rnode_get(table, p, src_p); - ... - - /* If this route is kernel/connected route, notify the dataplane to update kernel route table. */ - if (RIB_SYSTEM_ROUTE(re)) { - dplane_sys_route_add(rn, re); - } - - /* Link new re to node. */ - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - rib_addnode(rn, re, 1); - - /* Clean up */ - ... - return ret; -} -``` - -`rib_addnode`会将这个路由添加请求转发给rib的处理线程,并由它顺序的进行处理: - -```cpp -static void rib_addnode(struct route_node *rn, struct route_entry *re, int process) -{ - ... - rib_link(rn, re, process); -} - -static void rib_link(struct route_node *rn, struct route_entry *re, int process) -{ - rib_dest_t *dest = rib_dest_from_rnode(rn); - if (!dest) dest = zebra_rib_create_dest(rn); - re_list_add_head(&dest->routes, re); - ... - - if (process) rib_queue_add(rn); -} -``` - -请求会来到RIB的处理线程:`rib_process`,并由它来进行进一步的选路,然后将最优的路由添加到`zebra`的内部路由表(RIB)中: - -```cpp -/* Core function for processing routing information base. */ -static void rib_process(struct route_node *rn) -{ - struct route_entry *re; - struct route_entry *next; - struct route_entry *old_selected = NULL; - struct route_entry *new_selected = NULL; - struct route_entry *old_fib = NULL; - struct route_entry *new_fib = NULL; - struct route_entry *best = NULL; - rib_dest_t *dest; - ... - - dest = rib_dest_from_rnode(rn); - old_fib = dest->selected_fib; - ... - - /* Check every route entry and select the best route. */ - RNODE_FOREACH_RE_SAFE (rn, re, next) { - ... - - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) { - best = rib_choose_best(new_fib, re); - if (new_fib && best != new_fib) - UNSET_FLAG(new_fib->status, ROUTE_ENTRY_CHANGED); - new_fib = best; - } else { - best = rib_choose_best(new_selected, re); - if (new_selected && best != new_selected) - UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED); - new_selected = best; - } - - if (best != re) - UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - } /* RNODE_FOREACH_RE */ - ... - - /* Update fib according to selection results */ - if (new_fib && old_fib) - rib_process_update_fib(zvrf, rn, old_fib, new_fib); - else if (new_fib) - rib_process_add_fib(zvrf, rn, new_fib); - else if (old_fib) - rib_process_del_fib(zvrf, rn, old_fib); - - /* Remove all RE entries queued for removal */ - /* Check if the dest can be deleted now. */ - ... -} -``` - -对于新的路由,会调用`rib_process_add_fib`来将其添加到`zebra`的内部路由表中,然后通知dplane进行内核路由表的更新: - -```cpp -static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *new) -{ - hook_call(rib_update, rn, "new route selected"); - ... - - /* If labeled-unicast route, install transit LSP. */ - if (zebra_rib_labeled_unicast(new)) - zebra_mpls_lsp_install(zvrf, rn, new); - - rib_install_kernel(rn, new, NULL); - UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); -} - -void rib_install_kernel(struct route_node *rn, struct route_entry *re, - struct route_entry *old) -{ - struct rib_table_info *info = srcdest_rnode_table_info(rn); - enum zebra_dplane_result ret; - rib_dest_t *dest = rib_dest_from_rnode(rn); - ... - - /* Install the resolved nexthop object first. */ - zebra_nhg_install_kernel(re->nhe); - - /* If this is a replace to a new RE let the originator of the RE know that they've lost */ - if (old && (old != re) && (old->type != re->type)) - zsend_route_notify_owner(rn, old, ZAPI_ROUTE_BETTER_ADMIN_WON, info->afi, info->safi); - - /* Update fib selection */ - dest->selected_fib = re; - - /* Make sure we update the FPM any time we send new information to the kernel. */ - hook_call(rib_update, rn, "installing in kernel"); - - /* Send add or update */ - if (old) ret = dplane_route_update(rn, re, old); - else ret = dplane_route_add(rn, re); - ... -} -``` - -这里有两个重要的操作,一个自然是调用`dplane_route_*`函数来进行内核的路由表更新,另一个则是出现了两次的`hook_call`,fpm的钩子函数就是挂在这个地方,用来接收并转发路由表的更新通知。这里我们一个一个来看: - -#### dplane更新内核路由表 - -首先是dplane的`dplane_route_*`函数,它们的做的事情都一样:把请求打包,然后放入`dplane_thread`的消息队列中,并不会做任何实质的操作: - -```c -// File: src/sonic-frr/frr/zebra/zebra_dplane.c -enum zebra_dplane_result dplane_route_add(struct route_node *rn, struct route_entry *re) { - return dplane_route_update_internal(rn, re, NULL, DPLANE_OP_ROUTE_INSTALL); -} - -enum zebra_dplane_result dplane_route_update(struct route_node *rn, struct route_entry *re, struct route_entry *old_re) { - return dplane_route_update_internal(rn, re, old_re, DPLANE_OP_ROUTE_UPDATE); -} - -enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, struct route_entry *re) { - return dplane_route_update_internal(rn, re, NULL, DPLANE_OP_SYS_ROUTE_ADD); -} - -static enum zebra_dplane_result -dplane_route_update_internal(struct route_node *rn, struct route_entry *re, struct route_entry *old_re, enum dplane_op_e op) -{ - enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; - int ret = EINVAL; - - /* Create and init context */ - struct zebra_dplane_ctx *ctx = ...; - - /* Enqueue context for processing */ - ret = dplane_route_enqueue(ctx); - - /* Update counter */ - atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); - - if (ret == AOK) - result = ZEBRA_DPLANE_REQUEST_QUEUED; - - return result; -} -``` - -然后,我们就来到了数据面处理线程`dplane_thread`,其消息循环很简单,就是从队列中一个个取出消息,然后通过调用其处理函数: - -```c -// File: src/sonic-frr/frr/zebra/zebra_dplane.c -static int dplane_thread_loop(struct thread *event) -{ - ... - - while (prov) { - ... - - /* Process work here */ - (*prov->dp_fp)(prov); - - /* Check for zebra shutdown */ - /* Dequeue completed work from the provider */ - ... - - /* Locate next provider */ - DPLANE_LOCK(); - prov = TAILQ_NEXT(prov, dp_prov_link); - DPLANE_UNLOCK(); - } -} -``` - -默认情况下,`dplane_thread`会使用`kernel_dplane_process_func`来进行消息的处理,内部会根据请求的类型对内核的操作进行分发: - -```c -static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) -{ - enum zebra_dplane_result res; - struct zebra_dplane_ctx *ctx; - int counter, limit; - limit = dplane_provider_get_work_limit(prov); - - for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); - if (ctx == NULL) break; - - /* A previous provider plugin may have asked to skip the kernel update. */ - if (dplane_ctx_is_skip_kernel(ctx)) { - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - goto skip_one; - } - - /* Dispatch to appropriate kernel-facing apis */ - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - res = kernel_dplane_route_update(ctx); - break; - ... - } - ... - } - ... -} - -static enum zebra_dplane_result -kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) -{ - enum zebra_dplane_result res; - /* Call into the synchronous kernel-facing code here */ - res = kernel_route_update(ctx); - return res; -} -``` - -而`kernel_route_update`则是真正的内核操作了,它会通过netlink来通知内核路由更新: - -```c -// File: src/sonic-frr/frr/zebra/rt_netlink.c -// Update or delete a prefix from the kernel, using info from a dataplane context. -enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) -{ - int cmd, ret; - const struct prefix *p = dplane_ctx_get_dest(ctx); - struct nexthop *nexthop; - - if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { - cmd = RTM_DELROUTE; - } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { - cmd = RTM_NEWROUTE; - } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { - cmd = RTM_NEWROUTE; - } - - if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) - ret = netlink_route_multipath(cmd, ctx); - ... - - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); -} - -// Routing table change via netlink interface, using a dataplane context object -static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) -{ - // Build netlink request. - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[NL_PKT_BUF_SIZE]; - } req; - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); -    req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; -    ... - - /* Talk to netlink socket. */ - return netlink_talk_info(netlink_talk_filter, &req.n, dplane_ctx_get_ns(ctx), 0); -} -``` - -#### FPM路由更新转发 - -FPM(Forwarding Plane Manager)是FRR中用于通知其他进程路由变更的协议,其主要逻辑代码在`src/sonic-frr/frr/zebra/zebra_fpm.c`中。它默认有两套协议实现:protobuf和netlink,SONiC就是使用的是netlink协议。 - -上面我们已经提到,它通过钩子函数实现,监听RIB中的路由变化,并通过本地Socket转发给其他的进程。这个钩子会在启动的时候就注册好,其中和我们现在看的最相关的就是`rib_update`钩子了,如下所示: - -```c -static int zebra_fpm_module_init(void) -{ - hook_register(rib_update, zfpm_trigger_update); - hook_register(zebra_rmac_update, zfpm_trigger_rmac_update); - hook_register(frr_late_init, zfpm_init); - hook_register(frr_early_fini, zfpm_fini); - return 0; -} - -FRR_MODULE_SETUP(.name = "zebra_fpm", .version = FRR_VERSION, - .description = "zebra FPM (Forwarding Plane Manager) module", - .init = zebra_fpm_module_init, -); -``` - -当`rib_update`钩子被调用时,`zfpm_trigger_update`函数会被调用,它会将路由变更信息再次放入fpm的转发队列中,并触发写操作: - -```c -static int zfpm_trigger_update(struct route_node *rn, const char *reason) -{ - rib_dest_t *dest; - ... - - // Queue the update request - dest = rib_dest_from_rnode(rn); - SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM); - TAILQ_INSERT_TAIL(&zfpm_g->dest_q, dest, fpm_q_entries); - ... - - zfpm_write_on(); - return 0; -} - -static inline void zfpm_write_on(void) { - thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, &zfpm_g->t_write); -} -``` - -这个写操作的回调就会将其从队列中取出,并转换成FPM的消息格式,然后通过本地Socket转发给其他进程: - -```c -static int zfpm_write_cb(struct thread *thread) -{ - struct stream *s; - - do { - int bytes_to_write, bytes_written; - s = zfpm_g->obuf; - - // Convert route info to buffer here. - if (stream_empty(s)) zfpm_build_updates(); - - // Write to socket until we don' have anything to write or cannot write anymore (partial write). - bytes_to_write = stream_get_endp(s) - stream_get_getp(s); - bytes_written = write(zfpm_g->sock, stream_pnt(s), bytes_to_write); - ... - } while (1); - - if (zfpm_writes_pending()) zfpm_write_on(); - return 0; -} - -static void zfpm_build_updates(void) -{ - struct stream *s = zfpm_g->obuf; - do { - /* Stop processing the queues if zfpm_g->obuf is full or we do not have more updates to process */ - if (zfpm_build_mac_updates() == FPM_WRITE_STOP) break; - if (zfpm_build_route_updates() == FPM_WRITE_STOP) break; - } while (zfpm_updates_pending()); -} -``` - -到此,FRR的工作就完成了。 - -## SONiC路由变更工作流 - -当FRR变更内核路由配置后,SONiC便会收到来自Netlink和FPM的通知,然后进行一系列操作将其下发给ASIC,其主要流程如下: - -```mermaid -sequenceDiagram - autonumber - participant K as Linux Kernel - box purple bgp容器 - participant Z as zebra - participant FPM as fpmsyncd - end - box darkred database容器 - participant R as Redis - end - box darkblue swss容器 - participant OA as orchagent - end - box darkgreen syncd容器 - participant SD as syncd - end - participant A as ASIC - - K->>FPM: 内核路由变更时通过Netlink发送通知 - Z->>FPM: 通过FPM接口和Netlink
消息格式发送路由变更通知 - - FPM->>R: 通过ProducerStateTable
将路由变更信息写入
APPL_DB - - R->>OA: 通过ConsumerStateTable
接收路由变更信息 - - OA->>OA: 处理路由变更信息
生成SAI路由对象 - OA->>SD: 通过ProducerTable
或者ZMQ将SAI路由对象
发给syncd - - SD->>R: 接收SAI路由对象,写入ASIC_DB - SD->>A: 通过SAI接口
配置ASIC -``` - -### fpmsyncd更新Redis中的路由配置 - -首先,我们从源头看起。`fpmsyncd`在启动的时候便会开始监听FPM和Netlink的事件,用于接收路由变更消息: - -```cpp -// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp -int main(int argc, char **argv) -{ - ... - - DBConnector db("APPL_DB", 0); - RedisPipeline pipeline(&db); - RouteSync sync(&pipeline); - - // Register netlink message handler - NetLink netlink; - netlink.registerGroup(RTNLGRP_LINK); - - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); - - rtnl_route_read_protocol_names(DefaultRtProtoPath); - ... - - while (true) { - try { - // Launching FPM server and wait for zebra to connect. - FpmLink fpm(&sync); - ... - - fpm.accept(); - ... - } catch (FpmLink::FpmConnectionClosedException &e) { - // If connection is closed, keep retrying until it succeeds, before handling any other events. - cout << "Connection lost, reconnecting..." << endl; - } - ... - } -} -``` - -这样,所有的路由变更消息都会以Netlink的形式发送给`RouteSync`,其中[EVPN Type 5][EVPN]必须以原始消息的形式进行处理,所以会发送给`onMsgRaw`,其他的消息都会统一的发给处理Netlink的`onMsg`回调:(关于Netlink如何接收和处理消息,请移步[4.1.2 Netlink](./4-1-2-netlink.html)) - -```cpp -// File: src/sonic-swss/fpmsyncd/fpmlink.cpp -// Called from: FpmLink::readData() -void FpmLink::processFpmMessage(fpm_msg_hdr_t* hdr) -{ - size_t msg_len = fpm_msg_len(hdr); - nlmsghdr *nl_hdr = (nlmsghdr *)fpm_msg_data(hdr); - ... - - /* Read all netlink messages inside FPM message */ - for (; NLMSG_OK (nl_hdr, msg_len); nl_hdr = NLMSG_NEXT(nl_hdr, msg_len)) - { - /* - * EVPN Type5 Add Routes need to be process in Raw mode as they contain - * RMAC, VLAN and L3VNI information. - * Where as all other route will be using rtnl api to extract information - * from the netlink msg. - */ - bool isRaw = isRawProcessing(nl_hdr); - - nl_msg *msg = nlmsg_convert(nl_hdr); - ... - nlmsg_set_proto(msg, NETLINK_ROUTE); - - if (isRaw) { - /* EVPN Type5 Add route processing */ - /* This will call into onRawMsg() */ - processRawMsg(nl_hdr); - } else { - /* This will call into onMsg() */ - NetDispatcher::getInstance().onNetlinkMessage(msg); - } - - nlmsg_free(msg); - } -} - -void FpmLink::processRawMsg(struct nlmsghdr *h) -{ - m_routesync->onMsgRaw(h); -}; -``` - -接着,`RouteSync`收到路由变更的消息之后,会在`onMsg`和`onMsgRaw`中进行判断和分发: - -```cpp -// File: src/sonic-swss/fpmsyncd/routesync.cpp -void RouteSync::onMsgRaw(struct nlmsghdr *h) -{ - if ((h->nlmsg_type != RTM_NEWROUTE) && (h->nlmsg_type != RTM_DELROUTE)) - return; - ... - onEvpnRouteMsg(h, len); -} - -void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) -{ - // Refill Netlink cache here - ... - - struct rtnl_route *route_obj = (struct rtnl_route *)obj; - auto family = rtnl_route_get_family(route_obj); - if (family == AF_MPLS) { - onLabelRouteMsg(nlmsg_type, obj); - return; - } - ... - - unsigned int master_index = rtnl_route_get_table(route_obj); - char master_name[IFNAMSIZ] = {0}; - if (master_index) { - /* If the master device name starts with VNET_PREFIX, it is a VNET route. - The VNET name is exactly the name of the associated master device. */ - getIfName(master_index, master_name, IFNAMSIZ); - if (string(master_name).find(VNET_PREFIX) == 0) { - onVnetRouteMsg(nlmsg_type, obj, string(master_name)); - } - - /* Otherwise, it is a regular route (include VRF route). */ - else { - onRouteMsg(nlmsg_type, obj, master_name); - } - } else { - onRouteMsg(nlmsg_type, obj, NULL); - } -} -``` - -从上面的代码中,我们可以看到这里会有四种不同的路由处理入口,这些不同的路由会被最终通过各自的[ProducerStateTable](./4-2-2-redis-messaging-layer.html#producerstatetable--consumerstatetable)写入到`APPL_DB`中的不同的Table中: - -| 路由类型 | 处理函数 | Table | -| --- | --- | --- | -| MPLS | `onLabelRouteMsg` | LABLE_ROUTE_TABLE | -| Vnet VxLan Tunnel Route | `onVnetRouteMsg` | VNET_ROUTE_TUNNEL_TABLE | -| 其他Vnet路由 | `onVnetRouteMsg` | VNET_ROUTE_TABLE | -| EVPN Type 5 | `onEvpnRouteMsg` | ROUTE_TABLE | -| 普通路由 | `onRouteMsg` | ROUTE_TABLE | - -这里以普通路由来举例子,其他的函数的实现虽然有所不同,但是主体的思路是一样的: - -```cpp -// File: src/sonic-swss/fpmsyncd/routesync.cpp -void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char *vrf) -{ - // Parse route info from nl_object here. - ... - - // Get nexthop lists - string gw_list; - string intf_list; - string mpls_list; - getNextHopList(route_obj, gw_list, mpls_list, intf_list); - ... - - // Build route info here, including protocol, interface, next hops, MPLS, weights etc. - vector fvVector; - FieldValueTuple proto("protocol", proto_str); - FieldValueTuple gw("nexthop", gw_list); - ... - - fvVector.push_back(proto); - fvVector.push_back(gw); - ... - - // Push to ROUTE_TABLE via ProducerStateTable. - m_routeTable.set(destipprefix, fvVector); - SWSS_LOG_DEBUG("RouteTable set msg: %s %s %s %s", destipprefix, gw_list.c_str(), intf_list.c_str(), mpls_list.c_str()); - ... -} -``` - -### orchagent处理路由配置变化 - -接下来,这些路由信息会来到orchagent。在orchagent启动的时候,它会创建好`VNetRouteOrch`和`RouteOrch`对象,这两个对象分别用来监听和处理Vnet相关路由和EVPN/普通路由: - -```cpp -// File: src/sonic-swss/orchagent/orchdaemon.cpp -bool OrchDaemon::init() -{ - ... - - vector vnet_tables = { APP_VNET_RT_TABLE_NAME, APP_VNET_RT_TUNNEL_TABLE_NAME }; - VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, vnet_orch); - ... - - const int routeorch_pri = 5; - vector route_tables = { - { APP_ROUTE_TABLE_NAME, routeorch_pri }, - { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } - }; - gRouteOrch = new RouteOrch(m_applDb, route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch, gSrv6Orch); - ... -} -``` - -所有Orch对象的消息处理入口都是`doTask`,这里`RouteOrch`和`VNetRouteOrch`也不例外,这里我们以`RouteOrch`为例子,看看它是如何处理路由变化的。 - -```admonish note -从`RouteOrch`上,我们可以真切的感受到为什么这些类被命名为`Orch`。`RouteOrch`有2500多行,其中会有和很多其他Orch的交互,以及各种各样的细节…… 代码是相对难读,请大家读的时候一定保持耐心。 -``` - -`RouteOrch`在处理路由消息的时候有几点需要注意: - -- 从上面`init`函数,我们可以看到`RouteOrch`不仅会管理普通路由,还会管理MPLS路由,这两种路由的处理逻辑是不一样的,所以在下面的代码中,为了简化,我们只展示普通路由的处理逻辑。 -- 因为`ProducerStateTable`在传递和接受消息的时候都是批量传输的,所以,`RouteOrch`在处理消息的时候,也是批量处理的。为了支持批量处理,`RouteOrch`会借用`EntityBulker gRouteBulker`将需要改动的SAI路由对象缓存起来,然后在`doTask()`函数的最后,一次性将这些路由对象的改动应用到SAI中。 -- 路由的操作会需要很多其他的信息,比如每个Port的状态,每个Neighbor的状态,每个VRF的状态等等。为了获取这些信息,`RouteOrch`会与其他的Orch对象进行交互,比如`PortOrch`,`NeighOrch`,`VRFOrch`等等。 - -```cpp -// File: src/sonic-swss/orchagent/routeorch.cpp -void RouteOrch::doTask(Consumer& consumer) -{ - // Calling PortOrch to make sure all ports are ready before processing route messages. - if (!gPortsOrch->allPortsReady()) { return; } - - // Call doLabelTask() instead, if the incoming messages are from MPLS messages. Otherwise, move on as regular routes. - ... - - /* Default handling is for ROUTE_TABLE (regular routes) */ - auto it = consumer.m_toSync.begin(); - while (it != consumer.m_toSync.end()) { - // Add or remove routes with a route bulker - while (it != consumer.m_toSync.end()) - { - KeyOpFieldsValuesTuple t = it->second; - - // Parse route operation from the incoming message here. - string key = kfvKey(t); - string op = kfvOp(t); - ... - - // resync application: - // - When routeorch receives 'resync' message (key = "resync", op = "SET"), it marks all current routes as dirty - // and waits for 'resync complete' message. For all newly received routes, if they match current dirty routes, - // it unmarks them dirty. - // - After receiving 'resync complete' (key = "resync", op != "SET") message, it creates all newly added routes - // and removes all dirty routes. - ... - - // Parsing VRF and IP prefix from the incoming message here. - ... - - // Process regular route operations. - if (op == SET_COMMAND) - { - // Parse and validate route attributes from the incoming message here. - string ips; - string aliases; - ... - - // If the nexthop_group is empty, create the next hop group key based on the IPs and aliases. - // Otherwise, get the key from the NhgOrch. The result will be stored in the "nhg" variable below. - NextHopGroupKey& nhg = ctx.nhg; - ... - if (nhg_index.empty()) - { - // Here the nexthop_group is empty, so we create the next hop group key based on the IPs and aliases. - ... - - string nhg_str = ""; - if (blackhole) { - nhg = NextHopGroupKey(); - } else if (srv6_nh == true) { - ... - nhg = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh); - } else if (overlay_nh == false) { - ... - nhg = NextHopGroupKey(nhg_str, weights); - } else { - ... - nhg = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh); - } - } - else - { - // Here we have a nexthop_group, so we get the key from the NhgOrch. - const NhgBase& nh_group = getNhg(nhg_index); - nhg = nh_group.getNhgKey(); - ... - } - ... - - // Now we start to create the SAI route entry. - if (nhg.getSize() == 1 && nhg.hasIntfNextHop()) - { - // Skip certain routes, such as not valid, directly routes to tun0, linklocal or multicast routes, etc. - ... - - // Create SAI route entry in addRoute function. - if (addRoute(ctx, nhg)) it = consumer.m_toSync.erase(it); - else it++; - } - - /* - * Check if the route does not exist or needs to be updated or - * if the route is using a temporary next hop group owned by - * NhgOrch. - */ - else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() || - m_syncdRoutes.at(vrf_id).find(ip_prefix) == m_syncdRoutes.at(vrf_id).end() || - m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index) || - gRouteBulker.bulk_entry_pending_removal(route_entry) || - ctx.using_temp_nhg) - { - if (addRoute(ctx, nhg)) it = consumer.m_toSync.erase(it); - else it++; - } - ... - } - // Handle other ops, like DEL_COMMAND for route deletion, etc. - ... - } - - // Flush the route bulker, so routes will be written to syncd and ASIC - gRouteBulker.flush(); - - // Go through the bulker results. - // Handle SAI failures, update neighbors, counters, send notifications in add/removeRoutePost functions. - ... - - /* Remove next hop group if the reference count decreases to zero */ - ... - } -} -``` - -解析完路由操作后,`RouteOrch`会调用`addRoute`或者`removeRoute`函数来创建或者删除路由。这里以添加路由`addRoute`为例子来继续分析。它的逻辑主要分为几个大部分: - -1. 从NeighOrch中获取下一跳信息,并检查下一跳是否真的可用。 -2. 如果是新路由,或者是重新添加正在等待删除的路由,那么就会创建一个新的SAI路由对象 -3. 如果是已有的路由,那么就更新已有的SAI路由对象 - -```cpp -// File: src/sonic-swss/orchagent/routeorch.cpp -bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) -{ - // Get nexthop information from NeighOrch. - // We also need to check PortOrch for inband port, IntfsOrch to ensure the related interface is created and etc. - ... - - // Start to sync the SAI route entry. - sai_route_entry_t route_entry; - route_entry.vr_id = vrf_id; - route_entry.switch_id = gSwitchId; - copy(route_entry.destination, ipPrefix); - - sai_attribute_t route_attr; - auto& object_statuses = ctx.object_statuses; - - // Create a new route entry in this case. - // - // In case the entry is already pending removal in the bulk, it would be removed from m_syncdRoutes during the bulk call. - // Therefore, such entries need to be re-created rather than set attribute. - if (it_route == m_syncdRoutes.at(vrf_id).end() || gRouteBulker.bulk_entry_pending_removal(route_entry)) { - if (blackhole) { - route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; - route_attr.value.s32 = SAI_PACKET_ACTION_DROP; - } else { - route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; - route_attr.value.oid = next_hop_id; - } - - /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */ - object_statuses.emplace_back(); - sai_status_t status = gRouteBulker.create_entry(&object_statuses.back(), &route_entry, 1, &route_attr); - if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) { - return false; - } - } - - // Update existing route entry in this case. - else { - // Set the packet action to forward when there was no next hop (dropped) and not pointing to blackhole. - if (it_route->second.nhg_key.getSize() == 0 && !blackhole) { - route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; - route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD; - - object_statuses.emplace_back(); - gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); - } - - // Only 1 case is listed here as an example. Other cases are handled with similar logic by calling set_entry_attributes as well. - ... - } - ... -} -``` - -在创建和设置好所有的路由后,`RouteOrch`会调用`gRouteBulker.flush()`来将所有的路由写入到ASIC_DB中。`flush()`函数很简单,就是将所有的请求分批次进行处理,默认情况下每一批是1000个,这个定义在`OrchDaemon`中,并通过构造函数传入: - -```cpp -// File: src/sonic-swss/orchagent/orchdaemon.cpp -#define DEFAULT_MAX_BULK_SIZE 1000 -size_t gMaxBulkSize = DEFAULT_MAX_BULK_SIZE; - -// File: src/sonic-swss/orchagent/bulker.h -template -class EntityBulker -{ -public: - using Ts = SaiBulkerTraits; - using Te = typename Ts::entry_t; - ... - - void flush() - { - // Bulk remove entries - if (!removing_entries.empty()) { - // Split into batches of max_bulk_size, then call flush. Similar to creating_entries, so details are omitted. - std::vector rs; - ... - flush_removing_entries(rs); - removing_entries.clear(); - } - - // Bulk create entries - if (!creating_entries.empty()) { - // Split into batches of max_bulk_size, then call flush_creating_entries to call SAI batch create API to create - // the objects in batch. - std::vector rs; - std::vector tss; - std::vector cs; - - for (auto const& i: creating_entries) { - sai_object_id_t *pid = std::get<0>(i); - auto const& attrs = std::get<1>(i); - if (*pid == SAI_NULL_OBJECT_ID) { - rs.push_back(pid); - tss.push_back(attrs.data()); - cs.push_back((uint32_t)attrs.size()); - - // Batch create here. - if (rs.size() >= max_bulk_size) { - flush_creating_entries(rs, tss, cs); - } - } - } - - flush_creating_entries(rs, tss, cs); - creating_entries.clear(); - } - - // Bulk update existing entries - if (!setting_entries.empty()) { - // Split into batches of max_bulk_size, then call flush. Similar to creating_entries, so details are omitted. - std::vector rs; - std::vector ts; - std::vector status_vector; - ... - flush_setting_entries(rs, ts, status_vector); - setting_entries.clear(); - } - } - - sai_status_t flush_creating_entries( - _Inout_ std::vector &rs, - _Inout_ std::vector &tss, - _Inout_ std::vector &cs) - { - ... - - // Call SAI bulk create API - size_t count = rs.size(); - std::vector statuses(count); - sai_status_t status = (*create_entries)((uint32_t)count, rs.data(), cs.data(), tss.data() - , SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, statuses.data()); - - // Set results back to input entries and clean up the batch below. - for (size_t ir = 0; ir < count; ir++) { - auto& entry = rs[ir]; - sai_status_t *object_status = creating_entries[entry].second; - if (object_status) { - *object_status = statuses[ir]; - } - } - - rs.clear(); tss.clear(); cs.clear(); - return status; - } - - // flush_removing_entries and flush_setting_entries are similar to flush_creating_entries, so we omit them here. - ... -}; -``` - -### orchagent中的SAI对象转发 - -细心的小伙伴肯定已经发现了奇怪的地方,这里`EntityBulker`怎么看着像在直接调用SAI API呢?难道它们不应该是在syncd中调用的吗?如果我们对传入`EntityBulker`的SAI API对象进行跟踪,我们甚至会找到sai_route_api_t就是SAI的接口,而`orchagent`中还有SAI的初始化代码,如下: - -```cpp -// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h -/** - * @brief Router entry methods table retrieved with sai_api_query() - */ -typedef struct _sai_route_api_t -{ - sai_create_route_entry_fn create_route_entry; - sai_remove_route_entry_fn remove_route_entry; - sai_set_route_entry_attribute_fn set_route_entry_attribute; - sai_get_route_entry_attribute_fn get_route_entry_attribute; - - sai_bulk_create_route_entry_fn create_route_entries; - sai_bulk_remove_route_entry_fn remove_route_entries; - sai_bulk_set_route_entry_attribute_fn set_route_entries_attribute; - sai_bulk_get_route_entry_attribute_fn get_route_entries_attribute; -} sai_route_api_t; - -// File: src/sonic-swss/orchagent/saihelper.cpp -void initSaiApi() -{ - SWSS_LOG_ENTER(); - - if (ifstream(CONTEXT_CFG_FILE)) - { - SWSS_LOG_NOTICE("Context config file %s exists", CONTEXT_CFG_FILE); - gProfileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = CONTEXT_CFG_FILE; - } - - sai_api_initialize(0, (const sai_service_method_table_t *)&test_services); - sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api); - ... - sai_api_query(SAI_API_NEIGHBOR, (void **)&sai_neighbor_api); - sai_api_query(SAI_API_NEXT_HOP, (void **)&sai_next_hop_api); - sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **)&sai_next_hop_group_api); - sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api); - ... - - sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); - ... - sai_log_set(SAI_API_NEIGHBOR, SAI_LOG_LEVEL_NOTICE); - sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_LEVEL_NOTICE); - sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_LEVEL_NOTICE); - sai_log_set(SAI_API_ROUTE, SAI_LOG_LEVEL_NOTICE); - ... -} -``` - -相信大家第一次看到这个代码会感觉到非常的困惑。不过别着急,这其实就是`orchagent`中SAI对象的转发机制。 - -熟悉RPC的小伙伴一定不会对`proxy-stub`模式感到陌生 —— 利用统一的接口来定义通信双方调用接口,在调用方实现序列化和发送,然后再接收方实现接收,反序列化与分发。这里SONiC的做法也是类似的:利用SAI API本身作为统一的接口,并实现好序列化和发送功能给`orchagent`来调用,然后再`syncd`中实现接收,反序列化与分发功能。 - -这里,发送端叫做`ClientSai`,实现在`src/sonic-sairedis/lib/ClientSai.*`中。而序列化与反序列化实现在SAI metadata中:`src/sonic-sairedis/meta/sai_serialize.h`: - -```cpp -// File: src/sonic-sairedis/lib/ClientSai.h -namespace sairedis -{ - class ClientSai: - public sairedis::SaiInterface - { - ... - }; -} - -// File: src/sonic-sairedis/meta/sai_serialize.h -// Serialize -std::string sai_serialize_route_entry(_In_ const sai_route_entry_t &route_entry); -... - -// Deserialize -void sai_deserialize_route_entry(_In_ const std::string& s, _In_ sai_route_entry_t &route_entry); -... -``` - -`orchagent`在编译的时候,会去链接`libsairedis`,从而实现调用SAI API时,对SAI对象进行序列化和发送: - -```makefile -# File: src/sonic-swss/orchagent/Makefile.am -orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta -lsaimetadata -lswsscommon -lzmq -``` - -我们这里用Bulk Create作为例子,来看看`ClientSai`是如何实现序列化和发送的: - -```cpp -// File: src/sonic-sairedis/lib/ClientSai.cpp -sai_status_t ClientSai::bulkCreate( - _In_ sai_object_type_t object_type, - _In_ sai_object_id_t switch_id, - _In_ uint32_t object_count, - _In_ const uint32_t *attr_count, - _In_ const sai_attribute_t **attr_list, - _In_ sai_bulk_op_error_mode_t mode, - _Out_ sai_object_id_t *object_id, - _Out_ sai_status_t *object_statuses) -{ - MUTEX(); - REDIS_CHECK_API_INITIALIZED(); - - std::vector serialized_object_ids; - - // Server is responsible for generate new OID but for that we need switch ID - // to be sent to server as well, so instead of sending empty oids we will - // send switch IDs - for (uint32_t idx = 0; idx < object_count; idx++) { - serialized_object_ids.emplace_back(sai_serialize_object_id(switch_id)); - } - auto status = bulkCreate(object_type, serialized_object_ids, attr_count, attr_list, mode, object_statuses); - - // Since user requested create, OID value was created remotely and it was returned in m_lastCreateOids - for (uint32_t idx = 0; idx < object_count; idx++) { - if (object_statuses[idx] == SAI_STATUS_SUCCESS) { - object_id[idx] = m_lastCreateOids.at(idx); - } else { - object_id[idx] = SAI_NULL_OBJECT_ID; - } - } - - return status; -} - -sai_status_t ClientSai::bulkCreate( - _In_ sai_object_type_t object_type, - _In_ const std::vector &serialized_object_ids, - _In_ const uint32_t *attr_count, - _In_ const sai_attribute_t **attr_list, - _In_ sai_bulk_op_error_mode_t mode, - _Inout_ sai_status_t *object_statuses) -{ - ... - - // Calling SAI serialize APIs to serialize all objects - std::string str_object_type = sai_serialize_object_type(object_type); - std::vector entries; - for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) { - auto entry = SaiAttributeList::serialize_attr_list(object_type, attr_count[idx], attr_list[idx], false); - if (entry.empty()) { - swss::FieldValueTuple null("NULL", "NULL"); - entry.push_back(null); - } - - std::string str_attr = Globals::joinFieldValues(entry); - swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , str_attr); - entries.push_back(fvtNoStatus); - } - std::string key = str_object_type + ":" + std::to_string(entries.size()); - - // Send to syncd via the communication channel. - m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_CREATE); - - // Wait for response from syncd. - return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, (uint32_t)serialized_object_ids.size(), object_statuses); -} -``` - -最终,`ClientSai`会调用`m_communicationChannel->set()`,将序列化后的SAI对象发送给`syncd`。而这个Channel,在202106版本之前,就是[基于Redis的ProducerTable](https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h)了。可能是基于效率的考虑,从202111版本开始,这个Channel已经更改为[ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/ZeroMQChannel.h)了。 - -```cpp -// File: https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h -class RedisChannel: public Channel -{ - ... - - /** - * @brief Asic state channel. - * - * Used to sent commands like create/remove/set/get to syncd. - */ - std::shared_ptr m_asicState; - - ... -}; - -// File: src/sonic-sairedis/lib/ClientSai.cpp -sai_status_t ClientSai::initialize( - _In_ uint64_t flags, - _In_ const sai_service_method_table_t *service_method_table) -{ - ... - - m_communicationChannel = std::make_shared( - cc->m_zmqEndpoint, - cc->m_zmqNtfEndpoint, - std::bind(&ClientSai::handleNotification, this, _1, _2, _3)); - - m_apiInitialized = true; - - return SAI_STATUS_SUCCESS; -} -``` - -关于进程通信的方法,这里就不再赘述了,大家可以参考第四章描述的[进程间的通信机制](./4-2-2-redis-messaging-layer.html)。 - -### syncd更新ASIC - -最后,当SAI对象生成好并发送给`syncd`后,`syncd`会接收,处理,更新ASIC_DB,最后更新ASIC。这一段的工作流,我们已经在[Syncd-SAI工作流](./5-1-syncd-sai-workflow.html)中详细介绍过了,这里就不再赘述了,大家可以移步去查看。 - -# 参考资料 - -1. [SONiC Architecture][SONiCArch] -2. [Github repo: sonic-swss][SONiCSWSS] -3. [Github repo: sonic-swss-common][SONiCSWSSCommon] -4. [Github repo: sonic-frr][SONiCFRR] -5. [Github repo: sonic-utilities][SONiCUtil] -6. [Github repo: sonic-sairedis][SONiCSAIRedis] -7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP] -8. [FRRouting][FRRouting] -9. [FRRouting - BGP][BGP] -10. [FRRouting - FPM][FPM] -11. [Understanding EVPN Pure Type 5 Routes][EVPN] - -[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture -[SONiCSWSS]: https://github.com/sonic-net/sonic-swss -[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common -[SONiCFRR]: https://github.com/sonic-net/sonic-frr -[SONiCUtil]: https://github.com/sonic-net/sonic-utilities -[SONiCSAIRedis]: https://github.com/sonic-net/sonic-sairedis/ -[BGP]: https://datatracker.ietf.org/doc/html/rfc4271 -[FRRouting]: https://frrouting.org/ -[FPM]: https://docs.frrouting.org/projects/dev-guide/en/latest/fpm.html -[FRRBGP]: https://docs.frrouting.org/en/latest/bgp.html -[EVPN]: https://www.juniper.net/documentation/us/en/software/junos/evpn-vxlan/topics/concept/evpn-route-type5-understanding.html \ No newline at end of file diff --git a/src/5-2-2-route-update-in-frr.md b/src/5-2-2-route-update-in-frr.md new file mode 100644 index 0000000..7a3d12f --- /dev/null +++ b/src/5-2-2-route-update-in-frr.md @@ -0,0 +1,758 @@ +# Route Update in FRR + +Route update is almost the most important workflow in SONiC. The entire process starts from the `bgpd` process and eventually reaches the ASIC chip through SAI. Many processes are involved in between, and the workflow is quite complex. However, once we understand it, we can understand the design of SONiC and many other configuration workflows much better. Therefore, in this section, we will deeply dive into its overall process. + +To help us understand the workflow on the code level, we divide this workflow into two major parts: how FRR handles route changes in this chapter, and how the SONiC updates the routes and integrates with FRR in the next chapter. + +## FRR Handling Route Changes + +```mermaid +sequenceDiagram + autonumber + participant N as Neighbor Node + box purple BGP Container + participant B as bgpd + participant ZH as zebra
(Request Handling Thread) + participant ZF as zebra
(Route Handling Thread) + participant ZD as zebra
(Data Plane Handling Thread) + participant ZFPM as zebra
(FPM Forward Thread) + participant FPM as fpmsyncd + end + participant K as Linux Kernel + + N->>B: Establish BGP session,
send route update + B->>B: Route selection, update local routing table (RIB) + alt If route changes + B->>N: Notify other neighbor nodes of route change + end + B->>ZH: Notify Zebra to update routing table
through zlient local Socket + ZH->>ZH: Receive request from bgpd + ZH->>ZF: Put route request into
route handling thread's queue + ZF->>ZF: Update local routing table (RIB) + ZF->>ZD: Put route table update request into
data plane handling thread's
message queue + ZF->>ZFPM: Request FPM handling thread to forward route update + ZFPM->>FPM: Notify fpmsyncd to
issue route update
through FPM protocol + ZD->>K: Send Netlink message to update kernel routing table +``` + +```admonish note +Regarding the implementation of FRR, this section focuses more on explaining its workflow from the code perspective rather than the details of its BGP implementation. If you want to learn about the details of FRR's BGP implementation, you can refer to the [official documentation](https://docs.frrouting.org/en/latest/bgp.html). +``` + +## `bgpd` Handling Route Changes + +`bgpd` is the process in FRR specifically used to handle BGP sessions. It opens TCP port 179 to establish BGP connections with neighbors and handles routing table update requests. When a route changes, FRR also uses this session to notify other neighbors. + +When a request arrives at `bgpd`, it will land on the io thread first: `bgp_io`. As the name suggests, this thread is responsible for network read and write operations in `bgpd`: + +```c +// File: src/sonic-frr/frr/bgpd/bgp_io.c +static int bgp_process_reads(struct thread *thread) +{ + ... + + while (more) { + // Read packets here + ... + + // If we have more than 1 complete packet, mark it and process it later. + if (ringbuf_remain(ibw) >= pktsize) { + ... + added_pkt = true; + } else break; + } + ... + + if (added_pkt) + thread_add_event(bm->master, bgp_process_packet, peer, 0, &peer->t_process_packet); + + return 0; +} +``` + +After the packet is read, `bgpd` sends it to the main thread for processing. Here, `bgpd` dispatches the packet based on its type. And the route update requests will be handed over to `bpg_update_receive` for processing: + +```c +// File: src/sonic-frr/frr/bgpd/bgp_packet.c +int bgp_process_packet(struct thread *thread) +{ + ... + unsigned int processed = 0; + while (processed < rpkt_quanta_old) { + uint8_t type = 0; + bgp_size_t size; + ... + + /* read in the packet length and type */ + size = stream_getw(peer->curr); + type = stream_getc(peer->curr); + size -= BGP_HEADER_SIZE; + + switch (type) { + case BGP_MSG_OPEN: + ... + break; + case BGP_MSG_UPDATE: + ... + mprc = bgp_update_receive(peer, size); + ... + break; + ... +} + +// Process BGP UPDATE message for peer. +static int bgp_update_receive(struct peer *peer, bgp_size_t size) +{ + struct stream *s; + struct attr attr; + struct bgp_nlri nlris[NLRI_TYPE_MAX]; + ... + + // Parse attributes and NLRI + memset(&attr, 0, sizeof(struct attr)); + attr.label_index = BGP_INVALID_LABEL_INDEX; + attr.label = MPLS_INVALID_LABEL; + ... + + memset(&nlris, 0, sizeof(nlris)); + ... + + if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0) + || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { + // More parsing here + ... + + if (afi && peer->afc[afi][safi]) { + struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); + + /* End-of-RIB received */ + if (!CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED)) { + ... + if (gr_info->eor_required == gr_info->eor_received) { + ... + /* Best path selection */ + if (bgp_best_path_select_defer( peer->bgp, afi, safi) < 0) + return BGP_Stop; + } + } + ... + } + } + ... + + return Receive_UPDATE_message; +} +``` + +Then, `bgpd` starts checking for better paths and updates its local routing table (RIB, Routing Information Base): + +```c +// File: src/sonic-frr/frr/bgpd/bgp_route.c +/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */ +int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_dest *dest; + int cnt = 0; + struct afi_safi_info *thread_info; + ... + + /* Process the route list */ + for (dest = bgp_table_top(bgp->rib[afi][safi]); + dest && bgp->gr_info[afi][safi].gr_deferred != 0; + dest = bgp_route_next(dest)) + { + ... + bgp_process_main_one(bgp, dest, afi, safi); + ... + } + ... + + return 0; +} + +static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi) +{ + struct bgp_path_info *new_select; + struct bgp_path_info *old_select; + struct bgp_path_info_pair old_and_new; + ... + + const struct prefix *p = bgp_dest_get_prefix(dest); + ... + + /* Best path selection. */ + bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); + old_select = old_and_new.old; + new_select = old_and_new.new; + ... + + /* FIB update. */ + if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) + && !bgp_option_check(BGP_OPT_NO_FIB)) { + + if (new_select && new_select->type == ZEBRA_ROUTE_BGP + && (new_select->sub_type == BGP_ROUTE_NORMAL + || new_select->sub_type == BGP_ROUTE_AGGREGATE + || new_select->sub_type == BGP_ROUTE_IMPORTED)) { + ... + + if (old_select && is_route_parent_evpn(old_select)) + bgp_zebra_withdraw(p, old_select, bgp, safi); + + bgp_zebra_announce(dest, p, new_select, bgp, afi, safi); + } else { + /* Withdraw the route from the kernel. */ + ... + } + } + + /* EVPN route injection and clean up */ + ... + + UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED); + return; +} +``` + +Finally, `bgp_zebra_announce` notifies `zebra` to update the kernel routing table through `zclient`. + +```c +// File: src/sonic-frr/frr/bgpd/bgp_zebra.c +void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi) +{ + ... + zclient_route_send(valid_nh_count ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, zclient, &api); +} +``` + +`zclient` communicates with `zebra` using a local socket and provides a series of callback functions to receive notifications from `zebra`. The key code is shown as follows: + +```c +// File: src/sonic-frr/frr/bgpd/bgp_zebra.c +void bgp_zebra_init(struct thread_master *master, unsigned short instance) +{ + zclient_num_connects = 0; + + /* Set default values. */ + zclient = zclient_new(master, &zclient_options_default); + zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs); + zclient->zebra_connected = bgp_zebra_connected; + zclient->router_id_update = bgp_router_id_update; + zclient->interface_add = bgp_interface_add; + zclient->interface_delete = bgp_interface_delete; + zclient->interface_address_add = bgp_interface_address_add; + ... +} + +int zclient_socket_connect(struct zclient *zclient) +{ + int sock; + int ret; + + sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0); + ... + + /* Connect to zebra. */ + ret = connect(sock, (struct sockaddr *)&zclient_addr, zclient_addr_len); + ... + + zclient->sock = sock; + return sock; +} +``` + +In the `bgpd` container, we can find the socket file used for `zebra` communication in the `/run/frr` directory for simple verification: + +```bash +root@7260cx3:/run/frr# ls -l +total 12 +... +srwx------ 1 frr frr 0 Jun 16 09:16 zserv.api +``` + +## `zebra` Updating Routing Table + +Since FRR supports many routing protocols, if each routing protocol updates kernel independently, conflicts will inevitably arise, because it is difficult to coordinate. Therefore, FRR uses a separate process to communicate with all routing protocol handling processes, merges the information, and then update the kernel routing table. This process is `zebra`. + +In `zebra`, kernel updates occur in a separate data plane handling thread: `dplane_thread`. All requests are sent to `zebra` through `zclient`, then get processed, and finally get forwarded to `dplane_thread` for handling. In whis way, the route update will always be in order, which avoids any conflicts to happen. + +When `zebra` starts, it registers all request handlers. When a request arrives, the corresponding handler will be called based on the request type. And here is the key code: + +```c +// File: src/sonic-frr/frr/zebra/zapi_msg.c +void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { + [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add, + [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, + [ZEBRA_INTERFACE_ADD] = zread_interface_add, + [ZEBRA_INTERFACE_DELETE] = zread_interface_delete, + [ZEBRA_ROUTE_ADD] = zread_route_add, + [ZEBRA_ROUTE_DELETE] = zread_route_del, + [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add, + [ZEBRA_REDISTRIBUTE_DELETE] = zebra_redistribute_delete, + ... +``` + +Take adding a route (`zread_route_add`) as an example to explain the later workflow. From the following code, we can see that when a new route arrives, `zebra` will start checking and updating its internal routing table: + +```c +// File: src/sonic-frr/frr/zebra/zapi_msg.c +static void zread_route_add(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct route_entry *re; + struct nexthop_group *ng = NULL; + struct nhg_hash_entry nhe; + ... + + // Decode zclient request + s = msg; + if (zapi_route_decode(s, &api) < 0) { + return; + } + ... + + // Allocate new route entry. + re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); + re->type = api.type; + re->instance = api.instance; + ... + + // Init nexthop entry, if we have an id, then add route. + if (!re->nhe_id) { + zebra_nhe_init(&nhe, afi, ng->nexthop); + nhe.nhg.nexthop = ng->nexthop; + nhe.backup_info = bnhg; + } + ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, re, &nhe); + + // Update stats. IPv6 is omitted here for simplicity. + if (ret > 0) client->v4_route_add_cnt++; + else if (ret < 0) client->v4_route_upd8_cnt++; +} + +// File: src/sonic-frr/frr/zebra/zebra_rib.c +int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nhg_hash_entry *re_nhe) +{ + struct nhg_hash_entry *nhe = NULL; + struct route_table *table; + struct route_node *rn; + int ret = 0; + ... + + /* Find table and nexthop entry */ + table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re->table); + if (re->nhe_id > 0) nhe = zebra_nhg_lookup_id(re->nhe_id); + else nhe = zebra_nhg_rib_find_nhe(re_nhe, afi); + + /* Attach the re to the nhe's nexthop group. */ + route_entry_update_nhe(re, nhe); + + /* Make it sure prefixlen is applied to the prefix. */ + /* Set default distance by route type. */ + ... + + /* Lookup route node.*/ + rn = srcdest_rnode_get(table, p, src_p); + ... + + /* If this route is kernel/connected route, notify the dataplane to update kernel route table. */ + if (RIB_SYSTEM_ROUTE(re)) { + dplane_sys_route_add(rn, re); + } + + /* Link new re to node. */ + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + rib_addnode(rn, re, 1); + + /* Clean up */ + ... + return ret; +} +``` + +Here, `rib_addnode` will forward this route add request to the rib processing thread, where the requests are being processed sequentially: + +```cpp +static void rib_addnode(struct route_node *rn, struct route_entry *re, int process) +{ + ... + rib_link(rn, re, process); +} + +static void rib_link(struct route_node *rn, struct route_entry *re, int process) +{ + rib_dest_t *dest = rib_dest_from_rnode(rn); + if (!dest) dest = zebra_rib_create_dest(rn); + re_list_add_head(&dest->routes, re); + ... + + if (process) rib_queue_add(rn); +} +``` + +Then, the request arrives at the RIB processing thread: `rib_process`, which further selects the best route and adds it to `zebra`'s internal routing table (RIB): + +```cpp +/* Core function for processing routing information base. */ +static void rib_process(struct route_node *rn) +{ + struct route_entry *re; + struct route_entry *next; + struct route_entry *old_selected = NULL; + struct route_entry *new_selected = NULL; + struct route_entry *old_fib = NULL; + struct route_entry *new_fib = NULL; + struct route_entry *best = NULL; + rib_dest_t *dest; + ... + + dest = rib_dest_from_rnode(rn); + old_fib = dest->selected_fib; + ... + + /* Check every route entry and select the best route. */ + RNODE_FOREACH_RE_SAFE (rn, re, next) { + ... + + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) { + best = rib_choose_best(new_fib, re); + if (new_fib && best != new_fib) + UNSET_FLAG(new_fib->status, ROUTE_ENTRY_CHANGED); + new_fib = best; + } else { + best = rib_choose_best(new_selected, re); + if (new_selected && best != new_selected) + UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED); + new_selected = best; + } + + if (best != re) + UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + } /* RNODE_FOREACH_RE */ + ... + + /* Update fib according to selection results */ + if (new_fib && old_fib) + rib_process_update_fib(zvrf, rn, old_fib, new_fib); + else if (new_fib) + rib_process_add_fib(zvrf, rn, new_fib); + else if (old_fib) + rib_process_del_fib(zvrf, rn, old_fib); + + /* Remove all RE entries queued for removal */ + /* Check if the dest can be deleted now. */ + ... +} +``` + +For new routes, `rib_process_add_fib` is called to add them to `zebra`'s internal routing table and notify the dplane to update the kernel routing table: + +```cpp +static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *new) +{ + hook_call(rib_update, rn, "new route selected"); + ... + + /* If labeled-unicast route, install transit LSP. */ + if (zebra_rib_labeled_unicast(new)) + zebra_mpls_lsp_install(zvrf, rn, new); + + rib_install_kernel(rn, new, NULL); + UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); +} + +void rib_install_kernel(struct route_node *rn, struct route_entry *re, + struct route_entry *old) +{ + struct rib_table_info *info = srcdest_rnode_table_info(rn); + enum zebra_dplane_result ret; + rib_dest_t *dest = rib_dest_from_rnode(rn); + ... + + /* Install the resolved nexthop object first. */ + zebra_nhg_install_kernel(re->nhe); + + /* If this is a replace to a new RE let the originator of the RE know that they've lost */ + if (old && (old != re) && (old->type != re->type)) + zsend_route_notify_owner(rn, old, ZAPI_ROUTE_BETTER_ADMIN_WON, info->afi, info->safi); + + /* Update fib selection */ + dest->selected_fib = re; + + /* Make sure we update the FPM any time we send new information to the kernel. */ + hook_call(rib_update, rn, "installing in kernel"); + + /* Send add or update */ + if (old) ret = dplane_route_update(rn, re, old); + else ret = dplane_route_add(rn, re); + ... +} +``` + +There are two important operations here: one is to call the `dplane_route_*` functions to update the kernel routing table, and the other is the `hook_call` that appears twice here. The FPM hook function is hooked here to receive and forward routing table update notifications. + +Here, let's look at them one by one: + +### `dplane` Updating Kernel Routing Table + +Let's look at the dplane `dplane_route_*` functions first. They are essentially do the same thing: simply pack the request and put it into the `dplane_thread` message queue: + +```c +// File: src/sonic-frr/frr/zebra/zebra_dplane.c +enum zebra_dplane_result dplane_route_add(struct route_node *rn, struct route_entry *re) { + return dplane_route_update_internal(rn, re, NULL, DPLANE_OP_ROUTE_INSTALL); +} + +enum zebra_dplane_result dplane_route_update(struct route_node *rn, struct route_entry *re, struct route_entry *old_re) { + return dplane_route_update_internal(rn, re, old_re, DPLANE_OP_ROUTE_UPDATE); +} + +enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, struct route_entry *re) { + return dplane_route_update_internal(rn, re, NULL, DPLANE_OP_SYS_ROUTE_ADD); +} + +static enum zebra_dplane_result +dplane_route_update_internal(struct route_node *rn, struct route_entry *re, struct route_entry *old_re, enum dplane_op_e op) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + + /* Create and init context */ + struct zebra_dplane_ctx *ctx = ...; + + /* Enqueue context for processing */ + ret = dplane_route_enqueue(ctx); + + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + + return result; +} +``` + +Then, on the data plane handling thread `dplane_thread`, in its message loop, it take messages from the queue one by one and call their handling functions: + +```c +// File: src/sonic-frr/frr/zebra/zebra_dplane.c +static int dplane_thread_loop(struct thread *event) +{ + ... + + while (prov) { + ... + + /* Process work here */ + (*prov->dp_fp)(prov); + + /* Check for zebra shutdown */ + /* Dequeue completed work from the provider */ + ... + + /* Locate next provider */ + DPLANE_LOCK(); + prov = TAILQ_NEXT(prov, dp_prov_link); + DPLANE_UNLOCK(); + } +} +``` + +By default, `dplane_thread` uses `kernel_dplane_process_func` to process the messages. Inside this function, different kernel operations will be invoked based on the request type: + +```c +static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) +{ + enum zebra_dplane_result res; + struct zebra_dplane_ctx *ctx; + int counter, limit; + limit = dplane_provider_get_work_limit(prov); + + for (counter = 0; counter < limit; counter++) { + ctx = dplane_provider_dequeue_in_ctx(prov); + if (ctx == NULL) break; + + /* A previous provider plugin may have asked to skip the kernel update. */ + if (dplane_ctx_is_skip_kernel(ctx)) { + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + goto skip_one; + } + + /* Dispatch to appropriate kernel-facing apis */ + switch (dplane_ctx_get_op(ctx)) { + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + res = kernel_dplane_route_update(ctx); + break; + ... + } + ... + } + ... +} + +static enum zebra_dplane_result +kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) +{ + enum zebra_dplane_result res; + /* Call into the synchronous kernel-facing code here */ + res = kernel_route_update(ctx); + return res; +} +``` + +And `kernel_route_update` is the real kernel operation. It notifies the kernel of route updates through netlink: + +```c +// File: src/sonic-frr/frr/zebra/rt_netlink.c +// Update or delete a prefix from the kernel, using info from a dataplane context. +enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) +{ + int cmd, ret; + const struct prefix *p = dplane_ctx_get_dest(ctx); + struct nexthop *nexthop; + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { + cmd = RTM_DELROUTE; + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { + cmd = RTM_NEWROUTE; + } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) { + cmd = RTM_NEWROUTE; + } + + if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) + ret = netlink_route_multipath(cmd, ctx); + ... + + return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +} + +// Routing table change via netlink interface, using a dataplane context object +static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) +{ + // Build netlink request. + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[NL_PKT_BUF_SIZE]; + } req; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); +    req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; +    ... + + /* Talk to netlink socket. */ + return netlink_talk_info(netlink_talk_filter, &req.n, dplane_ctx_get_ns(ctx), 0); +} +``` + +### FPM Route Update Forwarding + +FPM (Forwarding Plane Manager) is the protocol in FRR used to notify other processes of route changes. Its main logic code is in `src/sonic-frr/frr/zebra/zebra_fpm.c`. It supports two protocols by default: `protobuf` and `netlink`. The one used in SONiC is the `netlink` protocol. + +As mentioned earlier, it is implemented through hook functions. By listening for route changes in the RIB, the updates are forwarded to other processes through a local socket. This hook is registered at startup. And the most relevant one to us is the `rib_update` hook, as shown below: + +```c +static int zebra_fpm_module_init(void) +{ + hook_register(rib_update, zfpm_trigger_update); + hook_register(zebra_rmac_update, zfpm_trigger_rmac_update); + hook_register(frr_late_init, zfpm_init); + hook_register(frr_early_fini, zfpm_fini); + return 0; +} + +FRR_MODULE_SETUP(.name = "zebra_fpm", .version = FRR_VERSION, + .description = "zebra FPM (Forwarding Plane Manager) module", + .init = zebra_fpm_module_init, +); +``` + +When the `rib_update` hook is called, the `zfpm_trigger_update` function will be called, which puts the route update info into the fpm forwarding queue and triggers a write operation: + +```c +static int zfpm_trigger_update(struct route_node *rn, const char *reason) +{ + rib_dest_t *dest; + ... + + // Queue the update request + dest = rib_dest_from_rnode(rn); + SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM); + TAILQ_INSERT_TAIL(&zfpm_g->dest_q, dest, fpm_q_entries); + ... + + zfpm_write_on(); + return 0; +} + +static inline void zfpm_write_on(void) { + thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, &zfpm_g->t_write); +} +``` + +The write callback takes the update from the queue, converts it into the FPM message format, and forwards it to other processes through a local socket: + +```c +static int zfpm_write_cb(struct thread *thread) +{ + struct stream *s; + + do { + int bytes_to_write, bytes_written; + s = zfpm_g->obuf; + + // Convert route info to buffer here. + if (stream_empty(s)) zfpm_build_updates(); + + // Write to socket until we don' have anything to write or cannot write anymore (partial write). + bytes_to_write = stream_get_endp(s) - stream_get_getp(s); + bytes_written = write(zfpm_g->sock, stream_pnt(s), bytes_to_write); + ... + } while (1); + + if (zfpm_writes_pending()) zfpm_write_on(); + return 0; +} + +static void zfpm_build_updates(void) +{ + struct stream *s = zfpm_g->obuf; + do { + /* Stop processing the queues if zfpm_g->obuf is full or we do not have more updates to process */ + if (zfpm_build_mac_updates() == FPM_WRITE_STOP) break; + if (zfpm_build_route_updates() == FPM_WRITE_STOP) break; + } while (zfpm_updates_pending()); +} +``` + +At this point, FRR's work is done. + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Github repo: sonic-frr][SONiCFRR] +5. [Github repo: sonic-utilities][SONiCUtil] +6. [Github repo: sonic-sairedis][SONiCSAIRedis] +7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP] +8. [FRRouting][FRRouting] +9. [FRRouting - BGP][BGP] +10. [FRRouting - FPM][FPM] +11. [Understanding EVPN Pure Type 5 Routes][EVPN] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[SONiCFRR]: https://github.com/sonic-net/sonic-frr +[SONiCUtil]: https://github.com/sonic-net/sonic-utilities +[SONiCSAIRedis]: https://github.com/sonic-net/sonic-sairedis/ +[BGP]: https://datatracker.ietf.org/doc/html/rfc4271 +[FRRouting]: https://frrouting.org/ +[FPM]: https://docs.frrouting.org/projects/dev-guide/en/latest/fpm.html +[EVPN]: https://www.juniper.net/documentation/us/en/software/junos/evpn-vxlan/topics/concept/evpn-route-type5-understanding.html diff --git a/src/5-2-3-route-update-in-sonic.md b/src/5-2-3-route-update-in-sonic.md new file mode 100644 index 0000000..23a4f6c --- /dev/null +++ b/src/5-2-3-route-update-in-sonic.md @@ -0,0 +1,757 @@ +# Route Update in SONiC + +After the work of FRR is done, the route update information is forwarded to SONiC, either via Netlink or FPM. This causes a series of operations in SONiC, and eventually updates the route table in the ASIC. + +The main workflow is shown as below: + +```mermaid +sequenceDiagram + autonumber + participant K as Linux Kernel + box purple bgp Container + participant Z as zebra + participant FPM as fpmsyncd + end + box darkred database Container + participant R as Redis + end + box darkblue swss Container + participant OA as orchagent + end + box darkgreen syncd Container + participant SD as syncd + end + participant A as ASIC + + K->>FPM: Send notification via Netlink
when kernel route changes + Z->>FPM: Send route update notification
via FPM interface and Netlink
message format + + FPM->>R: Write route update information
to APPL_DB through ProducerStateTable + + R->>OA: Receive route update information
through ConsumerStateTable + + OA->>OA: Process route update information
and generate SAI route object + OA->>SD: Send SAI route object
to syncd through ProducerTable
or ZMQ + + SD->>R: Receive SAI route object, write to ASIC_DB + SD->>A: Configure ASIC through SAI interface +``` + +## `fpmsyncd` Updating Route Configuration in Redis + +First, let's start from the source. When `fpmsyncd` launches, it starts listening for FPM and Netlink events to receive route change messages and forward to `RouteSync` for processing: + +```cpp +// File: src/sonic-swss/fpmsyncd/fpmsyncd.cpp +int main(int argc, char **argv) +{ + ... + + DBConnector db("APPL_DB", 0); + RedisPipeline pipeline(&db); + RouteSync sync(&pipeline); + + // Register netlink message handler + NetLink netlink; + netlink.registerGroup(RTNLGRP_LINK); + + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); + + rtnl_route_read_protocol_names(DefaultRtProtoPath); + ... + + while (true) { + try { + // Launching FPM server and wait for zebra to connect. + FpmLink fpm(&sync); + ... + + fpm.accept(); + ... + } catch (FpmLink::FpmConnectionClosedException &e) { + // If connection is closed, keep retrying until it succeeds, before handling any other events. + cout << "Connection lost, reconnecting..." << endl; + } + ... + } +} +``` + +In `FpmLink`, the FPM events will be converted into Netlink messages. This unifies the message that being sent to `RouteSync` to Netlink. And `RouteSync::onMsg` will be called for processing them (for how Netlink receives and processes messages, please refer to [4.1.2 Netlink](./4-1-2-netlink.html)): + +One small thing to notice is that - EVPN Type 5 messages must be processed in raw message form, so `RouteSync::onMsgRaw` will be called. + +```cpp +// File: src/sonic-swss/fpmsyncd/fpmlink.cpp +// Called from: FpmLink::readData() +void FpmLink::processFpmMessage(fpm_msg_hdr_t* hdr) +{ + size_t msg_len = fpm_msg_len(hdr); + nlmsghdr *nl_hdr = (nlmsghdr *)fpm_msg_data(hdr); + ... + + /* Read all netlink messages inside FPM message */ + for (; NLMSG_OK (nl_hdr, msg_len); nl_hdr = NLMSG_NEXT(nl_hdr, msg_len)) + { + /* + * EVPN Type5 Add Routes need to be process in Raw mode as they contain + * RMAC, VLAN and L3VNI information. + * Where as all other route will be using rtnl api to extract information + * from the netlink msg. + */ + bool isRaw = isRawProcessing(nl_hdr); + + nl_msg *msg = nlmsg_convert(nl_hdr); + ... + nlmsg_set_proto(msg, NETLINK_ROUTE); + + if (isRaw) { + /* EVPN Type5 Add route processing */ + /* This will call into onRawMsg() */ + processRawMsg(nl_hdr); + } else { + /* This will call into onMsg() */ + NetDispatcher::getInstance().onNetlinkMessage(msg); + } + + nlmsg_free(msg); + } +} + +void FpmLink::processRawMsg(struct nlmsghdr *h) +{ + m_routesync->onMsgRaw(h); +}; +``` + +Next, when `RouteSync` receives a route change message, it makes judgments and dispatches in `onMsg` and `onMsgRaw`: + +```cpp +// File: src/sonic-swss/fpmsyncd/routesync.cpp +void RouteSync::onMsgRaw(struct nlmsghdr *h) +{ + if ((h->nlmsg_type != RTM_NEWROUTE) && (h->nlmsg_type != RTM_DELROUTE)) + return; + ... + onEvpnRouteMsg(h, len); +} + +void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) +{ + // Refill Netlink cache here + ... + + struct rtnl_route *route_obj = (struct rtnl_route *)obj; + auto family = rtnl_route_get_family(route_obj); + if (family == AF_MPLS) { + onLabelRouteMsg(nlmsg_type, obj); + return; + } + ... + + unsigned int master_index = rtnl_route_get_table(route_obj); + char master_name[IFNAMSIZ] = {0}; + if (master_index) { + /* If the master device name starts with VNET_PREFIX, it is a VNET route. + The VNET name is exactly the name of the associated master device. */ + getIfName(master_index, master_name, IFNAMSIZ); + if (string(master_name).find(VNET_PREFIX) == 0) { + onVnetRouteMsg(nlmsg_type, obj, string(master_name)); + } + + /* Otherwise, it is a regular route (include VRF route). */ + else { + onRouteMsg(nlmsg_type, obj, master_name); + } + } else { + onRouteMsg(nlmsg_type, obj, NULL); + } +} +``` + +From the code above, we can see that there are four different route processing entry points. These different routes will be finally written to different tables in `APPL_DB` through their respective [ProducerStateTable](./4-2-5-producer-consumer-state-table.md): + +| Route Type | Entry Point | Table | +| --- | --- | --- | +| MPLS | `onLabelRouteMsg` | LABLE_ROUTE_TABLE | +| Vnet VxLan Tunnel Route | `onVnetRouteMsg` | VNET_ROUTE_TUNNEL_TABLE | +| Other Vnet Routes | `onVnetRouteMsg` | VNET_ROUTE_TABLE | +| EVPN Type 5 | `onEvpnRouteMsg` | ROUTE_TABLE | +| Regular Routes | `onRouteMsg` | ROUTE_TABLE | + +Here we take regular routes as an example. The implementation of other functions is different, but the basic idea is the same: + +```cpp +// File: src/sonic-swss/fpmsyncd/routesync.cpp +void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char *vrf) +{ + // Parse route info from nl_object here. + ... + + // Get nexthop lists + string gw_list; + string intf_list; + string mpls_list; + getNextHopList(route_obj, gw_list, mpls_list, intf_list); + ... + + // Build route info here, including protocol, interface, next hops, MPLS, weights etc. + vector fvVector; + FieldValueTuple proto("protocol", proto_str); + FieldValueTuple gw("nexthop", gw_list); + ... + + fvVector.push_back(proto); + fvVector.push_back(gw); + ... + + // Push to ROUTE_TABLE via ProducerStateTable. + m_routeTable.set(destipprefix, fvVector); + SWSS_LOG_DEBUG("RouteTable set msg: %s %s %s %s", destipprefix, gw_list.c_str(), intf_list.c_str(), mpls_list.c_str()); + ... +} +``` + +## `orchagent` Processing Route Configuration Changes + +Next, these route information will come to `orchagent`. When `orchagent` starts, it creates `VNetRouteOrch` and `RouteOrch` objects, which are used to listen and process Vnet-related routes and EVPN/regular routes respectively: + +```cpp +// File: src/sonic-swss/orchagent/orchdaemon.cpp +bool OrchDaemon::init() +{ + ... + + vector vnet_tables = { APP_VNET_RT_TABLE_NAME, APP_VNET_RT_TUNNEL_TABLE_NAME }; + VNetRouteOrch *vnet_rt_orch = new VNetRouteOrch(m_applDb, vnet_tables, vnet_orch); + ... + + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + gRouteOrch = new RouteOrch(m_applDb, route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch, gSrv6Orch); + ... +} +``` + +The entry function that process the incoming messages for all Orch objects is `doTask`. `RouteOrch` and `VNetRouteOrch` are the same. Here we take `RouteOrch` as an example to see how it handles route changes. + +```admonish note +From `RouteOrch`, we can truly feel why these classes are named `Orch`. `RouteOrch` has more than 2500 lines, involving interactions with many other Orch objects and tons of details... The code is relatively difficult to read, so please be patient when reading. +``` + +Before we dive into the code, we have a few things to note for `RouteOrch`: + +- From the above `init` function, we can see that `RouteOrch` not only manages regular routes but also manages MPLS routes. The logic for handling these two types of routes is different. Therefore, in the following code, to simplify, we only show the logic for handling the regular routes. +- Since `ProducerStateTable` transmits and receives messages in batches, `RouteOrch` also processes the route updates in batches. To support batch processing, `RouteOrch` uses `EntityBulker gRouteBulker` to cache the SAI route objects that need to be changed, and then applies these route object changes to SAI at the end of the `doTask()` function. +- Route operations require a lot of other information, such as the status of each port, the status of each neighbor, the status of each VRF, etc. To obtain this information, `RouteOrch` interacts with other Orch objects, such as `PortOrch`, `NeighOrch`, `VRFOrch`, etc. + +Let's start with the `RouteOrch::doTask` function. It parses the incoming route operation messages, then calls the `addRoute` or `removeRoute` function to create or delete routes. + +```cpp +// File: src/sonic-swss/orchagent/routeorch.cpp +void RouteOrch::doTask(Consumer& consumer) +{ + // Calling PortOrch to make sure all ports are ready before processing route messages. + if (!gPortsOrch->allPortsReady()) { return; } + + // Call doLabelTask() instead, if the incoming messages are from MPLS messages. Otherwise, move on as regular routes. + ... + + /* Default handling is for ROUTE_TABLE (regular routes) */ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) { + // Add or remove routes with a route bulker + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + // Parse route operation from the incoming message here. + string key = kfvKey(t); + string op = kfvOp(t); + ... + + // resync application: + // - When routeorch receives 'resync' message (key = "resync", op = "SET"), it marks all current routes as dirty + // and waits for 'resync complete' message. For all newly received routes, if they match current dirty routes, + // it unmarks them dirty. + // - After receiving 'resync complete' (key = "resync", op != "SET") message, it creates all newly added routes + // and removes all dirty routes. + ... + + // Parsing VRF and IP prefix from the incoming message here. + ... + + // Process regular route operations. + if (op == SET_COMMAND) + { + // Parse and validate route attributes from the incoming message here. + string ips; + string aliases; + ... + + // If the nexthop_group is empty, create the next hop group key based on the IPs and aliases. + // Otherwise, get the key from the NhgOrch. The result will be stored in the "nhg" variable below. + NextHopGroupKey& nhg = ctx.nhg; + ... + if (nhg_index.empty()) + { + // Here the nexthop_group is empty, so we create the next hop group key based on the IPs and aliases. + ... + + string nhg_str = ""; + if (blackhole) { + nhg = NextHopGroupKey(); + } else if (srv6_nh == true) { + ... + nhg = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh); + } else if (overlay_nh == false) { + ... + nhg = NextHopGroupKey(nhg_str, weights); + } else { + ... + nhg = NextHopGroupKey(nhg_str, overlay_nh, srv6_nh); + } + } + else + { + // Here we have a nexthop_group, so we get the key from the NhgOrch. + const NhgBase& nh_group = getNhg(nhg_index); + nhg = nh_group.getNhgKey(); + ... + } + ... + + // Now we start to create the SAI route entry. + if (nhg.getSize() == 1 && nhg.hasIntfNextHop()) + { + // Skip certain routes, such as not valid, directly routes to tun0, linklocal or multicast routes, etc. + ... + + // Create SAI route entry in addRoute function. + if (addRoute(ctx, nhg)) it = consumer.m_toSync.erase(it); + else it++; + } + + /* + * Check if the route does not exist or needs to be updated or + * if the route is using a temporary next hop group owned by + * NhgOrch. + */ + else if (m_syncdRoutes.find(vrf_id) == m_syncdRoutes.end() || + m_syncdRoutes.at(vrf_id).find(ip_prefix) == m_syncdRoutes.at(vrf_id).end() || + m_syncdRoutes.at(vrf_id).at(ip_prefix) != RouteNhg(nhg, ctx.nhg_index) || + gRouteBulker.bulk_entry_pending_removal(route_entry) || + ctx.using_temp_nhg) + { + if (addRoute(ctx, nhg)) it = consumer.m_toSync.erase(it); + else it++; + } + ... + } + // Handle other ops, like DEL_COMMAND for route deletion, etc. + ... + } + + // Flush the route bulker, so routes will be written to syncd and ASIC + gRouteBulker.flush(); + + // Go through the bulker results. + // Handle SAI failures, update neighbors, counters, send notifications in add/removeRoutePost functions. + ... + + /* Remove next hop group if the reference count decreases to zero */ + ... + } +} +``` + +Here we take `addRoute` as an example. It mainly does a few things below: + +1. Get next hop information from `NeighOrch` and check if the next hop is really available. +2. If the route is a new or re-added back while waiting to be deleted, a new SAI route object will be created. +3. If it is an existing route, the existing SAI route object is updated. + +```cpp +// File: src/sonic-swss/orchagent/routeorch.cpp +bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) +{ + // Get nexthop information from NeighOrch. + // We also need to check PortOrch for inband port, IntfsOrch to ensure the related interface is created and etc. + ... + + // Start to sync the SAI route entry. + sai_route_entry_t route_entry; + route_entry.vr_id = vrf_id; + route_entry.switch_id = gSwitchId; + copy(route_entry.destination, ipPrefix); + + sai_attribute_t route_attr; + auto& object_statuses = ctx.object_statuses; + + // Create a new route entry in this case. + // + // In case the entry is already pending removal in the bulk, it would be removed from m_syncdRoutes during the bulk call. + // Therefore, such entries need to be re-created rather than set attribute. + if (it_route == m_syncdRoutes.at(vrf_id).end() || gRouteBulker.bulk_entry_pending_removal(route_entry)) { + if (blackhole) { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_DROP; + } else { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + route_attr.value.oid = next_hop_id; + } + + /* Default SAI_ROUTE_ATTR_PACKET_ACTION is SAI_PACKET_ACTION_FORWARD */ + object_statuses.emplace_back(); + sai_status_t status = gRouteBulker.create_entry(&object_statuses.back(), &route_entry, 1, &route_attr); + if (status == SAI_STATUS_ITEM_ALREADY_EXISTS) { + return false; + } + } + + // Update existing route entry in this case. + else { + // Set the packet action to forward when there was no next hop (dropped) and not pointing to blackhole. + if (it_route->second.nhg_key.getSize() == 0 && !blackhole) { + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + } + + // Only 1 case is listed here as an example. Other cases are handled with similar logic by calling set_entry_attributes as well. + ... + } + ... +} +``` + +After creating and setting up all the routes, `RouteOrch` calls `gRouteBulker.flush()` to write all the routes to `ASIC_DB`. The `flush()` function is straightforward: it processes all requests in batches, with each batch being 1000 by default, defined in `OrchDaemon` and passed through the constructor: + +```cpp +// File: src/sonic-swss/orchagent/orchdaemon.cpp +#define DEFAULT_MAX_BULK_SIZE 1000 +size_t gMaxBulkSize = DEFAULT_MAX_BULK_SIZE; + +// File: src/sonic-swss/orchagent/bulker.h +template +class EntityBulker +{ +public: + using Ts = SaiBulkerTraits; + using Te = typename Ts::entry_t; + ... + + void flush() + { + // Bulk remove entries + if (!removing_entries.empty()) { + // Split into batches of max_bulk_size, then call flush. Similar to creating_entries, so details are omitted. + std::vector rs; + ... + flush_removing_entries(rs); + removing_entries.clear(); + } + + // Bulk create entries + if (!creating_entries.empty()) { + // Split into batches of max_bulk_size, then call flush_creating_entries to call SAI batch create API to create + // the objects in batch. + std::vector rs; + std::vector tss; + std::vector cs; + + for (auto const& i: creating_entries) { + sai_object_id_t *pid = std::get<0>(i); + auto const& attrs = std::get<1>(i); + if (*pid == SAI_NULL_OBJECT_ID) { + rs.push_back(pid); + tss.push_back(attrs.data()); + cs.push_back((uint32_t)attrs.size()); + + // Batch create here. + if (rs.size() >= max_bulk_size) { + flush_creating_entries(rs, tss, cs); + } + } + } + + flush_creating_entries(rs, tss, cs); + creating_entries.clear(); + } + + // Bulk update existing entries + if (!setting_entries.empty()) { + // Split into batches of max_bulk_size, then call flush. Similar to creating_entries, so details are omitted. + std::vector rs; + std::vector ts; + std::vector status_vector; + ... + flush_setting_entries(rs, ts, status_vector); + setting_entries.clear(); + } + } + + sai_status_t flush_creating_entries( + _Inout_ std::vector &rs, + _Inout_ std::vector &tss, + _Inout_ std::vector &cs) + { + ... + + // Call SAI bulk create API + size_t count = rs.size(); + std::vector statuses(count); + sai_status_t status = (*create_entries)((uint32_t)count, rs.data(), cs.data(), tss.data() + , SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, statuses.data()); + + // Set results back to input entries and clean up the batch below. + for (size_t ir = 0; ir < count; ir++) { + auto& entry = rs[ir]; + sai_status_t *object_status = creating_entries[entry].second; + if (object_status) { + *object_status = statuses[ir]; + } + } + + rs.clear(); tss.clear(); cs.clear(); + return status; + } + + // flush_removing_entries and flush_setting_entries are similar to flush_creating_entries, so we omit them here. + ... +}; +``` + +## SAI Object Forwarding in `orchagent` + +At this point, you might have noticed something strange - The `EntityBulker` seems to be directly calling the SAI API. Shouldn't they be called in `syncd`? If we follow the SAI API objects passed to `EntityBulker`, we will even find that `sai_route_api_t` is indeed the SAI interface, and there is SAI initialization code in `orchagent`, as follows: + +```cpp +// File: src/sonic-sairedis/debian/libsaivs-dev/usr/include/sai/sairoute.h +/** + * @brief Router entry methods table retrieved with sai_api_query() + */ +typedef struct _sai_route_api_t +{ + sai_create_route_entry_fn create_route_entry; + sai_remove_route_entry_fn remove_route_entry; + sai_set_route_entry_attribute_fn set_route_entry_attribute; + sai_get_route_entry_attribute_fn get_route_entry_attribute; + + sai_bulk_create_route_entry_fn create_route_entries; + sai_bulk_remove_route_entry_fn remove_route_entries; + sai_bulk_set_route_entry_attribute_fn set_route_entries_attribute; + sai_bulk_get_route_entry_attribute_fn get_route_entries_attribute; +} sai_route_api_t; + +// File: src/sonic-swss/orchagent/saihelper.cpp +void initSaiApi() +{ + SWSS_LOG_ENTER(); + + if (ifstream(CONTEXT_CFG_FILE)) + { + SWSS_LOG_NOTICE("Context config file %s exists", CONTEXT_CFG_FILE); + gProfileMap[SAI_REDIS_KEY_CONTEXT_CONFIG] = CONTEXT_CFG_FILE; + } + + sai_api_initialize(0, (const sai_service_method_table_t *)&test_services); + sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api); + ... + sai_api_query(SAI_API_NEIGHBOR, (void **)&sai_neighbor_api); + sai_api_query(SAI_API_NEXT_HOP, (void **)&sai_next_hop_api); + sai_api_query(SAI_API_NEXT_HOP_GROUP, (void **)&sai_next_hop_group_api); + sai_api_query(SAI_API_ROUTE, (void **)&sai_route_api); + ... + + sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); + ... + sai_log_set(SAI_API_NEIGHBOR, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_NEXT_HOP, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_NEXT_HOP_GROUP, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_ROUTE, SAI_LOG_LEVEL_NOTICE); + ... +} +``` + +I believe whoever saw this code for the first time will definitely feel confused. But don't worry, this is actually the SAI object forwarding mechanism in `orchagent`. + +If you are familiar with RPC, the `proxy-stub` pattern might sounds very familar to you - using a unified way to define the interfaces called by both parties in communication, implementing message serialization and sending on the client side, and implementing message receiving, deserialization, and dispatching on the server side. Here, SONiC does something similar: using the SAI API itself as a unified interface, implementing message serialization and sending for `orchagent` to call, and implementing message receiving, deserialization, and dispatch functions in `syncd`. + +Here, the sending end is called `ClientSai`, implemented in `src/sonic-sairedis/lib/ClientSai.*`. Serialization and deserialization are implemented in SAI metadata: `src/sonic-sairedis/meta/sai_serialize.h`: + +```cpp +// File: src/sonic-sairedis/lib/ClientSai.h +namespace sairedis +{ + class ClientSai: + public sairedis::SaiInterface + { + ... + }; +} + +// File: src/sonic-sairedis/meta/sai_serialize.h +// Serialize +std::string sai_serialize_route_entry(_In_ const sai_route_entry_t &route_entry); +... + +// Deserialize +void sai_deserialize_route_entry(_In_ const std::string& s, _In_ sai_route_entry_t &route_entry); +... +``` + +When `orchagent` is compiled, it links to `libsairedis`, which implements the SAI client and handles the serialization and message sending: + +```makefile +# File: src/sonic-swss/orchagent/Makefile.am +orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta -lsaimetadata -lswsscommon -lzmq +``` + +Here, we use Bulk Create as an example to see how `ClientSai` serializes and sends the SAI API call: + +```cpp +// File: src/sonic-sairedis/lib/ClientSai.cpp +sai_status_t ClientSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + MUTEX(); + REDIS_CHECK_API_INITIALIZED(); + + std::vector serialized_object_ids; + + // Server is responsible for generate new OID but for that we need switch ID + // to be sent to server as well, so instead of sending empty oids we will + // send switch IDs + for (uint32_t idx = 0; idx < object_count; idx++) { + serialized_object_ids.emplace_back(sai_serialize_object_id(switch_id)); + } + auto status = bulkCreate(object_type, serialized_object_ids, attr_count, attr_list, mode, object_statuses); + + // Since user requested create, OID value was created remotely and it was returned in m_lastCreateOids + for (uint32_t idx = 0; idx < object_count; idx++) { + if (object_statuses[idx] == SAI_STATUS_SUCCESS) { + object_id[idx] = m_lastCreateOids.at(idx); + } else { + object_id[idx] = SAI_NULL_OBJECT_ID; + } + } + + return status; +} + +sai_status_t ClientSai::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ const std::vector &serialized_object_ids, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Inout_ sai_status_t *object_statuses) +{ + ... + + // Calling SAI serialize APIs to serialize all objects + std::string str_object_type = sai_serialize_object_type(object_type); + std::vector entries; + for (size_t idx = 0; idx < serialized_object_ids.size(); ++idx) { + auto entry = SaiAttributeList::serialize_attr_list(object_type, attr_count[idx], attr_list[idx], false); + if (entry.empty()) { + swss::FieldValueTuple null("NULL", "NULL"); + entry.push_back(null); + } + + std::string str_attr = Globals::joinFieldValues(entry); + swss::FieldValueTuple fvtNoStatus(serialized_object_ids[idx] , str_attr); + entries.push_back(fvtNoStatus); + } + std::string key = str_object_type + ":" + std::to_string(entries.size()); + + // Send to syncd via the communication channel. + m_communicationChannel->set(key, entries, REDIS_ASIC_STATE_COMMAND_BULK_CREATE); + + // Wait for response from syncd. + return waitForBulkResponse(SAI_COMMON_API_BULK_CREATE, (uint32_t)serialized_object_ids.size(), object_statuses); +} +``` + +Finally, `ClientSai` calls `m_communicationChannel->set()` to send the serialized SAI objects to `syncd`. This channel, before the 202106 version, was the [ProducerTable based on Redis](https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h). Possibly for efficiency reasons, starting from the 202111 version, this channel has been changed to [ZMQ](https://github.com/sonic-net/sonic-sairedis/blob/202111/lib/ZeroMQChannel.h). + +```cpp +// File: https://github.com/sonic-net/sonic-sairedis/blob/202106/lib/inc/RedisChannel.h +class RedisChannel: public Channel +{ + ... + + /** + * @brief Asic state channel. + * + * Used to sent commands like create/remove/set/get to syncd. + */ + std::shared_ptr m_asicState; + + ... +}; + +// File: src/sonic-sairedis/lib/ClientSai.cpp +sai_status_t ClientSai::initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) +{ + ... + + m_communicationChannel = std::make_shared( + cc->m_zmqEndpoint, + cc->m_zmqNtfEndpoint, + std::bind(&ClientSai::handleNotification, this, _1, _2, _3)); + + m_apiInitialized = true; + + return SAI_STATUS_SUCCESS; +} +``` + +For the inter-process communication, we are going to skip the details here. Please feel free to refer to the [Redis-based channels](./4-2-redis-based-channels.md) described in Chapter 4. + +## `syncd` Updating ASIC + +Finally, when the SAI objects are generated and sent to `syncd`, `syncd` will receive, processa and updates `ASIC_DB`, then finally updates the ASIC. We have already described this workflow in detail in the [Syncd-SAI Workflow](./5-1-syncd-and-sai.md), so we will skip them here. For more details, please refer to the [Syncd-SAI Workflow chapter](./5-1-syncd-and-sai.md). + +# References + +1. [SONiC Architecture][SONiCArch] +2. [Github repo: sonic-swss][SONiCSWSS] +3. [Github repo: sonic-swss-common][SONiCSWSSCommon] +4. [Github repo: sonic-frr][SONiCFRR] +5. [Github repo: sonic-utilities][SONiCUtil] +6. [Github repo: sonic-sairedis][SONiCSAIRedis] +7. [RFC 4271: A Border Gateway Protocol 4 (BGP-4)][BGP] +8. [FRRouting][FRRouting] +9. [FRRouting - BGP][BGP] +10. [FRRouting - FPM][FPM] +11. [Understanding EVPN Pure Type 5 Routes][EVPN] + +[SONiCArch]: https://github.com/sonic-net/SONiC/wiki/Architecture +[SONiCSWSS]: https://github.com/sonic-net/sonic-swss +[SONiCSWSSCommon]: https://github.com/sonic-net/sonic-swss-common +[SONiCFRR]: https://github.com/sonic-net/sonic-frr +[SONiCUtil]: https://github.com/sonic-net/sonic-utilities +[SONiCSAIRedis]: https://github.com/sonic-net/sonic-sairedis/ +[BGP]: https://datatracker.ietf.org/doc/html/rfc4271 +[FRRouting]: https://frrouting.org/ +[FPM]: https://docs.frrouting.org/projects/dev-guide/en/latest/fpm.html +[EVPN]: https://www.juniper.net/documentation/us/en/software/junos/evpn-vxlan/topics/concept/evpn-route-type5-understanding.html diff --git a/src/5-2-bgp.md b/src/5-2-bgp.md index 5b70e64..7a15612 100644 --- a/src/5-2-bgp.md +++ b/src/5-2-bgp.md @@ -1,14 +1,14 @@ # BGP -[BGP][BGP]可能是交换机里面最常用,最重要,或者线上使用的最多的功能了。这一节,我们就来深入的看一下BGP相关的工作流。 +[BGP][BGP] might be the most commonly used and important feature in switches. In this section, we take a deeper look at BGP-related workflows. -## BGP相关进程 +## BGP Processes -SONiC使用[FRRouting][FRRouting]作为BGP的实现,用于负责BGP的协议处理。FRRouting是一个开源的路由软件,支持多种路由协议,包括BGP,OSPF,IS-IS,RIP,PIM,LDP等等。当FRR发布新版本后,SONiC会将其同步到[SONiC的FRR实现仓库:sonic-frr][SONiCFRR]中,每一个版本都对应这一个分支,比如`frr/8.2`。 +SONiC uses [FRRouting][FRRouting] as its BGP implementation, responsible for handling the BGP protocol. FRRouting is an open-source routing software that supports multiple routing protocols, including BGP, OSPF, IS-IS, RIP, PIM, LDP, etc. When a new version of FRR is released, SONiC synchronizes it to the [SONiC FRR repository: sonic-frr][SONiCFRR], with each version corresponding to a branch such as `frr/8.2`. -FRR主要由两个大部分组成,第一个部分是各个协议的实现,这些进程的名字都叫做`*d`,而当它们收到路由更新的通知的时候,就会告诉第二个部分,也就是`zebra`进程,然后`zebra`进程会进行选路,并将最优的路由信息同步到kernel中,其主体结构如下图所示: +FRR mainly consists of two major parts. The first part includes the implementations of each protocol, where processes are named `*d.` When they receive routing update notifications, they inform the second part, the "zebra" process. The `zebra` process performs route selection and synchronizes the best routing information to the kernel. Its main structure is shown below: -``` +```text +----+ +----+ +-----+ +----+ +----+ +----+ +-----+ |bgpd| |ripd| |ospfd| |ldpd| |pbrd| |pimd| |.....| +----+ +----+ +-----+ +----+ +----+ +----+ +-----+ @@ -27,9 +27,9 @@ FRR主要由两个大部分组成,第一个部分是各个协议的实现, +-------------+ +------------------+ +-------------+ ``` -在SONiC中,这些FRR的进程都跑在`bgp`的容器中。另外,为了将FRR和Redis连接起来,SONiC在`bgp`容器中还会运行一个叫做`fpgsyncd`的进程(Forwarding Plane Manager syncd),它的主要功能是监听kernel的路由更新,然后将其同步到APP_DB中。但是因为这个进程不是FRR的一部分,所以它的实现被放在了[sonic-swss][SONiCSWSS]仓库中。 +In SONiC, these FRR processes all run inside the `bgp` container. In addition, to integrate FRR with Redis, SONiC runs a process called `fpmsyncd` (Forwarding Plane Manager syncd) within the `bgp` container. Its main function is to listen to kernel routing updates and synchronize them to the `APPL_DB`. Because it is not part of FRR, its implementation is located in the [sonic-swss][SONiCSWSS] repository. -# 参考资料 +# References 1. [SONiC Architecture][SONiCArch] 2. [Github repo: sonic-swss][SONiCSWSS] diff --git a/src/5-core-components.md b/src/5-core-components.md index 88c01ca..cc61bad 100644 --- a/src/5-core-components.md +++ b/src/5-core-components.md @@ -1,9 +1,9 @@ -# 核心组件解析 +# Core Components -这一章,我们会从代码的层面上来深入的分析一下SONiC中一些比较有代表性的核心组件和它们的工作流。 +In this chapter, we take a deeper look at some of the representative core components in SONiC and their workflows from a code perspective. ```admonish note -为了方便阅读和理解,所有的代码都只是列出了最核心的代码来展现流程,并不是完整的代码,如果需要查看完整代码,请参考[仓库中的原始代码](./3-1-code-repos.html)。 +For helping us to read and understand, all the code shown here will be simplified to its core part to illustrate the process. If you would like to read the full code, please refer to [the original code in the repository](./3-1-code-repos.html). -另外,每个代码块的开头都给出了相关文件的路径,其使用的是仓库均为SONiC的主仓库:[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage)。 -``` \ No newline at end of file +Additionally, the relevant file path of the code will be shared in the beginning of each code block, which is based on the SONiC's main repository: [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage). If the code is not imported by buildimage repo, the full URL will be provided. +``` diff --git a/src/6-1-cold-boot.md b/src/6-1-cold-boot.md index 676c8b3..9e57a8e 100644 --- a/src/6-1-cold-boot.md +++ b/src/6-1-cold-boot.md @@ -1 +1 @@ -# 冷启动 +# Cold Boot diff --git a/src/6-2-fast-boot.md b/src/6-2-fast-boot.md index d53a2ff..35671ab 100644 --- a/src/6-2-fast-boot.md +++ b/src/6-2-fast-boot.md @@ -1 +1 @@ -# 快速启动 +# Fast Boot diff --git a/src/6-3-warm-boot.md b/src/6-3-warm-boot.md index 252cf7a..f9ebf2f 100644 --- a/src/6-3-warm-boot.md +++ b/src/6-3-warm-boot.md @@ -1 +1 @@ -# 热启动 +# Warm Boot diff --git a/src/6-boot.md b/src/6-boot.md index 16f5c6b..cdf500d 100644 --- a/src/6-boot.md +++ b/src/6-boot.md @@ -1 +1 @@ -# 启动流程 +# Boot diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0c71636..62b1d51 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,36 +1,40 @@ # Summary -- [SONiC入门指南](./1-intro.md) - - [安装](./1-1-install.md) - - [虚拟测试环境](./1-2-hello-world-virtually.md) - - [常用命令 (WIP)](./1-3-command-cheatsheet.md) -- [核心组件简介](./2-core-components-intro.md) - - [Redis数据库](./2-1-database.md) - - [服务与工作流简介](./2-2-services-intro.md) - - [核心容器](./2-3-key-containers.md) +- [Getting Started with SONiC](./1-intro.md) + - [Installation](./1-1-install.md) + - [Hello World! Virtually](./1-2-hello-world-virtually.md) + - [Commands Cheatsheet (WIP)](./1-3-command-cheatsheet.md) +- [Core Components Introduction](./2-core-components-intro.md) + - [Redis Database](./2-1-database.md) + - [Services and Workflows](./2-2-services-intro.md) + - [Key Containers](./2-3-key-containers.md) - [SAI](./2-4-sai-intro.md) -- [开发上手指南](./3-dev-guide.md) - - [代码仓库](./3-1-code-repos.md) - - [编译](./3-2-compile.md) - - [测试 (WIP)](./3-3-testing.md) - - [调试 (WIP)](./3-4-debugging.md) - - [SAI调试 (WIP)](./3-4-sai-debugging.md) -- [通信机制](./4-communications.md) - - [与内核的通信]() - - [命令行调用](./4-1-1-exec.md) +- [Developer Guide](./3-dev-guide.md) + - [Code Repositories](./3-1-code-repos.md) + - [Build](./3-2-build.md) + - [Testing (WIP)](./3-3-testing.md) + - [Debugging (WIP)](./3-4-debugging.md) + - [SAI Debugging (WIP)](./3-4-1-sai-debugging.md) +- [Service Communication](./4-communications.md) + - [Communicate via Kernel](./4-1-communicate-via-kernel.md) + - [Command Line Invocation](./4-1-1-exec.md) - [Netlink](./4-1-2-netlink.md) - - [基于Redis的通信]() - - [Redis封装](./4-2-1-redis-wrappers.md) - - [通信层](./4-2-2-redis-messaging-layer.md) - - [基于ZMQ的通信 (WIP)](./4-3-zmq-messaging.md) - - [服务层封装 - Orch](./4-4-orch-layer.md) - - [事件分发和错误处理](./4-5-event-polling-and-error-handling.md) -- [核心组件解析](./5-core-components.md) - - [Syncd与SAI](./5-1-syncd-and-sai.md) + - [Redis-based Channels](./4-2-redis-based-channels.md) + - [Redis Wrappers](./4-2-1-redis-wrappers.md) + - [SubscribeStateTable](./4-2-2-subscribe-state-table.md) + - [NotificationProducer/Consumer](./4-2-3-notification-producer-consumer.md) + - [Producer/ConsumerTable](./4-2-4-producer-consumer-table.md) + - [Producer/ConsumerStateTable](./4-2-5-producer-consumer-state-table.md) + - [ZMQ-based Channels (WIP)](./4-3-zmq-based-channels.md) + - [Orch Layer](./4-4-orch-layer.md) + - [Event Polling and Error Handling](./4-5-event-polling-and-error-handling.md) +- [Core Components Deep Dive](./5-core-components.md) + - [Syncd and SAI](./5-1-syncd-and-sai.md) - [BGP](./5-2-bgp.md) - - [BGP命令实现](./5-2-1-bgp-command-impl.md) - - [BGP路由变更下发](./5-2-2-bgp-route-update-workflow.md) -- [启动流程 (WIP)](./6-boot.md) - - [冷启动 (WIP)](./6-1-cold-boot.md) - - [快速启动 (WIP)](./6-2-fast-boot.md) - - [热启动 (WIP)](./6-3-warm-boot.md) + - [BGP CLI and vtysh](./5-2-1-bgp-cli.md) + - [Route Update in FRR](./5-2-2-route-update-in-frr.md) + - [Route Update in SONiC](./5-2-3-route-update-in-sonic.md) +- [Boot Process (WIP)](./6-boot.md) + - [Cold Boot (WIP)](./6-1-cold-boot.md) + - [Fast Boot (WIP)](./6-2-fast-boot.md) + - [Warm Boot (WIP)](./6-3-warm-boot.md) diff --git a/theme/index.hbs b/theme/index.hbs index da41cff..ef4fcdb 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -144,8 +144,8 @@ {{#if print_enable}} @@ -326,8 +326,8 @@