模块管理¶
Trove 历来都支持开源数据库。随着添加的数据存储越来越多,不可避免地,这种情况最终会改变,以包括专有数据库。从 Liberty 版本开始,情况就是如此(现在支持 Vertica 和 DB2),随之而来的是管理这些数据库许可证的问题。此外,运营商可能会发现将其他软件(可能需要最终用户“激活”,例如 New Relic 的分析套件)包含在他们的镜像中很有用。还需要一种激活此软件的方法。
本文中,将此第三方软件的“许可证”或“激活”或“配置”的应用称为“模块”管理。
Launchpad 蓝图:https://blueprints.launchpad.net/trove/+spec/module-management
问题描述¶
特定云的用户可能愿意以按需付费的模式从云供应商那里购买数据存储的使用许可证,这是目前假定的模式(因为 Vertica 和 DB2 redstack 镜像都包含一个功能齐全且获得许可的数据库)。但是,也希望允许用户“自带许可证”。在这种情况下,用户提供数据库所需的许可证文件,因此需要建立一种机制,通过使用用户提供的文件来“激活”和/或“续订”许可证。
对于运营商可能希望包含在他们的 Trove 镜像中的任何其他专有软件,也存在同样的问题。这些软件包通常也需要通过使用许可证密钥或文件(例如 New Relic 1)或某种配置进行激活。
提议的变更¶
Trove 对模块管理的责任将仅限于加密和存储所需数据文件(例如许可证文件)以及提供将此数据应用于新的或现有 Trove 实例或集群的方法。将建立一种机制,允许最终用户管理这些模块数据文件的添加、删除、列出、查看和更新。
还将提供在 Trove 实例上应用、删除、查询和检索实际“模块”数据文件的方法。
将在 create 和 cluster-create 命令中添加一个可重复的选项(–module),以允许临时模块选择。此外,模块可以设置为自动应用,这将使 Guest Agent 在使用相关数据存储组合创建的任何实例上安装该模块。
配置¶
预计将进行以下配置更改。
为了在模块创建时进行正确验证,需要一种指定有效模块“类型”的方法
cfg.StrOpt('module_types', default=None,
help='A list of module types supported.'),
需要一个密钥才能在将模块数据文件存储到数据库中之前对其进行加密
cfg.StrOpt('module_aes_cbc_key', default='module_aes_cbc_key',
help='OpenSSL aes_cbc key for module encryption.'),
数据库¶
将在 Trove 架构中添加一个新表 (modules)
列
类型
允许空值
描述
id
varchar(36)
否
模块 ID(自动生成)
type
varchar(255)
否
模块类型。这将直接与所需的插件相关(即必须存在此“类型”的插件)
tenant_id
varchar(36)
否
要应用模块的租户 ID。“all”表示模块适用于所有租户
数据存储
varchar(36)
否
要应用模块的数据存储名称。“all”表示模块适用于所有数据存储
datastore_version
varchar(36)
否
要应用模块的数据存储版本名称。“all”表示模块适用于所有数据存储
name
varchar(255)
否
模块名称
description
varchar(512)
是
模块描述
auto_apply
tinyint(1)
否
是否应在实例/集群创建期间自动应用此模块。如果未提供,则默认为“否”
visible
tinyint(1)
否
此模块是否应对非管理员用户可见。如果未提供,则默认为“是”
live_update
tinyint(1)
否
此模块是否可以在已应用实例仍然存在时进行更新。如果设置为“否”,则所有实例必须先移除相应的模块才能进行更新。默认为“否”
contents
blob
否
加密的模块内容
md5
varchar(32)
否
模块内容的 MD5 散列
created
DateTime
否
创建日期
updated
DateTime
否
更新日期
deleted
tinyint(1)
是
删除标志
deleted_at
DateTime
是
删除日期
将从 (datastore, datastore_version, name) 字段创建一个唯一索引,以便轻松确定要应用于特定实例的正确模块。
模块内容的 MD5 散列也将存储在模块记录中。查询正在运行的实例中的模块时,将报告此散列,因为原始模块记录在最初应用后可能已使用新内容进行了修改。
在给定实例上安装模块内容时,将以 <datastore>-<datastore_version>-<name>.lic 作为模式,在已知位置创建文件。
创建适用于“所有”租户或“所有”数据存储的模块以及自动应用的模块将需要管理员凭据。
将模块设置为“不可见”也是仅限管理员的选项。这将允许管理员根据需要向用户“隐藏”模块。标记为 visible=False 的模块将不会在列表或显示等命令中返回,除非由管理员用户请求。非管理员用户将无法应用不可见的模块,但是如果指定,它们仍将自动应用。
将在 Trove 架构中添加一个新表 (instance_modules) 以跟踪已应用于每个实例的模块
列
类型
允许空值
描述
id
varchar(36)
否
关联 ID(自动生成)
instance_id
varchar(36)
否
实例 ID
module_id
varchar(36)
否
模块 ID
md5
varchar(32)
否
模块内容的 MD5 散列
created
DateTime
否
创建日期
updated
DateTime
否
更新日期
deleted
tinyint(1)
是
删除标志
deleted_at
DateTime
是
删除日期
公共 API¶
新的 ReST API 调用将添加到 Trove 基础架构中。这些分为两类 - 用于管理实际模块维护的调用,以及用于处理实例交互的调用。
此外,create 和 cluster-create 调用将得到增强。
模块维护¶
要检索可以应用的所有模块的列表,将发出以下请求
请求
GET v1/modules
响应
{
'modules' : [
{
'id': <id>,
'type': 'vertica_license',
'tenant': <id>,
'datastore': 'vertica',
'datastore_version': 'all',
'name': '100GB',
'description': 'Vertica license for 100GB',
'auto_apply': False,
'visible': True, # returned for admin only
'live_update': False,
'md5': <md5>,
'created': <date>,
'updated': <date>,
},
{
'id': <id>,
'type': 'new_relic_activation',
'tenant': <id>,
'datastore': 'all',
'datastore_version': 'all',
'name': 'new_relic',
'description': 'New Relic activation',
'auto_apply': True,
'visible': True, # returned for admin only
'live_update': True,
'md5': <md5>,
'created': <date>,
'updated': <date>,
},
]
}
响应代码
200 Success
请注意,管理员用户将收到所有租户的模块,而普通用户将只看到其租户的模块。
要检索可以应用于特定数据存储的有效模块列表,将发出以下请求
请求
GET v1/datastores/{datastore_id}/modules
响应
{
'modules' : [
{
'id': <id>,
'type': 'new_relic_activation',
'tenant': <id>,
'datastore': 'all',
'datastore_version': 'all',
'name': 'new_relic',
'description': 'New Relic activation',
'auto_apply': True,
'visible': True, # returned for admin only
'live_update': True,
'md5': <md5>,
'updated': <date>,
},
]
}
响应代码
200 Success
要显示特定模块的详细信息,将发出以下请求
请求
GET v1/modules/<id>
响应
{
'id': <id>,
'type': 'new_relic_activation',
'tenant': <id>,
'datastore': 'all',
'datastore_version': 'all',
'name': 'new_relic',
'description': 'New Relic activation',
'auto_apply': True,
'visible': True, # returned for admin only
'live_update': True,
'md5': <md5>,
'created': <date>,
'updated': <date>,
}
响应代码
200 Success
404 Not Found
要创建模块,将发出以下请求
请求
POST /v1.0/modules
{
'type': 'vertica_license',
'tenant': <id>,
'datastore': 'vertica',
'datastore_version': 'all',
'name': '100GB',
'description': 'Vertica license for 100GB',
'auto_apply': False,
'visible': False, # admin-only option
'live_update': True,
'contents': <module_contents>,
}
响应
{
"module": {
'id': <id>,
'type': 'vertica_license',
'tenant': <id>,
'datastore': 'vertica',
'datastore_version': 'all',
'name': '100GB',
'description': 'Vertica license for 100GB',
'auto_apply': False,
'visible': False, # returned for admin only
'live_update': True,
'md5': <md5>,
'created': <date>,
'updated': <date>,
}
}
响应代码
200 Success
400 Bad Request
要更新模块,将发出以下请求
请求
PATCH /v1.0/modules/{module_id}
{
'type': 'new_type',
'tenant': <id>,
'datastore': 'new_datastore',
'datastore_version': 'new_datastore_version',
'name': 'new_name',
'description': 'new_description',
'auto_apply': True,
'visible': False, # admin-only option
'live_update': True,
'contents': <module_contents>,
}
响应
{
"module": {
'id': <id>,
'type': 'new_type',
'tenant': <id>,
'datastore': 'new_datastore',
'datastore_version': 'new_datastore_version',
'name': 'new_name',
'description': 'new_description',
'auto_apply': True,
'visible': False, # returned for admin only
'live_update': True,
'md5': <new_md5>,
'created': <date>,
'updated': <date>,
}
}
响应代码
200 Success
400 Bad Request
404 Not Found
要删除模块,将发出以下请求
请求
DELETE /v1.0/modules/{module_id}
{
}
响应
This operation has no response body
响应代码
200 Success
404 Not Found
要查询哪些实例已应用特定模块,将发出以下请求
请求
GET v1/modules/{module_id}/instances
{
}
响应
{
'instance': <id>,
'modules' : [
{
'name': '100GB',
'id': <id>,
'md5': <md5>,
'installed': <date>,
},
{
'name': 'new_relic',
'id': <id>,
'md5': <md5>,
'installed': <date>,
},
]
}
响应代码
200 Success
404 Not Found
实例交互¶
要将模块应用于实例,将发出以下请求
请求
POST v1/{tenant_id}/instances/{instance_id}/modules
{
'modules' : [
{
"id": <id>,
},
]
}
响应
{
'type': 'vertica_license',
'datastore': 'vertica',
'datastore_version': 'all',
'name': '100GB',
'md5': <md5>,
}
响应代码
202 Success
400 Bad Request
404 Not Found
要查询实例中已安装的模块,将发出以下请求
请求
GET v1/{tenant_id}/instances/{instance_id}/modules
{
}
响应
{
'modules' : [
{
'type': 'vertica_license',
'datastore': 'vertica',
'datastore_version': 'all',
'name': '100GB',
'filename': 'vertica-all-100GB.lic',
'md5': <md5>,
'installed': <date>,
'status': 'OK',
'error_message': None,
},
{
'type': 'new_relic_activation',
'datastore': 'all',
'datastore_version': 'all',
'name': 'new_relic',
'filename': 'all-all-new_relic.lic',
'md5': <md5>,
'installed': <date>,
'status': 'FAILED',
'error_message': 'New Relic binaries not found',
},
]
}
响应代码
200 Success
404 Not Found
要从实例中检索模块,将发出以下请求
请求
GET v1/{tenant_id}/instances/{instance_id}/modules/{module_id}
{
}
响应
{
'filename': 'vertica-all-100GB.lic',
'contents': <module_contents>,
'md5': <md5>,
}
响应代码
200 Success
404 Not Found
要从实例中删除模块,将发出以下请求
请求
DELETE v1/{tenant_id}/instances/{instance_id}/modules/{module_id}
{
}
响应
This operation has no response body
响应代码
202 Success
404 Not Found
创建增强功能¶
实例创建 API 将得到增强,以包含一个模块字段,其中包含要应用的模块列表。这些将在正常的“prepare”调用期间发送,并在正确配置此实例后调用适当的插件。
{
'modules' : [
{
"id": <id>,
},
]
}
以类似的方式,集群创建 API 也将得到增强,以在实例字段中包含模块信息,就像目前对 flavors、AZs 等所做的那样。
公共 API 安全¶
由于文件将在管理网络上以明文形式传输,因此如果网络遭到破坏,模块可能会被截获。
应确保创建的每个插件都不会“执行”提供的模块数据文件的内容,因为这会带来安全漏洞的机会。不过,这似乎不太可能(并且不会是建议实施的情况),因为大多数模块数据文件将传递给另一个进程进行验证,并且由该进程确保维护适当的安全性。代码审查对于确保没有插件意外执行此数据至关重要。
Python API¶
新的方法将添加到 Python API 以促进许可。一些现有方法也需要扩展。
模块维护¶
def module_list(self, datastore=None):
"""Get a list of all modules that can be applied. Return only
those that apply to the datastore if it is passed in.
"""
def module_list_instances(self, module):
"""Get a list of all instances that have a given module applied."""
def module_show(self, module):
"""Show the details of the module."""
def module_create(self, module_type, name, description, contents,
datastore, datastore_version='all', auto_apply=False,
all_tenants=False, visible=True, live_update=False):
"""Create a new module."""
def module_update(self, module, module_type=None, name=None,
description=None, contents=None, datastore=None,
datastore_version=None, auto_apply=None,
all_tenants=None, visible=None, live_update=None):
"""Update an existing module."""
def module_delete(self, module):
"""Delete a module."""
实例交互¶
def module_apply(self, instance, modules):
"""Apply modules to an instance."""
def module_query(self, instance):
"""Query an instance about installed modules."""
def module_retrieve(self, instance, module=None, filename=None):
"""Retrieve the module data file from an instance and save it in
filename. If module is not supplied, retrieve all the modules.
If filename is not supplied, use the generated filename found
on the instance.
"""
def module_remove(self, instance, module):
"""Remove a module from an instance."""
创建增强功能¶
对于 instance.create,模块字段将添加到调用中
def create(self, name, flavor_id, volume=None, databases=None, users=None,
restorePoint=None, availability_zone=None, datastore=None,
datastore_version=None, nics=None, configuration=None,
replica_of=None, slave_of=None, replica_count=None,
modules=None):
"""Create (boot) a new instance."""
对于 cluster.create,模块字段将添加到已传入的 ['cluster']['instances'] 数据结构中。
CLI (python-troveclient)¶
以下 Trove CLI 命令(完成后)将完全正常运行
module-list 显示租户的所有模块。
module-show 显示特定模块资源的详细信息。
module-create 创建新的模块资源。
- module-update 更新特定模块的模块详细信息
资源。
module-delete 删除模块资源。
module-apply 将给定模块应用于 Trove 实例。
- module-query 查询给定 Trove 实例中已安装的
模块。
module-retrieve 从 Trove 实例中检索当前模块。
module-remove 从 Trove 实例中移除模块。
- create –module [–module]
创建新实例并应用给定模块。
- cluster-create –instance=module=<id>[,module=<id>]
创建新集群并将给定模块应用于每个实例。
Guest Agent¶
在 Guest Agent 中,模块将通过基于 stevedore.driver.DriverManager 范例的插件式架构进行管理。每个插件都需要实现“apply”、“query”和“remove”操作。“query”操作需要报告模块“apply”操作的状态。这将(至少)报告“OK”或“FAILED”以及对相关软件用户而言合理的任何其他状态。如果可能,如果发生错误,应使用有用信息填充“error_message”字段。
将提供一个定义契约的简单插件“基类”。它还将提供诸如将文件内容放置到指定位置和检索文件的功能。这可以用作所有其他插件的基础。
Guest Agent 代码将使用模块“类型”来确定给定模块是否存在插件。如果找不到插件,则会将错误写入日志并停止处理。
为了提供一个具体的、实际的插件实现,将创建一个 Vertica 许可证模块插件,以允许将许可证应用于 Vertica 数据存储。还将创建一个 New Relic 插件,以说明在客户机镜像上激活其他第三方软件。
备选方案¶
无
Dashboard 影响 (UX)¶
需要向实例创建对话框添加一个多下拉菜单,其中包含所选数据存储的所有模块。这些模块以及任何自动应用的模块都需要在创建调用时一起发送。集群创建对话框也需要同样的操作。
需要创建一个模块详细信息面板。此面板将包含表示模块属性的字段(参见 module-create 命令)。
需要创建一个“模块”列表面板。此面板将具有“删除”和“更新”按钮,并具有指向每个列出的模块的详细信息页面的链接。这将是一个高级面板,类似于“实例”。
实例列表面板需要添加一个新操作:“应用模块”。这将弹出一个窗口,显示可用模块。然后,所选模块将传递给 module-apply 命令。
实例详细信息面板需要运行“module-query”并将结果显示在新的“模块”部分中。或者,可以在此处放置一个链接,该链接将打开一个模块列表面板,其中包含“module-query”调用的结果。在这里,需要“module-remove”和“module-retrieve”按钮。
升级影响¶
由于此更改是全新的,因此预计不会出现升级问题。
依赖项¶
无。
测试¶
将编写通用集成测试,但这些测试不会在 MySQL 测试下运行,因为它不需要基于模块的处理。
文档影响¶
这是一个全新的功能,因此需要文档。
附录¶
无