SONiC 管理框架

1 特性简介

        管理框架是一个SONiC应用程序,负责提供各种常见的配置管理工具(如H3C的COMSH、SNMP、NETCONF、WEB等),以便管理SONiC交换机的配置和状态。该应用程序管理工具的协调,以提供一种一致的方式来验证、应用和显示配置。

1.1 需求

  • 必须提供以下支持:

    1. 标准的YANG模型(例如OpenConfig,IETF,IEEE)
    2. 定制YANG模型(SONiC YANG)
    3. CLI行业标准 CLI / 类似 Cisco 的 CLI
  • 必须提供对OpenAPI 规范的支持,以生成 REST 服务器端代码。

  • 必须为NBIs提供支持,例如:

    1. CLI
    2. gNMI 
    3. REST/RESTCONF 
  • 必须支持以下安全特性:

    1. 基于证书的身份验证
    2. 基于用户/密码的身份验证
    3. 基于角色的授权
  • 开发人员工作流程的易用性

    1. 指定数据模型并尽可能地自动生成
  • 必须支持验证和错误处理 - 数据模型、平台能力/规模、动态资源

  • SNMP在SONiC中的集成留待以后研究

1.2 设计概述

        管理框架利用golang编写的翻译库(Translib)将向管理客户端公开的数据模型转换为Redis ABNF模式格式。受支持的管理服务器可以利用Translib将传入的有效负载转换为SONiC ABNF模式,反之亦然,这取决于传入的请求。Translib将满足REST和gNMI服务器的需求。如果需要,Translib可以增强以支持其他管理服务器。该框架将支持标准和自定义YANG模型,以与相应的管理服务器进行通信。当从两个不同的管理服务器同时执行写入时,管理框架还将负责维护数据一致性。管理框架将提供一种机制来验证和授权任何传入的请求。管理框架还将负责在将请求写入Redis DB之前验证请求。Config Validation Library(CVL,配置验证库)用于基于Redis ABNF模式的YANG的ABNF JSON的语法和语义验证。

1.2.1 基本方法

  • 管理架构采取综合性方法:
    • 基于YANG标准的YANG模型和定制YANG
    • Open API 规范
    • 行业标准 CLI(特指思科)
    • 配置验证
  • REST 服务器,gNMI 服务器,App 模块和 Translib - 全部使用Go实现
  • 使用Translib库和app特定模块进行翻译
  • 使用YGOT进行编组和解组
  • 使用CAS(Check-and-Set)trans更新Redis(无锁定,无回滚)
  • 使用ABNF模式的YANG模型进行配置验证
  • KLISH框架的CLI

1.2.2 容器

管理框架被设计为在一个名为“sonic-mgmt-framework”的单一容器中运行。该容器包括与Translib链接的REST服务器和CLI进程。 gNMI支持需要gNMI服务器,该服务器是sonic-telemetry容器的一部分。我们想将这个容器重命名为sonic-gnmi容器,因为现在它可以通过gNMI服务器执行配置。 将引入一个新的容器sonic-mgmt-common,以托管在mgmt-framework和sonic-telemetry容器中使用的公共代码。这个新的仓库将编译成静态库,将在其他两个仓库中使用。这样sonic-telemetry仓库可以在没有mgmt-framework的情况下编译。

2 功能

2.1 目标部署用例

  1. 行业标准 CLI,它将使用 REST 客户端与相应的服务器进行通信,以发送和接收数据。
  2. REST客户端,用户可以通过它在支持的YANG路径上执行POST、PUT、PATCH、DELETE、GET操作。
  3. 支持基于支持的 YANG 模型的功能、获取、设置和订阅的 gNMI 客户端。

3 设计

3.1 概述

SONiC管理框架包括两个工作流:

  1. 构建时间流
  2. 运行时间流

如下面的架构图所示。

黄色部分是工具生成的代码(依托标准yang以及sonic yang)。

浅蓝色部分是需要我们手动写的。

3.1.1 构建时间流

开发人员首先定义所需的管理对象和访问API,以提供给目标应用程序。这可以通过以下两种方式之一来完成:

  1. YANG数据模型
  2. OpenAPI规范

这可以是基于应用程序的独立选择。然而,请注意,使用 YANG 可以实现更丰富的数据建模,从而实现更好的数据验证。

  1. 对于YANG:如果开发人员选择标准的YANG模型(Openconfig,IETF等),则必须基于Redis ABNF模式编写一个单独的SONiC YANG模型,以验证Redis配置,并且应该在标准YANG模型到Redis DB转换的偏差文件中编写transformer提示,反之亦然(详情请参阅3.2.2.7 Transformer)。然而,如果根据指南编写自定义SONiC YANG模型,则会自动从中派生出CVL YANG,并用于验证目的,而无需为transformer提示编写任何偏差文件。基于给定的YANG模型作为输入,pyang编译器生成相应的OpenAPI规范,该规范依次给OpenAPI生成器以生成REST客户端SDK和REST服务器存根。YANG数据模型还提供给YGOT生成器以创建YGOT绑定。这些绑定用于Translib和所选App模块之间的接口。具体来说,Translib基于传入的服务器有效负载填充绑定结构,而App模块相应地处理该结构。此外,对于不直接映射到SONiC YANG结构的数据模型,还必须提供一个YANG注释文件。在这种情况下,请求将被填充到YGOT结构中,并传递给App模块进行转换。App模块使用YANG注释来帮助将YANG对象转换和映射到DB对象,反之亦然。

  2. 在OpenAPI规范的情况下:它直接给OpenAPI生成器生成REST客户端SDK和REST服务器存根,在这种情况下,REST服务器负责验证传入的请求是否符合OpenAPI,然后将相同的请求给Translib,没有YANG,因此没有生成或处理YGOT绑定,因此Translib基础设施将使用路径和原始JSON调用App模块函数进行转换,为了配置验证的目的,SONiC YANG模型必须基于Redis ABNF模式编写。

3.1.2 运行时间流

3.1.2.1 CLI
  1. CLI使用KLISH框架提供一个CLI shell。CLI请求使用c转换为相应的REST客户端请求,并发送到REST服务器。
  2. OpenAPI 生成的 REST 服务器处理来自 CLI 的所有 REST 请求,并调用一个公共处理器来处理所有创建、更新、替换、删除和获取操作以及路径和有效负载。
  3. Translib API然后调用Common App模块中定义的相应处理器,随后调用带有YGOT结构化请求的Transformer函数,以执行模型到模型的转换,即Openconfig模型到SONiC模型,以生成请求数据库映射。
  4. 通用应用处理器将请求数据库映射传递给DB访问模块,以访问REDIS数据库,处理请求数据库映射。GET操作的响应被传递给Transformer,以执行到Openconfig模型的反向转换,并以JSON响应的形式连接到CLI。
  5. CLI命令的进一步处理将由Translib组件处理,将在后面的章节中进行更详细的讨论。
3.1.2.2 REST
  1. REST 客户端将使用 OpenAPI 生成的客户端 SDK 向 REST 服务器发送请求。
  2. 然后,流程就类似于 CLI 中看到的流程。
3.1.2.3 gNMI 

GNMI服务定义了一种基于gRPC的协议,用于从目标设备修改和检索配置,以及从目标设备到数据采集系统的遥测流的控制和生成。

  1. 现有的SONiC遥测框架已经扩展,以支持新的GNMI服务。
  2. 支持所有 4 个 GNMI 服务:Get、Set、Capabilities 和 Subscribe。
  3. 添加了一个新的 transl 数据客户端来处理传入的基于 YANG 的请求(标准或专有)
  4. 新的transl数据客户端依赖于Translib提供的基础设施来翻译、获取和设置YANG对象。

.文档后面提供了关于 GNMI 服务器、客户端和工作流的更多细节。

3.2 SONiC管理框架组件

管理框架组件可以分为

  1. 构建时间组件
  2. 运行时间组件
3.2.1 构建时间组件

下面是管理框架的构建时组件

  1. Pyang编译器(用于YANG到OpenAPI的转换)
  2. OpenAPI 生成器
  3. YGOT generator 
  4. pyang编译器(用于YANG到YIN的转换)
3.2.1.1 YANG到OpenAPI转换器
3.2.1.1.1 概述

开源的基于Python的YANG解析器pyang用于YANG解析和构建Python对象字典,开发了一个自定义插件将这个Python对象字典翻译成OpenAPI规范,该OpenAPI规范是3.0兼容的。

URI格式和有效负载是RESTCONF协议,基于RFC8040。请求和响应体在该版本中是JSON格式。

3.2.1.1.2 支持的 HTTP 谓词

下表列出了为不同类型的 YANG 节点生成的 HTTP 方法。

YANG node type HTTP methods 
Configuration dataPOST, PUT, PATCH, DELETE, GET, HEAD
Non configuration data GET, HEAD 
YANG RPC POST 
3.2.1.1.3 支持的数据节点

对于 YANG 模型中列出的每个数据关键字节点,将生成 OpenAPI(路径)

  • Container 
  • List 
  • Leaf
  • Leaf-list
  • Rpc
3.2.1.1.4 数据类型详细信息
YANG Type OpenAPI Type OpenAPI 
int8Integer
int16Integer 
int32Integer 
int64Integer 
uint8Integer 
uint16Integer 
uint32Integer 
uint64Integer 
decimal64Number 
String string 
Enum Enum 
Identityref String (Future can be Enum)String (Future 可以是 Enum)
longInteger 
Boolean Boolean 
Binary String with Format as Binary
bitsinteger 
  • 所有列表键在有效载荷和URI中都是强制的。
  • YANG 强制语句将映射到 OpenAPI 中所需的语句。
  • 默认值,枚举映射到OpenAPI的默认值和枚举语句
  • 由于OpenAPI规范没有提供定义unsigned类型的方法,因此引入了一个自定义的供应商扩展x-yang-type,它将包含叶片/叶片列表的实际yang数据类型。

Exampe below 

  /restconf/data/ietf-snmp:snmp/tlstm/cert-to-name={id}:
    put:
      tags:
      - ietf-snmp
      parameters:
      - name: id
        in: path
        required: true
        schema:
          format: int32
          maximum: 4294967295
          minimum: 0
          type: integer
          **x-yang-type: uint32**
  • 如上例所示,所有Integer类型都将有最大值和最小值字段。如果range语句在YANG节点上定义,那么最大值和最小值的值将来自range语句,否则将考虑由YANG指定的默认范围。还有一个自定义的供应商扩展,称为x-range,将包括range语句值的副本。如果一个类型有一个以上的range语句(可能间接使用typedef),所有的range语句都将被包括在x-range扩展中,并用“|”字符分开。

Exampe below 

    - format: int32
    **maximum: 2147483647**
    **minimum: 1**
    type: integer
    **x-range: 1..2147483647 | 4..10**
    x-yang-type: int32
  • 字符串数据类型将有一个供应商扩展x-pattern,其值将是一个正则表达式。这个字段只在YANG节点有它时出现。尽管OpenAPI 3.0在schema对象下有一个pattern字段,但它没有被使用,因为OpenAPI 3.0所需的正则表达式是W3C正则表达式,而Openconfig模型遵循POSIX风格的正则表达式。如果一个类型有更多的正则表达式语句(可能间接使用typedefs),所有的正则表达式语句都将被包括在x-pattern扩展的一部分中,它们将被AND在一起。

