You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何简化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:07:34