Neutron-lib 中解耦数据库资源模型导入/访问

这项工作与增强请求无关,因此没有 RFE。本文档的意图是讨论如何解耦 neutron.db 以及相关的数据库导入/访问,作为整体 neutron-lib 工作的一部分。

当前 neutron 数据库访问可以分为以下几个高级别类别

  • 数据库 API 与实用工具

  • 核心与扩展数据库 Mixin

  • 数据库资源模型

  • 数据库迁移

由于数据库访问模式涵盖了广泛的逻辑/代码,将提出一组规范,每个规范侧重于单一的访问模式。

本规范专门针对数据库资源模型访问。

有关当前 neutron-lib 相关蓝图,请参阅 [1][2]

问题描述

作为 neutron-lib 工作的一部分,我们需要解耦依赖于 neutron(例如 import)的树外网络项目。然而,今天有大量的 neutron 消费者导入了与数据库相关的 neutron 模块,包括模型 [3] [4]

消费者访问数据库模型通常用于

  • 定义 neutron 和其他项目模型之间的关系。请注意,虽然在定义关系时可以使用字符串名称,但由于 [8],这并非在所有情况下都可行的解决方案。

  • 使用模型和/或其字段构建查询。随着版本化对象(versioned objects)的引入,消费者可以通过对象上的方法有效地查询。因此,本规范的交付成果通过 neutron-lib 提供对版本化对象的访问,从而实现这种方法。

  • 导入模型以使其可用于数据库工具。这包括数据库迁移工具,因此,虽然本规范不能直接解决迁移访问问题,但它需要为其奠定基础(在单独的规范中涵盖)。

本规范的意图是提出一种方法,通过在 neutron-lib 中建立桥接,从而提供对数据库模型的访问,而无需直接 neutron 导入,从而打破消费者对 neutron 内部数据库模型的依赖。

提议的变更

虽然一个可预见的解决方案是使用可发现的入口点(stevedore)发布模型或使用工厂注册它们,但这些模型必须进行版本化,以便消费者可以确定兼容性。如果没有版本化,底层模型可能会在消费者不知情的情况下发生变化,从而导致消费者出现问题。

实现版本化模型使用一些新的机制很简单,但我们已经有了这样一个版本化方案;neutron 版本化对象。Neutron 对象已经包含对底层模型的引用以及一个随着模型变化而递增的版本号 [5]。因此,如果我们能提供一种让消费者获取 neutron 对象的方式,他们就可以访问版本、相应的模型等。

本规范建议使用一个简单的入口点方案,其中所有版本化对象都定义为可以被 neutron-lib 中的一些桥接逻辑查找和提供的入口点。更具体地说

  • Neutron 版本化对象可以通过 setup.cfg 中的入口点“发布”,其中每个入口点都是一个版本化对象类。这会将对象暴露给 neutron-lib,neutron-lib 可以使用 stevedore 发现和加载它们。

  • neutron-lib 中的一个简单的 API,允许消费者在运行时检索版本化对象。在基本形式上,消费者请求类型为 X 的对象, whereupon neutron-lib 从入口点查找它并将其具体的对象类返回给消费者(例如,load_class('Port') 查找并返回 Port 的版本化对象类)。

  • 如果消费者需要创建从 neutron-lib 返回的版本化对象的实例,他们可以直接调用其构造函数来创建新实例。由于版本化对象构造函数基于对象的 fields,并且 fields 与模型相关联,因此消费者可以查询对象的 VERSION 以确定与构造函数的兼容性。

以下代码片段说明了从消费者角度来看的 API

from neutron_lib.objects import registry
# .. other imports

# Loading the object from neutron-lib uses stevedore to find the requested
# object. Also note that under the covers the model is imported since the
# concrete versioned object must import the model to ref it.
port_ovo = registry.load_class(constants.PORT)

if port_ovo.VERSION > '1.1':
    raise UnsupportedException("Can't use port objects greater than 1.1")

# It's just an OVO class, so we can use it's static/class methods directly
a_port = port_ovo.get_object(...)

# Create a new versioned object; it's VERSION determines the constructor's
# supported kwargs so consumers can detect compatibility
new_port = port_ovo(context, project_id=...)

如前所述,虽然此解决方案不能完全解决数据库迁移访问问题,但它为我们提供了一种查找和加载版本化对象的方法,这些对象必须导入相应的模型。

使用此方案,我们应该能够消除消费者对以下内容的导入

  • 现有的 neutron.objects 导入 [6]。消费者现在从本文档中提供的 neutron-lib API 获取其对象。

  • 构建查询;版本对象具有允许消费者有效查询的方法,因此消费者不应需要构建直接查询。

此外,此功能的推出对主代码路径的影响应该最小;neutron 中真正的变化是使用 setup.cfg 暴露对象。neutron-lib 逻辑可以独立实现、测试和推出,从而降低风险。

有关示例概念验证,请参阅 [7] 上的补丁,这些补丁使用这种方法来删除/使用 vmware-nsx 模块中的版本化对象。

参考资料