Example below 

    - maxLength: 18446744073709551615
    minLength: 0
    type: string
    **x-pattern: (([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\%[\p{N}\p{L}]+)?**
    x-yang-type: string

注意:在上面的代码片段中,% 前面多加了一个反斜杠。在 SONiC 中使用实际的 jinja2 代码时,请删除反斜杠。

  • 如上例所示,所有String类型都将有maxLength和minLength字段。如果length声明在YANG节点上定义,那么maxLength和minLength的值将来自length声明,否则将考虑YANG指定的默认长度。还有一个自定义的供应商扩展,称为x-length,将包括length声明的值的副本。如果一个类型有一个以上的length声明(可能间接使用typedef),所有的length声明将被包括为x-length扩展的一部分,由“|”字符分开。

  • 一个Union数据类型通过oneOf指令来支持,oneOf是openAPI 3.0支持的JSON模式指令,它接受一个对象数组,在任何给定的时间,只有一个项目可以被使用。

Example below 

    - name: security-model
    in: path
    required: true
    schema:
        oneOf:
        - enum:
        - v1
        - v2c
        - usm
        - tsm
        maxLength: 18446744073709551615
        minLength: 0
        type: string
        x-yang-type: string
        - format: int32
        maximum: 2147483647
        minimum: 1
        type: integer
        x-range: 1..2147483647
        x-yang-type: int32
3.2.1.1.5 特殊处理
  • 如果 YANG 节点定义了 max-elements 和 min-elements 语句,那么 list 和 leaf-list 将具有 maxItems 和 minItems 属性。

Example below 

    security-model:
        type: array
        items:
        oneOf:
        - enum:
            - v1
            - v2c
            - usm
            - tsm
            maxLength: 18446744073709551615
            minLength: 0
            type: string
            x-yang-type: string
        - format: int32
            maximum: 2147483647
            minimum: -2147483648
            type: integer
            x-range: 1..2147483647
            x-yang-type: int32
        minItems: 1
  • 使用oneOf指令支持选择case语句。

Example below 

    ietf-snmp:auth:
        type: object
        properties: {}
        **oneOf:**
        - type: object
        properties:
            md5:
            type: object
            properties:
                key:
                maxLength: 18446744073709551615
                minLength: 0
                type: string
                x-pattern: ([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?
                x-yang-type: string
            required:
            - key
        - type: object
        properties:
            sha:
            type: object
            properties:
                key:
                maxLength: 18446744073709551615
                minLength: 0
                type: string
                x-pattern: ([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?
                x-yang-type: string
            required:
            - key
3.2.1.1.6 未来的增强
  • 支持额外的YANG动作和通知(如果需要)。
  • 支持RESTCONF查询参数,如深度、过滤器等
  • 将 Yang 标识转换为枚举
3.2.1.2 OpenAPI生成器

OpenAPI-codegen 工具(github.com/OpenAPITools/openapi-generator)用于从 OpenAPI 定义生成 REST 服务器和客户端代码,它使用从 YANG 文件生成的 OpenAPI 定义和任何其他手动编写的 OpenAPI 定义文件。

REST服务器是用Go语言生成的,使用定制的OpenAPI-codegen模板来使每个服务器存根调用一个公共请求处理函数,公共请求处理函数将调用Translib API来服务请求。

REST客户端是用Python语言生成的,客户端应用可以使用标准的OpenAPI-codegen工具用任何语言生成REST客户端。

3.2.1.3 YGOT 生成器

YGOT生成器为管理YANG生成Go绑定结构,Translib使用生成的Go绑定结构来验证传入的有效负载,并帮助将Redis数据转换为管理YANG特定的JSON输出有效负载。

3.2.1.4 pyang compiler 

开源的pyang工具用于编译YANG模型并生成所需格式的输出文件。例如,编译SONiC YANG模型并生成YIN模式文件以进行验证。同样,在编译YANG模型后,生成OpenAPI规格文件,以使用OpenAPI-codegen生成REST客户端SDK和服务器代码。

3.2.2 运行时组件

下面是管理框架中的运行时组件

  1. CLI
  2. REST客户端 SDK
  3. REST 服务器
  4. gNMI 客户端
  5. gNMI 服务器
  6. Translib
  7. 配置验证库(CVL)
  8. Redis DB
  9. Non DB data provider 
3.2.2.1 CLI

开源Klish集成到sonic-mgmt-framework容器中,提供命令行接口工具,以在SONiC中更高效地执行网络操作。Klish将提供命令解析、语法验证、命令帮助和命令自动完成的核心功能。下图显示了CLI命令是如何构建、处理和执行的。

  1. 用户输入CLI命令
  2. 调用命令标签中定义的动作器。
  3. Actioner调用sub shell或内置方法,使用基于C的libcurl或Python Requests包对REST服务器进行REST客户端API调用。
  4. 从OpenAPI clientAPI接收响应并将其传递给渲染脚本。
  5. 渲染器脚本处理来自 REST 客户端的 JSON 响应,并可选地使用 Jinja 模板格式化输出。
  6. CLI 输出被呈现到终端。
3.2.2.1.1 CLI组件

CLI 由以下组件组成。

  • Open source Klish - CLI解析框架,支持命令行接口Shell
  • 用XML文件定义CLI命令行选项和操作。
    • Klish使用XML定义CLI命令来构建命令树。Klish提供模块化的CLI树配置,以从多个XML文件构建命令树。XML元素可以用宏和实体引用定义,然后通过实用程序脚本进行预处理,以生成最终由Klish使用的扩展XML文件。
  • Actioner - 定义为命令  action  的 Python 脚本或内置方法,用于形成请求体并调用 OpenAPI 客户端 API
  • Renderer - 使用 Jinja 模板定义的 Python 脚本。接收来自 Swagger API 的 JSON 响应,并使用 jinja2 模板文件来渲染和格式化 CLI 输出。
  • Preprocess scripts  - 验证 XML 文件,并从开发人员友好的表单中应用一些处理到与 Klish 兼容的“原始”表单中。
3.2.2.1.2 XML文件的预处理

在构建时执行多个脚本来预处理特殊的XML标签/属性 - MACRO替换,添加管道处理等 - 并以Klish可消费的形式生成目标XML文件。

在编译过程中,XML文件也需要验证。根据 sonic-clish.xsd 中定义的详细模式,在进行宏替换和管道处理之后,使用 xmllint 验证所有处理过的XML文件。一旦XML文件完全验证和预处理完毕,目标XML文件就会生成到文件夹 ${workspace}/sonic-mgmt-framework/build/cli/target/command-tree 中。

The following preprocessing scripts are introduced:下面介绍一些预处理脚本:

  • klish_ins_def_cmd.py - 将“exit”和“end”命令附加到 Klish XML 文件的视图中
  • klish_insert_pipe.py  - 扩展每个show并使用pipe选项获取COMMAND
  • klish_platform_features_process.sh  - 验证所有平台 XML 文件。生成 entity.XML 文件。
  • klish_replace_macro.py  - 在 Klish XML 文件上执行宏替换
3.2.2.1.3 宏

有一些CLI命令可以有相同的选项集,其中XML标签集需要在所有这些CLI命令中重复。宏可以用来避免这种重复,并将选项保存在一个地方,这样就有可能在多个命令定义中引用一个宏。在某些情况下,我们可能需要在这些宏选项的值中使用变量。在这种情况下,也可以将这些XML属性作为参数传递给宏,并在宏定义中替换这些值。宏定义被称为 <MACRO name="name of macro"> 和 </MACRO> 标签。 klish_replace_macro.py 用于在编译时处理宏,以扩展对目标XML文件的引用。 宏定义文件位于 ${workspace}/sonic-mgmt-framework/src/CLI/clitree/macro 文件夹中。

Example: 例子:

Before macro substitution:在宏替换之前:

<VIEW name="configure-if-view">
    <!-- ip access-group -->
    <COMMAND
         name="ip access-group"
         help="Specify access control for packets"
         >
    <MACRO name="ACG-OPTIONS" arg=""></MACRO>
       ...
    </COMMAND>

After macro substitution:宏替换后:

<VIEW name="configure-if-view">
    <!-- ip access-group -->
    <COMMAND name="ip access-group" help="Specify access control for packets">
      <PARAM name="access-list-name" help="Name of access-list (Max size 140)" ptype="STRING"/>
      <PARAM name="direction-switch" help="Configure the direction to apply the access list" ptype="SUBCOMMAND" mode="switch">
        <PARAM name="in" help="Apply access-list in incoming direction" ptype="SUBCOMMAND" mode="subcommand"/>
        <PARAM name="out" help="Apply access-list in outgoing direction" ptype="SUBCOMMAND" mode="subcommand"/>
      </PARAM>
        ...
    </COMMAND>
3.2.2.1.4 实体

XML文件可以包含一个引用预定义值的ENTITY。Entity通常用于定义特定于平台的值,并由 klish_platform_features_process.sh 处理,以将ENTITY值前置到目标XML文件中。默认情况下,有一个名为 platform_dummy.XML 的默认文件,它定义了一个平台默认的ENTITY列表。请注意,特定于平台目前还不支持。

Example: platform_dummy.XML示例: platform_dummy.XML

<ENTITYLIST>
  <ENTITYNAME value="1">START_PORT_ID</ENTITYNAME>
  <ENTITYNAME value="32">MAX_PORT_ID</ENTITYNAME>
  <ENTITYNAME value="1">START_SUB_PORT_ID</ENTITYNAME>
  <ENTITYNAME value="4">MAX_SUB_PORT_ID</ENTITYNAME>
  <ENTITYNAME value="9276">MAX_MTU</ENTITYNAME>
 </ENTITYLIST>

The ENTITY name can be referenced in command definitions. For example, PTYPE for RANGE_MTU, used for interface commands:ENTITY 名称可以在命令定义中引用,例如,PTYPE 为 RANGE_MTU,用于接口命令:

<PTYPE
  help=""
	method="integer"
  name="RANGE_MTU"
	pattern="1312..&MAX_MTU;"
/>
3.2.2.1.5 Actioner 

Actioner 用于将 CLI 命令转换为目标资源上相应的 OpenAPI 客户端请求。它在 XML 文件中使用  <ACTION>  标签定义。Actioner 中有三种不同的方法:sub-shell、内置的 clish_restcl 和 clish_pyobj。CLI 内置函数“clish_pyobj”和“clish_restcl”可以通过消除子shell解释器开销来减少执行命令的时间。

  1. sonic_cli_<module_name>.py生成一个解释 Python 脚本的子 shell Klish 生成一个解释命令的  <ACTION>  标签中定义的 Python 脚本的子 shell。 这个子 shell 运行包装器脚本  sonic_cli_<module_name>.py
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>	sonic_cli_<module_name>.py <OpenAPI client method> [parameters . . .]
</code></span></span></span></span>

 sonic_cli_<module_name>.py  有一个调度函数,用于调用 OpenAPI 客户端方法,参数从用户输入传递。

Example: 例子:

<VIEW name="configure-if-view">
    <!-- ip access-group -->
    <COMMAND
         name="ip access-group"
         help="Specify access control for packets"
         >
    <MACRO name="ACG-OPTIONS" arg=""></MACRO>
    <ACTION>
        if test "${direction-switch}" = "in"; then
            Python $SONIC_CLI_ROOT/target/sonic-cli.py post_list_base_interfaces_interface ${access-list-name} ACL_IPV4 ${iface} ingress
        else
            Python $SONIC_CLI_ROOT/target/sonic-cli.py post_list_base_interfaces_interface ${access-list-name} ACL_IPV4 ${iface} egress
        fi
    </ACTION>
    </COMMAND>
    ...

调用内置函数clish_restcl,使用libcurl进行REST客户端调用 这个内置函数使用libcurl连接到REST服务器 ACTION标签参数的格式: oper= url= body={..} oper可以是PUT/POST/PATCH/DELETE body是可选的。 由于我们目前不使用jinja模板处理渲染,所以不支持oper GET。 示例:

<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code><ACTION builtin="clish_restcl">oper=PATCH url=/restconf/data/openconfig-interfaces:interfaces/interface=Vlan${vlan-id}/config body={"openconfig-interfaces:config": {"name": "Vlan${vlan-id}"}}</ACTION>
</code></span></span></span></span>
调用内置函数clish_pyobj,使用嵌入式Python进行REST客户端调用 这个内置函数使用嵌入式Python方法。

ACTION标签参数的格式: (是没有扩展名的动作脚本。名称不能包含“-”)

Example: 例子:

<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code><ACTION builtin="clish_pyobj">sonic_cli_if patch_openconfig_interfaces_interfaces_interface_config Vlan${vlan-id}</ACTION>
</code></span></span></span></span>
3.2.2.1.6 Renderer scripts

动作脚本接收来自 OpenAPI 客户端 API 的 JSON 输出并调用渲染脚本,渲染脚本将 JSON 响应发送到 jinja2 模板文件以解析响应并生成 CLI 输出。

Example: "show acl" 例如:show acl

<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>{\% set acl_sets = acl_out['openconfig_aclacl']['acl_sets']['acl_set'] \%}
   {\% for acl_set in acl_sets \%}
       Name:  {{ acl_set['state']['description'] }}
   {\% endfor \%}
</code></span></span></span></span>

NOTE: An extra backslash is added in front of % in the above code snippet. Remove the backslash while using the actual jinja2 code in SONiC.注意:在上面的代码片段中,% 前面多加了一个反斜杠。在 SONiC 中使用实际的 jinja2 代码时,请删除反斜杠。

3.2.2.1.7 CLI 的通用 REST 客户端

Actioner脚本可以使用通用REST客户端cli_client.py与REST服务器通信。 它是OpenAPI客户端SDK的替代品。 这个客户端是量身定制的,用于连接本地REST服务器并处理CLI动作器用例。

3.2.2.1.8 工作流(添加新的 CLI)

当要添加新的 CLI 时,需要遵循以下步骤。

  1. 创建一个 XML 文件,定义 CLI 命令和命令所需的参数。
  2. 定义要显示的 CLI 帮助字符串和参数的数据类型. 新的参数类型 (PTYPES), 宏和实体可以在 XML 文件中定义和使用. 有效的 XML 标签在  sonic-clish.xsd  文件中定义.
  3. 将动作添加到  <ACTION>  标签中,以便使用 OpenAPI 客户端方法名和参数运行包装器脚本,或者使用通用的 REST 客户端与 rest-server 通信。
  4. 将代码添加到包装器脚本中,以在  generate_body()  中构造有效负载并处理响应。
  5. 对于'show' 命令,创建一个 Jinja 模板来格式化输出。
3.2.2.2 REST Client SDK

框架提供OpenAPI-codegen生成的Python客户端SDK。开发人员可以根据需要从OpenAPI定义生成其他编程语言的客户端SDK代码。

客户端应用程序可以使用 OpenAPI 生成的客户端 SDK 或任何其他 REST 客户端工具与 REST 服务器通信。

3.2.2.3 gNMI客户端

SONiC Telemetry服务提供gNMI服务器,而客户端必须由用户提供。gNMI通常用作编程接口,因此通常使用各自的gRPC库(https://github.com/grpc/grpc)从编程语言环境中直接调用。为了测试和脚本目的,还提供了几个CLI程序。

采用了google开发的gnmi客户端,并由JapanYANG进行修(github.com/jipanYANG/gnxi/gnmi_get, github.com/jipanYANG/gnxi/gnmi_set), 并进一步修改以支持新功能。还采用了openconfig的gnmi_cli(https://github.com/openconfig/gnmi/tree/master/cmd/gnmi_cli)来测试订阅和能力操作。最后,开发了一个新的客户端gnoi_client,用于测试gNOI RPC操作。

注意:尽管 gRPC 协议允许使用多种编码,但我们的使用仅限于 JSON_IETF 编码。

Supported RPC Operations:支持的 RPC 操作:
  • Get:获取一个或多个路径,并在GetResponse中返回值。
  • Set: 更新、替换或删除对象
    • Update: 一个或多个要更新的对象的列表。
    • Replace: 替换现有对象的一个或多个对象的列表,任何未指定的字段将默认为空。
    • Delete: 一个或多个要删除的对象路径的列表。
  • 功能:返回gNMI版本和支持的模型和模型版本列表。
    • 一个gNMI扩展字段也被添加了,以返回SONiC模型的“bundle版本”。
  • 订阅:
    • 使用流或轮询订阅路径,或基于一次订阅,仅使用完整的当前状态或更新的值。
      • Once: 获取单个订阅消息。
      • Poll: 从客户端获取每个投票请求的订阅消息。
      • Stream:  为每个对象更新(称为 ON_CHANGE 模式)获取一个订阅消息,或者在使用样本模式时,在每个样本间隔获取一个订阅消息。target_defined 使用为特定对象预配置的值。由于性能考虑,并非所有路径都支持 ON_CHANGE 模式,而所有路径都支持样本模式,因此目标定义也支持样本模式,因为如果 ON_CHANGE 不支持,它将默认为样本。
Example Client Operations:示例客户操作:

使用开源客户端,这些是示例客户端操作。.json测试有效负载文件在这里:https://github.com/project-arlo/sonic-mgmt-common/tree/master/src/Translib/test

Get: 

./gnmi_get -xpath /openconfig-acl:acl/interfaces -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty

Set:
Replace: 
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>`./gnmi_set -replace /openconfig-acl:acl/:@./test/01_create_MyACL1_MyACL2.json -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty`
</code></span></span></span></span>
Delete: 
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>`./gnmi_set -delete /openconfig-acl:acl/ -target_addr 127.0.0.1:8080 -insecure`
</code></span></span></span></span>
Subscribe:

Streaming sample based: 

./gnmi_cli -insecure -logtostderr -address 127.0.0.1:8080 -query_type s -streaming_sample_interval 3 -streaming_type SAMPLE -q /openconfig-acl:acl/ -v 0 -target YANG

Poll based:

./gnmi_cli -insecure -logtostderr -address 127.0.0.1:8080 -query_type p -polling_interval 1s -count 5 -q /openconfig-acl:acl/ -v 0 -target YANG

Once based:

./gnmi_cli -insecure -logtostderr -address 127.0.0.1:8080 -query_type o -q /openconfig-acl:acl/ -v 0 -target YANG

gNOI:
gNOI(gRPC Network Operations Interface)扩展了gNMI服务器,增加了新的自定义RPC来执行交换机的管理功能。
gNOI Clear Neighbor:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>gnoi_client -module Sonic -rpc clearNeighbors -jsonin '{"sonic-neighbor:input": {"force": true, "ip": "4.4.4.1"}}' -insecure
</code></span></span></span></span>
3.2.2.4 REST Server 

REST 管理服务器是一个用 Go 语言实现的 HTTP 服务器。 它支持以下操作:

  • RESTCONF APIs for YANG data
  • 用于手动定义OpenAPI的REST APIs
3.2.2.4.1 Transport options

REST服务器只支持HTTPS传输,默认监听端口443。 服务器端口可以通过ConfigDB REST_SERVER表中的条目进行更改。

默认情况下,使用临时的自签名证书作为TLS服务器证书。 可以通过REST_SERVER表指定有效的TLS私钥和证书文件路径来重写它。

表模式在 DB Schema 部分中描述。

3.2.2.4.2 Translib linking

REST服务器将静态链接到Translib。对于每个REST请求,服务器 调用Translib API,然后调用适当的应用模块来处理请求。 下面是HTTP操作到Translib API的映射:

HTTP Method HTTP方法Translib API Translib 接口Request data 请求数据Response data 响应数据
GET translib.Get translibpath status, payload 
POST translib.Create translibpath, payload status 
POST (for YANG RPC) translib.Action translibpath, payload status, payload 
PATCH translib.Update translibpath, payload status 
PUT translib.Replace translibpath, payloadstatus
DELETEtranslib.Delete translibpath status
HEAD translib.Get translibpathstatus, (payload ignored)status,
OPTIONS ---

更多关于Translib API的细节请参见3.2.2.6节。

REST OPTIONS 请求不调用任何 Translib API。它们在“Allow”响应头中返回请求路径支持的 HTTP 方法列表。如果支持 PATCH 方法,则响应 还将包含一个“Accept-Patch”响应头,其值为“application/yang-data json”。

3.2.2.4.3 Media Types 

YANG定义的RESTCONF API支持application/yang-data json媒体类型。 请求和响应有效载荷遵循RFC7951定义的编码规则。媒体类型application/yang-data xml在第一个版本中不支持。

OpenAPI定义的REST API可以使用任何媒体类型,这取决于应用模块的实现。然而,REST服务器不支持内容类型协商。 一个REST API不应该被设计为消费或生产多种内容类型。 每个REST API的OpenAPI定义应该在其“consumes”和“produces”语句中最多有一个锥形媒体类型。

3.2.2.4.4 有效载荷验证

REST服务器不会验证YANG定义的RESTCONF API的请求有效载荷。 有效载荷在加载到YGOT绑定时会在较低的层自动验证。

对于OpenAPI定义的REST API,REST服务器将提供有限的有效负载验证。JSON请求有效负载(内容类型application/json)将根据OpenAPI定义的模式进行验证。响应数据和非JSON请求数据不会由REST服务器进行验证——这是留给App模块的。

3.2.2.4.5 并发

REST服务器将接受并发请求。Translib提供适当的锁定机制——并行读取和顺序写入。

3.2.2.4.6 API 版本控制

REST 服务器允许客户端通过自定义的 HTTP 头 Accept-Version 指定 API 版本。

<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#24292f"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>Accept-Version: 1.0.3
</code></span></span></span></span>

 explains Translib version checking logic.版本文本应该以MAJOR.MINOR.PATCH格式。REST服务器将从请求头部提取版本文本,并将其作为ClientVersion参数传递给Translib API。 3.2.2.6.4.2节解释了Translib版本检查逻辑。

如果请求中没有 Accept-Version 头,则绕过版本检查。

对于YANG定义的RESTCONF API,服务器的版本可以通过标准YANG模块库API“GET /restconf/data/ietf-yang-library:modules-state/module-set-id”来发现。 客户端不应该为这个API传递Accept-Version头部!这是安全的,因为module-set-id是RFC7895为此目的定义的标准API。

在这个版本中,OpenAPI手动定义不支持API版本控制。 即使指定了Accept-Version头部值,也会被忽略。

3.2.2.4.7 RESTCONF Entity-tag

REST服务器不维护配置数据节点的实体标记和最后修改时间戳。 GET和HEAD响应不包括“ETag”和“Last-Modified”头。也不支持RFC7232风格的HTTP条件请求。 REST服务器会忽略“If-Match”、“If-Modified-Since”之类的条件请求头。

3.2.2.4.8 RESTCONF Discovery

REST服务器支持以下API,以便客户机发现各种RESTCONF协议特性,如RFC8040所述。

Method 方法Path 路径Purpose 目的
GET /.well-known/host-metaRESTCONF root path 
GET /restconf/yang-library-versionYANG library version
GET /restconf/data/ietf-yang-library:modules-stateRFC7895 YANG module library
GET /restconf/data/ietf-restconf-monitoring:restconf-state/capabilitiesRESTCONF Capabilities 
GET /models/yang/{filename}YANG download 
3.2.2.4.8.1 RESTCONF root

服务器支持通过“GET /.well-known/host-meta”API 发现RESTCONF根目录,如RFC8040,第3.1节所述。 RESTCONF根目录是“/restconf”。

3.2.2.4.8.2 Yang 库版本

REST服务器支持“GET /restconf/yang-library-version”API来通知YANG库版本。 响应将表明版本“2016-06-21”,如RFC8040,第3.3.3节所述。 这通知服务器支持符合RFC7895的YANG库操作。

3.2.2.4.8.3 YANG模块库

REST服务器允许客户端通过“GET /restconf/data/ietf-yang-library:modules-state”API发现并下载服务器支持的所有YANG模块。响应数据包括每个RFC7895要求的YANG模块信息。

REST服务器允许客户端通过“GET /models/yang/{ filename} ”API下载YANG文件。 YANG模块库响应包括每个YANG模块条目的完整下载URL。

注意:RFC7895最近被RFC8525废弃了。 它支持带有多个数据存储和数据存储模式的YANG库。然而这些功能在SONiC中不可用。因此REST服务器(目前)没有实现RFC8525 YANG库API。

3.2.2.4.8.4 RESTCONF 功能

REST服务器支持“GET /restconf/data/ietf-restconf-monitoring:restconf-state/capabilities”API来发布其功能,如RFC8040章节9.1所述。 响应包括以下提到的功能信息。

urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=report-all
3.2.2.4.9 RESTCONF 查询参数

RESTCONF查询参数将在未来的版本中得到支持。在当前版本中,REST服务器将忽略所有查询参数。

3.2.2.4.10 RESTCONF 操作

REST服务器支持通过“POST /restconf/operations/{ rpc_name} ”API调用YANG RPC, 如RFC8040,第4.4.2节所述。 在这个版本中不支持通过YANG v1.1动作语句建模的操作。

YANG RPC 输入和输出数据模型无法使用 YGOT 绑定对象。 因此,REST 服务器或 Translib 不会对这些 API 执行有效负载验证。 应用程序模块将接收原始 JSON 数据。

3.2.2.4.11 RESTCONF Notifications

框架不支持RESTCONF通知,客户端可以使用gNMI进行监控和通知。

3.2.2.4.12 身份验证

RREST 服务器支持以下身份验证模式。

  • HTTP基本认证(用户名/密码认证)
  • 使用JSON Web Token(JWT)格式的HTTP Bearer令牌身份验证。
  • TLS证书认证
  • 以上三种模式的任意组合
  • 没有认证

详细信息见SONiC RBAC HLD。

默认情况下,HTTP Baisc 和 bearer 令牌身份验证模式是启用的。 可以通过 ConfigDB REST_SERVER 表项覆盖它。

3.2.2.4.13 错误响应

当请求处理失败时,REST服务器返回HTTP客户端错误(4xx)或服务器错误(5xx)状态。响应状态和有效负载将按照RESTCONF规范-RCF8040,section7。 错误响应数据将是以下结构的JSON。响应内容类型将是“application/yang-data json”。

+---- errors
     +---- error*
          +---- error-type       "protocol" or "application"
          +---- error-tag        string
          +---- error-app-tag?   string
          +---- error-path?      xpath
          +---- error-message?   string

注意:REST服务器不会填充error-app-tag和error-path字段。它可以在未来的版本中得到增强。一个示例错误响应:

{
  "ietf-restconf:errors" : {
    "error" : [
      {
        "error-type" : "application",
        "error-tag" : "invalid-value",
        "error-message" : "VLAN 100 not found"
      }
    ]
  }
}

error-type 可以是“protocol”或“application”,指示错误的来源。 RESTCONF 定义了两个错误类型枚举“transport”和“rpc”;REST 服务器不使用它们。

error-tag 表示错误的性质,如 RFC8040,第 7 节所述。

error-message 字段携带了可显示给最终用户的友好错误消息。这是一个可选字段;系统错误不包括错误消息,或者有类似“内部错误”的通用消息。应用程序模块开发人员应该在返回应用程序错误时使用友好的消息。在CVL约束违反的情况下,REST服务器将从CVL模式YANG的“error-message”语句中提取错误消息。

下面的表格列出了可能的错误条件,以及响应状态和REST服务器返回的数据。

Method 方法Error condition 错误条件Status 状态error-type 几类error-tagerror-message 错误消息
any 任何Incorrect request data 请求数据不正确400protocol 协议invalid-value 无效值
write 写Bad content-type 坏的内容类型415protocol 协议invalid-value 无效值Unsupported content-type 不支持的内容类型
write 写OpenAPI schema validation failsOpenAPI schema validation fails 验证失败400protocol 协议invalid-value 无效值Content not as per schema内容不符合模式
write 写YGOT schema validation fails验证失败400protocol 协议invalid-value 无效值YGOT returned message YGOT返回的消息
any 任何Invalid user credentials 无效用户凭据401protocol 协议access-denied 如果Authentication failed 身份验证失败
write 写User is not an admin用户不是管理员403protocol 协议access-denied 如果Authorization failed 授权失败
write 写Translib commit failure Translib提交失败409protocol 协议in-use 好了
any 任何Bad Accept-Version value 错误的接受版本值400protocol 协议invalid-value 无效值Invalid Accept-Version 无效的接受版本
any 任何Version ckeck fails 版本校验失败400protocol 协议operation-not-supported 不支持的操作Unsupported client version X.Y.Z
any 任何Unknown HTTP server failure500protocol 协议operation-failed 操作失败Internal error 内部错误
any 任何Not supported by App module405application 应用程序operation-not-supported 不支持的操作App module returned message
any 任何Incorrect payload 不正确的负载400application 应用程序invalid-value 无效值App module returned message
any 任何Resource not found 未找到资源404application 应用程序invalid-value 无效值App module returned message
POST 帖子Resource exists 资源不存在409application 应用程序resource-deniedApp module returned message
any 任何Unknown error in Translib500application 应用程序operation-failed 操作失败Internal error 内部错误
any 任何Unknown App module failure500application 应用程序operation-failed 操作失败App module returned message
any 任何CVL constraint failure500application 应用程序invalid-value 无效值error-message defined in SONiC YANG
3.2.2.4.14 DB Schema

在ConfigDB中引入了一个新的表“REST_SERVER”,用于维护REST服务器配置。

key         = REST_SERVER|default   ; REST server configurations.
;field      = value
port        = 1*5DIGIT              ; server port - defaults to 443
client_auth = "none" / "password" / "jwt" / "cert" 
                                    ; Client authentication mode.
                                    ; none: No authentication, all clients
                                    ;       are allowed. Should be used only
                                    ;       for debugging.
                                    ; password: HTTP Basic authentication.
                                    ; jwt : HTTP Bearer Token authentication with
                                    ;       JSON Web Token format.
                                    ; cert: Certificate based authentication.
                                    ;       Requires ca_crt configuration.
                                    ; Any combination of "password", "jwt" and "cert" modes can be
                                    ; enabled by specifying a comma separated values.
                                    ; Eg: "password,jwt" enables both password and jwt modes.
log_level   = DIGIT                 ; Verbosity for glog.V logs
server_crt  = 1*VCHAR               ; Path to TLS certificate file
server_key  = 1*VCHAR               ; Path to TLS private key file
ca_crt      = 1*VCHAR               ; Path to the CA certificate to be used for
                                    ; client certificate validation.
3.2.2.4.15 API文档

REST服务器将提供基于Swagger UI的在线文档和测试UI,用于所有支持的REST API。文档可以通过浏览器启动URL https://REST_SERVER_IP/ui来访问。该页面将列出所有支持的OpenAPI定义文件(包括YANG生成的和手动的),以及为它们打开Swagger UI的链接。

3.2.2.5 gNMI 服务器
  1. gNMI服务器是远程测量过程的一部分,支持远程测量和gNMI。
  2. gRPC服务器打开一个TCP端口,只允许有效的相互认证TLS连接,这需要安装有效的客户端、服务器和CA证书以及正确配置的DNS。 允许多个同时连接到gNMI服务器。
  3. gNMI Agent 使用 db 客户端以及非 db 客户端直接访问和修改 Redis DB 中的数据。
  4. Translib客户端用于提供替代模型的访问,例如Openconfig模型,而不是本地Redis模式,只要Translib支持这些模型。Translib提供本地Redis模型和期望的北向模型之间的双向转换,以及这些模型对象的通知/更新,以支持遥测和异步更新,警报和事件。Translib还应该提供它支持哪些模型的信息,以便在gNMI Capabilities响应中返回信息。
  5. gNMI服务器定义了gNMI规范要求的四个RPC函数:Get、Set、Capabilities和Subscribe。
  6. 由于db、non-db和Translib客户端提供了支持这些功能的功能,gNMI只需要将路径和对象有效负载转换为客户端调用的正确参数,并将结果打包回响应gNMI对象中,以返回到gNMI客户端,这是一个简单的操作,因为不需要在gNMI服务器本身中进行额外的数据处理。 当新模型添加到Translib时,不需要在gNMI服务器中进行额外的工作来支持它们。
  7. 一个Set请求中的所有操作都在一个单一的事务中处理,这个事务要么成功要么失败,db,non-db和Translib客户端必须支持Bulk操作,以实现事务行为,gNMI服务器必须使用这个Bulk操作来处理Set请求。
  8. 订阅操作:一旦轮询和流要求gRPC连接保持打开状态直到订阅完成。这意味着必须支持许多连接。订阅提供几个选项,例如只发送对象更新(而不是整个对象),这需要来自数据库客户端的支持。订阅还允许由客户端定义的周期采样。这必须在gNMI代理本身中处理。这需要为这种类型的每个订阅连接设置一个定时器,以便定期轮询数据库客户端并在订阅响应中返回结果。当订阅gRPC连接关闭时,这些定时器应该被销毁。
3.2.2.5.1 修改/添加的文件:
|-- gnmi_server
|   |-- client_subscribe.go
|   |-- server.go ------------------- MODIFIED (Handles creation of transl_data_client for GET/SET/CAPABILITY)
|   |-- server_test.go
|-- sonic_data_client
|   |-- db_client.go ---------------- MODIFIED (Common interface Stub code for new functions as all data clients implement common interface functions)
|   |-- non_db_client.go ------------ MODIFIED (Common interface Stub code for new functions as all data clients implement common interface functions)
|   |-- transl_data_client.go ------- ADDED    (Specific processing for GET/SET/CAPABILITY for transl data clients)
|   |-- trie.go
|   |-- virtual_db.go
|
|-- transl_utils -------------------- ADDED
    |-- transl_utils.go ------------- ADDED    (Layer for invoking Translib APIs)

3.2.2.5.2 样品要求

使用gnmi_get.go命令获取MyACL4的ACL设置,设置参数包括:-xpath /openconfig-acl:acl/acl-sets/acl-set[name=MyACL4][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=1] -target_addr 10.130.84.34:8081 -alsologtostderr -insecure true -pretty

在gnmi_set中替换ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV4的ACL_IPV

go run gnmi_capabilities.go -target_addr 10.130.84.34:8081 -alsologtostderr -insecure true -pretty

3.2.2.6 翻译

Translib是一个库,它将管理服务器请求与SONiC数据提供者进行互换。Translib为管理服务器提供了以下API。

    func Create(req SetRequest) (SetResponse, error)
    func Update(req SetRequest) (SetResponse, error)
    func Replace(req SetRequest) (SetResponse, error)
    func Delete(req SetRequest) (SetResponse, error)
    func Get(req GetRequest) (GetResponse, error)
    func Action(req ActionRequest) (ActionResponse, error)
    func Bulk(req BulkRequest) (BulkResponse, error)
    func Subscribe(req SubscribeRequest) ([]*IsSubscribeResponse, error)
    func IsSubscribeSupported(req IsSubscribeRequest) ([]*IsSubscribeResponse, error)
    func GetModels() ([]ModelData, error)

    Translib Structures:
    type ErrSource int

    const (
        ProtoErr ErrSource = iota
        AppErr
    )

    type SetRequest struct {
        Path    string
        Payload []byte
        User    string
        ClientVersion Version
    }

    type SetResponse struct {
        ErrSrc ErrSource
        Err    error
    }

    type GetRequest struct {
        Path    string
        User    string
        ClientVersion Version
    }

    type GetResponse struct {
        Payload []byte
        ErrSrc  ErrSource
    }

    type ActionRequest struct {
        Path    string
        Payload []byte
        User    string
        ClientVersion Version
    }

    type ActionResponse struct {
        Payload []byte
        ErrSrc  ErrSource
    }

    type BulkRequest struct {
        DeleteRequest  []SetRequest
        ReplaceRequest []SetRequest
        UpdateRequest  []SetRequest
        CreateRequest  []SetRequest
        User           string
        ClientVersion  Version
    }

    type BulkResponse struct {
        DeleteResponse  []SetResponse
        ReplaceResponse []SetResponse
        UpdateResponse  []SetResponse
        CreateResponse  []SetResponse
    }

    type SubscribeRequest struct {
        Paths           []string
        Q               *queue.PriorityQueue
        Stop            chan struct{}
        User            string
        ClientVersion   Version
    }

    type SubscribeResponse struct {
        Path         string
        Payload      []byte
        Timestamp    int64
        SyncComplete bool
        IsTerminated bool
    }

    type NotificationType int

    const (
        Sample NotificationType = iota
        OnChange
    )

    type IsSubscribeRequest struct {
        Paths               []string
        User                string
    }

    type IsSubscribeResponse struct {
        Path                string
        IsOnChangeSupported bool
        MinInterval         int
        Err                 error
        PreferredType       NotificationType
    }

    type ModelData struct {
        Name string
        Org  string
        Ver  string
    }

    type Version struct {
        Major uint32
        Minor uint32
        Patch uint32
    }

Translib 有以下子模块来帮助数据转换

  1. App Interface
  2. Translib Request Handlers
  3. YGOT request binder
  4. DB access layer 
  5. App Modules
  6. Transformer 
 3.2.2.6.1 应用界面

App Interface 帮助识别负责处理传入请求的应用程序模块。它为应用程序模块提供了以下 API,以便在应用程序模块初始化期间向 App Interface 注册自己。

    func Register(path string, appInfo *AppInfo) error
        This method can be used by any App module to register itself with the Translib infra.
        Input Parameters:
        path - base path of the model that this App module services
        appInfo - This contains the reflect types of the App module structure that needs to be instantiated for each request, corresponding YGOT structure reflect type to instantiate the corresponding YGOT structure and boolean indicating if this is native App module to differentiate between OpenAPI spec servicing App module and the YANG serving App module.
        Returns:
        error - error string  
    func AddModel(model *gnmi.ModelData) error
        This method can be used to register the models that the App module supports with the Translib infra.
        Input Parameters:
        model - Filled ModelData structure containing the Name, Organisation and version of the model that is being supported.
        Returns:
        error - error string

    App Interface Structures:
    //Structure containing App module information
    type AppInfo struct {
        AppType         reflect.Type
        YGOTRootType    reflect.Type
        IsNative        bool
        tablesToWatch   []*db.TableSpec
    }

    Example Usages:
    func init () {
        log.Info("Init called for ACL module")
        err := register("/openconfig-acl:acl",
                &appInfo{appType: reflect.TypeOf(AclApp{}),
                ygotRootType:  reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}),
                isNative:      false,
                tablesToWatch: []*db.TableSpec{&db.TableSpec{Name: ACL_TABLE}, &db.TableSpec{Name: RULE_TABLE}}})

        if err != nil {
            log.Fatal("Register ACL App module with App Interface failed with error=", err)
        }

        err = appinterface.AddModel(&gnmi.ModelData{Name:"openconfig-acl",
                                                    Organization:"OpenConfig working group",
                                                    Version:"1.0.2"})
        if err != nil {
            log.Fatal("Adding model data to appinterface failed with error=", err)
        }
    }

    type AclApp struct {
        path string
        YGOTRoot *YGOT.GoStruct
        YGOTTarget *interface{}
    }

Translib 请求处理器使用 App 接口获取所有 App 模块信息,这取决于作为请求一部分的传入路径。

3.2.2.6.2 Translib 请求处理程序

这些是Translib公开的API的处理器。每当请求到达请求处理器时,处理器使用App接口来获取可以根据传入路径处理请求的App模块。然后,如果需要,它会使用YGOT绑定模块将传入路径和请求的有效负载转换为YGOT结构。填充的YGOT结构被提供给App模块进行处理。Translib还与DB访问层交互,以启动、提交和中止事务。

3.2.2.6.3 YGOT请求绑定器

YGOT请求绑定模块使用YGOT工具来执行解组和验证。YGOT(YANG Go Tools)是一个开源工具,它拥有Go实用程序的集合,用于

  1. 在构建时为给定的 YANG 模块生成一组 Go 结构以进行绑定
  2. 将给定的请求解组到 Go 结构对象中。这些对象遵循 YANG 模型中定义的相同层次结构,它只是给定请求的数据实例树,但使用生成的 Go 结构表示。
  3. 根据YANG模型验证Go结构的内容(例如,验证范围和正则表达式约束)。
  4. 将 Go 结构对象渲染为输出格式 - 例如 JSON。

这个 RequestBinder 模块公开了下面提到的 API,这些 API 将被用来将请求解组到 Go 结构对象中,并验证请求。

func getRequestBinder(uri *string, payload *[]byte, opcode int, appRootNodeType *reflect.Type) *requestBinder
    This method is used to create the requestBinder object which keeps the given request information such as uri, payload, App module root type, and unmarshall the same into object bindings
    Input parameters:
        uri -  path of the target object in the request.
        payload - payload content of given the request and the type is byte array
        opcode - type of the operation (CREATE, DELETE, UPDATE, REPLACE) of the given request, and the type is enum
        appRootNodeType - pointer to the reflect.Type object of the App module root node's YGOT structure object
    Returns:
    requestBinder -  pointer to the requestBinder object instance

func (binder *requestBinder) unMarshall() (*YGOT.GoStruct, *interface{}, error)
    This method is be used to unmarshall the request into Go structure objects, and validates the request against YANG model schema
    Returns:
    YGOT.GoStruct - root Go structure object of type Device.
    interface{} - pointer to the interface type of the Go structure object instance of the given target path
    error - error object to describe the error if the unmarshalling fails, otherwise nil

实用工具方法: 这些实用工具方法提供了下面提到的对YGOT结构的常见操作,这些操作是App模块所需要的。

func getParentNode(targetUri *string, deviceObj *ocbinds.Device) (*interface{}, *YANG.Entry, error)
    This method is used to get parent object of the given target object's uri path
    Input parameters:
    targetUri - path of the target URI
    deviceObj - pointer to the base root object Device
    Returns
    interface{} - pointer to the parent object of the given target object's URI path
    YANG.Entry - pointer to the YANG schema of the parent object
    error - error object to describe the error if this methods fails to return the parent object, otherwise nil

func getNodeName(targetUri *string, deviceObj *ocbinds.Device) (string, error)
    This method is used to get the YANG node name of the given target object's uri path.
    Input parameters:
    targetUri - path of the target URI  
    deviceObj - pointer to the base root object Device
    Returns:
    string - YANG node name of the given target object
    error - error object to describe the error if this methods fails to return the parent object, otherwise nil

func getObjectFieldName(targetUri *string, deviceObj *ocbinds.Device, YGOTTarget *interface{}) (string, error)
    This method is used to get the go structure object field name of the given target object.
    Input parameters:
        targetUri - path of the target URI
        deviceObj - pointer to the base root object Device
        YGOTTarget - pointer to the interface type of the target object.
    Returns:
    string - object field name of the given target object
    error - error object to describe the error if this methods fails to perform the desired operation, otherwise nil
3.2.2.6.4 YANG模型版本控制
3.2.2.6.4.1版本管理

ranslib维护一个“YANG绑定包版本”,这是所有部署在管理框架服务中的YANG模块的集合版本号。这个版本在一个配置文件中维护。它使用MAJOR.MINOR.PATCH语法,按照语义版本规范。 开发人员在做出任何YANG更改时,必须更新绑定包版本号。

在这个版本中,主版本固定为1。 当任何YANG模型以非向后兼容的方式更改时,它将在未来的版本中递增。以下是候选版本:

  • 删除、重命名或重新定位数据节点
  • 更改列表键属性
  • 将节点的数据类型更改为不兼容的类型
  • 改变leafref目标

如果 YANG 更改以向后兼容的方式修改了 API,则 Minor 版本将递增。补丁版本将重置为 0。 此类别的 YANG 候选更改是:

  • 添加新的YANG模块
  • 添加新的 YANG 数据节点
  • 将 YANG 数据节点标记为不推荐使用
  • 将节点的数据类型更改为兼容类型
  • 添加新的枚举或标识

补丁版本为不改变YANG API的外观修复而增加。 这类候选YANG变化是:

  • 改变描述,美化。
  • 将模式或节点的范围扩展到更广泛的集合。
  • 改变必须表达,以接受更多的情况。
  • 错误消息或错误标记更改。
3.2.2.6.4.2 版本检查

版本检查确保translib只处理来自兼容客户端的请求。 所有translib API都接受一个可选的ClientVersion参数。 客户端可以通过这个参数传递他们能够处理的YANG包版本。 如果指定,请求将只在满足以下条件时才被处理。

Base_Version <= ClientVersion <= YANG_Bundle_Version

当前版本的基本版本号为1.0.0。 在将来的主要版本增加时,它将发生变化。

如果客户端没有在请求中发送版本号,则会绕过版本检查。 然而,由于输入或模式不匹配,有些请求可能会成功,有些则可能会失败。

3.2.2.6.5 数据库访问层

DB访问层实现了一个包装器在go-redis包上, 以以下方式增强功能:

* Provide a sonic-py-swsssdk like API in Go
* Enable support for concurrent access via Redis CAS (Check-And-Set)
  transactions.
* Invoke the CVL for validation before write operations to the Redis DB

这些API大致可分为以下几个方面:

* Initialization/Close: NewDB(), DeleteDB()
* Read                : GetEntry(), GetKeys(), GetKeysPattern(), GetTable()
* Write               : SetEntry(), CreateEntry(), ModEntry(), DeleteEntry()
                        DeleteEntryField()
* Transactions        : StartTx(), CommitTx(), AbortTx(), AppendWatchTx()
* Map                 : GetMap(), GetMapAll()
* Subscriptions       : SubscribeDB(), UnsubscribeDB()
* Publish             : Publish()

详细方法签名: 请参考代码中详细的方法签名。

通过Redis CAS事务并发访问:

Upto 4 levels of concurrent write access support.

1. Table based watch keys (Recommended):
   At App module registration, the set of Tables that are to be managed by
   the module are provided. External (i.e. non-Management-Framework)
   applications may choose to watch and set these same table keys to detect
   and prevent concurrent write access. The format of the table key is
   "CONFIG_DB_UPDATED_<TABLE>".  (Eg: CONFIG_DB_UPDATED_ACL_TABLE)

2. Row based watch keys:
   For every transaction, the App module provides a list of keys that it
   would need exclusive access to for the transaction to succeed. Hence,
   this is more complex for the app modules to implement. The external
   applications need not be aware of table based keys. However, the
   concurrent modification of yet to be created keys (i.e. keys which are
   not in the DB, but might be created by a concurrent application) may not
   be detected.

3. A combination of 1. and 2.:
   More complex, but easier concurrent write access detection.

4. None:
   For applications not needing concurrent write access protections.

DB访问层,Redis,CVL交互:

DB access       |  PySWSSSDK API   |  RedisDB Call at  | CVL Call at
                |                  |  at CommitTx      | invocation
----------------|------------------|-------------------|--------------------
SetEntry(k,v)   | set_entry(k,v)   | HMSET(fields in v)|If HGETALL=no entry
                |                  | HDEL(fields !in v | ValidateEditConfig
                |                  |  but in           | (OP_CREATE)
                |                  |  previous HGETALL)|
                |                  |                   |Else
                |                  |                   | ValidateEditConfig(
                |                  |                   |  {OP_UPDATE,
                |                  |                   |  DEL_FIELDS})
----------------|------------------|-------------------|--------------------
CreateEntry(k,v)|    none          | HMSET(fields in v)| ValidateEditConfig(
                |                  |                   |  OP_CREATE)
----------------|------------------|-------------------|--------------------
ModEntry(k,v)   | mod_entry(k,v)   | HMSET(fields in v)| ValidateEditConfig(
                |                  |                   |  OP_UPDATE)
----------------|------------------|-------------------|--------------------
DeleteEntry(k,v)|set,mod_entry(k,0)| DEL               | ValidateEditConfig(
                |                  |                   |  OP_DELETE)
----------------|------------------|-------------------|--------------------
DeleteEntryField|    none          | HDEL(fields)      | ValidateEditConfig(
(k,v)           |                  |                   |   DEL_FIELDS)

注释:1. 如果 v 中包含空字段,则 SetEntry(), CreateEntry(), ModEntry() 将映射到 DeleteEntry()。[ 将来可能会有一个选项来禁用此功能。]

3.2.2.7 Transformer

Transformer为开发人员提供基础架构,可以将数据从YANG转换到ABNF/Redis模式,反之亦然,使用YANG扩展注释到YANG路径来提供翻译方法。在运行时,YANG扩展映射到一个内存中的Transformer规范,该规范提供YANG和ABNF/Redis模式之间的双向映射,以便Transformer在处理SET/GET操作时执行数据转换。

如果NBI应用程序使用SONiC YANG模块,Transformer可以在YANG对象和SONiC DB对象之间进行1:1映射,而无需编写特殊的转换代码.如果NBI应用程序使用openconfig YANG,则可能需要特殊处理来转换YANG和ABNF模式之间的数据.在这种情况下,您可以注释YANG扩展并编写回调函数来执行所需的转换.

对于特殊处理,开发人员需要提供:

  1. 一个注释文件,用于定义YANG路径上的YANG扩展,需要翻译 例如在openconfig-acl.yang中,ACL FORWARDING_ACTION "ACCEPT"映射到"FORWARD",ACL序列id '1'映射到Redis DB中的'RULE_1'等。
  2. 执行翻译的Transformer回调函数
3.2.2.7.1 组件

Transformer 由以下组件和数据组成:

  • 翻译提示:Transformer Spec
  • Spec Builder:加载 YANG 和注释文件,动态构建 YANG 模式树和 Transformer Spec。
  • Transformer Core:执行主要的transformer任务,例如编码/解码YGOT,遍历有效载荷,查找transformer规范,调用transformer方法,构建结果,错误报告等。
  • 内置的默认 Transformer 方法:执行静态翻译
  • 重载 Transformer 方法:由 Transformer 核心调用的回调函数,以便使用开发人员提供的翻译逻辑执行复杂的翻译。
  • 输出框架:聚合从默认和重载方法返回的翻译片段,以构建输出有效负载。
  • 方法重载器:在数据转换期间动态查找和调用重载的转换器方法。
  • YANG 模式树:为 Transformer 提供模式信息,以便 Transformer 可以访问这些信息来获取节点信息,比如默认值、父节点/子节点等。

 3.2.2.7.2 设计

来自北向接口(NBI)的请求由Translib公共API处理 - 创建,替换,更新,删除,(CRUD)和Get - 调用公共应用模块上的特定方法。公共应用程序调用Transformer API来翻译请求,然后使用翻译后的数据继续到DB/CVL层,以在Redis DB中设置或获取数据。公共应用程序作为默认应用程序模块,通常使用Transformer处理SET(CRUD)和GET以及订阅请求。请注意,如果需要,可以向Translib注册特定的应用程序模块来处理请求。

在 Transformer 初始化时,它会加载与应用相关的 YANG 模块,并使用扩展解析 YANG 模块,以动态构建内存中的模式树和转换器规范。

transformer规格定义如下结构:

type yangXpathInfo  struct {
    yangDataType   string
    tableName      *string
    xfmrTbl        *string
    childTable      []string
    dbEntry        *yang.Entry
    yangEntry      *yang.Entry
    keyXpath       map[int]*[]string
    delim          string
    fieldName      string
    xfmrFunc       string
    xfmrField      string
    xfmrPost       string
    validateFunc   string
    rpcFunc        string
    xfmrKey        string
    keyName        *string
    dbIndex        db.DBNum
    keyLevel       int
    isKey          bool
    defVal         string
    hasChildSubTree bool
}

当一个请求以YGOT结构的形式从Translib请求处理器到达公共应用时,请求被传递给Transformer,它解码YGOT结构以读取请求有效负载并查找规范以获得翻译提示。注意,Transformer不能用于OpenAPI规范。Transformer规范的结构采用双向映射,允许Transformer将基于YANG的数据映射到ABNF数据,反之亦然。反向映射用于将从Redis DB读取的数据填充到YANG结构,以便响应获取操作。

Transformer有一个内置的默认transformer方法,可以执行静态的、简单的从YANG到ABNF或反之的转换。它执行简单的映射,例如直接的名称/值映射,表/键/字段名,这些都可以由YANG扩展自定义。

此外,对于非ABNF YANG模型的更复杂的翻译,例如OpenConfig模型,Transformer还允许开发人员通过在YANG扩展中指定回调函数来重载默认方法,以将开发人员提供的翻译代码作为回调函数来执行翻译。Transformer动态调用这些函数,而不是使用默认方法。每个transformer回调函数必须定义以支持双向翻译,即YangToDb_和DbToYang_,它们由Transformer核心调用。

3.2.2.7.3 Process 

CRUD 请求(配置)通过以下步骤处理:

  1. 应用模块调用transformer,传递一个YGOT填充的Go结构,将YANG翻译成ABNF
  2. 应用模块调用CVL API获取所有依赖表列表来监视表,并获取有序表列表
  3. Transformer 分配带有三维映射的缓冲区: [table-name][key-values][attributes]
  4. Transformer 解码 YGOT 结构并遍历传入的请求以获得 YANG 节点名
  5. Transformer 查找 Transformer Spec 来检查给定路径是否存在翻译提示。
  6. 如果没有找到任何 spec 或 hint,则按原样复制名称和值。
  7. 如果发现提示,检查提示以执行操作,要么是简单的数据转换,要么是调用外部回调。
  8. 重复步骤 4 到 7 直到遍历完成
  9. 调用任何带注释的Transformer后函数
  10. Transformer 聚合结果返回给 App 模块
  11. 应用模块继续更新数据库,以确保数据库更新按照从步骤2中学习到的顺序进行。

GET请求通过以下步骤处理:

  1. App模块要求transformer将URL转换为keyspec到查询目标。
  2. Transformer 继续遍历带有 keyspec 的数据库以获得结果。
  3. Transformer 将 ABNF 的结果转换为 YANG,使用默认的 transformer 方法或回调函数
  4. Transformer 聚合翻译结果,返回到 App 模块解组 JSON 有效载荷
3.2.2.7.4 Common App

Common App 是一个默认应用程序,它处理 SONiC 或 OpenConfig YANG 模块的 GET/SET/Subscribe 请求,除非应用程序模块注册到 Translib。

下面是示例图,展示了通用应用如何支持SET(CRUD)/GET请求。

如果一个请求与多个表相关联,公共应用模块将按照从CVL层学习到的表顺序处理DB更新。 例如,如果NBI使用的sonic-acl.yang和带有CREATE操作的有效负载的数据同时包含ACL_TABLE和ACL_RULE,公共应用将更新CONFIG-DB以创建一个ACL TABLE实例,然后按此顺序创建ACL_RULE条目。DELETE操作的顺序相反,即ACL_RULE之后是ACL_TABLE。

3.2.2.7.5 YANG Extensions

翻译提示被定义为 YANG 扩展,以支持简单的表/字段名映射或通过外部回调实现更复杂的数据翻译。



注意,键转换器、字段转换器和子树转换器有一个对回调函数,与使用前缀- YangToDBfunction, DbToYangfunction的2路转换相关联。不是强制实现两个函数。例如,如果您只需要GET操作的翻译,您可以只实现DbToYangfunction。

开发人员可以生成模板注释文件,并根据需要定义 yang 路径的扩展,以便在 YANG 和 ABNF 格式之间转换数据。请参阅 3.2.2.7.8 工具。

这里是一份通用指南,你可以查看在实现模型时可以注释哪些扩展。

  1. sonic-ext:table-name [string] : 将一个 YANG 容器/列表映射到 TABLE 名称,由默认的 transformer 方法处理。参数是一个静态映射到给定 YANG 容器或列表节点的表名。 该表名将继承到所有后代节点,除非定义了另一个。

  2. sonic-ext:field-name [string] : 将一个 YANG leafy - leaf 或 leaf-list - node 映射到 FIELD name,由默认的 transformer 方法处理

  3. sonic-ext:key-delimiter [string] : 重写Redis DB中使用的默认键分隔符,由默认的transformer方法处理。 除非定义了扩展名,否则Transformer会使用默认的分隔符 - CONFIG_DB: "|", APPL_DB: ":", ASIC_DB: "|", COUNTERS_DB: ":", FLEX_COUNTER_DB: "|", STATE_DB: "|"

  4. sonic-ext:key-name [string] : 固定键名,用于映射到带有固定键的TABLE的YANG容器,由默认的transformer方法处理。用于定义固定键,主要用于映射到TABLE键的容器。 例如Redis可以有一个“STP|GLOBAL”散列。

  5. sonic-ext:key-transformer [function] : 重载默认方法,使用回调生成DB键,当YANG列表中的键值与DB表中的键值不同时使用。 应该实现一对回调函数来支持2路转换 - YangToDBfunction,DbToYangfunction

  6. sonic-ext:field-transformer [function] : 重载默认方法,使用回调生成FIELD值,当YANG列表中定义的叶子/叶子列表值与DB中的字段值不同时使用。 应该实现一对回调函数来支持2路转换 - YangToDB函数,DbToYang函数

  7. sonic-ext:subtree-transformer [function] : 重载默认方法,为当前子树提供一个回调函数,允许子树转换器完全控制翻译。注意,如果任何其他扩展,例如表名等,被注释到子树上的节点,它们将不起作用。 子树转换器被继承到所有后代节点,除非定义另一个子树转换器,即子树转换器回调的范围被限制在当前节点和沿着YANG路径的后代节点,直到注释一个新的子树转换器。 应该实现一对回调函数来支持2路翻译 - YangToDB函数,DbToYang函数

  8. sonic-ext:db-name [string] : 访问数据的数据库名称 - "APPL_DB", "ASIC_DB", "COUNTERS_DB", "CONFIG_DB", "FLEX_COUNTER_DB", "STATE_DB"。默认的数据库名称是CONFIG_DB,用于对非CONFIG_DB的GET操作,仅适用于SONiC YANG。由Transformer核心处理以遍历数据库。 除非另一个,否则db-name将继承到所有后代节点。必须用表名定义。

  9. sonic-ext:post-transformer [function] : 一个特殊的钩子,在传递给common-app之前更新DB请求,类似于transformer在最后调用的延迟YangToDB子树回调。 用于在传递给common-app之前向从transformer返回的映射添加/更新额外的数据,例如添加默认的acl规则 注意,post-transformer只能注释到每个模块中的顶级容器,并在翻译过程中为给定的节点调用一次。

  10. sonic-ext:table-transformer [function] : 动态映射一个YANG容器/列表到表名(S),允许表转换器将一个YANG列表/容器映射到表名。 用于基于URI和有效负载动态映射一个YANG列表/容器到表名。 表转换器被继承到所有后代节点,除非另一个被定义。

  11. sonic-ext:get-validate [function] : 一个特殊的钩子,用于验证 YANG 节点,填充从数据库读取的数据,允许开发人员在构建响应负载时指示 Transformer 在多个节点中选择一个 YANG 节点。 通常用于检查“when”条件,以验证多个节点中的 YANG 节点,只从同级节点中选择有效节点。

container global
   sonic-ext:table-name “STP”
   sonic-ext:key-name “GLOBAL”
1)	If the translation is simple mapping between YANG container/list and TABLE, consider using the extensions - table-name, field-name, optionally key-delimiter 
2)	If the translation requires a complex translation with your codes, consider the following transformer extensions - key-transformer, field-transformer, subtree-transformer to take a control during translation. Note that multiple subtree-transformers can be annotated along YANG path to divide the scope
3)	If multiple tables are mapped to a YANG list, e.g. openconfig-interface.yang, use the table-transformer to dynamically choose tables based on URI/payload 
4)	In Get operation access to non CONFIG_DB, you can use the db-name extension
5)	In Get operation, you can annotate the subtree-transformer on the node to implement your own data access and translation with DbToYangxxx function
6)	In case of mapping a container to TABLE/KET, you can use the key-name along with the table-name extension
3.2.2.7.6 公共函数

