7
votes

I am having an issue sorting IPs via Jinja2 and Ansible. Here are my variables and jinja2 code for ansible templates.

roles/DNS/vars/main.yml:

---
DC1:
   srv1:
     ip: 10.2.110.3
   srv2:
     ip: 10.2.110.11
   srv3:
     ip: 10.2.110.19
   srv4:
     ip: 10.2.110.24

DC2:
   srv5:
     ip: 172.26.158.3
   srv6:
     ip: 172.26.158.11
   srv7:
     ip: 172.26.158.19
   srv8:
     ip: 172.26.158.24

roles/DNS/templates/db.example.com.j2:

$TTL 86400
@       IN SOA                  example.com. root.example.com. (
                                2014051001  ; serial
                                      3600  ; refresh
                                      1800  ; retry
                                    604800  ; expire
                                     86400  ; minimum
)

; Name server
                       IN      NS      dns01.example.com.

; Name server A record
dns01.example.com.        IN      A       10.2.110.92


; 10.2.110.0/24 A records in this Domain
{% for hostname, dnsattr in DC1.iteritems() %}
{{hostname}}.example.com.   IN      A       {{dnsattr.ip}}


; 172.26.158.0/24 A records in this Domain
{% for hostname, dnsattr in DC2.iteritems() %}
{{hostname}}.example.com.   IN      A       {{dnsattr.ip}}

roles/DNS/tasks/main.yml:

- name: Update DNS zone file db.example.com 
  template: 
    src: db.example.com.j2
    dest: "/tmp/db.example.com"
  with_items: "{{DC1,DC2}}"

- name: Restart DNS Server
  service:
    name: named
    state: restarted

The DNS zone files get created correctly, but the IPs are not numerically sorted. I have tried using the following with no luck:

Sorts on hostname alphabetically

{% for hostname, dnsattr in center.iteritems() | sort %}

Does not find the attribute dnsattr

{% for hostname, dnsattr in center.iteritems() | sort(attribute='dnsattr.ip') %}

Does not find the attribute ip

{% for hostname, dnsattr in center.iteritems() | sort(attribute='ip') %}
2
main.yml is badly formatted – duplicate ip property, db.example.com.j2 uses undefined center variable. And you state that The DNS zone files get created correctly. Really?Konstantin Suvorov
Hi Konstantin, Sorry. I pasted the main.yml data incorrectly in my post. I have edited the posted data to correctly reflect my dev environment. The sort issue still persists.stovie1000
what about center variable in the template?Konstantin Suvorov
Another copy paste issue. The data and variables should be correct now.stovie1000
can you refactor input data to be a list? [{host: 'srv1',ip:'10.2.110.3'}, etc.]?Konstantin Suvorov

2 Answers

1
votes

To have the IPs numerically sorted you could implement and use your own filter plugin (btw I'd be interested in any other solution):

In ansible.cfg add filter_plugins = path/to/filter_plugins.

In path/to/filter_plugins/ip_filters.py:

#!/usr/bin/python

def ip_sort(ip1, ip2):
    # Sort on the last number 
    return int(ip1.split('.')[-1]) - int(ip2.split('.')[-1])

class FilterModule(object):
    def filters(self):
        return {
            'sort_ip_filter': self.sort_ip_filter,
        }

    def sort_ip_filter(self, ip_list):
        return sorted(ip_list, cmp=ip_sort)

Then, in Ansible:

- name: "Sort ips"
  debug:
    msg: vars='{{ my_ips | sort_ip_filter }}'

I would also use the ipaddr filter to ensure the format is right:

- name: "Sort ips"
  debug:
    msg: vars='{{ my_ips | ipaddr | sort_ip_filter }}'
1
votes

I don't see any docs pointing this out, I just used logic to figure this out, after realizing a conversion back and forth between IP and int exists.
(Note: method 1 works for me in Ansible 2.9.1, the docs say method 2 has worked since ansible 2.4.x)

Say we have 2 files: inventory.ini and playbook.yml

inventory.ini
[kube_nodes]
10.0.0.12
10.0.0.60
10.0.0.3

playbook.yml
---
- name: method 1
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
  - name: Unsorted IP ordering
    debug: 
      msg: "{{ groups.kube_nodes }}"
  - name: Ascending IP ordering
    debug: 
      msg: "{{ groups.kube_nodes | list | ipaddr('int') | sort | ipaddr }}"
  - name: Decending IP ordering
    debug: 
      msg: "{{ groups.kube_nodes | list | ipaddr('int') | sort(reverse=true) | ipaddr }}"

Then we can use
Bash# ansible-playbook playbook.yml -i inventory.ini

and see
Unsorted IP ordering = 10.0.0.12, 10.0.0.60, 10.0.0.3
Ascending IP ordering = 10.0.0.3, 10.0.0.12, 10.0.0.60
Decendind IP ordering = 10.0.0.60, 10.0.0.12, 10.0.0.3


Alternatively, you may be able to make use of: specifying order of hosts at the play level: https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#hosts-and-users

- name: method 2
  hosts: all
  order: sorted #reverse_sorted, inventory, reverse_inventory, shuffle
  connection: local
  gather_facts: False
  tasks:
  - debug: var=inventory_hostname