double with_items and loops

I have the following, and wish to combine them, but don’t know how because my with_items is referencing a variable. Wondering if someone can guide me (if possible) on how to combine these two stanzas:

`

  • name: Add DNS1 to ifcfg-* files if missing
    lineinfile:
    path: “{{ net_path }}{{ item }}”
    state: present
    regexp: “^DNS1”
    line: “DNS1={{ dns1 }}”
    with_items: “{{ ifcfg_list.stdout_lines }}”
    ignore_errors: yes
    notify:

  • Networking

  • name: Add DNS2 to ifcfg-* files if missing
    lineinfile:
    path: “{{ net_path }}{{ item }}”
    state: present
    regexp: “^DNS2”
    line: “DNS2={{ dns2 }}”
    with_items: “{{ ifcfg_list.stdout_lines }}”
    ignore_errors: yes
    notify:

  • Networking

`

Here is how far I get:

How do I incorporate: “{{ ifcfg_list.stdout_lines }}”`

That allows me to loop through all ifcfg-* files (except ifcfg-lo)

  • name: Add DNS to ifcfg-* files if missing
    lineinfile:
    path: “{{ net_path }}{{ item.nic }}” #<— This item is no longer reference by ifcfg_list.stdout_lines
    state: present
    regexp: “{{ item.regexp }}”
    line: “{{ item.line }}”
    with_items:
  • {regexp: “^DNS1”, line: “DNS1={{ dns1 }}”, nic: “ifcfg_list.stdout_lines”} #<— The nic section of these looks incorrect to me, and I doubt it would work
  • {regexp: "```^DNS2", line: "```DNS2={{ dns2 }}", nic: "ifcfg_list.stdout_lines"}
    `````ignore_errors: yes notify: - Networking`

`

Use the find module to search for the files and exclude the ifcfg-lo and register the result.

Then you can use the with_nested in the lineinfile.

I tried the following, but it failed. Likely some syntax issues (doesn’t like my var). I am having a hard time finding comprehensive docs on with_nested. I will keep trying, but if you have some suggestions, I am all ears.

`

  • name: Add DNS entries to ifcfg-* files if missing
    lineinfile:
    path: “{{ net_path }}{{ item[0] }}”
    state: present
    regexp: “{{ item[1] }}”
    line: “{{ item[2] }}”
    with_nested:
  • ifcfg_list.stdout_lines
  • [ ‘^DNS1’, ‘^DNS2’ ]
  • [ ‘DNS1={{ dns1 }}’, ‘DNS2={{ dns2 }}’ ]

`

Failed message:

`
failed: [ansible-oel6] (item=[u’ifcfg_list.stdout_lines’, u’^DNS1’, u’DNS1=10.253.1.23’]) => {“changed”: false, “item”: [“ifcfg_list.stdout_lines”, “^DNS1”, “DNS1=10.253.1.23”], “msg”: “Destination /etc/sysconfig/network-scripts/ifcfg_list.stdout_lines does not exist !”, “rc”: 257}

`

``

It ultimately does what I want, but changes stuff every time. I can’t figure out the logic that is doing that. Any ideas?

TASK [dns_update : Add DNS entries to ifcfg-* files if missing] ************************************************************************************************************************************************************************** ok: [ansible-oel7] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS1=10.1.1.10']) ok: [ansible-oel6] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS1=10.1.1.10']) ok: [ansible-rhel6] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS1=10.1.1.10']) ok: [ansible-rhel7] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS1=10.1.1.10']) changed: [ansible-rhel6] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS2=10.1.1.11']) changed: [ansible-oel7] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS2=10.1.1.11']) changed: [ansible-oel6] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS2=10.1.1.11']) changed: [ansible-rhel7] => (item=[u'ifcfg-eth0', u'^DNS1.*', u'DNS2=10.1.1.11']) changed: [ansible-oel7] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS1=10.1.1.10']) changed: [ansible-rhel6] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS1=10.1.1.10']) changed: [ansible-oel6] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS1=10.1.1.10']) changed: [ansible-rhel7] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS1=10.1.1.10']) ok: [ansible-oel7] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS2=10.1.1.11']) ok: [ansible-oel6] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS2=10.1.1.11']) ok: [ansible-rhel6] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS2=10.1.1.11']) ok: [ansible-rhel7] => (item=[u'ifcfg-eth0', u'^DNS2.*', u'DNS2=10.1.1.11'])