XlateToDb()  和  GetAndXlateFromDb  被通用应用程序用于请求翻译。

func XlateToDb(path string, opcode int, d *db.DB, yg *ygot.GoStruct, yt *interface{}) (map[string]map[string]db.Value, error) {}

func GetAndXlateFromDB(xpath string, uri *ygot.GoStruct, dbs [db.MaxDB]*db.DB) ([]byte, error) {}
3.2.2.7.7 重载方法

外部转换器回调函数的原型定义如下:

type XfmrParams struct {
        d *db.DB
        dbs [db.MaxDB]*db.DB
        curDb db.DBNum
        ygRoot *ygot.GoStruct
        uri string
        requestUri string //original uri using which a curl/NBI request is made
        oper int
        key string
        dbDataMap *map[db.DBNum]map[string]map[string]db.Value
        subOpDataMap map[int]*RedisDbMap // used to add an in-flight data with a sub-op
        param interface{}
        txCache *sync.Map
        skipOrdTblChk *bool
}

/**
 * KeyXfmrYangToDb type is defined to use for conversion of Yang key to DB Key 
 * Transformer function definition.
 * Param: XfmrParams structure having Database info, YgotRoot, operation, Xpath
 * Return: Database keys to access db entry, error
 **/
type KeyXfmrYangToDb func (inParams XfmrParams) (string, error)
/**
 * KeyXfmrDbToYang type is defined to use for conversion of DB key to Yang key
 * Transformer function definition.
 * Param: XfmrParams structure having Database info, operation, Database keys to access db entry
 * Return: multi dimensional map to hold the yang key attributes of complete xpath, error
 **/
