Compare commits
	
		
			10 commits
		
	
	
		
			db397cb2b1
			...
			668af8bdb6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 668af8bdb6 | |||
| 0e9dac6985 | |||
| 16f34c4502 | |||
| 8c82af23e4 | |||
| 7656c05b2d | |||
| 8a9d47f176 | |||
| 457ab7d3b7 | |||
| 1c0709a6a6 | |||
| c07c03a430 | |||
| 2443a90bc5 | 
					 17 changed files with 75 additions and 88 deletions
				
			
		|  | @ -24,11 +24,11 @@ Create a read-only token in NetBox. Set variables required to access NetBox: | |||
| 
 | ||||
| Run one-off tasks with (add `--key-file` or other options as necessary): | ||||
| 
 | ||||
|     ansible -i inventory.yml -m ping 'spine-*' | ||||
|     ansible -m ping 'spine-*' | ||||
| 
 | ||||
| Run a playbook with: | ||||
| 
 | ||||
|     ansible-playbook setup.yml -i inventory.yml -l 'spine-*' | ||||
|     ansible-playbook setup.yml -l 'spine-*' | ||||
| 
 | ||||
| ## NetBox data | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| [defaults] | ||||
| nocows = true | ||||
| filter_plugins = filter_plugins | ||||
| inventory = inventory.yml | ||||
| remote_user = root | ||||
| vault_identity = network | ||||
| filter_plugins = filter_plugins | ||||
|  |  | |||
|  | @ -1 +0,0 @@ | |||
| vlans: "{{ query('netbox.netbox.nb_lookup', 'vlans', api_filter='group=new-net', raw_data=true) | sort(attribute='vid') }}" | ||||
|  | @ -213,17 +213,15 @@ 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 vrf in inside_vrfs %} | ||||
| {% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vrf_id='~vrf.id, raw_data=true) | ||||
|         | sort(attribute='family.value') %} | ||||
| {% for prefix in prefixes %} | ||||
| {% for prefix in vrf_prefixes | ||||
|     | selectattr('vrf.id', 'in', inside_vrfs|map(attribute='id')) | ||||
|     | sort(attribute='family.value') | sort(attribute='vlan.vid') %} | ||||
| {% if prefix.family.value == 4 %} | ||||
| ip prefix-list office permit {{ prefix.prefix }} ge 24 | ||||
| {% else %} | ||||
| ipv6 prefix-list office permit {{ prefix.prefix }} ge 64 | ||||
| {% endif %} | ||||
| {% endfor %} | ||||
| {% endfor %} | ||||
| 
 | ||||
| {% if wg_net is defined %} | ||||
| ip prefix-list vpn permit {{ wg_net | ipaddr('subnet') }} | ||||
|  | @ -237,7 +235,7 @@ ip prefix-list nat permit {{ wg_ip | ipaddr('host') }} | |||
| ip prefix-list nat permit {{ network }} | ||||
| {% endfor %} | ||||
| 
 | ||||
| {% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true, api_filter='role=bgp') | selectattr('tenant') %} | ||||
| {% for prefix in bgp_prefixes | sort(attribute='family.value') %} | ||||
| {% if prefix.family.value == 4 %} | ||||
| ip prefix-list dc permit {{ prefix.prefix }} ge 32 | ||||
| {% else %} | ||||
|  | @ -281,7 +279,7 @@ route-map inside-import permit 21 | |||
|   match ipv6 address prefix-list office | ||||
| 
 | ||||
| # Route maps for advertised and received routes. | ||||
| # Inside ↔ fabric. | ||||
| # Default VRF ↔ fabric. | ||||
| route-map default->fabric permit 10 | ||||
|   match ip address prefix-list default | ||||
| route-map default->fabric permit 11 | ||||
|  | @ -296,7 +294,7 @@ route-map fabric->default permit 20 | |||
| route-map fabric->default permit 21 | ||||
|   match ipv6 address prefix-list dc | ||||
| 
 | ||||
| # Inside ↔ firewall. | ||||
| # Inside VRF ↔ firewall. | ||||
| route-map inside->firewall permit 1 | ||||
|   match interface lo | ||||
| route-map inside->firewall permit 20 | ||||
|  | @ -313,7 +311,7 @@ route-map firewall->inside permit 10 | |||
| route-map firewall->inside permit 11 | ||||
|   match ipv6 address prefix-list default | ||||
| 
 | ||||
| # Outside ↔ firewall. | ||||
| # Outside VRF ↔ firewall. | ||||
| route-map outside->firewall permit 10 | ||||
|   match ip address prefix-list default | ||||
| route-map outside->firewall permit 11 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| {% set dhcp_networks = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='role=dhcp-pool', raw_data=true) | ||||
|     | selectattr('vlan') | map(attribute='vlan.vid') | sort -%} | ||||
| {% set dhcp_vlans = vrf_prefixes | selectattr('custom_fields.dhcp_ranges') | ||||
|     | map(attribute='vlan.vid') | sort -%} | ||||
| 
 | ||||
