带有可变变量的模板有没有更好的方法?
Template with variable variables is there a better way?
我正在设置一组 ansible 任务来管理大量系统。用于构建和设置以及持续更新。
正在处理网络脚本任务和 运行 问题。
- 每个盒子上的接口数量是可变的。
- 每个接口上的IP地址数量是可变的
我有每个主机 machine 的 host_vars 文件。在该文件中,我定义了接口的数量和名称以及 IP 地址。第一个任务获取接口的 mac 地址。第二个任务使用数据对接口配置文件进行模板化。
host_vars 文件示例。
interfaces:
- eth0
- eth1
eth0_ip: 10.135.61.213
eth0_subnet: 255.255.255.0
eth1_ip: 10.135.8.190
eth1_subnet: 255.255.255.248
eth1_ip_secondary: 10.135.8.191
eth1_ip_secondary_subnet: 255.255.255.248
任务。
- name: "get mac address from target system"
shell: "{{ 'cat /sys/class/net/' + item + '/address' }}"
register: macAddresses
check_mode: no
with_items: "{{interfaces}}"
- name: check/update network interfaces
template:
src: template/etc/sysconfig/network-scripts/ifcfg-ethx
dest: "{{ '/etc/sysconfig/network-scripts/ifcfg-' + item.item }}"
group: root
owner: root
mode: 0644
with_items: "{{macAddresses.results}}"
模板界面文件
DEVICE={{ item.item }}
ONBOOT=yes
HWADDR={{ item.stdout }}
TYPE=Ethernet
BOOTPROTO=none
{{ 'IPADDR0=' + lookup('vars', item.item + '_ip') }}
{{ 'NETMASK0=' + lookup('vars', item.item + '_subnet') }}
{% if lookup('vars', item.item + '_ip_secondary') is defined %}
{{ 'IPADDR1=' + lookup('vars', item.item + '_ip_secondary') }}
{{ 'NETMASK1=' + lookup('vars', item.item + '_ip_secondary_subnet') }}
{% endif %}
如果在 host_vars 文件中设置了变量,if 语句就可以正常工作,但如果没有,则失败。
TASK [network : check/update network interfaces] ***************************************************************************************************************************************
failed: [localhost] (item={'_ansible_parsed': True, 'stderr_lines': [], u'changed': True, u'stdout': u'00:50:56:ba:af:b2', '_ansible_item_result': True, u'delta': u'0:00:00.102648', 'stdout_lines': [u'00:50:56:ba:af:b2'], '_ansible_item_label': u'eth0', u'end': u'2019-04-06 14:00:28.468507', '_ansible_no_log': False, 'failed': False, u'cmd': u'cat /sys/class/net/eth0/address', 'item': u'eth0', u'stderr': u'', u'rc': 0, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'cat /sys/class/net/eth0/address', u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin': None}}, u'start': u'2019-04-06 14:00:28.365859', '_ansible_ignore_errors': None}) => {"changed": false, "item": {"changed": true, "cmd": "cat /sys/class/net/eth0/address", "delta": "0:00:00.102648", "end": "2019-04-06 14:00:28.468507", "failed": false, "invocation": {"module_args": {"_raw_params": "cat /sys/class/net/eth0/address", "_uses_shell": true, "argv": null, "chdir": null, "creates": null, "executable": null, "removes": null, "stdin": null, "warn": true}}, "item": "eth0", "rc": 0, "start": "2019-04-06 14:00:28.365859", "stderr": "", "stderr_lines": [], "stdout": "00:50:56:ba:af:b2", "stdout_lines": ["00:50:56:ba:af:b2"]}, "msg": "AnsibleUndefinedVariable: No variable found with this name: eth0_ip_secondary"}
changed: [localhost] => (item={'_ansible_parsed': True, 'stderr_lines': [], u'changed': True, u'stdout': u'00:50:56:ba:ce:08', '_ansible_item_result': True, u'delta': u'0:00:00.095483', 'stdout_lines': [u'00:50:56:ba:ce:08'], '_ansible_item_label': u'eth1', u'end': u'2019-04-06 14:00:28.976139', '_ansible_no_log': False, 'failed': False, u'cmd': u'cat /sys/class/net/eth1/address', 'item': u'eth1', u'stderr': u'', u'rc': 0, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'cat /sys/class/net/eth1/address', u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin': None}}, u'start': u'2019-04-06 14:00:28.880656', '_ansible_ignore_errors': None})
如有任何建议,我们将不胜感激。谢谢!
我认为您可以通过多种方式简化您的剧本。首先,没有理由将 "secondary" ip 地址与主地址有任何不同:您可以从 IPADDR0
和 NETMASK0
开始第一个并从那里增加索引(IPADDR1
、NETMASK1
等等)。考虑到这一点,我们可以像这样重构接口数据:
---
interfaces:
eth0:
addresses:
- ipaddr: 10.135.61.213
mask: 255.255.255.0
eth1:
addresses:
- ipaddr: 10.235.8.190
mask: 255.255.255.248
- ipaddr: 10.135.8.191
mask: 255.255.255.248
由于interfaces
现在是一个字典,我们需要修改剧本来匹配:
---
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: item
loop: "{{ interfaces|dict2items }}"
- name: "get mac address from target system"
shell: "{{ 'cat /sys/class/net/' + item.key + '/address' }}"
register: macAddresses
check_mode: no
loop: "{{interfaces|dict2items }}"
- debug:
var: macAddresses.results
- name: check/update network interfaces
template:
src: ./ifcfg-ethx
dest: "./conf/ifcfg-{{ item.item.key }}"
loop: "{{macAddresses.results}}"
最后我们可以删除模板中对 lookup
的一堆不必要的调用,我们可以使用一个简单的 for
循环来迭代每个接口的地址:
DEVICE={{ item.item.key }}
ONBOOT=yes
HWADDR={{ item.stdout }}
TYPE=Ethernet
BOOTPROTO=none
{% for address in item.item.value.addresses|default([]) %}
IPADDR{{ loop.index0 }}={{ address.ipaddr }}
NETMASK{{ loop.index0 }}={{ address.mask }}
{% endfor %}
这将导致输出如下:
$ cat ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
HWADDR=52:54:00:7a:a2:a5
TYPE=Ethernet
BOOTPROTO=none
IPADDR0=10.135.61.213
NETMASK0=255.255.255.0
$ cat ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
HWADDR=52:54:00:0e:72:1e
TYPE=Ethernet
BOOTPROTO=none
IPADDR0=10.235.8.190
NETMASK0=255.255.255.248
IPADDR1=10.135.8.191
NETMASK1=255.255.255.248
我正在设置一组 ansible 任务来管理大量系统。用于构建和设置以及持续更新。
正在处理网络脚本任务和 运行 问题。
- 每个盒子上的接口数量是可变的。
- 每个接口上的IP地址数量是可变的
我有每个主机 machine 的 host_vars 文件。在该文件中,我定义了接口的数量和名称以及 IP 地址。第一个任务获取接口的 mac 地址。第二个任务使用数据对接口配置文件进行模板化。
host_vars 文件示例。
interfaces:
- eth0
- eth1
eth0_ip: 10.135.61.213
eth0_subnet: 255.255.255.0
eth1_ip: 10.135.8.190
eth1_subnet: 255.255.255.248
eth1_ip_secondary: 10.135.8.191
eth1_ip_secondary_subnet: 255.255.255.248
任务。
- name: "get mac address from target system"
shell: "{{ 'cat /sys/class/net/' + item + '/address' }}"
register: macAddresses
check_mode: no
with_items: "{{interfaces}}"
- name: check/update network interfaces
template:
src: template/etc/sysconfig/network-scripts/ifcfg-ethx
dest: "{{ '/etc/sysconfig/network-scripts/ifcfg-' + item.item }}"
group: root
owner: root
mode: 0644
with_items: "{{macAddresses.results}}"
模板界面文件
DEVICE={{ item.item }}
ONBOOT=yes
HWADDR={{ item.stdout }}
TYPE=Ethernet
BOOTPROTO=none
{{ 'IPADDR0=' + lookup('vars', item.item + '_ip') }}
{{ 'NETMASK0=' + lookup('vars', item.item + '_subnet') }}
{% if lookup('vars', item.item + '_ip_secondary') is defined %}
{{ 'IPADDR1=' + lookup('vars', item.item + '_ip_secondary') }}
{{ 'NETMASK1=' + lookup('vars', item.item + '_ip_secondary_subnet') }}
{% endif %}
如果在 host_vars 文件中设置了变量,if 语句就可以正常工作,但如果没有,则失败。
TASK [network : check/update network interfaces] ***************************************************************************************************************************************
failed: [localhost] (item={'_ansible_parsed': True, 'stderr_lines': [], u'changed': True, u'stdout': u'00:50:56:ba:af:b2', '_ansible_item_result': True, u'delta': u'0:00:00.102648', 'stdout_lines': [u'00:50:56:ba:af:b2'], '_ansible_item_label': u'eth0', u'end': u'2019-04-06 14:00:28.468507', '_ansible_no_log': False, 'failed': False, u'cmd': u'cat /sys/class/net/eth0/address', 'item': u'eth0', u'stderr': u'', u'rc': 0, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'cat /sys/class/net/eth0/address', u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin': None}}, u'start': u'2019-04-06 14:00:28.365859', '_ansible_ignore_errors': None}) => {"changed": false, "item": {"changed": true, "cmd": "cat /sys/class/net/eth0/address", "delta": "0:00:00.102648", "end": "2019-04-06 14:00:28.468507", "failed": false, "invocation": {"module_args": {"_raw_params": "cat /sys/class/net/eth0/address", "_uses_shell": true, "argv": null, "chdir": null, "creates": null, "executable": null, "removes": null, "stdin": null, "warn": true}}, "item": "eth0", "rc": 0, "start": "2019-04-06 14:00:28.365859", "stderr": "", "stderr_lines": [], "stdout": "00:50:56:ba:af:b2", "stdout_lines": ["00:50:56:ba:af:b2"]}, "msg": "AnsibleUndefinedVariable: No variable found with this name: eth0_ip_secondary"}
changed: [localhost] => (item={'_ansible_parsed': True, 'stderr_lines': [], u'changed': True, u'stdout': u'00:50:56:ba:ce:08', '_ansible_item_result': True, u'delta': u'0:00:00.095483', 'stdout_lines': [u'00:50:56:ba:ce:08'], '_ansible_item_label': u'eth1', u'end': u'2019-04-06 14:00:28.976139', '_ansible_no_log': False, 'failed': False, u'cmd': u'cat /sys/class/net/eth1/address', 'item': u'eth1', u'stderr': u'', u'rc': 0, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'cat /sys/class/net/eth1/address', u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin': None}}, u'start': u'2019-04-06 14:00:28.880656', '_ansible_ignore_errors': None})
如有任何建议,我们将不胜感激。谢谢!
我认为您可以通过多种方式简化您的剧本。首先,没有理由将 "secondary" ip 地址与主地址有任何不同:您可以从 IPADDR0
和 NETMASK0
开始第一个并从那里增加索引(IPADDR1
、NETMASK1
等等)。考虑到这一点,我们可以像这样重构接口数据:
---
interfaces:
eth0:
addresses:
- ipaddr: 10.135.61.213
mask: 255.255.255.0
eth1:
addresses:
- ipaddr: 10.235.8.190
mask: 255.255.255.248
- ipaddr: 10.135.8.191
mask: 255.255.255.248
由于interfaces
现在是一个字典,我们需要修改剧本来匹配:
---
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: item
loop: "{{ interfaces|dict2items }}"
- name: "get mac address from target system"
shell: "{{ 'cat /sys/class/net/' + item.key + '/address' }}"
register: macAddresses
check_mode: no
loop: "{{interfaces|dict2items }}"
- debug:
var: macAddresses.results
- name: check/update network interfaces
template:
src: ./ifcfg-ethx
dest: "./conf/ifcfg-{{ item.item.key }}"
loop: "{{macAddresses.results}}"
最后我们可以删除模板中对 lookup
的一堆不必要的调用,我们可以使用一个简单的 for
循环来迭代每个接口的地址:
DEVICE={{ item.item.key }}
ONBOOT=yes
HWADDR={{ item.stdout }}
TYPE=Ethernet
BOOTPROTO=none
{% for address in item.item.value.addresses|default([]) %}
IPADDR{{ loop.index0 }}={{ address.ipaddr }}
NETMASK{{ loop.index0 }}={{ address.mask }}
{% endfor %}
这将导致输出如下:
$ cat ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
HWADDR=52:54:00:7a:a2:a5
TYPE=Ethernet
BOOTPROTO=none
IPADDR0=10.135.61.213
NETMASK0=255.255.255.0
$ cat ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
HWADDR=52:54:00:0e:72:1e
TYPE=Ethernet
BOOTPROTO=none
IPADDR0=10.235.8.190
NETMASK0=255.255.255.248
IPADDR1=10.135.8.191
NETMASK1=255.255.255.248