type KeyXfmrDbToYang func (inParams XfmrParams) (map[string]interface{}, error)

/**
 * FieldXfmrYangToDb type is defined to use for conversion of yang Field to DB field
 * Transformer function definition.
 * Param: Database info, YgotRoot, operation, Xpath
 * Return: multi dimensional map to hold the DB data, error
 **/
type FieldXfmrYangToDb func (inParams XfmrParams) (map[string]string, error)
/**
 * FieldXfmrDbtoYang type is defined to use for conversion of DB field to Yang field
 * Transformer function definition.
 * Param: XfmrParams structure having Database info, operation, DB data in multidimensional map, output param YgotRoot
 * Return: error
 **/
type FieldXfmrDbtoYang func (inParams XfmrParams)  (map[string]interface{}, error)

/**
 * SubTreeXfmrYangToDb type is defined to use for handling the yang subtree to DB
 * Transformer function definition.
 * Param: XfmrParams structure having Database info, YgotRoot, operation, Xpath
 * Return: multi dimensional map to hold the DB data, error
 **/
type SubTreeXfmrYangToDb func (inParams XfmrParams) (map[string]map[string]db.Value, error)
/**
 * SubTreeXfmrDbToYang type is defined to use for handling the DB to Yang subtree
 * Transformer function definition.
 * Param : XfmrParams structure having Database pointers, current db, operation, DB data in multidimensional map, output param YgotRoot, uri
 * Return :  error
 **/