| # What servers should the DHCP relay forward requests to? | ||||
| SERVERS="{{ dhcp }}" | ||||
|  | @ -10,7 +10,7 @@ SERVERS="{{ dhcp }}" | |||
| # 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_networks) | ||||
|     | 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? | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| {# interfaces that belong to this bridge #} | ||||
| {% set ports = interfaces | selectattr('enabled') | selectattr('bridge') | selectattr('bridge.name', '==', bridge.name) %} | ||||
| {# allowed VLANs can be specified on the bridge, any of its ports, or all VLANs in NetBox #} | ||||
| {% set my_vlans = bridge.tagged_vlans or (ports | iface_vlans | flatten | sort(attribute='vid') | unique) or vlans %} | ||||
| {% set my_vlans = bridge.tagged_vlans or vlans %} | ||||
| {% set my_vlan_ids = my_vlans | map(attribute='vid') | sort -%} | ||||
| 
 | ||||
| auto {{ bridge.name }} | ||||
|  |  | |||
|  | @ -1,11 +1,7 @@ | |||
| # https://docs.nvidia.com/networking-ethernet-software/cumulus-linux/Layer-1-and-Switch-Ports/Interface-Configuration-and-Management/Switch-Port-Attributes/#breakout-ports | ||||
| {% for interface in interfaces | selectattr('name', 'match', '^swp[0-9]+$') %} | ||||
| {{ interface.name|regex_replace('^swp', '') }}= | ||||
| {%- if interfaces|selectattr('name', 'match', '^'+interface.name+'s[0-9]+$') %} | ||||
| 4x | ||||
| {% elif not interface.enabled %} | ||||
| disabled | ||||
| {% else %} | ||||
| 1x | ||||
| {% endif %} | ||||
| {% for interface in interfaces | selectattr('name', 'match', '^swp[0-9]+(s0)?$') %} | ||||
| {# get '1' from 'swp1' and '2' from 'swp2s0' #} | ||||
| {% set port = interface.name | regex_replace('^swp([0-9]+).*$', '\\1') %} | ||||
| {% set count = interfaces | selectattr('name', 'match', '^swp'+port+'(s[0-9])*$') | length %} | ||||
| {{ port }}={% if interface.enabled or count > 1 %}{{ count }}x{% else %}disabled{% endif +%} | ||||
| {% endfor %} | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| {% set fhrp_assignments = query('netbox.netbox.nb_lookup', 'fhrp-group-assignments', raw_data=true) %} | ||||
| {% set fhrp_groups = query('netbox.netbox.nb_lookup', 'fhrp-groups', raw_data=true) -%} | ||||
| 
 | ||||
| {% for iface in interfaces | rejectattr('name', 'in', ('lo', 'bridge')) | rejectattr('mgmt_only') | selectattr('enabled') %} | ||||
| auto {{ iface.name }} | ||||
| iface {{ iface.name }} | ||||
|  | @ -37,13 +40,13 @@ iface {{ iface.name }} | |||
| {% endif %} | ||||
| 
 | ||||
| {#- Addresses. #} | ||||
| {% for addr in iface.ip_addresses | rejectattr('role') %} | ||||
| {% for addr in iface.ip_addresses %} | ||||
|     address {{ addr.address }} | ||||
| {% endfor %} | ||||
| {% set anycast = iface.ip_addresses | selectattr('role') | selectattr('role.value', '==', 'anycast') | ||||
|         | map(attribute='address') %} | ||||
| {% if anycast %} | ||||
|     address-virtual 00:00:5e:00:01:01 {{ anycast | ipaddr(1) | join(' ') }} | ||||
| {% if iface.count_fhrp_groups > 0 %} | ||||
| {% set fhrp_assignment = fhrp_assignments | selectattr('interface.id', '==', iface.id) | first %} | ||||
| {% set fhrp_group = fhrp_groups | selectattr('id', '==', fhrp_assignment.group.id) | first %} | ||||
|     address-virtual 00:00:5e:00:01:01 {{ fhrp_group.ip_addresses | sort(attribute='family') | map(attribute='address') | join(' ') }} | ||||
| {% endif %} | ||||
| 
 | ||||
| {% endfor %} | ||||
|  |  | |||
							
								
								
									
										16
									
								
								roles/facts/tasks/main.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								roles/facts/tasks/main.yml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| # Make expensive lookups to NetBox once for later reference by any host. | ||||
| - name: Lookup networks and prefixes | ||||
|   set_fact: | ||||
|     vlans: '{{ query("netbox.netbox.nb_lookup", "vlans", api_filter="group=new-net", raw_data=true) | ||||
|         | sort(attribute="vid") }}' | ||||
|     prefixes: '{{ query("netbox.netbox.nb_lookup", "prefixes", raw_data=true) | ||||
|         | sort(attribute="prefix") | sort(attribute="family.value") }}' | ||||
| 
 | ||||
