ipcalc #ansible filter – manage floating ip ranges for packet.net

Post based on https://github.com/digineo/ansible-ipcalc

I’ve been working many times with floating IPs for my SDWAN demos and deployments. It’s been some kind of annoying to change the configuration files so many times. Figure what IPs I should assigned to every server depending on the segment and netmask that packet gave me. Those changes also, are prone to typos that waste precious time.

On this post I’m going to show you the ansible filters regarding ipcalc on a real use case.

What do you need to start?

A Centos/RedHat server pip. I used a container image on my case:

docker run -d --name brickle-lab-data-only pinrojas/nuage-ansible:v1.2 true
docker run -d -i -t --volumes-from brickle-lab-data-only --name brickle-lab pinrojas/nuage-ansible:v1.2
docker exec -ti brickle-lab /bin/bash 

You will create a container called brickle-lab and a data container called brickle-lab-data-only
more details, check my post “data-only containers for ansible automation”

Finally, you will be on the container will all you need to run your playbooks and use your ipcalc filters.

The filter

Create or choose your carpet to run your playbook
Then, create a folder called “filter_plugins”
This is the filter I’ve created in that folder:

# https://github.com/digineo/ansible-ipcalc
#
# save this file in $ansible/filter_plugins/
#
# example usage in a jinja2 template:
# {% set network = "172.16.0.1/24" | ipcalc %}
#
# {{ "192.168.0.1" | ipadd(3) }} == "192.168.0.4"
# {{ "fe80::" | ipadd("::3") }} == "fe80::3"
#

import ipcalc

class FilterModule (object):
    def filters(self):
        return {
            "ipcalc": self.ipcalc,
            "ipadd":  self.ipadd,
        }

    def ipcalc(self, value):
        net    = ipcalc.Network(value)
        result = {
            'version'   : net.version(),
            'netmask'   : str(net.netmask()),
            'subnet'    : net.subnet(),
            'size'      : net.size(),
            'prefix'    : sum([bin(int(x)).count("1") for x in str(net.netmask()).split(".")])
        }

        if net.version() == 6:
            result['network']  = net.network().to_compressed()
            result['host_min'] = net.host_first().to_compressed()
            result['host_max'] = net.host_last().to_compressed()
        if net.version() == 4:
            result['network']   = str(net.network())
            result['host_min']  = str(net.host_first())
            result['host_max']  = str(net.host_last())
            result['broadcast'] = str(net.broadcast())

        return result

    # Add two addresses
    # works for IPv4 and IPv6
    def ipadd(self, one, another):
        version = 6 if (':' in one) else 4
        addr    = ipcalc.IP(ipcalc.IP(one).ip + ipcalc.IP(another).ip, version=version)
        if version == 6:
            return addr.to_compressed()
        else:
            return str(addr)

What do I want to get from it?

This is the final file I’m intended to get as result:

public_dns: 8.8.8.8

nuage_core_location: ewr1
prefix_elastic_ip: 29
netmask_elastic_ip: 255.255.255.248
gateway_elastic_ip: 147.75.38.9
util_elastic_ip: 147.75.38.10
vsc1_elastic_ip: 147.75.38.11
vsc2_elastic_ip: 147.75.38.12
dhcp_min_elastic_ip: 147.75.38.13
dhcp_max_elastic_ip: 147.75.38.14
dhcp_net_elastic_ip: 147.75.38.8
dhcp_bcst_elastic_ip: 147.75.38.15
dhcp_mask_elastic_ip: 255.255.255.248

branchA_location: sjc1


branchB_location: sjc1
# first uplink
bB1_gateway: 147.75.88.13
bB1_prefix: 30
bB1_dhcp_min: 147.75.88.14
bB1_dhcp_max: 147.75.88.14
bB1_dhcp_net: 147.75.88.12
bB1_dhcp_mask: 255.255.255.252
bB1_dhcp_bcst: 147.75.88.15
# second uplink
bB2_gateway: 147.75.88.117
bB2_prefix: 30
bB2_dhcp_min: 147.75.88.118
bB2_dhcp_max: 147.75.88.118
bB2_dhcp_net: 147.75.88.116
bB2_dhcp_mask: 255.255.255.252
bB2_dhcp_bcst: 147.75.88.119

How am I gonna get it?

I’ve created the folder template in my playbook dir. And I’ve created this file called elastic_ip.j2:

public_dns: 8.8.8.8

{% set nuage_core_net = nuage_core_net | ipcalc %}
nuage_core_location: {{ nuage_core_loc }}
prefix_elastic_ip: {{ nuage_core_net.prefix }}
netmask_elastic_ip: {{ nuage_core_net.netmask }}
gateway_elastic_ip: {{ nuage_core_net.host_min }}
util_elastic_ip: {{ nuage_core_net.host_min | ipadd(1) }}
vsc1_elastic_ip: {{ nuage_core_net.host_min | ipadd(2) }}
vsc2_elastic_ip: {{ nuage_core_net.host_min | ipadd(3) }}
dhcp_min_elastic_ip: {{ nuage_core_net.host_min | ipadd(4) }}
dhcp_max_elastic_ip: {{ nuage_core_net.host_max }}
dhcp_net_elastic_ip: {{ nuage_core_net.network }}
dhcp_bcst_elastic_ip: {{ nuage_core_net.broadcast }}
dhcp_mask_elastic_ip: {{ nuage_core_net.netmask }}

branchA_location: {{ bA_loc }}


branchB_location: {{ bB_loc }}
# first uplink
{% set bB1_net = bB1_net | ipcalc %}
bB1_gateway: {{ bB1_net.host_min }}
bB1_prefix: {{ bB1_net.prefix }}
bB1_dhcp_min: {{ bB1_net.host_min | ipadd(1) }}
bB1_dhcp_max: {{ bB1_net.host_min | ipadd(1) }}
bB1_dhcp_net: {{ bB1_net.network }}
bB1_dhcp_mask: {{ bB1_net.netmask }}
bB1_dhcp_bcst: {{ bB1_net.broadcast }}
# second uplink
{% set bB2_net = bB2_net | ipcalc %}
bB2_gateway: {{ bB2_net.host_min }}
bB2_prefix: {{ bB2_net.prefix }}
bB2_dhcp_min: {{ bB2_net.host_min | ipadd(1) }}
bB2_dhcp_max: {{ bB2_net.host_min | ipadd(1) }}
bB2_dhcp_net: {{ bB2_net.network }}
bB2_dhcp_mask: {{ bB2_net.netmask }}
bB2_dhcp_bcst: {{ bB2_net.broadcast }}

Time to make it work

This is the playbook called “elastic_ip_cfg.yml”:

- hosts: localhost
  gather_facts: no
  tasks:
    - name: Create files in extras
      template: src=elastic_ip.j2 dest="{{ playbook_dir }}/cfg/{{ nuage_release }}/elastic_ip.yml" backup=no

You should see something like this:

[dev@cc9816a6e5f8 pk-elastic-ip]$ ansible-playbook -e nuage_release=5.2.2-docker-sdwan-elastic-ip -e nuage_core_loc=ewr1 -e nuage_core_net=147.75.72.133/29 -e bB_loc=sjc1 -e bA_loc=sjc1 -e bB1_net=147.75.88.18/30 -e bB2_net=147.75.88.116/30  elastic_ip_cfg.yml
 [WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] *************************************************************************************************************************************************************

TASK [Create files in extras] ************************************************************************************************************************************************
changed: [localhost]

PLAY RECAP *******************************************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0

And we’re Done!. see ya!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s