The more I read about this, the more I think it is working out of luck. In fact, I don’t understand how it is working now based on the output previously mentioned. According to that both DNS1 and DNS2 should be pointing to dns2 or 10.1.1.11. Thoughts?

Your nested has 3 levels and that gets you in trouble since you get this four combinations
ifcfg-eth0 , ^DNS1.* , DNS1=10.1.1.10
ifcfg-eth0 , ^DNS1.* , DNS2=10.1.1.11
ifcfg-eth0 , ^DNS2.* , DNS1=10.1.1.10
ifcfg-eth0 , ^DNS2.* , DNS2=10.1.1.11

But you only want these two
ifcfg-eth0 , ^DNS1.* , DNS1=10.1.1.10
ifcfg-eth0 , ^DNS2.* , DNS2=10.1.1.11

So try this

- name: Add DNS entries to ifcfg-* files if missing
   lineinfile:
     path: "{{ net_path }}{{ item[0] }}"
     state: present
     regexp: "^{{ item[1] }}"
     line: "{{ item[1] }}={{ vars[item[1] | lower] }}"
   with_nested:
     - '{{ ifcfg_list.stdout_lines }}'
     - [ 'DNS1', 'DNS2' ]

Kai, thank you. That is awesome. I wouldn’t have thought of that. I do have some follow-up questions, just to understand.

`
line: “{{ item[1] }}={{ vars[item[1] | lower] }}”

`
What does the = sign do here? Is it overriding the with_nested item[1]?
What is vars specifying? If I came close to ever doing this on my own I wouldn’t have added vars… so I am curious.

I know what the rest of it is doing, but am a little fuzzy in regards to the two things above.

>
> So try this
>
> - name: Add DNS entries to ifcfg-* files if missing
> lineinfile:
> path: "{{ net_path }}{{ item[0] }}"
> state: present
> regexp: "^{{ item[1] }}"
> line: "{{ item[1] }}={{ vars[item[1] | lower] }}"
> with_nested:
> - '{{ ifcfg_list.stdout_lines }}'
> - [ 'DNS1', 'DNS2' ]
>
>
>

Kai, thank you. That is awesome. I wouldn't have thought of that. I do
have some follow-up questions, just to understand.

I'll try to explain.

line: "{{ item[1] }}={{ vars[item[1] | lower] }}"
What does the = sign do here? Is it overriding the with_nested item[1]?

The = sign is just the = sign.
In the first iteration the "item[1] = DNS1".
You need you the line in the file to be DNS1=10.1.1.10

So {{ item[0] }} would be replaced by the content, so it becomes DNS1, we need the character = to get DNS=

What is vars specifying? If I came close to ever doing this on my own I
wouldn't have added vars.... so I am curious.

Your DNS IP is in the variable named dns, to use the content of a variable as a variable name you need to use vars or hostvars.
vars if the same as hostvars[inventory_hostname].

item[1] is DNS (upper case) but the variable name is dns (lower case) so "vars[item[1] | lower]" becomes vars['dns'] that is the same {{ dns }} and you get 10.1.1.10.

I know what the rest of it is doing, but am a little fuzzy in regards to
the two things above.

Hopefully my explanation is understandable.

If you are editing these files the inifile module is much better btw. Take a look

Kai, perfect, thank you. It can get confusing looking at the various variables and {{items}}. Your explanation makes sense.

Thanks, I will.

Malcom! Thanks for info on the ini_file module! That will be much better to use…now I can cut back on the horribly complicated regexp stuff!

Another one to look at is the newly added xml module, if you have to suffer the pain of xml config files.