type SubTreeXfmrDbToYang func (inParams XfmrParams) (error)
/**
 * ValidateCallpoint is used to validate a YANG node during data translation back to YANG as a response to GET
 * Param : XfmrParams structure having Database pointers, current db, operation, DB data in multidimensional map, output param YgotRoot, uri
 * Return :  bool
 **/
type ValidateCallpoint func (inParams XfmrParams) (bool)
/**
 * RpcCallpoint is used to invoke a callback for action
 * Param : []byte input payload, dbi indices
 * Return :  []byte output payload, error
 **/
type RpcCallpoint func (body []byte, dbs [db.MaxDB]*db.DB) ([]byte, error)
/**
 * PostXfmrFunc type is defined to use for handling any default handling operations required as part of the CREATE
 * Transformer function definition.
 * Param: XfmrParams structure having database pointers, current db, operation, DB data in multidimensional map, YgotRoot, uri
 * Return: multi dimensional map to hold the DB data, error
 **/
type PostXfmrFunc func (inParams XfmrParams) (map[string]map[string]db.Value, error)
/**
 * TableXfmrFunc type is defined to use for table transformer function for dynamic derviation of redis table.
 * Param: XfmrParams structure having database pointers, current db, operation, DB data in multidimensional map, YgotRoot, uri
 * Return: List of table names, error
 **/
type TableXfmrFunc func (inParams XfmrParams) ([]string, error)
3.2.2.7.8 Utilities

goyang 包被扩展为为任何输入 yang 文件生成模板注释文件。一个新的输出格式类型“annotate”可以用来生成模板注释文件。goyang 的用法如下:

Usage: goyang [-?] [--format FORMAT] [--ignore-circdep] [--path DIR[,DIR...]] [--trace TRACEFILE] [FORMAT OPTIONS] [SOURCE] [...]
 -?, --help  display help
     --format=FORMAT
             format to display: annotate, tree, types
     --ignore-circdep
             ignore circular dependencies between submodules
     --path=DIR[,DIR...]
             comma separated list of directories to add to search path
     --trace=TRACEFILE
             write trace into to TRACEFILE

Formats:
    annotate - generate template file for yang annotations

    tree - display in a tree format

    types - display found types
        --types_debug  display debug information
        --types_verbose
                       include base information

将$(SONIC_MGMT_FRAMEWORK)/gopkgs/bin添加到PATH中以运行goyang二进制文件。

例如:

goyang --format=annotate --path=/path/to/yang/models openconfig-acl.yang  > openconfig-acl-annot.yang

Sample output:
module openconfig-acl-annot {
  
    yang-version "1"

    namespace "http://openconfig.net/yang/annotation";
    prefix "oc-acl-annot"

    import openconfig-packet-match { prefix oc-pkt-match }
    import openconfig-interfaces { prefix oc-if }
    import openconfig-yang-types { prefix oc-yang }
    import openconfig-extensions { prefix oc-ext }

