diff --git a/filter_plugins/util.py b/filter_plugins/util.py new file mode 100644 index 0000000..50c2c60 --- /dev/null +++ b/filter_plugins/util.py @@ -0,0 +1,14 @@ +#!/usr/bin/python + +class FilterModule(object): + '''Helper filters to make Ansible less unpleasant''' + def filters(self): + return { + 'list2dict': self.list2dict, + } + + def list2dict(self, items, key): + ''' + Like items2dict but keep entire dictionaries as values. + ''' + return {item[key]: item for item in items} diff --git a/roles/exit/tasks/dhcp.yml b/roles/exit/tasks/dhcp.yml index 37aec1a..fa1c71a 100644 --- a/roles/exit/tasks/dhcp.yml +++ b/roles/exit/tasks/dhcp.yml @@ -5,7 +5,7 @@ - name: Install keepalived import_tasks: keepalived.yml -- name: Configure relay for old DHCP server +- name: Configure DHCP relays template: dest: '/etc/default/isc-dhcp-relay-{{ prefixes | selectattr("prefix", "==", item.0 | ipaddr("network/prefix")) | map(attribute="vrf.name") | first }}' src: isc-dhcp-relay.j2 diff --git a/roles/exit/templates/frr.conf.j2 b/roles/exit/templates/frr.conf.j2 index 556bd4e..d4f6bfe 100644 --- a/roles/exit/templates/frr.conf.j2 +++ b/roles/exit/templates/frr.conf.j2 @@ -3,7 +3,7 @@ | selectattr('role') | selectattr('role.value', '==', 'loopback') | map(attribute='address') %} {% set inside_vrfs = interfaces | selectattr('parent') | selectattr('parent.name', '==', 'bridge') - | selectattr('vrf') | map(attribute='vrf') | rejectattr('name', '==', 'outside') -%} + | selectattr('vrf') | map(attribute='vrf.name') | reject('==', 'outside') | sort %} frr defaults datacenter log syslog informational @@ -158,7 +158,7 @@ router bgp {{ asn.asn }} vrf inside redistribute connected route-map loopback-inside {% for vrf in inside_vrfs %} - import vrf {{ vrf.name }} + import vrf {{ vrf }} {% endfor %} import vrf default import vrf route-map inside-import @@ -180,14 +180,14 @@ router bgp {{ asn.asn }} vrf inside redistribute connected route-map loopback-inside {% for vrf in inside_vrfs %} - import vrf {{ vrf.name }} + import vrf {{ vrf }} {% endfor %} import vrf default import vrf route-map inside-import exit-address-family -{% for vrf in inside_vrfs %} +{% for vrf in vrfs.values() | selectattr('name', 'in', inside_vrfs) %} # VRF for L2 network {{ vrf.name }}. Imports gateway from inside VRF. router bgp {{ asn.asn }} vrf {{ vrf.name }} bgp bestpath as-path multipath-relax @@ -195,13 +195,27 @@ router bgp {{ asn.asn }} vrf {{ vrf.name }} address-family ipv4 unicast redistribute connected import vrf inside +{% if vrf.custom_fields.imports %} +{% for import in vrf.custom_fields.imports %} + import vrf {{ import.name }} +{% endfor %} + import vrf route-map {{ vrf.name }}-import +{% else %} import vrf route-map office-import +{% endif %} exit-address-family address-family ipv6 unicast redistribute connected import vrf inside +{% if vrf.custom_fields.imports %} +{% for import in vrf.custom_fields.imports %} + import vrf {{ import.name }} +{% endfor %} + import vrf route-map {{ vrf.name }}-import +{% else %} import vrf route-map office-import +{% endif %} exit-address-family {% endfor %} @@ -213,8 +227,8 @@ ipv6 prefix-list default permit ::/0 ip prefix-list fabric permit 10.34.0.0/24 ge 32 ipv6 prefix-list fabric permit 2001:1470:fffd:3400::/64 ge 128 -{% for prefix in vrf_prefixes - | selectattr('vrf.id', 'in', inside_vrfs|map(attribute='id')) +# common prefix list for all inside networks +{% for prefix in vrf_prefixes | selectattr('vrf.name', 'in', inside_vrfs) | sort(attribute='family.value') | sort(attribute='vlan.vid') %} {% if prefix.family.value == 4 %} ip prefix-list office permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('prefix') }} @@ -223,6 +237,16 @@ ipv6 prefix-list office permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr( {% endif %} {% endfor %} +# individual prefix lists for each inside network +{% for prefix in vrf_prefixes | selectattr('vrf.name', 'in', inside_vrfs) + | sort(attribute='family.value') | sort(attribute='vlan.vid') %} +{% if prefix.family.value == 4 %} +ip prefix-list {{ prefix.vrf.name }} permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('prefix') }} +{% else %} +ipv6 prefix-list {{ prefix.vrf.name }} permit {{ prefix.prefix }} ge {{ prefix.prefix | ipaddr('prefix') }} +{% endif %} +{% endfor %} + {% if wg_net is defined %} ip prefix-list vpn permit {{ wg_net | ipaddr('subnet') }} {% endif %} @@ -278,6 +302,20 @@ route-map inside-import permit 20 route-map inside-import permit 21 match ipv6 address prefix-list office +{% for vrf in vrfs.values() | selectattr('custom_fields.imports') %} +route-map {{ vrf.name }}-import permit 10 + match ip address prefix-list default +route-map {{ vrf.name }}-import permit 11 + match ipv6 address prefix-list default +{% for import in vrf.custom_fields.imports %} +route-map {{ vrf.name }}-import permit {{ 100 + 10*loop.index0 }} + match ip address prefix-list {{ import.name }} +route-map {{ vrf.name }}-import permit {{ 101 + 10*loop.index0 }} + match ipv6 address prefix-list {{ import.name }} +{% endfor %} + +{% endfor %} + # Route maps for advertised and received routes. # Default VRF ↔ fabric. route-map default->fabric permit 10 diff --git a/roles/facts/tasks/main.yml b/roles/facts/tasks/main.yml index 568fe41..5f7ce96 100644 --- a/roles/facts/tasks/main.yml +++ b/roles/facts/tasks/main.yml @@ -3,6 +3,7 @@ set_fact: vlans: '{{ query("netbox.netbox.nb_lookup", "vlans", api_filter="group=new-net", raw_data=true) | sort(attribute="vid") }}' + vrfs: '{{ query("netbox.netbox.nb_lookup", "vrfs", raw_data=true) | list2dict("name") }}' prefixes: '{{ query("netbox.netbox.nb_lookup", "prefixes", raw_data=true) | sort(attribute="prefix") | sort(attribute="family.value") }}'