Neutron 常见分类框架

https://blueprints.launchpad.net/neutron/+spec/common-classification-framework https://wiki.openstack.org/wiki/Neutron/CommonClassificationFramework

问题描述

不同的 Neutron 项目、服务、特性以及相关项目拥有不同的流量分类器或分类 API。它们在语法和语义上都存在差异。一些例子包括

  • 安全组规则 [6]

  • openstack/neutron-fwaas [7]

  • openstack/networking-sfc (流分类器) [8]

  • openstack/neutron-classifier [2]

  • openstack/networking-bgpvpn [12]

  • openstack/tap-as-a-service [13]

  • Neutron QoS [10]

此外,还有其他项目具有分类 API,例如 openstack/group-based-policy [9],并且可能还有更多项目希望在其自身的 API 中支持分类,从而重复造轮子,并进一步分割 OpenStack 生态系统中定义流量分类所使用的语言。

本规范引入了 Neutron 常见分类框架 (CCF),它是先前努力的结晶:通用分类器 [1]、openstack/neutron-classifier [2] 和通用流分类器 [11]

该 RFE 的先前规范已存档在 [1]

提议的变更

术语

  • 常见分类框架 (CCF):该项目的名称,包括下面介绍的基础模型和资源、API、逻辑和版本化对象,以允许消费服务从框架中获取资源。

  • 分类:为定义原子流量分类而引入的 Neutron 资源。

  • 分类组 (CG):为将分类或其他分类组分组在一起而引入的 Neutron 资源,指定它们之间的共同关系。这是最终将被消费服务使用的资源。

  • 分类类型 (CT):每个分类都有一个类型,定义该分类的格式、支持的字段和语法验证器。

  • 分类 API:通用分类框架提供的用户面向的 REST API,用于实例化上述资源。

  • 消费服务 (CS):Neutron 本身或 Neutron 项目、服务、扩展或特性,能够消费分类组(将其作为资源获取),潜在的消费服务示例是问题描述中列出的那些。这些服务不直接与分类 API 通信。

  • 用户:最终用户,无论是人类还是机器,租户还是管理员,通常是云消费者,调用分类 API 和任何消费服务的 REST API。

本规范定义了要引入的新资源类型、初始的一组分类类型及其模型,以及分类 API,以便定义此类流量分类,用户将调用这些分类。

专用的分类 API 提供了一种定义和创建分类和 CG 的方法,将其存储为任何其他资源。CS 能够将这些流量分类定义作为 oslo.versionedobjects (OVO) 获取,并将其提供给各自的内部网络分类器(即,能够匹配流量的机制,例如通过各自的 ML2 机制驱动程序使用 Open vSwitch,或 iptables - 这实际上取决于使用分类组的服务及其配置)。消费服务如何使用消耗的资源完全超出 CCF 的范围。

API 中不允许更新分类或 CG 资源,除非更改与流量分类语义无关,例如名称和描述。这样,也可以避免处理向 CS 宣传资源更改的复杂性,至少对于 CCF 的第一个版本来说,这是有益的,因为它使项目更简单。

消费服务不需要保留消耗的分类的本地数据库副本,而只需映射 CG UUID 与消费服务自身的资源。因此,通用分类框架应防止在至少一个消费服务的资源使用它们时删除分类。

创建通用分类框架所需的更改摘要

  • 创建一个 Neutron 扩展,以暴露新版本的分类 API,因为其他修改需要新的扩展。

  • 创建支持的协议类型/定义的模型。

  • 创建一个 Neutron 服务插件和 DB 层,以根据定义的类型和各自的支持模型验证 API 定义的分类,并将它们存储在数据库中。

  • 采用 OVO,以便定义的每个分类都成为一个版本化和可序列化的对象,该对象将从通用分类框架传输到消费服务。

  • 使用 OSC 创建 CLI,以便用户可以轻松定义自己的分类。

虽然超出范围,但这是消费服务中需要的更改摘要

  • 添加对 CCF 自身的扩展的依赖。

  • 在某些 API 资源中添加(或重用)字段,以便接收分类组 UUID(例如,networking-sfc port-chain 的 flow_classifiers 字段可以期望一个通用的分类组 UUID,而不是本地流分类器 UUID)。

  • 评估提供的分类,以确定它们是否与特定扩展的分类器(机制)兼容。

  • 添加逻辑以通过 UUID 获取 CG,在此过程中使用 OVO,可能通过 neutron-lib (xgerman)。

  • 将分类的定义转换为特定的分类器机制或语言(例如,创建 Open vSwitch 流以匹配流量)。

  • 通常,消费服务已经拥有自己的 CLI,因此必须与对其自身 API 和/或分类获取代码所做的更改保持一致。

[5] 显示了用户、通用分类框架和消费服务之间的关系。