    deviation oc-acl:openconfig-acl {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:state {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:state/oc-acl:counter-capability {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:acl-sets {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set {
      deviate add {
      }
    }
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:type {
      deviate add {
      }
    }
...
...
    deviation oc-acl:openconfig-acl/oc-acl:acl/oc-acl:config {
      deviate add {
      }
    }
}
3.2.2.8 配置验证库(CVL)

配置验证库(CVL)是一个独立的库,用于验证基于ABNF模式的SONiC(Redis)配置。这个库可以被Cfg-gen、Translib、ZTP等组件使用,在将SONiC配置数据写入Redis DB之前对其进行验证。理想情况下,任何将config_db.json文件导入Redis DB的组件都可以调用CVL API来验证配置。

CVL使用基于ABNF模式和各种约束编写的SONiC YANG模型。这些原生的YANG模型很简单,与相关的ABNF模式有非常紧密的映射。自定义YANG扩展(注释)用于自定义验证目的。特定的YANG扩展(而不是元数据)用于将ABNF数据转换为YANG数据。在CVL上下文中,这些YANG模型被称为CVL YANG模型,并在构建时从SONiC YANG生成。开源libyang库用于执行YANG数据验证。

通过添加其他数据定义,如状态数据(只读数据)、RPC或通知,SONiC YANG可以用作管理接口的北向 YANG。这些 YANG 模型被称为 SONiC NBI YANG 模型。由于 CVL 仅验证配置数据,因此这些数据定义语句被 CVL 忽略。在构建时,CVL YANG 实际上是在 CVL 特定的 pyang 插件的帮助下从 SONiC NBI YANG 模型生成的。

CVL支持基于多数据库实例HLD规范的多个用户定义的Redis数据库实例.在CVL初始化时,从预定义的位置读取DB配置文件,并将详细信息存储在内部数据结构中.CVL实现内部API,使用存储在内部数据结构中的详细信息连接到Redis数据库实例.

3.2.2.8.1 Architecture 3.2.2.8.1 体系结构
  1. 在构建期间,开发人员根据SONiC YANG指南,基于ABNF模式编写SONiC YANG模式,并根据需要添加元数据和约束。为此定义了自定义YANG扩展。
  2. 使用Pyang编译器编译YANG模型,借助CVL特定的pyang插件从SONiC YANG中派生出YIN模式文件,最后将生成的YIN文件打包在构建中。
  3. 在启动/初始化序列中,解析从SONiC YANG模型生成的YIN模式,并使用libyang API构建模式树。
  4. 应用程序调用CVL API来验证配置。在Translib库的情况下,DB访问层调用适当的CVL API来验证配置。
  5. ABNF JSON 通过翻译器生成 YANG 数据。嵌入在 YANG 模式中的元数据用于帮助这个翻译过程。
  6. 然后将 YANG 数据提供给 libyang 进行语法验证,如果发生错误,CVL 将返回适当的错误代码和详细信息给应用程序,而不会继续进行。
  7. 如果语法验证成功,CVL 使用从翻译的 YANG 数据中获取的依赖数据,或者如果需要,从 Redis DB 中获取依赖数据。 依赖数据是指验证 YANG 语法中表达的约束(如 'leafref','must', 'when' 等)所需的数据。
  8. 最后,将翻译好的YANG数据和依赖数据合并,并提供给libyang进行语义验证。如果发生错误,CVL将返回适当的错误代码和详细信息给应用程序,否则返回成功。
  9. 平台验证是仅在动态平台数据作为输入的情况下进行的特定语法和语义验证。

“偏差”的例子:

3.2.2.8.2.3 平台特定验证

平台约束验证可以有两种类型。

3.2.2.8.2.3.1静态平台约束验证

平台约束(范围,枚举,“必须”/“当”表达式等)在SONiC YANG偏差模型中表示每个特征。

3.2.2.8.2.2 语义验证

检查其他表中键引用的存在性 检查同一表中字段之间的任何条件 检查不同表之间字段的任何条件

3.2.2.8.2 验证类型

配置验证器根据YIN模式对元数据进行语法、语义和平台验证。

3.2.2.8.2.1 语法验证

下面是 config 验证库支持的一些语法验证

基本数据类型 枚举 范围 模式匹配 检查必填字段 检查默认字段 检查键的数量和类型 检查表格大小等。

module sonic-acl-deviation {
	......
	......
	deviation /sacl:sonic-acl/sacl:ACL_TABLE/sacl:ACL_TABLE_LIST {
		deviate add {
			max-elements 3;
		}
	}

	deviation /sacl:sonic-acl/sacl:ACL_RULE/sacl:ACL_RULE_LIST {
		deviate add {
			max-elements 768;
		}
	}
}
  • 所有的偏差模型都与相应的CVL YANG模型一起编译,并放置在特定于平台的架构文件夹中。在运行时,根据预先提供的“DEVICE_METADATA:platform”字段检测到的平台,应用偏差文件。

  • 下面是平台特定偏差文件的示例文件夹结构。

3.2.2.8.2.3.2 动态平台约束验证
3.2.2.8.2.3.2.1 平台数据在Redis DB表中。
  • 基于Redis DB,SONiC YANG模型可以添加平台特定的YANG数据定义,通过交叉引用平台YANG模型,在特征YANG中使用“must”或“when”等约束。
3.2.2.8.2.3.2.2 平台数据通过API可获取
  • 如果约束不能使用 YANG 语法表达,或者平台数据可以通过特性/组件 API(由特性公开的 API 来查询特定于平台的常量、资源限制等)获得,则需要通过自定义 YANG 扩展在 SONiC YANG 模型中连接自定义验证。
  • .特性开发人员使用函数式验证代码实现自定义验证函数。验证函数可以调用特性/组件 API 并获取检查约束所需的参数。
  • 基于 YANG 扩展语法,CVL 将调用适当的自定义验证函数,并使用 YANG 实例数据进行验证。下面是自定义验证上下文结构定义。
    	//Custom validation context passed to custom validation function
    	type CustValidationCtxt struct {
    		ReqData []CVLEditConfigData //All request data
    		CurCfg *CVLEditConfigData //Current request data for which validation should be done
    		YNodeName string //YANG node name
    		YNodeVal string  //YANG node value, leaf-list will have "," separated value
    		YCur *xmlquery.Node //YANG data tree
    		RClient *redis.Client //Redis client
    	}

3.2.2.8.3 CVL API
        //Structure for key and data in API
        type CVLEditConfigData struct {
                VType CVLValidateType //Validation type
                VOp CVLOperation      //Operation type
                Key string      //Key format : "PORT|Ethernet4"
                Data map[string]string //Value :  {"alias": "40GE0/28", "mtu" : 9100,  "admin_status":  down}
        }

        /* CVL Error Structure. */
        type CVLErrorInfo struct {
                TableName string      /* Table having error */
                ErrCode  CVLRetCode   /* Error Code describing type of error. */
                Keys    []string      /* Keys of the Table having error. */
                Value    string        /* Field Value throwing error */
                Field	 string        /* Field Name throwing error . */
                Msg     string        /* Detailed error message. */
                ConstraintErrMsg  string  /* Constraint error message. */
        }

        /* Structure for dependent entry to be deleted */
        type CVLDepDataForDelete struct {
                RefKey string /* Ref Key which is getting deleted */
                Entry  map[string]map[string]string /* Entry or field which should be deleted as a result */
        }

       /* Maintain time stats for call to ValidateEditConfig(). */
        type ValidationTimeStats struct {
                Hits uint          /* Total number of times ValidateEditConfig() called */
                Time time.Duration /* Total time spent in ValidateEditConfig() */
                Peak time.Duration /* Highest time spent in ValidateEditConfig() */
        }

        /* Validation type */
        type CVLValidateType uint
        const (
                VALIDATE_NONE CVLValidateType = iota //Data is used as dependent data
                VALIDATE_SYNTAX //Syntax is checked and data is used as dependent data
                VALIDATE_SEMANTICS //Semantics is checked
                VALIDATE_ALL //Syntax and Semantics are checked
        )
	
        /* Validation operation */	
        type CVLOperation uint
        const (
                OP_NONE   CVLOperation = 0 //Used to just validate the config without any operation
                OP_CREATE = 1 << 0//For Create operation
                OP_UPDATE = 1 << 1//For Update operation
                OP_DELETE = 1 << 2//For Delete operation
        )

        /* Error code */
        type CVLRetCode int
        const (
                CVL_SUCCESS CVLRetCode = iota
                CVL_SYNTAX_ERROR /* Generic syntax error */
                CVL_SEMANTIC_ERROR /* Generic semantic error */
                CVL_ERROR /* Generic error */
                CVL_SYNTAX_MISSING_FIELD /* Missing field */
                CVL_SYNTAX_INVALID_FIELD /* Invalid Field  */
                CVL_SYNTAX_INVALID_INPUT_DATA /*Invalid Input Data */
                CVL_SYNTAX_MULTIPLE_INSTANCE /* Multiple Field Instances */
                CVL_SYNTAX_DUPLICATE /* Duplicate Fields  */
                CVL_SYNTAX_ENUM_INVALID /* Invalid enum value */
                CVL_SYNTAX_ENUM_INVALID_NAME /* Invalid enum name  */
                CVL_SYNTAX_ENUM_WHITESPACE /* Enum name with leading/trailing whitespaces */
                CVL_SYNTAX_OUT_OF_RANGE /* Value out of range/length/pattern (data) */
                CVL_SYNTAX_MINIMUM_INVALID /* min-elements constraint not honored  */
                CVL_SYNTAX_MAXIMUM_INVALID /* max-elements constraint not honored */
                CVL_SEMANTIC_DEPENDENT_DATA_MISSING /* Dependent Data is missing */
                CVL_SEMANTIC_MANDATORY_DATA_MISSING /* Mandatory Data is missing */
                CVL_SEMANTIC_KEY_ALREADY_EXIST /* Key already existing. */
                CVL_SEMANTIC_KEY_NOT_EXIST /* Key is missing. */
                CVL_SEMANTIC_KEY_DUPLICATE /* Duplicate key. */
                CVL_SEMANTIC_KEY_INVALID /* Invaid key */
                CVL_NOT_IMPLEMENTED /* Not implemented */
                CVL_INTERNAL_UNKNOWN /*Internal unknown error */
                CVL_FAILURE          /* Generic failure */
        )

  1. func Initialize() CVLRetCode

    • 只初始化一次库,一旦库已经初始化,后续调用将不会影响它。如果导入了 'cvl' 包,则自动调用此函数。
  2. func Finish() 

    • 清理库资源。在不需要更多的验证或进程即将退出时,最好调用此函数。
  3. func (c *CVL) ValidateConfig(jsonData string)  CVLRetCode

    • 只验证包含同一表的多个行实例的JSON缓冲区,数据实例来自不同的表。所有依赖项都在有效负载中提供。这对于批量数据验证很有用。
  4. func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (cvlErr CVLErrorInfo, ret CVLRetCode)

    • 验证JSON数据的创建/更新/删除操作。语法或语义验证可以单独或一起完成。相关数据应作为验证成功的依赖数据提供。
  5. func (c *CVL) ValidateKeys(key []string)  CVLRetCode

    • 验证键值是否在数据库中存在,检查键值是否符合模式格式,键值应该以表名作为前缀。
  6. func (c *CVL) ValidateFields(key string, field string, value string)  CVLRetCode

    • 验证表中的字段:值对,键应该以表名作为前缀。
  7. func (c *CVL) SortDepTables(inTableList []string) ([]string, CVLRetCode)

    • 根据leafref赋予的依赖关系对给定表的列表进行排序。
  8. func (c *CVL) GetOrderedTables(yangModule string) ([]string, CVLRetCode)

    • 获取一个给定YANG模块中基于leafref关系的表的排序列表。
  9. func (c *CVL) GetDepTables(yangModule string, tableName string) ([]string, CVLRetCode)

    • .获取一个 YANG 模块中给定表的依赖表列表。
  10. func (c *CVL) GetDepDataForDelete(redisKey string) ([]CVLDepDataForDelete)

    • 获取要删除或修改的依赖数据(Redis 键),以便为给定的条目进行删除。
  11. func GetValidationTimeStats() (ValidationTimeStats) - Provides statistics details of time spent in ValidateEditConfig().

  12. func ClearValidationTimeStats() - Clears statistics details of time spent in ValidateEditConfig().

3.2.2.9 Redis DB

Please see 3.2.2.6.5 DB access layer

3.2.2.10 非数据库数据提供程序

目前,每个应用模块都需要为应用特定的配置执行专有访问机制。

4 Flow Diagrams 4流程图

4.1 REST SET流程

  1. REST客户端可以发送任何写命令,如POST、PUT、PATCH或DELETE,它将由REST网关处理。
  2. REST网关中的所有处理程序都将调用命令请求处理程序。
  3. 命令的身份验证和授权在这里完成。
  4. 请求处理程序调用Translib公开的一个写API。
  5. Translib 基础设施用请求的有效负载填充 YGOT 结构并执行语法验证。
  6. Translib 获取写锁(互斥锁)以避免同一进程在同一时间发生另一次写操作。
  7. Translib 基础结构获取与传入的 URI 对应的 App 模块。
  8. Translib 基础架构调用 App 模块的 initialize 函数,并提供 YGOT 结构、路径和有效负载。
  9. app模块将传入的数据缓存到app结构中。
  10. App模块调用Transformer函数,将请求从缓存的YGOT结构转换为Redis ABNF格式,并获取所有将作为该请求一部分受到影响的键。
  11. app模块返回它想要保持监视的键列表以及状态。
  12. Translib 基础结构调用由 DB 访问层公开的开始事务请求。
  13. 数据库访问层对Redis数据库中的所有键执行一个WATCH操作。如果这些键中的任何一个被外部修改,那么第26步中的EXEC调用将会失败。
  14. Redis返回的状态。
  15. 从数据库访问层返回的状态。
  16. Translib 然后调用 App 模块的 processWrite API。
  17. 应用模块执行对数据库访问层的翻译数据写入。
  18. 数据库访问层使用CVL验证写入,然后缓存它们。
  19. 从数据库访问层返回的状态。
  20. 从App模块返回的状态。
  21. Translib 基础结构调用数据库访问层上的提交事务。
  22. 数据库访问层首先调用Redis数据库的MULTI请求,表明有多个写入进来,所以一起提交所有东西。所有写入都成功,否则什么都不成功。
  23. Redis返回的状态。
  24. 所有缓存写入的流水线都从数据库访问层执行。
  25. 从Redis返回的状态。
  26. 对Redis数据库执行EXEC调用,如果调用失败,则说明我们监视的某个键发生了变化,并且没有写入Redis数据库。
  27. Redis DB返回的状态。
  28. 从数据库访问层返回的状态。
  29. 在步骤6中获得的写入锁被释放。
  30. 从Translib基础设施返回的状态。
  31. REST 状态从请求处理程序返回。
  32. REST 响应由 REST 网关发送到 REST 客户端。

4.2 REST GET流程

  1. REST客户端的REST GET请求被发送到REST网关。
  2. REST网关调用一个公共的请求处理器。
  3. 对传入的请求进行身份验证。
  4. 请求处理器调用Translib公开的GET API,并提供请求的URI。
  5. Translib 基础架构获取与传入的 uri 对应的 App 模块。
  6. Translib 基础架构调用 App 模块的 initialize 函数,并提供 YGOT 结构和路径。
  7. 从App模块返回的状态。
  8. App模块查询Transformer,以翻译需要查询的Redis键的路径。
  9. 从App模块返回的状态。
  10. Translib 基础架构调用 App 模块的 processGet 函数。
  11. 应用模块调用数据库访问层公开的读取API,从Redis数据库读取数据。
  12. 从Redis DB读取数据并返回给App模块
  13. App模块用来自Redis DB的数据填充YGOT结构,并验证填充后的YGOT结构的语法。
  14. App模块将YGOT结构转换为JSON格式。
  15. IETF JSON 有效负载被返回到 Translib 基础设施。
  16. 将 IETF JSON 有效负载返回给请求处理程序。
  17. 响应被返回到REST网关。
  18. REST 响应从 REST 网关返回到 REST 客户机。

4.3 Translib Initialization flow4.3 Translib 初始化流程

  1. App module 1 init is invoked
  2. App module 1 calls Register function exposed by Translib infra to register itself with the Translib.
  3. App module 2 init is invoked
  4. App module 2 calls Register function exposed by Translib infra to register itself with the Translib.
  5. App module N init is invoked
  6. App module N calls Register function exposed by Translib infra to register itself with the Translib.

这样,多个应用程序模块在启动时使用Translib基础设施进行初始化。

4.4 gNMI flow 

  1. GNMI请求在各自的GET/SET处理程序中着陆,然后将请求重定向到相应的数据客户端。
  2. 如果用户没有提供目标字段,那么默认情况下请求将被发送到 transl_data_client。
  3. 接下来,transl_data_client 提供更高层次的抽象,并对多个路径的响应进行排序。
  4. Transl Utils层调用Translib API,它反过来调用App-Module API,并根据需要从Redis Db on-DB中检索和修改数据。

4.5 CVL flow 

上面是解释 CVL 步骤的顺序图。注意,为了简洁起见,这里没有显示 DB Access 层和 Redis 之间的交互,包括事务。

  1. REST/GNMI 调用 Translib 公开的一个写 API。
  2. Translib 基础设施用请求的有效负载填充 YGOT 结构并执行语法验证。
  3. Translib 获取写锁(互斥锁)以避免同一进程在同一时间发生另一次写操作。
  4. Translib 基础架构获取与传入的 uri 对应的 App 模块。
  5. Translib 基础架构调用 App 模块的 initialize 函数,并提供 YGOT 结构、路径和有效负载。
  6. App模块调用Transformer将请求从缓存的YGOT结构转换为Redis ABNF格式,它还获取了所有将作为该请求一部分受到影响的键。
  7. app modules 返回它想要监视的键列表和状态。
  8. Translib 基础结构调用由 DB 访问层公开的开始事务请求。
  9. 从数据库访问层返回的状态。
  10. Translib 然后调用 App 模块的 processWrite API。
  11. 应用模块执行对数据库访问层的翻译数据写入。
  12. 数据库访问层调用validateWrite来执行CREATE/UPDATE/DELETE操作。它被调用时带有键和Redis/ABNF有效负载。
  13. validateSyntax() 将 Redis 数据提供给内部的 translator,它会生成 YANG XML,这个 XML 被提供给 libyang 来验证语法。
  14. 如果成功,控制进入下一步,否则返回错误给数据库访问层,下一步是确保键出现在Redis数据库中用于更新/删除操作,但键不应该出现在创建操作中。
  15. 检查键后返回状态。
  16. CVL 从传入的 Redis 负载中获取相关数据,例如,如果在单个请求中创建了 ACL_TABLE 和 ACL_RULE。
  17. 否则依赖应该存在于Redis数据库中,查询被发送到Redis来获取它。
  18. Redis返回查询的响应。
  19. 最后,请求数据和依赖项被合并,并调用validateSemantics()。
  20. 如果以上步骤成功,则返回成功,否则返回失败并提供错误详细信息。
  21. 数据库访问层将状态响应转发给应用程序。
  22. App模块将状态响应转发到Translib基础设施。
  23. Translib 基础结构调用数据库访问层上的提交事务。
  24. 执行提交操作后,从数据库访问层返回状态。
  25. 在步骤3中获得的写入锁被释放。
  26. 最终响应从 Translib 基础设施返回到 REST/GNMI。

5 开发人员工作流程

开发人员的工作流程不同于标准的YANG(IETF/OpenConfig)和专有的YANG。当为新功能选择基于标准的YANG模型时,相关的Redis DB设计应该考虑到该模型的设计 - 它们之间的映射越近,管理路径中所需的翻译逻辑就越少。这简化了工作流程,因为Redis模式和NB YANG模式都对齐了,可以避免翻译智能。

当 YANG 没有直接映射到 Redis DB 时,管理框架提供了通过开发人员编写的自定义函数来表示复杂翻译的机制。

应该总是为新功能开发SONiC YANG,并且应该完全基于DB中的配置对象模式。

对于开发者更喜欢为新的或现有的SONIC特性编写非标准YANG模型的情况,YANG应该与Redis模式保持一致,这样同一YANG可以用于northbound和CVL。这简化了开发者的工作流程(在5.1节中解释)。

5.1 非标准的开发人员工作流程

下面给出了编写非标准 YANG 用于管理框架的典型步骤。

  • 基于Redis数据库设计,编写SONiC YANG。
  • .添加“must”、“when”表达式来捕获配置对象之间的依赖关系。
  • 将所需的非配置、通知和 RPC 对象添加到 YANG。
  • 为转换器添加元数据。
5.1.1 定义SONiC YANG模式

.Redis模式需要在SONiC专有的YANG模型中表达,包括所有数据类型和约束。需要使用适当的自定义YANG扩展来表达元数据。配置验证库(CVL)使用YANG模型来提供自动的语法和语义验证,以及用于北向管理接口的Translib。

.如果某些约束不能用 YANG 语法表达,则需要编写自定义验证代码。

 SONiC YANG Guidelines 

请参阅SONiC YANG指南,了解关于SONiC YANG写作的详细指南。

5.1.2 为基于 YANG 的 API 生成 REST 服务器存根和客户端 SDK
  • 将主要的 YANG 模块放在 sonic-mgmt-common/models/yang 目录下。
    • 通过将 YANG 模块放置在这个目录中,在下一次构建中,OpenAPI YAML (OpenAPI 规范)将为 YANG 生成。
    • 如果有扩展主YANG模块的YANG,这个扩展的YANG也应该放在sonic-mgmt-common/models/yang目录中。
  • .将所有依赖于YANG模块(例如子模块或定义类型定义的YANG)导入到主YANG模块中,并将其放在sonic-mgmt-common/models/yang/common目录下。
    • 通过将 YANG 模块放置在这个目录中,OpenAPI YAML (OpenAPI 规范)不会为 YANG 模块生成,但是放置在 sonic-mgmt-common/models/yang 下的 YANG 可以利用或引用类型,以及来自这个目录中 YANG 模块的其他 YANG 约束。
    • 例如:ietf-inet-types.yang,它主要包含其他YANG模型使用的类型定义,通常我们不喜欢为这个YANG提供YAML,这种类型的YANG文件可以放在sonic-mgmt-common/models/yang/common下。
  • 当make命令作为构建的一部分执行时,REST服务器存根和客户端SDK的生成将自动发生。
5.1.3 配置翻译应用程序(Go语言)

配置翻译应用程序由两部分组成 - 转换器和应用程序模块。他们将北向API模式中的数据翻译成本地Redis模式(参见5.1.1节),反之亦然。所有北向API服务,如REST,GNMI,NETCONF调用此应用程序通过Translib API调用来读写数据。

Key features: 主要特点:

  • Go language. 

  • YANG到Redis和反之的数据转换由Transformer处理。数据转换基于按照5.1.1节开发的YANG模型进行。

  • 数据处理由App模块负责

    • 应用程序通过YGOT结构消费/生产YANG数据。

    • 框架提供了访问Redis数据库的Go语言API。API类似于sonic-py-swsssdk仓库中定义的现有Python API。

    • 可以在这些格式之间进行转换。

      • 用于读操作
        • App模块接收到一个Go结构体中的YANG路径
        • App模块使用Transformer从这个路径获取一个DB条目引用。
        • App模块读取Redis条目
        • tApp模块使用Transformer将返回的DB对象转换为Go结构体。
        • 应用模块返回此到TransLib
      • For write operations 对于写操作
        • App模块接收目标YANG路径和Go结构体中的数据
        • App模块使用Transformer将Go结构体转换为适当的Redis调用。
        • DB Access层负责DB事务 - 写所有内容或不写任何内容
  • REST服务器为应用模块的快速测试提供了UI。这个UI列出了YANG的所有REST API,并提供了尝试它们的选项。

  • Pytest自动化集成可以使用直接的REST调用或CLI(它在内部也使用REST API),框架生成REST客户端SDK以促进直接的REST API调用。

5.1.4 IS CLI 

.IS CLI 是使用 KLISH 框架实现的。

  • CLI树在XML文件中表示为节点数据类型和层次结构,并带有不同的模式。
  • Actioner 需要在XML中连接相应的CLI语法。动作处理器应该被开发来调用客户端SDK API(即一个动作处理器可能需要调用多个客户端SDK API)。
  • Show命令输出格式化是使用Jinja模板实现的,所以开发者需要检查现有的模板是否可用,或者需要编写新的模板。
5.1.5 gNMI

gNMI 不需要任何特定的步骤。

5.2 基于标准的开发人员工作流(例如 OpenConfig/IETF)

.5.2.1 识别北向API功能的标准YANG模块。

开发人员首先选择标准YANG作为管理数据模型的基础。对于SONiC,OpenConfig是首选的源。然而,在缺乏合适的模型的情况下,可以考虑其他标准(例如IETF、IEEE、OEM事实上的标准)。

SONiC功能实现可能与所选模型不完全匹配——YANG中可能有部分没有被功能实现(或功能有限制),或者开发人员可能希望公开YANG中没有覆盖的功能对象。在这种情况下,开发人员应相应地扩展模型。首选方法是编写偏差文件,然后在其中使用相应的修改语句(例如,偏离,增大)。

5.2.2 为新特性定义 Redis 模式(不适用于遗留/现有特性)

Redis DB设计应该考虑到YANG设计,并尽可能地接近它。这简化了管理实现中的翻译过程。如果这是不可能或不合适的,则必须提供自定义翻译代码。

5.2.3 定义SONiC YANG模式

Redis模式需要在SONiC YANG模型中表达,包括所有数据类型和约束。需要使用适当的自定义YANG扩展来表达这些元数据。配置验证库(CVL)使用YANG模型来提供自动的语法和语义验证。

如果某些约束不能用 YANG 语法表达,则需要编写自定义验证代码。

请参阅SONiC YANG指南,了解关于SONiC YANG写作的详细指南。

5.2.4 为基于 YANG 的 API 生成 REST 服务器存根和客户端 SDK
  • 将主要的 YANG 模块放在 sonic-mgmt-common/models/yang 目录下。
    • 通过将 YANG 模块放置在这个目录中,YAML (OpenAPI 规范)将为 YANG 生成。
    • 如果有扩展主YANG模块的YANG,这个扩展的YANG也应该放在sonic-mgmt-common/models/yang目录中。
  • 将所有依赖的 YANG 模块,如子模块或定义类型定义的 YANG 等,放在 sonic-mgmt-common/models/yang/common 目录下。
    • 通过将 YANG 模块放置在这个目录中,YAML (OpenAPI 规范)不会为 YANG 模块生成,但是放置在 sonic-mgmt-common/models/yang 下的 YANG 可以利用或引用类型,以及来自这个目录中 YANG 模块的其他 YANG 约束。
    • 例如:ietf-inet-types.yang,它主要包含其他YANG模型使用的类型定义,通常我们不喜欢为这个YANG提供YAML,这种类型的YANG文件可以放在sonic-mgmt-common/models/yang/common下。
  • 当make命令作为构建的一部分执行时,REST服务器存根和客户端SDK的生成将自动发生。
5.2.5 配置翻译应用程序(Go语言)

配置翻译应用程序由两部分组成 - 转换器和应用程序模块。他们将北向API模式(在步骤#1中定义)中的数据翻译成本地Redis模式(在步骤#2中定义),反之亦然。所有北向API服务,如REST,GNMI,NETCONF调用此应用程序读写数据。

主要特点:

  • Go language. 
  • YANG到Redis以及反之的数据转换由Transformer处理。为了方便数据转换,开发人员只需要提供数据模型的YANG文件。
    • 数据模型的 YANG 文件
    • sonic-mgmt-common/models/yang可选的,一个YANG注释文件(参考3.2.2.7.8 Utilities)定义翻译提示,将YANG对象映射到DB对象。这些翻译提示是执行复杂翻译的外部回调,而简单的翻译由Transformer的内置方法处理。注释文件也放置在 sonic-mgmt-common/models/yang
    • sonic-mgmt-common/src/translib/transformer在  sonic-mgmt-common/src/translib/transformer 中定义翻译回调的代码
  • The processing of data is taken care by App module数据处理由App模块负责
    • App consumes/produces YANG data through YGOT structures.应用程序通过YGOT结构消费/生产YANG数据。
    • Framework provides Go language APIs for Redis DB access. APIs are similar to existing Python APIs defined in sonic-py-swsssdk repo.框架提供了访问Redis数据库的Go语言API。API类似于sonic-py-swsssdk仓库中定义的现有Python API。
    • For read operation 用于读操作
      • App module receives the YANG path to read in a Go structApp模块接收到一个Go结构体中的YANG路径
      • App module uses the Transformer to get a DB entry(s) reference from this pathApp模块使用Transformer从这个路径获取一个DB条目引用。
      • App module reads the Redis entry(s)App模块读取Redis条目
      • App module uses the Transformer to convert the returned DB object(s) to a Go structApp模块使用Transformer将返回的DB对象转换为Go结构体。
      • App module returns this to TransLib应用模块返回此到TransLib
    • For write operations 对于写操作
      • App module receives the target YANG path and data as YGOT treeApp模块接收目标YANG路径和数据作为YGOT树
      • App module translates the YGOT tree data into appropriate Redis calls using reference from Transformer应用模块使用来自 Transformer 的引用将 YGOT 树数据转换为适当的 Redis 调用。
        • App module also handles additional complex logic like transaction ordering or checking for dependenciesApp模块还处理其他复杂的逻辑,如事务排序或检查依赖性。
      • Translation Framework takes care of DB transactions - write everything or none翻译框架负责数据库事务 - 写入所有内容或无内容
  • REST server provides a test UI for quick UT of translation app. This UI lists all REST APIs for a YANG and provide option to try them out. REST server invokes Translation Apps.REST服务器为翻译应用的快速测试提供了一个UI。这个UI列出了YANG的所有REST API,并提供了尝试它们的选项。REST服务器调用翻译应用。
  • Spytest automation integration can make use of direct REST calls or CLI (which also makes use of REST internally - step#5). Framework generates REST client SDK to facilitate direct REST calls.Spytest自动化集成可以使用直接的REST调用或CLI(它也在内部使用REST - 第5步)。框架生成REST客户端SDK以促进直接的REST调用。
5.2.6 IS CLI

I CLI是使用KLISH框架实现的,步骤与SONiC YANG模型相同,请参阅5.1.4 IS CLI。

5.2.7 gNMI 

需要任何特定的步骤。

6错误处理

验证是在北向接口和对数据库模式进行的。对于无效的配置返回适当的错误代码。 所有应用程序错误都记录到 syslog 中。

7 可用性和调试

  1. 详细的系统日志消息,以帮助跟踪故障。
  2. 当调试框架可用时,将添加调试命令。
  3. 使用SIGUR1信号启用/禁用CPU分析。

8 Warm Boot Support

管理框架在warmboot期间不会中断数据平面通信。warmboot不需要特殊处理。

9可伸缩性

可管理性框架将是可扩展的,以处理符合标准/SONiC阳模型的巨大有效载荷。

10 Unit Test 10单元测试

GNMI GNMI公司
  1. Verify that gnmi_get is working at Toplevel module验证 gnmi_get 在顶层模块中正常工作
  2. Verify thet gnmi_get is working for each ACL Table验证 gnmi_get 是否对每个 ACL 表有效
  3. Verify gnmi_get working for each ACL Rule:验证gnmi_get对每个ACL规则都有效:
  4. Verify that gnmi_get is working for all ACL interfaces验证 gnmi_get 对所有 ACL 接口都有效
  5. Verify that gnmi_get is working for each ACL interface name验证 gnmi_get 对于每个 ACL 接口名都有效
  6. Verify that gnmi_get fails for non-existent ACL name and type验证 gnmi_get 对于不存在的 ACL 名称和类型失败
  7. Verify that TopLevel node can be deleted验证 TopLevel 节点可以被删除
  8. Verify that a particular ACL Table can be deleted验证一个特定的ACL表可以被删除
  9. Verify that ACL rule can be deleted验证ACL规则是否可以被删除
  10. Verify that ACL table can be created验证ACL表是否可以创建
  11. Verify that ACL rule can be created验证ACL规则可以被创建
  12. Verify that ACL binding can be created验证ACL绑定可以创建
  13. Verify that creating rule on non existent ACL gives error验证在不存在的 ACL 上创建规则会出错
  14. Verify that giving invalid interface number is payload gives error.验证给定的无效接口号是负载给出错误。
  15. Verify that GNMI capabalities is returning correctly.验证GNMI能力是否正确返回。
Request Binder (YGOT) 请求绑定器(YGOT)
  1. create a YGOT object binding for the uri ends with container创建一个 YGOT 对象绑定,用于 uri 结束容器
  2. create a YGOT object binding for the uri ends with leaf创建一个YGOT对象绑定,用于uri ends with leaf
  3. create a YGOT object binding for the uri ends with list创建一个 YGOT 对象绑定到 uri 结束的列表
  4. create a YGOT object binding for the uri ends with leaf-list创建一个YGOT对象绑定,用于uri ends with leaf-list
  5. create a YGOT object binding for the uri which has keys为包含键的URI创建一个YGOT对象绑定
  6. create a YGOT object binding for the uri which has keys and ends with list with keys创建一个 YGOT 对象绑定,该绑定用于URI,它具有键,并以带有键的列表结尾。
  7. validate the uri which has the correct number of keys验证URI的键数是否正确
  8. validate the uri which has the invalid node name验证具有无效节点名的URI
  9. validate the uri which has the invalid key value验证具有无效键值的URI
  10. validate the uri which has the incorrect number of keys验证URI的键数不正确
  11. validate the uri which has the invalid leaf value验证具有无效叶子值的URI
  12. validate the payload which has the incorrect number of keys验证具有不正确键数的有效负载
  13. validate the payload which has the invalid node name验证具有无效节点名的有效负载
  14. validate the payload which has the invalid leaf value验证具有无效叶节点值的负载
  15. validate the uri and the payload with the "CREATE" operation使用“CREATE”操作验证URI和有效负载
  16. validate the uri and the payload with the "UPDATE" operation使用“UPDATE”操作验证URI和有效负载
  17. validate the uri and the payload with the "DELETE" operation使用“DELETE”操作验证URI和有效载荷
  18. validate the uri and the payload with the "REPLACE" operation使用“REPLACE”操作验证URI和有效负载
  19. validate the getNodeName method for LIST node验证LIST节点的getNodeName方法
  20. validate the getNodeName method for leaf node验证叶节点的 getNodeName 方法
  21. validate the getNodeName method for leaf-list node验证叶子节点的 getNodeName 方法
  22. validate the getParentNode method for LIST node验证 LIST 节点的 getParentNode 方法
  23. validate the getParentNode method for leaf node验证叶节点的 getParentNode 方法
  24. validate the getParentNode method for leaf-list node验证叶子节点的 getParentNode 方法
  25. validate the getObjectFieldName method for LIST node验证LIST节点的getObjectFieldName方法
  26. validate the getObjectFieldName method for leaf node验证叶节点的 getObjectFieldName 方法
  27. validate the getObjectFieldName method for leaf-list node验证叶子列表节点的 getObjectFieldName 方法
DB access layer 数据库访问层
  1. Create, and close a DB connection. (NewDB(), DeleteDB())创建并关闭数据库连接。(NewDB(),DeleteDB())
  2. Get an entry (GetEntry())获取一个条目(GetEntry())
  3. Set an entry without Transaction (SetEntry())在没有事务的情况下设置一个条目(SetEntry())
  4. Delete an entry without Transaction (DeleteEntry())删除一个没有事务的条目 (DeleteEntry())
  5. Get a Table (GetTable()) 获取一个表(GetTable())
  6. Set an entry with Transaction (StartTx(), SetEntry(), CommitTx())使用Transaction (StartTx(), SetEntry(), CommitTx())设置一个条目
  7. Delete an entry with Transaction (StartTx(), DeleteEntry(), CommitTx())删除一个条目与事务(StartTx(), DeleteEntry(), CommitTx())
  8. Abort Transaction. (StartTx(), DeleteEntry(), AbortTx())中止事务。(StartTx()、DeleteEntry()、AbortTx())
  9. Get multiple keys (GetKeys())获取多个键(GetKeys())
  10. Delete multiple keys (DeleteKeys())删除多个键(DeleteKeys())
  11. Delete Table (DeleteTable())删除表(DeleteTable())
  12. Set an entry with Transaction using WatchKeys Check-And-Set(CAS)使用WatchKeys Check-And-Set(CAS)设置一个Transaction条目
  13. Set an entry with Transaction using Table CAS使用表CAS设置一个Transaction条目
  14. Set an entry with Transaction using WatchKeys, and Table CAS使用WatchKeys和表CAS设置一个条目
  15. Set an entry with Transaction with empty WatchKeys, and Table CAS设置一个带有空 WatchKeys 的 Transaction 条目,以及表 CAS
  16. Negative Test(NT): Fail a Transaction using WatchKeys CAS负面测试(NT):使用WatchKeys CAS失败的事务
  17. NT: Fail a Transaction using Table CAS使用表CAS失败的事务
  18. NT: Abort an Transaction with empty WatchKeys/Table CAS使用空WatchKeys/Table CAS中止事务
  19. NT: Check V logs, Error logsNT:检查V日志,错误日志
  20. NT: GetEntry() EntryNotExist.
ACL app (via REST) ACL应用程序(通过REST)
  1. Verify that if no ACL and Rules configured, top level GET request should return empty response验证如果没有配置ACL和规则,顶层GET请求应该返回空响应。
  2. Verify that bulk request for ACLs, multiple Rules within each ACLs and interface bindings are getting created with POST request at top level验证对 ACL 的批量请求,每个 ACL 中的多个规则和接口绑定正在使用 POST 请求在顶层创建
  3. Verify that all ACLs and Rules and interface bindings are shown with top level GET request验证所有ACL和规则以及接口绑定都显示在顶级GET请求中。
  4. Verify that GET returns all Rules for single ACL验证GET返回单个ACL的所有规则
  5. Verify that GET returns Rules details for single Rule验证GET返回单个规则的规则详细信息
  6. Verify that GET returns all interfaces at top level ACL-interfaces验证GET返回顶级ACL接口的所有接口
  7. Verify that GET returns one interface binding验证GET返回一个接口绑定
  8. Verify that single or multiple new Rule(s) can be added to existing ACL using POST/PATCH request验证单个或多个新规则是否可以使用 POST/PATCH 请求添加到现有的 ACL
  9. Verify that single or mutiple new ACLs can be added using POST/PATCH request验证单个或多个新的ACL可以使用POST/PATCH请求添加
  10. Verify that single or multiple new interface bindings can be added to existing ACL using POST/PATCH request验证单个或多个新接口绑定可以使用 POST/PATCH 请求添加到现有的 ACL
  11. Verify that single Rule is deleted from an ACL with DELETE request验证一个规则是否被DELETE请求从ACL中删除
  12. Verify that single ACL along with all its Rules and bindings are deleted with DELETE request验证单个ACL及其所有规则和绑定都已通过DELETE请求删除。
  13. Verify that single interface binding is deleted with DELETE request验证单个接口绑定已用DELETE请求删除。
  14. Verify that all ACLs and Rules and interface bindings are deleted with top level DELETE request验证所有ACL和规则以及接口绑定都已通过顶级DELETE请求删除。
  15. Verify that CVL throws error is ACL is created with name and type same as existing ACL with POST request验证CVL是否会抛出错误,即ACL的名称和类型与现有的ACL相同,并使用POST请求创建ACL。
  16. Verify that CVL throws error is RULE is created with SeqId, ACL name and type same as existing Rule with POST request验证CVL是否抛出错误:RULE创建时SeqId、ACL名称和类型与现有的Rule相同,且使用POST请求。
  17. Verify that GET returns error for non exising ACL or Rule验证GET返回错误的ACL或规则不存在
  18. Verify that CVL returns errors on creating rule under non existent ACL using POST request验证 CVL 在使用 POST 请求在不存在的 ACL 下创建规则时返回错误
  19. Verify that CVL returns error on giving invalid interface number in payload during binding creation验证在绑定创建期间,如果在有效负载中给出无效接口号,CVL 会返回错误。
CVL
  1. Check if CVL validation passes when data is given as JSON file当数据以JSON文件的形式提供时,检查CVL验证是否通过。
  2. Check if CVL Validation passes for Tables with repeated keys like QUEUE,WRED_PROFILE and SCHEDULER检查是否CVL验证通过表与重复的键如QUEUE,WRED_PROFILE和SCHEDULER
  3. Check if CVL throws error when bad schema is passed当传递了错误的模式时,检查 CVL 是否抛出错误。
  4. Check if debug trace level is changed as per updated conf file on receiving SIGUSR2在接收到 SIGUSR2 时检查调试跟踪级别是否根据更新的配置文件更改。
  5. Check must constraint for DELETE throws failure if condition fails, (if acl is a bound to port, deleting the acl rule throws error due to must constraint)如果条件失败,检查DELETE的必须约束会抛出失败,(如果acl是端口的绑定,删除acl规则会由于必须约束而抛出错误)
  6. Check if CVL Validation passes when data has cascaded leafref dependency (Vlan Member->Vlan->Port)当数据具有级联leafref依赖时,检查CVL验证是否通过(Vlan成员->Vlan->端口)
  7. Check if Proper Error Tag is returned when must condition is not satisfied当必须条件不满足时,检查是否返回适当的错误标记。
  8. Check if CVL Validation passes if Redis is loaded with dependent data for UPDATE operation.如果Redis加载了UPDATE操作的依赖数据,检查CVL验证是否通过。
  9. Check is CVL Error is returned when any mandatory node is not provided.当没有提供任何强制节点时,将返回 CVL Error。
  10. Check if CVL validation passes when global cache is updated for PORT Table for "must" expressions.检查当全局缓存更新为“必须”表达式端口表时,CVL验证是否通过。
  11. Check if CVL is able to validate JSON data given in JSON file for VLAN , ACL models检查CVL是否能够验证VLAN和ACL模型中JSON文件中的JSON数据。
  12. Check if CVL initialization is successful检查CVL初始化是否成功
  13. Check if CVL is able to validate JSON data given in string format for CABLE LENGTH检查CVL是否能够验证以字符串格式给出的CABLE LENGTH的JSON数据
  14. Check if CVL failure is returned if input JSON data has incorrect key如果输入的JSON数据键不正确,检查是否返回CVL失败。
  15. Check if CVL is returning CVL_SUCCESS for Create operation if Dependent Data is present in Redis如果Redis中存在依赖数据,检查CVL是否为创建操作返回CVL_SUCCESS
  16. Check if CVL is returning CVL_FAILURE for Create operation with invalid field for CABLE_LENGTH .检查CVL是否返回CVL_FAILURE,因为CABLE_LENGTH的字段无效。
  17. Check is CVL Error is returned for any invalid field in leaf检查是否CVL,如果叶子中任何无效字段返回错误
  18. Check is Valid CVL_SUCCESS is returned for Valid field for ACL_TABLE when data is given in Test Structure当在测试结构中给出数据时,ACL_TABLE的Valid字段返回CVL_SUCCESS。
  19. Check is Valid CVL_SUCCESS is returned for Valid field for ACL_RULE where Dependent data is provided in same session当ACL_RULE的Valid字段返回CVL_SUCCESS时,在同一个会话中提供了相关数据。
  20. Check if CVL is returning CVL_FAILURE for Create operation with invalid Enum vaue检查CVL是否返回CVL_FAILURE,因为创建操作使用了无效的枚举值
  21. Check if CVL validation fails when incorrect IP address prefix is provided.检查当提供不正确的 IP 地址前缀时 CVL 验证是否失败。
  22. Check is CVL validation fails when incorrect IP address is provided.当提供不正确的 IP 地址时,检查 CVL 验证失败。
  23. Check is CVL validation fails when out of bound are provided.当超出范围时,检查CVL验证失败。
  24. Check is CVL validation fails when invalid IP protocol当无效的IP协议时,检查CVL验证失败。
  25. Check is CVL validation fails when out of range values are provided.当提供超出范围的值时,检查 CVL 验证失败。
  26. Check if CVL validation fails when incorrect key name is provided .检查当提供不正确的密钥名时,CVL验证是否失败。
  27. Check if CVL validation passes is any allowed special character is list name.检查CVL验证是否通过,列表名是否允许任何特殊字符。
  28. Check if CVL validation fails when key names contains junk characters.检查当键名包含垃圾字符时,CVL验证是否失败。
  29. Check if CVL validation fails when additional extra node is provided检查当提供额外的节点时,CVL验证是否失败
  30. Check is CVL validation passes when JSON data is given as buffer for DEVICE METEADATA当JSON数据作为DEVICE METEADATA的缓冲区时,检查CVL验证是否通过。
  31. Check if CVL validation fails when key name does not contain separators.检查当键名不包含分隔符时是否CVL验证失败。
  32. Check if CVL validation fails when one of the keys is missing for Create operation当创建操作的键中缺少一个时,检查 CVL 验证是否失败。
  33. Check if CVL validation fails when there are no keys between separators for Create operation当Create操作的分隔符之间没有键时,检查CVL验证是否失败。
  34. Check if CVL validation fails when missing dependent data is provided for Update operation in same transaction检查在同一事务中更新操作提供缺失的依赖数据时,CVL验证是否失败。
  35. Check if CVL validation fails when missing dependent data is provided for Create operation in same transaction.检查当在同一个事务中为创建操作提供缺失的依赖数据时,CVL验证是否失败。
  36. Check if CVL validation fails when there are no keys between separators for DELETE operation当DELETE操作的分隔符之间没有键时,检查CVL验证是否失败。
  37. Check if CVL validation fails when there are no keys between separators for UPDATE operation当UPDATE操作的分隔符之间没有键时,检查CVL验证是否失败。
  38. Check if CVL validation fails when invalid key separators are provided for Delete operation当为 Delete 操作提供无效的键分隔符时,检查 CVL 验证是否失败。
  39. Check if CVL validation fails if UPDATE operation is given with invalid enum value如果使用无效的枚举值执行UPDATE操作,检查CVL验证是否失败。
  40. Check if CVL validation fails if UPDATE operation is given with invalid key containing missing keys如果UPDATE操作给出的键是包含缺失键的无效键,检查CVL验证是否失败。
  41. Check if CVL validation passes with dependent data present in same transaction for DELETE operation.检查CVL验证是否通过,同时检查DELETE操作中是否存在同一事务中的依赖数据。
  42. Check if CVL validation fails if DELETE operation is given with missing key for DELETE operation.如果DELETE操作缺少DELETE操作的键,检查CVL验证是否失败。
  43. Check if CVL validation fails if UPDATE operation is given with missing key如果UPDATE操作中缺少键,检查CVL验证是否失败
  44. Check if CVL validation fails when an existing key is provided in CREATE operation检查在 CREATE 操作中提供现有键时是否会失败 CVL 验证
  45. Check if CVL validation passes for INTERFACE table检查INTERFACE表是否通过CVL验证
  46. Check if CVL validation fails when configuration not satisfying must constraint is provided当配置不满足必须约束时,检查CVL验证是否失败
  47. Check if CVL validation passes when Redis has valid dependent data for UPDATE operation当Redis有有效的依赖数据用于UPDATE操作时,检查CVL验证是否通过。
  48. Check if CVL validation fails when two different sequences are passed(Create and Update is same transaction)检查当两个不同的序列传递时(创建和更新是相同的事务)CVL验证是否失败
  49. Check if CVL validation fails for UPDATE operation when Redis does not have dependent data.当Redis没有依赖数据时,检查UPDATE操作的CVL验证是否失败。
  50. Check if CVL validation passes with valid dependent data given for CREATE operation.检查CVL验证是否通过,并为CREATE操作提供有效的相关数据。
  51. Check if CVL validation fails when user tries to delete non existent key当用户试图删除不存在的密钥时,检查CVL验证是否失败。
  52. Check if CVL Validation passes if Cache contains dependent data populated in same sessions but separate transaction.如果缓存包含在相同会话中但独立事务中填充的依赖数据,检查CVL验证是否通过。
  53. Check if CVL Validation passes if Cache data dependent data that is populated across sessions检查CVL验证是否通过,如果缓存数据依赖数据是跨会话填充的
  54. Check if CVL Validation fails when incorrect dependent Data is provided for CREATE operation当为 CREATE 操作提供不正确的依赖数据时,检查 CVL 验证是否失败。
  55. Check if CVL validation passes when valid dependent data is provided for CREATE operation当为 CREATE 操作提供有效的相关数据时,检查 CVL 验证是否通过。
  56. Check if Proper Error Tag is returned when must condition is not satisfied in "range"检查当必须条件在“范围”中不满足时是否返回正确的错误标签
  57. Check if Proper Error Tag is returned when must condition is not satisfied in "length"检查当“length”中必须条件不满足时是否返回适当的错误标签
  58. Check if Proper Error Tag is returned when must condition is not satisfied in "pattern"检查当“pattern”中的必须条件不满足时是否返回适当的错误标记
  59. Check if DELETE fails when ACL Table is tried to Rule or when DELETE tries to delete TABLE with non-empty leafref检查当ACL表被试图规则或当DELETE试图删除表与非空leafref时DELETE是否失败
  60. Check if validation fails when non-existent dependent data is provided.检查当提供不存在的依赖数据时验证是否失败。
  61. Check if CVL validation fails when DELETE tries to delete leafref of another table(delete ACL table referenced by ACL rule)检查当DELETE尝试删除另一个表的leafref时,CVL验证是否失败(删除ACL规则引用的ACL表)
  62. Check if CVL Validation fails when unrelated chained dependent data is given.检查当给定无关的链式依赖数据时,CVL验证是否失败。
  63. Check if CVL Validation fails when VLAN range is out of bound and proper error message is returned当VLAN范围超出范围时,检查CVL验证是否失败,并返回正确的错误消息。
  64. Check if Logs are printed as per configuration in log configuration file.检查日志是否按照日志配置文件中的配置打印。
  65. Check if DELETE operation is performed on single field检查是否在单个字段上执行了DELETE操作
  66. Check if CVL validation passes when valid dependent data is provided using a JSON file.当使用JSON文件提供有效的依赖数据时,检查CVL验证是否通过。
  67. Check if CVL validation is passed when when delete is performed on Table and then connected leafref当在表上执行删除操作并连接到leafref时,检查CVL验证是否通过。
  68. Check if CVL validation is passes when JSON data can be given in file format当JSON数据可以以文件格式提供时,检查CVL验证是否通过。
  69. Check if CVL Finish operation is successful检查CVL Finish操作是否成功
  70. Check if CVL validation passes when Entry can be deleted and created in same transaction当条目可以在同一个事务中被删除和创建时,检查CVL验证是否通过。
  71. Check if CVL validation passes when two UPDATE operation are given当两个 UPDATE 操作同时进行时,检查 CVL 验证是否通过。
  72. Check if CVL validation passes when 'leafref' points to a key in another table having multiple keys.当'leafref'指向另一个表中具有多个键的键时,检查CVL验证是否通过。
  73. Check if CVL validation passes when 'leafref' points to non-key in another table.当'leafref'指向另一个表中的非键时,检查CVL验证是否通过。
  74. Check if CVL validation passes when 'leafref' points to a key which is drived in predicate from another table in cascaded fashion.检查当'leafref'指向一个键时,是否通过CVL验证,该键是通过谓词从另一个表级联方式驱动的。
  75. Check if CVL validation passes when 'must' condition involves checking with fields having default value which are not provided in request a data.当“必须”条件涉及到检查带有默认值的字段时,检查CVL验证是否通过,而这些字段在请求数据时没有提供。
  76. Check if CVL validation passes when 'must' condition has predicate field/key value derived from another table using another predicate in cascaded fashion.检查当'must'条件有谓词字段/键值时,是否通过CVL验证,该值使用级联方式从另一个表中派生。
  77. Check if CVL validation passes for 'when' condition present within a leaf/leaf-list.检查CVL验证是否通过'when'条件出现在一个叶/叶列表。
  78. Check if CVL validation passes for 'when' condition present in choice/case node.检查在选择/案例节点中是否通过了“when”条件的CVL验证。
  79. Check if CVL validation passes if 'max-elements' is present in a YANG list.检查如果'max-elements'出现在YANG列表中,是否通过CVL验证。
  80. 检查CVL是否可以根据leafref强加的依赖关系对给定表的列表进行排序。
  81. 检查CVL是否可以返回给定YANG模块中基于leafref关系的排序表列表。
  82. 检查CVL是否可以返回YANG模块中给定表的依赖表列表。
  83. 检查CVL是否可以返回给定条目被删除或修改的依赖数据(Redis键)。

11 Appendix A 11附录A

以下是管理框架中使用的开源工具列表

  1. Gorilla/mux
  2. Go datastructures
  3. Swagger
  4. gNMI client
  5. goyang
  6. YGOT
  7. GO Redis
  8. Logging
  9. Profiling
  10. Validation
  11. JSON query 
  12. XML query
  13. Sorting 
  14. pyangbind
  15. libYang
  16. OpenAPI

12 附录B

以下是GNMI容器中使用的开源库列表。 请始终参考sonic-gnmi容器的Makefile以获取当前包列表。

  1. GRPC
  2. GNMI
  3. Protobuf
  4. goyang
  5. GO
  6. YGOT
  7. Logging
  8. GO Context
  9. Credentials
  10. Validation 
  11. GNXI utils
  12. Gorilla/mux
  13. jipanyang/xpath
  14. c9s/procinfo
  15. Workkiva/Queue
  16. jipanyang/gnmi client
  17. xeipuuv/gojsonschema
  • 19
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值