改进 Extraroute API

https://bugs.launchpad.net/neutron/+bug/1826396

正如在 openstack-discuss 线程 中讨论的那样,我们可以改进 extraroute API,以更好地支持 Neutron API 客户端,特别是 Heat。

问题描述

第一个问题是,当前的 extraroute API 不允许原子地添加/删除特定的路由表条目。在当前的 API 中,路由器的 routes 属性(包含所有路由表条目)必须一次性更新。因此,添加和删除必须在客户端进行。因此,多个客户端争相更新 routes 属性,更新可能会丢失。

可以通过 参考部分中包含的简单 HOT 模板 轻松(但非确定性地)重现此问题。

$ openstack stack create --wait -t extraroute-concurrent.yaml stack0
$ openstack stack resource list stack0 --filter name=router0 -f value -c physical_resource_id \
  | xargs -r openstack router show -f value -c routes

Neutron API 的程序化用户感知到的第二个问题是,无法表达额外的路由的所有权,因为它们没有唯一的标识符。例如,考虑两个 Heat 堆栈(或 Neutron API 的任何其他用户)需要并因此创建相同的额外路由。当其中一个被删除时,除非我们有一种跟踪相同额外路由的多种需求的方法,否则额外的路由将从两者中删除。由于解决第二个问题将涉及更改额外路由的概念抽象,因此此规范中未解决此问题。

提议的变更

此 RFE 建议以一种新的方式公开 Neutron 的 extraroute 功能,而不仅仅是当前路由器上的 ‘routes’ 属性。

引入新的 API 扩展:extraroutes-atomic

PUT /v2.0/routers/{router_id}/add_extraroutes

{ "router":
  { "routes":
    [ { "destination": "179.24.1.0/24",
        "nexthop": "172.24.3.99" },
      ...
    ]
  }
}
200 OK

{ "router":
  { "id": "1ecae6b8-be64-11e9-98ba-733d5460217b",
    "name": "router1",
    "routes":
    [ { "destination": "179.24.1.0/24",
        "nexthop": "172.24.3.99" },
      ...
    ],
    ...
  }
}
PUT /v2.0/routers/{router_id}/remove_extraroutes

{ "router":
  { "routes":
    [ { "destination": "179.24.1.0/24",
        "nexthop": "172.24.3.99" },
      ...
    ]
  }
}
200 OK

{ "router":
  { "id": "1ecae6b8-be64-11e9-98ba-733d5460217b",
    "name": "router1",
    "routes":
    [ remaining routes ],
    ...
  }
}

不允许部分失败。如果添加或删除任何路由表条目失败,则整个更新将被回滚。

如果要添加(删除)的路由表条目已经存在(丢失),则不是错误,而是被视为成功的更新。

重复和重叠路由的处理方式不会从当前行为更改。也就是说,完全重复的路由会被拒绝,但允许重叠的路由。

删除路由器会级联到删除该路由器上的所有额外路由。

Neutron 中的 DB 和 OVO 层已经准备好进行此更改,routes 属性已经被分解为它自己的表

mysql> describe routerroutes;
+-------------+-------------+------+-----+---------+-------+
| Field       | Type        | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| destination | varchar(64) | NO   | PRI | NULL    |       |
| nexthop     | varchar(64) | NO   | PRI | NULL    |       |
| router_id   | varchar(36) | NO   | PRI | NULL    |       |
+-------------+-------------+------+-----+---------+-------+

进一步的更改包括

  • 文档:api-ref。

  • 单元测试。

  • neutron-tempest-plugin 中的 Tempest 测试。

  • 适配 python-neutronclient。

  • 适配 openstackclient。

  • 适配 openstacksdk。

  • 我也希望基于这项工作改进 Heat 的 OS::Neutron::Extraroute 资源,但将在其自身的 Heat 蓝图描述。

弃用

一旦新的 API 合并,直接更新路由器的 ‘routes’ 属性将被弃用,但与 Neutron 长期不进行向后不兼容的 API 更改的传统保持一致,旧的 extraroutes 扩展不会被删除。

其他影响

预计对升级没有影响。预计对配置没有影响。预计对 RPC 没有影响。

备选方案

比较和交换

Neutron 具有 比较和交换 API 更新逻辑。但是,使用它来解决此问题在客户端会很麻烦(如果 routes 属性在客户端编辑时发生更改,则重试直到成功)。此外,随着争用客户端数量的增加,使用比较和交换 API 将会产生不必要的 API 负载,因为更新请求必须被丢弃并重试。

此外,当前客户端代码库中没有内置对比较和交换操作的支持。

有关其他替代方案,请参阅此规范的审查讨论。

参考资料

extraroute-concurrent.yaml

description: test of extraroute concurrency
heat_template_version: 2015-04-30

resources:

  net0:
    type: OS::Neutron::Net

  subnet0:
    type: OS::Neutron::Subnet
    properties:
      network: { get_resource: net0 }
      cidr: 10.0.0.0/24

  router0:
    type: OS::Neutron::Router

  routerinterface0:
    type: OS::Neutron::RouterInterface
    properties:
      router: { get_resource: router0 }
      subnet: { get_resource: subnet0 }

  extraroute0:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.0.0/24
      nexthop: 10.0.0.10
      router_id: { get_resource: router0 }

  extraroute1:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.1.0/24
      nexthop: 10.0.0.11
      router_id: { get_resource: router0 }

  extraroute2:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.2.0/24
      nexthop: 10.0.0.12
      router_id: { get_resource: router0 }

  extraroute3:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.3.0/24
      nexthop: 10.0.0.13
      router_id: { get_resource: router0 }

  extraroute4:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.4.0/24
      nexthop: 10.0.0.14
      router_id: { get_resource: router0 }

  extraroute5:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.5.0/24
      nexthop: 10.0.0.15
      router_id: { get_resource: router0 }

  extraroute6:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.6.0/24
      nexthop: 10.0.0.16
      router_id: { get_resource: router0 }

  extraroute7:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.7.0/24
      nexthop: 10.0.0.17
      router_id: { get_resource: router0 }

  extraroute8:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.8.0/24
      nexthop: 10.0.0.18
      router_id: { get_resource: router0 }

  extraroute9:
    type: OS::Neutron::ExtraRoute
    properties:
      destination: 10.1.9.0/24
      nexthop: 10.0.0.19
      router_id: { get_resource: router0 }