0

im setting up a set of ansible tasks to manage a large amount of system. both for building and setup and ongoing updates.

working on the network script tasks and running into issues.

  1. the number of interfaces on each box is variable.
  2. the number of ip addresses on each interface is variable

i have a host_vars file for each host machine. in that file i define the number and names of the interfaces as well as the ip addresses. the first tasks gets the mac addresses of the interfaces. and the second task templates the interface config files with the data.

example of a host_vars file.

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

the tasks.

- 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}}"

template interface file

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 %}

the if statement works just fine if the variable is set in the host_vars file but fails if it is not.

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})

any suggestions would be greatly appreciated. thank you!

1 Answers1

1

I think you can simplify your playbook in a number of ways. First, there's no reason to treat "secondary" ip addresses any different from the primary address: you can just start with IPADDR0 and NETMASK0 for the first one and increment the index from there (IPADDR1, NETMASK1 and so on). With this in mind, we can restructure the interface data like this:

---
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

Since interfaces is now a dictionary, we need to modify the playbook to match:

---
- 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}}"

And finally we can remove a bunch of unnecessary calls to lookup in the template, and we can use a simple for loop to iterate over the addresses for each interface:

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 %}

This will result in output along the lines of:

$ 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
larsks
  • 277,717
  • 41
  • 399
  • 399
  • WOW that is amazing!! i was wondering if my host_vars setup was making my solution overly complicated. this works perfectly. i can also see how to use this in other things to define vars better. thank you!!!! – Jesse Smith Apr 07 '19 at 02:59
  • Glad to help! If this answered your question, you can click on the checkbox to the left of the answer. – larsks Apr 07 '19 at 12:29