要解决的问题
想象一个简单的功能请求:编写一些 代码,在外部系统中创建几条记录,以记录一个 VLAN 的一些详细信息。我最近很想做一些实验室的管理工作来完成这个任务。这个外部系统是一个常见的互联网协议地址管理(IPAM)工具,但对于一个更抽象的配置管理数据库(CMDB)或一个与网络无关的记录来说,困难是一样的。在这个例子中,我创建一个记录的直接愿望就是让系统保存记录而已。
如果我们的目标是一个超紧凑的、直接的、笨拙的宏,那么它可能用 100 行代码就能写出来。如果我记得 API,我也许能在一个小时内把它敲出来,该代码的作用不会超过预期,除了确切的成品之外,什么也没留下。对它的目的而言是完美的,但是对未来的扩展毫无用处。
如今,我希望几乎每个人都能从一个角色和几个任务文件开始这项任务,准备扩展到十几个创建、读取、更新和删除(CRUD)操作。因为我不了解这个 API,我可能会花上几个小时到几天的时间,仅仅是摆弄它,弄清楚它的内部模式和工艺,弥和它的功能和我用代码编写出来的意图之间的差距。
在研究 API 的时候,我发现创建一个 VLAN 记录需要一个父对象引用 vlan_view_ref
。这看起来像一个路径片段,里面有随机字符。也许它是一个哈希,也许它真的是随机的,我不确定。我猜想,许多在泥泞中挣扎的人,在迫在眉睫的截止日期前,可能会把这个任意的字符串复制粘贴到 Ansible 中,然后继续混下去。忽略这个角色的实现细节,显而易见这个剧本级的任务应该是这样:
-
- name: "Create VLAN"
-
include_role:
-
name: otherthing
-
tasks_from: vlan_create.yml
-
vars:
-
vlan_name: "lab-infra"
-
vlan_tag: 100
-
vlan_view_ref: "vlan_view/747f602d-0381"
不幸的是,除了通过 API,vlan_view_ref
标识符是不可用的,所以即使把它移到清单文件或额外的变量中也没有什么帮助。剧本的用户需要对系统有一些更深入的理解,才能找出正确的引用 ID。
在实验室建设的情况下,我会经常重新部署这个记录系统。因此,这个父对象引用 ID 每天都会发生变化,我不希望每次都要手动找出它。所以,我肯定要按名称搜索该引用。没问题:
-
- name: Get Lab vlan view reference
-
include_role:
-
name: otherthing
-
tasks_from: search_for.yml
-
vars:
-
_resource: vlan_view
-
_query: "name={{ vlan_parent_view_name }}"
最终,它进行了一个 REST 调用。这将“返回” 一个 JSON,按照惯例,为了便于在角色外访问,我把它填充进了 _otherthing_search_result
中,。search_for.yml
的实现是抽象的,它总是返回一个包含零或多个结果的字典。
正如我读过的几乎所有真实世界的 Ansible 代码所证明的那样,大多数 Ansible 开发者将会继续前进,好像一切都很好,并且可以直接访问预期的单个结果:
-
- name: Remember our default vlan view ref
-
set_fact:
-
_thatthig_vlan_view_ref: "{{ _otherthing_search_result[0]._ref }}"
-
- name: "Create VLAN"
-
include_role:
-
name: otherthing
-
tasks_from: vlan_create.yml
-
vars:
-
vlan_name: "lab-infra"
-
vlan_tag: 100
-
vlan_view_ref: "{{ vlan_parent_view_name }}"
但有时 _otherthing_search_result[0]
是未定义的,所以 _thatthig_vlan_view_ref
也将是未定义的。很有可能是因为代码运行在不同的真实环境中,而有人忘记了在清单中或在命令行中更新 {{ vlan_parent_view_name }}
。或者,无论公平与否,也许有人进入了工具的图形用户界面(GUI),删除了记录或更改了它的名称什么的。