数据模型影响

通过将流量分类按单个类型分组,称为分类类型,将允许将来添加类型并在未来达成一致,同时保持其余类型不变。现有类型当然可以更新,这得益于版本控制。因此,这种方法遵循模块化而非单体的方式来定义分类。查看现有的 neutron-classifier 项目 [2],它被决定为保留通用分类器 [3](在 2015 年东京峰会上),其中可以看到类似于此处提出的架构的提示,如 [4] 所示。因此,该框架部分基于 neutron-classifier,但主要区别在于它为用户提供了一个 REST API 来定义分类。

在深入研究数据模型、API 以及感兴趣的 Neutron 子项目如何使用分类的更多细节之前,需要澄清几点

  • 分类类型可以在 CCF 的每个版本中引入或扩展(例如,使用新的字段)。将添加 API 扩展以反映 REST API 中的这些添加并保持向后兼容性。

  • 一个分类属于一个类型,例如,以太网、IP、HTTP 或 CCF 特定版本时支持的任何其他类型。定义,即匹配的字段,取决于指定的类型。

  • 为了澄清,分类类型定义了分类的可能字段和值的集合(本质上,该类型的实例)。分类类型在代码中定义,而分类通过 REST API 作为这些类型的实例创建。

  • 不需要定义所有支持的字段 - 只需要消费服务所需的字段 - 消费服务应该在消费时对其进行验证。

  • 还有分类组,允许将分类或其他分类组使用布尔运算符分组在一起。CG 是最终将被消费服务使用的资源。

  • CCF 必须能够检查分类组是否当前正在使用,并防止如果正在使用则删除它。

  • 消费服务只需要读取分类,而不能创建或删除它们。它们需要在用户面向的分类 API 中预先创建。

CCF 的初始模型将包括以下分类类型:以太网、IPv4、IPv6、TCP 和 UDP,这些组合足以提供任何 5 元组分类。

下表显示了分类组的属性(RW 上的星号表示该属性不可更新)

属性名称

类型

访问 CRUD

默认值

验证/转换

描述

id

字符串 (UUID)

RO,全部

生成

uuid

Identity

project_id

字符串 (UUID)

RO,项目

从身份验证令牌

uuid

项目ID

name

string

RW,项目

string

分类组名称

description

string

RW,项目

string

人类可读的描述

共享

bool

RW,项目

False

布尔值

与其他项目共享

运算符

字符串(值)

RW*,项目

“and”

[“and”,

“or”]

布尔连接词:AND/OR

classification_groups

list

RW*,项目

[]

包含的分类组列表

classifications

list

RW* 项目

[]

包含的分类列表

消费服务将消费分类组,而不是原子分类(这将给 CCF 和 CS 数据库之间的关系带来更多困难),任何分类都需要分组到一个分类组中才能单独消费。因此,对于仅包含一个分类的分类组,应忽略“operator”字段。

下表显示了任何本规范中规定的类型的分类的属性(RW 上的星号表示该属性不可更新)

属性名称

类型

访问

默认值

验证/转换

描述

id

字符串 (UUID)

RO,全部

生成

uuid

Identity

project_id

字符串 (UUID)

RO,项目

从身份验证令牌

uuid

项目ID

name

string

RW,项目

string

分类名称

description

string

RW,项目

string

人类可读的描述

type

string

RW*,项目

从类型的枚举

分类的类型

negated

bool

RW*,项目

False

布尔值

是否否定分类(布尔 NOT)

definition

类型特定的属性将在此处,鉴于其数量,除非请求,否则我不会详细说明它们。

