Fragment Merging¶
一旦 teuthology 构建了 YAML 片段矩阵,这些片段就必须合并在一起并进行处理。直到 2022 年,这个合并过程都是静态的:所有 YAML 片段都按字典顺序连接在一起,重复的片段成员会进行深度合并(例如,“tasks”数组)。现在,片段和整个作业规范可以根据嵌入在片段中的 Lua 脚本进行动态更改或丢弃。
premerge Scripts¶
脚本执行的第一阶段发生在预合并(premerge)步骤中。每个片段都可以有自己的预合并脚本,该脚本在片段合并之前运行。脚本定义如下:
teuthology:
premerge: |
if yaml.os_type == 'ubuntu' then reject() end
同样,这个脚本将在 YAML 片段合并到作业的完整 YAML 规范之前运行。该脚本可以访问到目前为止由在此片段之前合并的片段生成的 YAML 作业描述(yaml 变量)(记住:片段按字典顺序排列)。在上述情况下,os_type 被检查,如果作业配置为在 Ubuntu 上运行,则该片段将被丢弃(拒绝)。注意:这不考虑作业的默认 os_type,该默认 os_type 尚未知晓;只有 YAML 片段指定的 os_type 才能在这些脚本中使用。
当在预合并步骤中运行时,reject 函数会导致该片段从作业中丢弃:它的任何 YAML 都不会合并到作业中。 accept 函数会导致片段合并。默认操作是接受片段。
postmerge Scripts¶
脚本执行的第二阶段是后合并(postmerge)步骤,在所有片段合并后运行。此时,作业的 YAML 规范几乎全部完成。脚本现在可以对 YAML 进行最终修改,或者完全拒绝该作业,从而使其从计划执行的作业列表中删除。一个后合并脚本示例:
teuthology:
postmerge:
- if yaml.os_type == "ubuntu" then reject() end
这个脚本是相同的,但效果不同:在组合了作业的所有 YAML 片段之后,如果 os_type 是“ubuntu”,则整个作业被丢弃(过滤掉/拒绝)。后合并脚本也以字符串列表的形式在 teuthology.postmerge 数组中指定,该数组可能跨越多个片段。在后合并步骤中,所有这些字符串都被连接起来,然后作为一个脚本执行。您可以使用它来定义变量、函数或您需要的任何其他内容。
脚本可以访问整个 yaml 对象,并可以使用它进行高级检查。也可以通过编程方式更改 YAML 定义:
teuthology:
postmerge:
- |
-- use the lupa "attrgetter" to fetch attrs not items via Lua's indexing
local attr = py_attrgetter
local tasks = py_list()
for i = 1, 3 do
local task = py_dict()
task.exec = py_dict()
task.exec["mon.a"] = py_list()
attr(task.exec["mon.a"]).append("echo "..i)
attr(tasks).append(task)
end
deep_merge(yaml.tasks, tasks)
这将等同于 YAML 片段包含:
tasks:
- exec:
mon.a:
- echo 1
- exec:
mon.a:
- echo 2
- exec:
mon.a:
- echo 3
但这些任务是在所有片段加载后才附加到末尾的。这与片段合并时(按字典顺序)正常附加任务的方式不同。
API¶
脚本被良好地沙盒化,只能访问少量 Lua 内置库。还可以访问一些 Python/Lupa 特定的函数,这些函数带有 py_ 前缀。不允许进行 I/O 或其他系统功能。
可用的 Lua 内置库包括:
assert
error
ipairs
pairs
tonumber
tostring
此外,通过 Lupa 公开的 Python 函数包括:
py_attrgetter = python.as_attrgetter
py_dict = python.builtins.dict
py_list = python.builtins.list
py_tuple = python.builtins.tuple
py_enumerate = python.enumerate
py_iterex = python.iterex
py_itemgetter = python.as_itemgetter
这些都带有 py_ 前缀。有关更多信息,请参阅 Lupa 文档。
最后,teuthology 为脚本公开了以下函数:
accept()
accept 函数会停止脚本执行并导致片段被合并(预合并脚本)或作业被接受以进行调度(后合并脚本)。脚本的默认操作是接受。
reject()
reject 函数会停止脚本执行并导致片段被丢弃(预合并脚本)或作业被拒绝以进行调度(后合并脚本)。
deep_merge(a, b)
deep_merge 函数来自 teuthology 代码库。它用于合并 YAML 结构。为了方便起见,它被提供出来以简化 Python (yaml) 对象上的常见操作。该函数将 b 合并到 a 中。
log
log Python 类(对象)允许 Lua 在 teuthology-suite 日志中留下调试信息。
yaml_load(str)
此函数加载 YAML 字符串并将其作为 Python 结构(字典、列表等)返回。
Concrete Example¶
fs:upgrade:mds_upgrade_sequence 子套件测试当集群由 cephadm 管理时,CephFS 的升级顺序是否被遵循。此套件中最有趣的 YAML 集位于 tasks/ 中:
%
0-from/
pacific.yaml
v16.2.4.yaml
1-volume/
0-create.yaml
1-ranks/
1.yaml
2.yaml
2-allow_standby_replay/
yes.yaml
no.yaml
3-inline
yes.yaml
no.yaml
4-verify.yaml
2-client.yaml
3-upgrade-with-workload.yaml
4-verify.yaml
基本上:从 Pacific 的两个版本之一升级集群,创建一个卷(fs),可能在 MDSMap 中调整一些旋钮,并验证升级是否正确完成。这工作得很好,是构建有效测试矩阵的一个很好的例子。
我们想要测试的功能是 MDS 的新升级过程。它只需要“使文件系统失败(failing)”,这将从 MDSMap 中删除所有正在运行的 MDS,并阻止任何 MDS“加入”文件系统(变为活动状态)。然后升级过程会升级软件包,重新启动 MDS,然后设置文件系统以允许 MDS 加入(变为活动状态)。理想情况下,我们可以这样修改矩阵:
%
fail_fs/
yes.yaml
no.yaml
tasks/
%
0-from/
pacific.yaml
v16.2.4.yaml
1-volume/
0-create.yaml
1-ranks/
1.yaml
2.yaml
2-allow_standby_replay/
yes.yaml
no.yaml
3-inline
yes.yaml
no.yaml
4-verify.yaml
2-client.yaml
3-upgrade-with-workload.yaml
4-verify.yaml
所以我们只需在 fail_fs 中更改(或不更改)单个配置选项,以开启该升级路径:
overrides:
ceph:
conf:
mgr:
mgr/orchestrator/fail_fs: true
然而,复杂之处在于这个新的 fail_fs 配置选项只有最新的 mgr(main 分支或可能是最新的 pacific 或 quincy)才能理解……并且 mons 不允许您设置一个它不知道存在的配置。因此,我们必须进行错开的升级来测试这个新的升级路径:mgr 必须升级,设置一个配置选项来更改 MDS 升级的执行方式,然后集群才能继续升级。
问题在于:mgr 只知道如何从 v16.2.10 开始进行错开的升级。因此,我们甚至无法从 v16.2.4 升级来测试这个新的升级路径。
(人们可能倾向于在 QA 中移除 v16.2.4 作为升级路径,但由于 v16.2.4 和 v16.2.5 之间 MDSMap 发生了重大(破坏性)更改,我们必须继续测试它。移除它是不可接受的。)
为了解决这个尴尬的问题,我们可以使用片段合并的新脚本来控制是否设置这个 mgr/orchestrator/fail_fs 配置选项。如果我们要从 v16.2.4 升级,那么就丢弃矩阵中所有也想测试这个新的 MDS 升级过程的作业。所以我们修改 yaml 片段如下:
fail_fs/no.yaml:
teuthology:
variables:
fail_fs: false
overrides:
ceph:
conf:
mgr:
mgr/orchestrator/fail_fs: false
fail_fs/yes.yaml:
teuthology:
variables:
fail_fs: true
overrides:
ceph:
conf:
mgr:
mgr/orchestrator/fail_fs: true
tasks/0-from/v16.2.4.yaml:
teuthology:
postmerge:
- if yaml.teuthology.variables.fail_fs then reject() end
...
我们在 teuthology['variables'] 字典中设置了一个变量(为了方便编程),该变量指示合并后的 YAML 是否包含 fail_fs 功能。然后,如果我们要从 v16.2.4 升级并且该变量为 true,则丢弃矩阵中的那组作业。这有效地阻止了在集群从 v16.2.4 升级时对该升级过程的任何测试。
注意:最终合并的 QA 代码还包含一个 YAML 片段,用于对 ceph-mgr 执行错开的升级。如果我们在不测试 fail_fs 的情况下,则使用预合并脚本丢弃此 YAML 片段;如果不需要,就没有理由进行错开的升级。如果您想了解它是如何工作的,请参阅代码!
Why Lua¶
Lua 是一个小型、可扩展且易于沙盒化的脚本环境。Python 很难正确地沙盒化,其限制使得它难以嵌入到 YAML 中(例如代码块的缩进)。
Python-Lua¶
Lupa 是“lunatic”python 项目的最新衍生版本。它允许 Python 和 Lua 世界之间进行轻松的交叉通信。