Compare commits

..

2 commits

Author SHA1 Message Date
Timotej Lazar 82b10e8133 exit: support custom VRF imports
Ten minutes to set up and ten hours to convince Ansible to not be
quite so retarded. The list2dict filter seems to be the (or another)
missing piece. Now let’s rewrite everything else using it. Or not.
2024-07-15 14:22:42 +02:00
Timotej Lazar 99aef43574 exit: add DHCP relay for new server
Really quite shoddy as it is right now. Should get better once the old
server is retired.
2024-07-14 14:51:23 +02:00
8 changed files with 85 additions and 48 deletions

14
filter_plugins/util.py Normal file
View file

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

View file

@ -13,3 +13,4 @@ query_filters:
- role: 'compute-node'
- role: 'firewall'
- role: 'switch'
- role: 'server'

View file

@ -1,18 +0,0 @@
#!/bin/sh
class="${1}"
name="${2}"
state="${3}"
case "${state}" in
"MASTER" | "FAULT")
systemctl start "${name}"
;;
"BACKUP" | "STOP")
systemctl stop "${name}"
;;
*)
logger "keepalived unknown state for ${name}: ${state}"
exit 1
;;
esac

View file

@ -1,16 +1,17 @@
# TODO rewrite task and templates into something sane once the old DHCP server is retired
- set_fact:
dhcp_servers: '{{ prefixes | selectattr("custom_fields.dhcp_server") | groupby(attribute="custom_fields.dhcp_server.address") }}'
- name: Install keepalived
import_tasks: keepalived.yml
- name: Create keepalive notify script for systemd services
copy:
dest: /usr/local/bin/
src: keepalive-service
mode: 0755
- name: Configure DHCP relays
template:
dest: "/etc/default/isc-dhcp-relay"
dest: '/etc/default/isc-dhcp-relay-{{ prefixes | selectattr("prefix", "==", item.0 | ipaddr("network/prefix")) | map(attribute="vrf.name") | first }}'
src: isc-dhcp-relay.j2
loop: '{{ dhcp_servers }}'
loop_control:
label: "{{ item.0 }}"
notify: restart keepalived
- name: Set up keepalived

View file

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

View file

@ -1,17 +1,12 @@
{% set dhcp_vlans = vrf_prefixes | selectattr('custom_fields.dhcp_ranges')
| map(attribute='vlan.vid') | sort -%}
{% set my_server = item.0 %}
{% set my_vlans = item.1 | map(attribute='vlan.vid') | sort %}
{% set my_prefix = prefixes | selectattr("prefix", "==", my_server | ipaddr("network/prefix")) | first -%}
# What servers should the DHCP relay forward requests to?
SERVERS="{{ dhcp }}"
# On what interfaces should the DHCP relay (dhrelay) serve DHCP requests?
# Always include the interface towards the DHCP server.
# This variable requires a -i for each interface configured above.
# This will be used in the actual dhcrelay command
# For example, "-i eth0 -i eth1"
INTF_CMD="{{ interfaces | selectattr('parent') | selectattr('parent.name', '==', 'bridge')
| selectattr('untagged_vlan') | selectattr('untagged_vlan.vid', 'in', dhcp_vlans)
| map(attribute='name') | sort | map('regex_replace', '^', '-id ') | join(' ') }} -iu {{ iface_uplink }} -iu peerlink.4"
# Additional options that are passed to the DHCP relay daemon?
OPTIONS="-U outside"
SERVERS="{{ my_server | ipaddr('address') }}"
{% if my_prefix.vrf.name == 'outside' %}
INTF_CMD="{{ my_vlans | map('regex_replace', '^', '-id bridge.') | join(' ') }} -iu {{ iface_uplink }} -iu peerlink.4 -U {{ my_prefix.vrf.name }}"
OPTIONS=""
{% else %}
INTF_CMD="{{ my_vlans | map('regex_replace', '^', '-id bridge.') | join(' ') }} -U bridge.{{ my_prefix.vlan.vid }}"
OPTIONS="--giaddr-src"
{% endif %}

View file

@ -1,3 +1,5 @@
{% set dhcrelays = prefixes | selectattr('prefix', 'in', dhcp_servers | map('first') | ipaddr("network/prefix"))
| map(attribute="vrf.name") | map('regex_replace', '^', 'dhcrelay@') %}
{% set exits = [inventory_hostname, peer]|sort -%}
global_defs {
@ -18,5 +20,8 @@ vrrp_instance dhcrelay {
@^{{ exit }} {{ "169.254.1.0/24" | ipaddr(loop.index + 1) | ipaddr('address') }}
{% endfor %}
}
notify /usr/local/bin/keepalive-service
notify_master "systemctl start {{ dhcrelays | join(' ') }}"
notify_fault "systemctl start {{ dhcrelays | join(' ') }}"
notify_backup "systemctl stop {{ dhcrelays | join(' ') }}"
notify_stop "systemctl stop {{ dhcrelays | join(' ') }}"
}

View file

@ -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") }}'