分类组和每种类型的分类将存储为以下表和关系(表名带有前缀 ccf_

                          +---------------------+
                          |classification_groups|
                          +---------------------+
                          |id                   |*
                          |cg_id                +--------+
                          |name                 |        |
                          |description          |        |
                          |project_id           |        |
                          |shared               +--------+
                          |operator             |1
                          +---------------------+
                                      |1
                                      |
                                      |*
                      +------------------------------+
                      |classification_groups_mapping |
                      +------------------------------+
                      |cg_id                         |
                      |classification_id             |
                      +------------------------------+
                                      |1
+--------------------+                |                +--------------------+
|ipv4_classifications|                |                |ipv6_classifications|
+--------------------+                |                +--------------------+
|classification_id   |                |                |classification_id   |
|ihl                 |1               |               1|traffic_class       |
|diffserv            +--------+       |       +--------+traffic_class_mask  |
|diffserv_mask       |        |       |       |        |length              |
|length              |        |       |       |        |next_header         |
|flags               |        |       |       |        |hops                |
|flags_mask          |        |       |       |        |src_addr            |
|ttl                 |        |1      |1     1|        |dst_addr            |
|protocol            |     +---------------------+     +--------------------+
|src_addr            |     |classifications      |
|dst_addr            |     +---------------------+
|options             |     |id                   |
|options_mask        |     |name                 |
+--------------------+     |description          |
                           |project_id           |
                           |shared               |     +-------------------+
                           |type                 |     |tcp_classifications|
                           |negated              |     +-------------------+
                           +---------------------+     |classification_id  |
+-------------------+        1|      1|       |1       |src_port           |
|udp_classifications|         |       |       |        |dst_port           |
+-------------------+         |       |       |        |flags              |
|classification_id  |1        |       |       |       1|flags_mask         |
|src_port           +---------+       |       +--------+window             |
|dst_port           |                1|                |data_offset        |
|length             |     +------------------------+   |option_kind        |
|window_size        |     |ethernet_classifications|   +-------------------+
+-------------------+     +------------------------+
                          |classification_id       |
                          |preamble                |
                          |src_addr                |
                          |dst_addr                |
                          |ethertype               |
                          +------------------------+

上述数据库模式中的分类类型的一些字段,例如 lengthsrc_addr 等,将允许通过使用逗号或连字符输入范围或列表,例如。

屏蔽字段允许用户指定在分类期间应查找相应主字段的哪些单独位。例如。

除了上述分类类型之外,以下类型预计也将包含在 CCF 的第一个版本中:- Neutron(目标/源端口、子网、网络,至少)- ICMP - ICMPv6 - SCTP - ARP - VLAN - GRE - VXLAN - Geneve - MPLS - NSH

分类类型用于选择分类的适当模型,从而确定它将存储在哪个表中。

分类组存储在单个表中,可以指向其他分类组,以允许混合布尔运算符。

有两个重要的字段用于布尔逻辑

  • operator 在分类组中:指定用于连接该组的所有子分类和分类组的布尔运算符。这可以是 AND 或 OR。

  • negated 每个分类的“用法”:指定在映射到分类组时是否否定分类的定义,本质上是一个布尔 NOT。这可以是 True 或 False。请注意,分类组不能使用此模型被否定。

REST API

正在引入一个新的 API 扩展。分类 API 的基本 URL 为 /v2.0/。

下表总结了可用的 URI

+----------------------+------------------------------------+-------+
|Resource              |URI                                 |Type   |
+======================+====================================+=======+
|classification_types  |/classification_types               |GET    |
+----------------------+------------------------------------+-------+
|classification_group  |/classification_groups/             |POST   |
+----------------------+------------------------------------+-------+
|classification_groups |/classification_groups              |GET    |
+----------------------+------------------------------------+-------+
|classification_group  |/classification_groups/{id}         |GET    |
+----------------------+------------------------------------+-------+
|classification_group  |/classification_groups/{id}         |PUT    |
+----------------------+------------------------------------+-------+
|classification_group  |/classification_groups/{id}         |DELETE |
+----------------------+------------------------------------+-------+
|classification        |/classifications/                   |POST   |
+----------------------+------------------------------------+-------+
|classifications       |/classifications                    |GET    |
+----------------------+------------------------------------+-------+
|classification        |/classifications/{id}               |GET    |
+----------------------+------------------------------------+-------+
|classification        |/classifications/{id}               |PUT    |
+----------------------+------------------------------------+-------+
|classification        |/classifications/{id}               |DELETE |
+----------------------+------------------------------------+-------+

CCF 应提供一种标记分类组为正在使用(或增加使用计数)的方法,并提供一种检查该方法并中止某些操作(如果该组正在使用)的方法。

CCF 不提供任何将分类组同步到消费服务的机制。

包含两个分类的分类组的示例。

列出可用的分类类型

GET /v2.0/classification_types

Response:
{
   "classification_types": [{"type": "ethernet"},
                            {"type": "ipv4"},
                            {"type": "ipv6"},
                            {"type": "tcp"},
                            {"type": "udp"}]
}

创建 TCP 类型的分类

POST /v2.0/classifications/
{
    "classification": {
        "name": "not_tcp_syns",
        "type": "tcp",
        "negated": true,
        "definition": {
            "control_flags": "0x2",
            "control_flags_mask: "0x2"
        }
    }
}

Response:
{
    "classification": {
        "id": "3dcc561a-1bb8-11e7-b615-23717626a4e5",
        "project_id": "0a36035e-1bb9-11e7-b8ef-e782361fd276",
        "name": "not_tcp_syns",
        "description": "",
        "type": "tcp",
        "negated": true,
        "shared": false,
        "definition": {
            "src_port": null,
            "dst_port": null,
            "control_flags": "0x2",
            "control_flags_mask: "0x2",
            "ecn": null,
            "ecn_mask": null,
            "min_window": null,
            "max_window": null,
            "min_data_offset": null,
            "max_data_offset": null,
            "option_kind": null
        }
    }
}

创建以太网类型的分类

POST /v2.0/classifications/
{
    "classification": {
        "name": "ipv4_over_eth",
        "type": "ethernet",
        "definition": {
            "ethertype": "0x800"
        }
    }
}

Response:
{
    "classification": {
        "id": "021c1ad2-1bb9-11e7-907d-937a75c8a5db",
        "project_id": "0a36035e-1bb9-11e7-b8ef-e782361fd276",
        "name": "ipv4_over_eth",
        "description": "",
        "type": "ethernet",
        "negated": false,
        "shared": false,
        "definition": {
            "negated": false,
            "preamble": null,
            "src_addr": null,
            "dst_addr": null,
            "ethertype": "0x800"
        }
    }
}

创建分类组

POST /v2.0/classification_groups/
{
    "classification_group": {
        "name": "no_syns_on_ipv4",
        "description": "Any IPv4 traffic carried over Ethernet except TCP SYNs.",
        "operator": "and",
        "classifications": [
            "3dcc561a-1bb8-11e7-b615-23717626a4e5",
            "021c1ad2-1bb9-11e7-907d-937a75c8a5db"
        ]
    }
}

Response:
{
    "classification_group": {
        "id": "387299fa-250d-11e7-8620-b38d21865984",
        "project_id": "0a36035e-1bb9-11e7-b8ef-e782361fd276",
        "name": "no_syns_on_ipv4",
        "description": "Any IPv4 traffic carried over Ethernet except TCP SYNs.",
        "shared": false,
        "operator": "and"
        "classifications": [
            "3dcc561a-1bb8-11e7-b615-23717626a4e5",
            "021c1ad2-1bb9-11e7-907d-937a75c8a5db"
        ],
        "classification_groups": []
    }
}

列出分类组

GET /v2.0/classification_groups

Response:
{
    "classification_groups": [
        {
            "id": "387299fa-250d-11e7-8620-b38d21865984",
            "project_id": "0a36035e-1bb9-11e7-b8ef-e782361fd276",
            "name": "no_syns_on_ipv4",
            "description": "Any IPv4 traffic carried over Ethernet except TCP SYNs.",
            "shared": false,
            "operator": "and"
            "classifications": [
                "3dcc561a-1bb8-11e7-b615-23717626a4e5",
                "021c1ad2-1bb9-11e7-907d-937a75c8a5db"
            ],
            "classification_groups": []
        },
        {
            ...
        }
    ]
}

社区影响

打算消费分类的服务只需要

  • 稍微修改其现有的 REST API,以允许传递分类组 UUID,如果它们还没有用于此目的的端点(例如,networking-sfc 不需要 port-chain API 资源更改)。

  • 实现分类定义的底层获取,支持 OVO。

其他最终用户影响

将扩展 OpenStack 客户端以支持分类 API。

一个假设场景的可能的 CLI 语法,使用 Neutron QoS 作为消费服务和上述 API 调用中呈现的分类组,以说明工作流程

$ openstack network classification create --type=tcp --control_flags=0x2 --control_flags_mask=0x2 --negated tcp_syns
$ openstack network classification create --type=ethernet --ethertype=0x800 ipv4_over_eth
$ openstack network classification group create --description "Any IPv4 traffic carried over Ethernet except TCP SYNs." \
      --classification tcp_syns --classification ipv4_over_eth --operator and no_syns_on_ipv4
$ openstack network qos rule create --type bandwidth-limit --max-kbps 8000 --classification-group no_syns_on_ipv4 myqospolicy

备选方案

数据模型和 REST API 的可能替代方案是

  • Neutron QoS 启发模型;

  • 使用 oslo.versionedobjects 序列化为 JSON 的 REST API,以支持此处指定的完全相同的资源。

  • 关于初始模型,它也可以包括安全组规则 UUID、BGP VPN 资源 UUID、HTTP 以及更多。

实现

已经作为初始概念验证开始工作,可在 [14] 中找到。在 neutron-classifier 存储库上合并后,工作将继续朝着本规范中概述的目标进行。

负责人

我们请求 Neutron 团队提供 neutron-classifier 仓库的访问权限。

鉴于 PoC 代码的结果,预计不会重用 neutron-classifier 中现有的代码,因此该仓库将被清空(但所有历史记录将被保留)。

工作项

  • 原型/PoC (Pike-1) (David) - 已完成。

  • 完成规范 (Pike) (Igor) - 正在进行中。

  • 获取合并到仓库的权限 (Pike) (Igor) - 正在进行中。

  • 实现该项目的第一个可用版本 (Pike 到 Queens) (David, Igor)

  • 将首次支持带到 Neutron 服务 (TBD) (Queens 到 Rocky)

参考资料