| - name: Select VLAN and BGP prefixes | ||||
|   set_fact: | ||||
|     vrf_prefixes: '{{ prefixes | selectattr("vrf") | ||||
|         | selectattr("vlan") | selectattr("vlan.id", "in", vlans|map(attribute="id")) | ||||
|         | sort(attribute="vlan.vid") }}' | ||||
|     bgp_prefixes: '{{ prefixes | selectattr("tenant") | ||||
|         | selectattr("role") | selectattr("role.slug", "==", "bgp") | ||||
|         | sort(attribute="tenant.slug") }}' | ||||
|  | @ -6,6 +6,10 @@ | |||
|   command: mkinitfs | ||||
|   when: "'handler' not in ansible_skip_tags" | ||||
| 
 | ||||
| - name: reboot | ||||
|   reboot: | ||||
|   when: "'handler' not in ansible_skip_tags" | ||||
| 
 | ||||
| - name: reload frr | ||||
|   command: /usr/lib/frr/frr-reload.py --reload /etc/frr/frr.conf | ||||
|   when: "'handler' not in ansible_skip_tags" | ||||
|  |  | |||
|  | @ -3,23 +3,13 @@ | |||
|     dest: /etc/network/interfaces.d/mgmt.intf | ||||
|     src: mgmt.intf.j2 | ||||
|     mode: 0644 | ||||
|   register: task_mgmt_interface | ||||
|   notify: reboot | ||||
| 
 | ||||
| - name: Run SSH in management VRF | ||||
|   lineinfile: | ||||
|     path: /etc/conf.d/sshd | ||||
|     regexp: "#* *vrf=" | ||||
|     line: "vrf=\"mgmt\"" | ||||
|   register: task_ssh_vrf | ||||
|   notify: reboot | ||||
| 
 | ||||
| - name: Reboot for new VRF | ||||
|   reboot: | ||||
|   when: task_mgmt_interface.changed or task_ssh_vrf.changed | ||||
|   register: task_reboot | ||||
| 
 | ||||
| - name: Reset the connection | ||||
|   meta: reset_connection | ||||
| 
 | ||||
| - name: Wait for the network device to reload | ||||
|   wait_for_connection: | ||||
|     delay: 10 | ||||
|   when: task_reboot.changed | ||||
| - meta: flush_handlers | ||||
|  |  | |||
|  | @ -82,16 +82,13 @@ ipv6 prefix-list default permit ::/0 | |||
| 
 | ||||
| ip prefix-list fabric permit 10.34.0.0/24 ge 32 | ||||
| 
 | ||||
