如何简化Ansible Playbook中主机名与网络类型的校验断言代码
简化Ansible中重复的hostname网络范围校验代码
问题分析
原Playbook通过大量重复的assert任务校验传入的hostname是否属于指定clusterName对应的network范围,代码冗余度高、维护成本大。可以通过变量结构重构和动态变量调用大幅简化代码。
优化方案
1. 重构变量结构
将分散的区域、网络变量整合成结构化的regions配置,统一管理各区域的主机前缀和不同环境下的网络端口范围,彻底消除重复定义:
- hosts: localhost gather_facts: false vars: regions: cz: prefix: prg env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221] us: prefix: phx env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221] my: prefix: kul env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221]
2. 动态提取目标区域与环境
从传入的clusterName中自动解析出对应的区域(如cz)和环境(如prod/test),避免硬编码判断逻辑:
tasks: - name: 提取目标区域和环境并校验格式 ansible.builtin.set_fact: target_region: "{{ clusterName | regex_search('^([a-z]{2})') }}" target_env: "{{ clusterName | regex_search('prod|test') }}" ansible.builtin.assert: that: - target_region is not none - target_env is not none fail_msg: "clusterName格式错误,需以cz/us/my+prod/test开头,例如czprodcls001"
3. 动态生成校验变量
根据解析出的区域和环境,自动生成待校验的主机列表、合法的网络主机范围,无需为每个组合单独定义变量:
- name: 生成校验用动态变量 ansible.builtin.set_fact: # 筛选传入hostname中匹配当前区域+环境前缀的主机 target_hosts: "{{ hostname | select('match', regions[target_region].prefix ~ target_env ~ 'srv') | list }}" # 生成指定network对应的合法主机列表 allowed_hosts: "{{ [regions[target_region].prefix ~ target_env ~ 'srv00'] | product(range(*regions[target_region].env[target_env][network.split('_')[1]])) | map('join') | list }}" vars: network_type: "{{ network.split('_')[1] }}" # 校验network参数是否合法 ansible.builtin.assert: that: network_type in regions[target_region].env[target_env] fail_msg: "当前环境{{ target_env }}不支持{{ network }}网络类型,可选类型: {{ regions[target_region].env[target_env].keys() }}"
4. 单个assert完成校验
用一个assert任务替代原有的大量重复任务,完成所有主机的范围校验:
- name: 校验hostname是否在指定network范围内 ansible.builtin.assert: that: target_hosts | difference(allowed_hosts) | length == 0 fail_msg: "以下主机不在{{ network }}范围内: {{ target_hosts | difference(allowed_hosts) }}" success_msg: "所有主机均符合{{ network }}范围要求"
完整简化版Playbook
- hosts: localhost gather_facts: false vars: regions: cz: prefix: prg env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221] us: prefix: phx env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221] my: prefix: kul env: prod: mgmt: [4281, 4395] int: [3481, 4195] dmz: [5281, 6395] bkp: [1281, 2395] test: mgmt: [8281, 8321] int: [8181, 8221] dmz: [9181, 9221] tasks: - name: 提取目标区域和环境并校验格式 ansible.builtin.set_fact: target_region: "{{ clusterName | regex_search('^([a-z]{2})') }}" target_env: "{{ clusterName | regex_search('prod|test') }}" ansible.builtin.assert: that: - target_region is not none - target_env is not none fail_msg: "clusterName格式错误,需以cz/us/my+prod/test开头,例如czprodcls001" - name: 生成校验用动态变量 ansible.builtin.set_fact: target_hosts: "{{ hostname | select('match', regions[target_region].prefix ~ target_env ~ 'srv') | list }}" allowed_hosts: "{{ [regions[target_region].prefix ~ target_env ~ 'srv00'] | product(range(*regions[target_region].env[target_env][network.split('_')[1]])) | map('join') | list }}" vars: network_type: "{{ network.split('_')[1] }}" ansible.builtin.assert: that: network_type in regions[target_region].env[target_env] fail_msg: "当前环境{{ target_env }}不支持{{ network }}网络类型,可选类型: {{ regions[target_region].env[target_env].keys() }}" - name: 校验hostname是否在指定network范围内 ansible.builtin.assert: that: target_hosts | difference(allowed_hosts) | length == 0 fail_msg: "以下主机不在{{ network }}范围内: {{ target_hosts | difference(allowed_hosts) }}" success_msg: "所有主机均符合{{ network }}范围要求"
注意事项
- 原Playbook中
us区域的prod_int_network和prod_bkp_network存在笔误(phxrodsrv00应为phxprodsrv00),已在优化版中修正。 - 新增了
clusterName和network参数的格式校验,提升了Playbook的健壮性。
运行命令
保持原命令不变,例如:
ansible-playbook pb.yml -e '{"hostname": ["prgprodsrv004281", "prgprodsrv004382","prgprodsrv008281","prgprodsrv009599"], "clusterName": "czprodcls001", "network": "prod_mgmt_network"}'
内容的提问来源于stack exchange,提问作者chin wooi Ng
相关产品推荐
相关产品推荐