| {% for vlan in vlans %} | ||||
| {% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vlan_id='~vlan.id, raw_data=true) %} | ||||
| {% if prefix.vrf and prefix.vrf.name != 'outside' %} | ||||
| {% for prefix in vrf_prefixes | rejectattr('vrf.name', '==', 'outside') | ||||
|     | sort(attribute='family.value') %} | ||||
| {% if prefix.family.value == 4 %} | ||||
| ip prefix-list office permit {{ prefix.prefix }} ge 24 | ||||
| {% elif prefix.family.value == 6 %} | ||||
| ipv6 prefix-list office permit {{ prefix.prefix }} ge 64 | ||||
| {% endif %} | ||||
| {% endif %} | ||||
| {% endfor %} | ||||
| {% endfor %} | ||||
| 
 | ||||
| {% if wg_net is defined %} | ||||
|  |  | |||
|  | @ -1,18 +1,9 @@ | |||
| {% for vlan in vlans %} | ||||
| {% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', api_filter='vlan_id='~vlan.id, raw_data=true) %} | ||||
| {% set prefixes4 = prefixes | selectattr('family.value', '==', 4) | map(attribute='prefix') %} | ||||
| {% set prefixes6 = prefixes | selectattr('family.value', '==', 6) | map(attribute='prefix') %} | ||||
| set {{ vlan.name }} { | ||||
|     type ipv4_addr; flags interval | ||||
| {% if prefixes4 %} | ||||
|     elements = { {{ prefixes4 | join(', ') }} } | ||||
| {% endif %} | ||||
| } | ||||
| set {{ vlan.name }}/6 { | ||||
|     type ipv6_addr; flags interval | ||||
| {% if prefixes6 %} | ||||
|     elements = { {{ prefixes6 | join(', ') }} } | ||||
| {% endif %} | ||||
| {% for family, family_prefixes in vrf_prefixes | groupby('family.value') %} | ||||
| {% for vlan, vlan_prefixes in family_prefixes | groupby('vlan.vid') %} | ||||
| set {{ vlan_prefixes[0].vlan.name }}{% if family == 6 %}/6{% endif %} { | ||||
|     type ipv{{ family }}_addr; flags interval | ||||
|     elements = { {{ vlan_prefixes | map(attribute='prefix') | join(',') }} } | ||||
| } | ||||
| {% endfor %} | ||||
| 
 | ||||
| {% endfor %} | ||||
|  |  | |||
|  | @ -80,16 +80,6 @@ table inet filter { | |||
|         ct status dnat accept \ | ||||
|         comment "Forward DNAT traffic for servers and suchlike" | ||||
| 
 | ||||
|         # Forward IPv4 to/from VPN users in the same network. | ||||
| {% for vlan in vlans %} | ||||
|         iif @inside ip saddr @{{ vlan.name }} ip daddr @{{ vlan.name }} accept | ||||
| {% endfor %} | ||||
| 
 | ||||
|         # Forward IPv6 to/from VPN users in the same network. | ||||
| {% for vlan in vlans %} | ||||
|         iif @inside ip6 saddr @{{ vlan.name }}/6 ip6 daddr @{{ vlan.name }}/6 accept | ||||
| {% endfor %} | ||||
| 
 | ||||
|         include "/etc/nftables.d/forward.nft*" | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,11 +1,8 @@ | |||
| {% set prefixes = query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true) -%} | ||||
| 
 | ||||
| { | ||||
| {% for vlan in vlans %} | ||||
| {% set vlan_prefixes = prefixes | selectattr('vlan') | selectattr('vlan.id', '==', vlan.id) | map(attribute='prefix') %} | ||||
|   "{{ vlan.name }}": { | ||||
|     "ip": {{ vlan_prefixes | ipv4 | to_json }}, | ||||
|     "ip6": {{ vlan_prefixes | ipv6 | to_json }} | ||||
| {% for vlan, addrs in vrf_prefixes | groupby('vlan.vid') %} | ||||
|   "{{ addrs[0].vlan.name }}": { | ||||
|     "ip": {{ addrs | selectattr('family.value', '==', 4) | map(attribute='prefix') | to_json }}, | ||||
|     "ip6": {{ addrs | selectattr('family.value', '==', 6) | map(attribute='prefix') | to_json }} | ||||
|   }{% if not loop.last %},{% endif +%} | ||||
| {% endfor %} | ||||
| } | ||||
|  |  | |||
|  | @ -78,16 +78,13 @@ route-map loopbacks permit 10 | |||
| ip prefix-list default permit 0.0.0.0/0 | ||||
| ipv6 prefix-list default permit ::/0 | ||||
| 
 | ||||
| {% for tenant in my_tenants %} | ||||
| {% for prefix in query('netbox.netbox.nb_lookup', 'prefixes', raw_data=true, api_filter='tenant='~tenant) | ||||
|     | selectattr('role') | selectattr('role.slug', '==', 'bgp') | rejectattr('vlan') %} | ||||
| {% for prefix in bgp_prefixes | selectattr('tenant.slug', 'in', my_tenants) %} | ||||
| {% if prefix.family.value == 4 %} | ||||
| ip prefix-list dc-{{ tenant }} permit {{ prefix.prefix }} ge 32 | ||||
| ip prefix-list dc-{{ prefix.tenant.slug }} permit {{ prefix.prefix }} ge 32 | ||||
| {% else %} | ||||
| ipv6 prefix-list dc-{{ tenant }} permit {{ prefix.prefix }} ge 64 | ||||
| ipv6 prefix-list dc-{{ prefix.tenant.slug }} permit {{ prefix.prefix }} ge 64 | ||||
| {% endif %} | ||||
| {% endfor %} | ||||
| {% endfor %} | ||||
| 
 | ||||
| # We only announce the default route to DC servers. | ||||
| route-map default->dc permit 10 | ||||
|  |  | |||
|  | @ -1,3 +1,9 @@ | |||
| - hosts: '*' | ||||
|   gather_facts: false | ||||
|   roles: | ||||
|     - facts | ||||
| 
 | ||||
| # Set up fabric. | ||||
| - hosts: spine-* | ||||
|   roles: | ||||
|     - spine | ||||
|  | @ -10,11 +16,13 @@ | |||
|   roles: | ||||
|     - exit | ||||
| 
 | ||||
| # Set up access switches. | ||||
| - hosts: access-*, sw-* | ||||
|   gather_facts: false | ||||
|   roles: | ||||
|     - access | ||||
| 
 | ||||
| # Set up firewall. | ||||
| - hosts: fw-* | ||||
|   roles: | ||||
|     - firewall | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue