ADD import roles
This commit is contained in:
parent
4798dde38c
commit
e6027e9078
176
.gitignore
vendored
176
.gitignore
vendored
@ -45,3 +45,179 @@ tags
|
||||
.ionide
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/ansible,vim,visualstudiocode
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||
|
||||
3
roles/bird/defaults/main.yml
Normal file
3
roles/bird/defaults/main.yml
Normal file
@ -0,0 +1,3 @@
|
||||
bird_enabled: false
|
||||
bird_bgp_advertised_networks: []
|
||||
bird_bgp_neighbors: []
|
||||
5
roles/bird/handlers/main.yml
Normal file
5
roles/bird/handlers/main.yml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Reconfigure bird
|
||||
shell: /usr/sbin/birdc configure
|
||||
- name: Reload sysctl
|
||||
ansible.builtin.command: sysctl --system
|
||||
62
roles/bird/tasks/main.yml
Normal file
62
roles/bird/tasks/main.yml
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
- name: Set OS dependent variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
- "{{ ansible_system | lower }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
ignore_errors: True
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: OS is supported
|
||||
assert:
|
||||
that: __os_supported
|
||||
quiet: True
|
||||
vars:
|
||||
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Install required packages
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||
tags:
|
||||
- install
|
||||
|
||||
- name: Enable routing in sysctl
|
||||
template:
|
||||
src: sysctl.conf.j2
|
||||
dest: /etc/sysctl.d/90-routing.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload sysctl
|
||||
|
||||
- name: Install bird configuration
|
||||
template:
|
||||
src: bird.conf.j2
|
||||
dest: "/etc/bird/bird.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
when: bird_enabled
|
||||
notify: Reconfigure bird
|
||||
|
||||
- name: Enable bird service
|
||||
ansible.builtin.systemd_service:
|
||||
name: bird.service
|
||||
state: "{{ 'started' if bird_enabled else 'stopped' }}"
|
||||
enabled: "{{ bird_enabled }}"
|
||||
when: not ansible_check_mode
|
||||
261
roles/bird/templates/bird.conf.j2
Normal file
261
roles/bird/templates/bird.conf.j2
Normal file
@ -0,0 +1,261 @@
|
||||
# {{ ansible_managed }}
|
||||
|
||||
router id {{ bird_router_id }};
|
||||
|
||||
|
||||
protocol device {
|
||||
scan time 10;
|
||||
}
|
||||
|
||||
protocol kernel {
|
||||
scan time 5;
|
||||
|
||||
ipv6 {
|
||||
import none;
|
||||
export filter {
|
||||
{% if bird_prefsrc is defined %}
|
||||
krt_prefsrc = {{ bird_prefsrc }};
|
||||
{% endif %}
|
||||
if source = RTS_BGP then accept;
|
||||
if source = RTS_STATIC then accept;
|
||||
if source = RTS_DEVICE then reject;
|
||||
accept;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
protocol kernel {
|
||||
scan time 5;
|
||||
|
||||
ipv4 {
|
||||
import none;
|
||||
export filter {
|
||||
{% if bird_prefsrc_v4 is defined %}
|
||||
krt_prefsrc = {{ bird_prefsrc_v4 }};
|
||||
{% endif %}
|
||||
if source = RTS_BGP then accept;
|
||||
if source = RTS_STATIC then accept;
|
||||
if source = RTS_DEVICE then reject;
|
||||
accept;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
protocol static {
|
||||
{% for net in bird_bgp_advertised_networks %}
|
||||
route {{ net }} reject;
|
||||
{% endfor %}
|
||||
|
||||
ipv6 {
|
||||
import all;
|
||||
export none;
|
||||
};
|
||||
}
|
||||
|
||||
protocol static {
|
||||
{% for net in bird_bgp_advertised_networks_v4 | default([]) %}
|
||||
route {{ net }} reject;
|
||||
{% endfor %}
|
||||
ipv4 {
|
||||
import all;
|
||||
export none;
|
||||
};
|
||||
}
|
||||
|
||||
{%if bird_ospf_interfaces | default([]) | length > 0%}
|
||||
############################################
|
||||
# _|_| _|_|_| _|_|_| _|_|_|_| #
|
||||
# _| _| _| _| _| _| #
|
||||
# _| _| _|_| _|_|_| _|_|_| #
|
||||
# _| _| _| _| _| #
|
||||
# _|_| _|_|_| _| _| #
|
||||
############################################
|
||||
|
||||
# Filter to export all direct and bgp routes into OSPF
|
||||
filter export_all_OSPF {
|
||||
if ( source = RTS_DEVICE ) then accept;
|
||||
if ( source = RTS_STATIC_DEVICE ) then accept;
|
||||
if ( source = RTS_STATIC ) then accept;
|
||||
if ( source = RTS_BGP ) then accept;
|
||||
reject;
|
||||
}
|
||||
|
||||
protocol ospf v3 ospf6 {
|
||||
ipv6 {
|
||||
{% if bird_ospf_export_all | default(false) %}
|
||||
export filter export_all_OSPF;
|
||||
{% endif %}
|
||||
};
|
||||
area 0 {
|
||||
{%for iface in bird_ospf_interfaces%}
|
||||
interface "{{ iface.name }}" {
|
||||
{%if iface.stub | default(false)%}
|
||||
stub;
|
||||
{%endif%}
|
||||
};
|
||||
{%endfor%}
|
||||
};
|
||||
}
|
||||
{%endif%}
|
||||
|
||||
|
||||
{% if bird_bgp_neighbors | default([]) | length > 0%}
|
||||
###################################
|
||||
# _|_|_| _|_|_| _|_|_| #
|
||||
# _| _| _| _| _| #
|
||||
# _|_|_| _| _|_| _|_|_| #
|
||||
# _| _| _| _| _| #
|
||||
# _|_|_| _|_|_| _| #
|
||||
###################################
|
||||
define OWNNETSET = [{{ bird_bgp_advertised_networks | join(', ') }}];
|
||||
define OWNNETSET_v4 = [{{ bird_bgp_advertised_networks_v4 | default([]) | join(', ') }}];
|
||||
define OWNASN = {{ bird_bgp_local_asn }};
|
||||
|
||||
function is_self_net() {
|
||||
if net ~ OWNNETSET then return true;
|
||||
if net ~ OWNNETSET_v4 then return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function has_private_asn() {
|
||||
# Check if the AS path contains any private ASN
|
||||
if (bgp_path ~ [64512..65534]) || (bgp_path ~ [4200000000..4294967294]) then {
|
||||
print "Rejecting route with private ASN in AS path: ", bgp_path;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_reserved_net() {
|
||||
# ULA
|
||||
if net ~ fc00::/7 then return true;
|
||||
# Link-local
|
||||
if net ~ fe80::/10 then return true;
|
||||
# Documentation
|
||||
if net ~ 2001:db8::/32 then return true;
|
||||
# Discard
|
||||
if net ~ 100::/64 then return true;
|
||||
# Orchid
|
||||
if net ~ 2001:20::/28 then return true;
|
||||
|
||||
# IPv4
|
||||
if net ~ 0.0.0.0/8 then return true;
|
||||
if net ~ 10.0.0.0/8 then return true;
|
||||
if net ~ 100.64.0.0/10 then return true;
|
||||
if net ~ 127.0.0.0/8 then return true;
|
||||
if net ~ 169.254.0.0/16 then return true;
|
||||
if net ~ 172.16.0.0/12 then return true;
|
||||
if net ~ 192.0.0.0/24 then return true;
|
||||
if net ~ 192.0.2.0/24 then return true;
|
||||
if net ~ 192.88.99.0/24 then return true;
|
||||
if net ~ 192.168.0.0/16 then return true;
|
||||
if net ~ 198.18.0.0/15 then return true;
|
||||
if net ~ 198.51.100.0/24 then return true;
|
||||
if net ~ 203.0.113.0/24 then return true;
|
||||
if net ~ 224.0.0.0/4 then return true;
|
||||
if net ~ 233.252.0.0/24 then return true;
|
||||
if net ~ 240.0.0.0/4 then return true;
|
||||
if net ~ 255.255.255.255/32 then return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
{%for peer in bird_bgp_neighbors%}
|
||||
# {{ peer.name }}
|
||||
{% set peer_name = peer.name | lower | regex_replace('[\s\.\-\$%&/()=]', '_') %}
|
||||
|
||||
filter {{ peer_name }}_BGP_IMPORT {
|
||||
# don't re-import networks announced by ourself
|
||||
if is_self_net() then reject;
|
||||
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
|
||||
# don't import reserved networks
|
||||
if is_reserved_net() then reject;
|
||||
{% endif %}
|
||||
# never accept routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
|
||||
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
|
||||
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
|
||||
# prevent importing paths with private ASNs
|
||||
if has_private_asn() then reject;
|
||||
{% endif %}
|
||||
{% if (peer.max_as_path_length is defined) or (bird_bgp_max_as_path_length is defined) %}
|
||||
if bgp_path.len > {{ peer.max_as_path_length | default(bird_bgp_max_as_path_length) }} then reject;
|
||||
{% endif %}
|
||||
{% if (peer.restrict_prefixes | default([]) | length > 0) %}
|
||||
if net !~ [{{ peer.restrict_prefixes | join(', ') }}] then reject;
|
||||
{% endif %}
|
||||
accept;
|
||||
}
|
||||
|
||||
# export rules for {{ peer_name }}
|
||||
filter {{ peer_name }}_ALLOWED_BGP {
|
||||
{% for n in range(peer.as_prepend | default(1)) %}
|
||||
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
|
||||
{% endfor %}
|
||||
{% if not (peer.allow_reserved_networks | default(bird_bgp_allow_reserved_networks | default(false))) %}
|
||||
# don't export reserved networks
|
||||
if is_reserved_net() then reject;
|
||||
{% endif %}
|
||||
# never send routes more specific than /{{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }}
|
||||
if net.len > {{ peer.min_pref_len | default(bird_bgp_min_pref_len | default(48)) }} then reject;
|
||||
{% if not (peer.allow_private_asn | default(bird_bgp_allow_private_asn | default(false))) %}
|
||||
# prevent exposing paths with private ASNs
|
||||
if has_private_asn() then reject;
|
||||
{% endif %}
|
||||
if is_self_net() then accept;
|
||||
{% if (peer.redistribute_learned | default(false)) %}
|
||||
# redistribute all other routes learned from BGP
|
||||
if source = RTS_BGP then {
|
||||
{% if not (peer.redistribute_reserved | default(false)) %}
|
||||
# don't redistribute reserved networks
|
||||
if is_reserved_net() then reject;
|
||||
|
||||
{% endif %}
|
||||
{% for n in range(peer.redistribute_prepend | default(0)) %}
|
||||
bgp_path.prepend({{ peer.local_asn | default('OWNASN') }});
|
||||
{% endfor %}
|
||||
accept;
|
||||
}
|
||||
{% endif %}
|
||||
reject;
|
||||
}
|
||||
protocol bgp {{ peer_name }} {
|
||||
local {{ peer.local_ip }} as {{ peer.local_asn | default('OWNASN') }};
|
||||
neighbor {{ peer.neighbor_ip }} as {{ peer.neighbor_asn }};
|
||||
description "{{ peer.description | default(peer.name) }}";
|
||||
{% if peer.password is defined %}
|
||||
password "{{ peer.password }}";
|
||||
{% endif %}
|
||||
{% if peer.metric is defined %}
|
||||
path metric {{ peer.metric }};
|
||||
{% endif %}
|
||||
|
||||
{% if peer.ipv4 | default(false) == true %}
|
||||
ipv4 {
|
||||
import filter {{ peer_name }}_BGP_IMPORT;
|
||||
export filter {{ peer_name }}_ALLOWED_BGP;
|
||||
{% if peer.import_limit | default(1000) != false %}
|
||||
import limit {{ peer.import_limit | default(1000) }} action block;
|
||||
{% endif %}
|
||||
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
|
||||
};
|
||||
{% else %}
|
||||
ipv6 {
|
||||
import filter {{ peer_name }}_BGP_IMPORT;
|
||||
export filter {{ peer_name }}_ALLOWED_BGP;
|
||||
{% if peer.import_limit | default(1000) != false %}
|
||||
import limit {{ peer.import_limit | default(1000) }} action block;
|
||||
{% endif %}
|
||||
next hop {{ ('address ' + peer.next_hop) if peer.next_hop is defined else 'self' }};
|
||||
};
|
||||
{% endif %}
|
||||
}
|
||||
{%endfor%}
|
||||
|
||||
|
||||
# TODO: roa with routinator...
|
||||
|
||||
# roa4 table dn42_roa;
|
||||
# protocol static {
|
||||
# roa4 { table dn42_roa; };
|
||||
# include "/etc/bird/roa_dn42.conf";
|
||||
# };
|
||||
{% endif %}
|
||||
2
roles/bird/templates/sysctl.conf.j2
Normal file
2
roles/bird/templates/sysctl.conf.j2
Normal file
@ -0,0 +1,2 @@
|
||||
net.ipv4.ip_forward = 1
|
||||
net.ipv6.conf.all.forwarding = 1
|
||||
3
roles/bird/vars/debian.yml
Normal file
3
roles/bird/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
||||
bird_os_supported: true
|
||||
bird_packages:
|
||||
- bird2
|
||||
1
roles/bird/vars/main.yml
Normal file
1
roles/bird/vars/main.yml
Normal file
@ -0,0 +1 @@
|
||||
bird_os_supported: false
|
||||
2
roles/interfaces/defaults/main.yml
Normal file
2
roles/interfaces/defaults/main.yml
Normal file
@ -0,0 +1,2 @@
|
||||
net_interfaces: []
|
||||
interfaces_manage_ifupdown: true
|
||||
12
roles/interfaces/handlers/main.yml
Normal file
12
roles/interfaces/handlers/main.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Reload udev
|
||||
# TODO! FIXME!
|
||||
systemd_service:
|
||||
name: systemd-udev-trigger.service
|
||||
state: restarted
|
||||
- name: Reload ifupdown
|
||||
# TODO! FIXME! This drops some packages. Can this be done better?
|
||||
systemd_service:
|
||||
name: networking.service
|
||||
state: restarted
|
||||
throttle: 1
|
||||
57
roles/interfaces/tasks/main.yml
Normal file
57
roles/interfaces/tasks/main.yml
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
- name: Set OS dependent variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
- "{{ ansible_system | lower }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
ignore_errors: True
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: OS is supported
|
||||
assert:
|
||||
that: __os_supported
|
||||
quiet: True
|
||||
vars:
|
||||
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Install required packages
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||
tags:
|
||||
- install
|
||||
|
||||
- name: Install persistent if-names
|
||||
template:
|
||||
src: 60-persistent-net.rules.j2
|
||||
dest: "/etc/udev/rules.d/60-persistent-net.rules"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Reload udev
|
||||
|
||||
- name: Install ifupdown config
|
||||
template:
|
||||
src: ifupdown2.j2
|
||||
dest: "/etc/network/interfaces.d/ansible"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Reload ifupdown
|
||||
5
roles/interfaces/templates/60-persistent-net.rules.j2
Normal file
5
roles/interfaces/templates/60-persistent-net.rules.j2
Normal file
@ -0,0 +1,5 @@
|
||||
{% for iface in net_interfaces %}
|
||||
{% if iface.name is defined and iface.mac is defined %}
|
||||
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="{{ iface.mac }}", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="e*", NAME="{{ iface.name }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
43
roles/interfaces/templates/ifupdown2.j2
Normal file
43
roles/interfaces/templates/ifupdown2.j2
Normal file
@ -0,0 +1,43 @@
|
||||
# {{ ansible_managed }}
|
||||
|
||||
{% macro iface_config(iface, type, inet) %}
|
||||
iface {{ iface.name }} {{ type }} {{ inet.conf | default('manual') }}
|
||||
{% if iface.vlan_raw_device is defined %}
|
||||
vlan-raw-device {{ iface.vlan_raw_device }}
|
||||
{% endif %}
|
||||
{% if iface.dummy | default(false) %}
|
||||
pre-up ip link show $IFACE > /dev/null || sudo ip link add $IFACE type dummy
|
||||
post-down ip link show $IFACE > /dev/null && sudo ip link del $IFACE
|
||||
{% endif %}
|
||||
{% for addr in inet.addr %}
|
||||
up ip addr add {{ addr }} dev $IFACE
|
||||
pre-down ip addr del {{ addr }} dev $IFACE
|
||||
{% endfor %}
|
||||
{% if inet.gateway is defined and inet.gateway %}
|
||||
up ip r add default via {{ inet.gateway }} dev $IFACE
|
||||
pre-down ip r del default via {{ inet.gateway }} dev $IFACE
|
||||
{% endif %}
|
||||
{% if inet.routes is defined and inet.routes %}
|
||||
{% for route in inet.routes %}
|
||||
post-up ip r add {{ route.dst }} via {{ route.via }} dev $IFACE
|
||||
pre-down ip r del {{ route.dst }} via {{ route.via }} dev $IFACE
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if inet.custom is defined and inet.custom %}
|
||||
{{ inet.custom | default('') | indent(4) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% for iface in net_interfaces %}
|
||||
{% if interfaces_manage_ifupdown or (iface.ifupdown | default(false)) %}
|
||||
# {{ iface.name }}
|
||||
allow-hotplug {{ iface.name }}
|
||||
{% if iface.inet is defined and iface.inet %}
|
||||
{{ iface_config(iface, 'inet', iface.inet) }}
|
||||
{% endif %}
|
||||
{% if iface.inet6 is defined and iface.inet6 %}
|
||||
{{ iface_config(iface, 'inet6', iface.inet6) }}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
5
roles/interfaces/vars/debian.yml
Normal file
5
roles/interfaces/vars/debian.yml
Normal file
@ -0,0 +1,5 @@
|
||||
interfaces_os_supported: true
|
||||
interfaces_packages:
|
||||
- ifupdown
|
||||
- udev
|
||||
- vlan
|
||||
1
roles/interfaces/vars/main.yml
Normal file
1
roles/interfaces/vars/main.yml
Normal file
@ -0,0 +1 @@
|
||||
interfaces_os_supported: false
|
||||
1
roles/isc_dhcp/defaults/main.yml
Normal file
1
roles/isc_dhcp/defaults/main.yml
Normal file
@ -0,0 +1 @@
|
||||
isc_dhcp_enabled: false
|
||||
6
roles/isc_dhcp/handlers/main.yml
Normal file
6
roles/isc_dhcp/handlers/main.yml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Restart isc_dhcp
|
||||
ansible.builtin.systemd_service:
|
||||
name: isc-dhcp-server.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
64
roles/isc_dhcp/tasks/main.yml
Normal file
64
roles/isc_dhcp/tasks/main.yml
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
- name: Set OS dependent variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
- "{{ ansible_system | lower }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
ignore_errors: True
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: OS is supported
|
||||
assert:
|
||||
that: __os_supported
|
||||
quiet: True
|
||||
vars:
|
||||
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Install required packages
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||
when: radvd_enabled
|
||||
tags:
|
||||
- install
|
||||
|
||||
- name: Install dhcpd configuration
|
||||
template:
|
||||
src: dhcpd.conf.j2
|
||||
dest: "/etc/dhcp/dhcpd.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Restart isc_dhcp
|
||||
|
||||
- name: Install dhcpd configuration
|
||||
template:
|
||||
src: isc-dhcp-server.j2
|
||||
dest: "/etc/default/isc-dhcp-server"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Restart isc_dhcp
|
||||
|
||||
- name: Enable dhcpd service
|
||||
ansible.builtin.systemd_service:
|
||||
name: isc-dhcp-server.service
|
||||
state: started
|
||||
enabled: true
|
||||
21
roles/isc_dhcp/templates/dhcpd.conf.j2
Normal file
21
roles/isc_dhcp/templates/dhcpd.conf.j2
Normal file
@ -0,0 +1,21 @@
|
||||
# {{ ansible_managed }}
|
||||
|
||||
ddns-update-style none;
|
||||
authoritative;
|
||||
|
||||
{% for net in isc_dhcp_networks | default([]) %}
|
||||
# {{ net.interface }}
|
||||
subnet {{ net.subnet }} netmask {{ net.netmask }} {
|
||||
interface {{ net.interface }};
|
||||
option routers {{ net.router }};
|
||||
option subnet-mask {{ net.netmask }};
|
||||
{% if net.dns is defined %}
|
||||
option domain-name-servers {{ net.dns }};
|
||||
{% endif %}
|
||||
option domain-name "{{ net.domain | default('hiwi.net.scc.kit.edu') }}";
|
||||
range {{ net.range_start }} {{ net.range_end }};
|
||||
default-lease-time 600;
|
||||
max-lease-time 7200;
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
2
roles/isc_dhcp/templates/isc-dhcp-server.j2
Normal file
2
roles/isc_dhcp/templates/isc-dhcp-server.j2
Normal file
@ -0,0 +1,2 @@
|
||||
# {{ ansible_managed }}
|
||||
INTERFACESv4="{{ isc_dhcp_networks | map(attribute='interface') | join(' ') }}"
|
||||
3
roles/isc_dhcp/vars/debian.yml
Normal file
3
roles/isc_dhcp/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
||||
isc_dhcp_os_supported: true
|
||||
isc_dhcp_packages:
|
||||
- isc-dhcp-server
|
||||
1
roles/isc_dhcp/vars/main.yml
Normal file
1
roles/isc_dhcp/vars/main.yml
Normal file
@ -0,0 +1 @@
|
||||
isc_dhcp_os_supported: false
|
||||
2
roles/keepalived/defaults/main.yml
Normal file
2
roles/keepalived/defaults/main.yml
Normal file
@ -0,0 +1,2 @@
|
||||
keepalived_enabled: false
|
||||
keepalived_interfaces: []
|
||||
9
roles/keepalived/handlers/main.yml
Normal file
9
roles/keepalived/handlers/main.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: Restart keepalived
|
||||
systemd_service:
|
||||
name: keepalived.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
when: not ansible_check_mode
|
||||
- name: Reload sysctl
|
||||
ansible.builtin.command: sysctl --system
|
||||
52
roles/keepalived/tasks/main.yml
Normal file
52
roles/keepalived/tasks/main.yml
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
- name: Set OS dependent variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
- "{{ ansible_system | lower }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
ignore_errors: True
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: OS is supported
|
||||
assert:
|
||||
that: __os_supported
|
||||
quiet: True
|
||||
vars:
|
||||
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Install required packages
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||
tags:
|
||||
- install
|
||||
|
||||
- name: Install keepalived configuration
|
||||
template:
|
||||
src: keepalived.conf.j2
|
||||
dest: "/etc/keepalived/keepalived.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Restart keepalived
|
||||
|
||||
- name: Enable keepalived service
|
||||
ansible.builtin.systemd_service:
|
||||
name: keepalived.service
|
||||
state: "{{ 'started' if keepalived_enabled else 'stopped' }}"
|
||||
enabled: "{{ keepalived_enabled }}"
|
||||
when: not ansible_check_mode
|
||||
27
roles/keepalived/templates/keepalived.conf.j2
Normal file
27
roles/keepalived/templates/keepalived.conf.j2
Normal file
@ -0,0 +1,27 @@
|
||||
# {{ ansible_managed }}
|
||||
vrrp_sync_group G1 {
|
||||
group {
|
||||
{%for iface in keepalived_interfaces %}
|
||||
{{ iface.name }}
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
|
||||
{%for iface in keepalived_interfaces %}
|
||||
vrrp_instance {{ iface.name }} {
|
||||
state {{ keepalived_init_state | default('BACKUP') }}
|
||||
interface {{ iface.interface | default(iface.name) }}
|
||||
virtual_router_id 42
|
||||
priority {{ keepalived_host_prio }}
|
||||
advert_int 2
|
||||
authentication {
|
||||
auth_type PASS
|
||||
auth_pass {{ iface.password | default(keepalived_password) }}
|
||||
}
|
||||
virtual_ipaddress {
|
||||
{% for addr in iface.addresses %}
|
||||
{{ addr }}
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
{% endfor %}
|
||||
3
roles/keepalived/vars/debian.yml
Normal file
3
roles/keepalived/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
||||
keepalived_os_supported: true
|
||||
keepalived_packages:
|
||||
- keepalived
|
||||
1
roles/keepalived/vars/main.yml
Normal file
1
roles/keepalived/vars/main.yml
Normal file
@ -0,0 +1 @@
|
||||
keepalived_os_supported: false
|
||||
1
roles/radvd/defaults/main.yml
Normal file
1
roles/radvd/defaults/main.yml
Normal file
@ -0,0 +1 @@
|
||||
radvd_enabled: false
|
||||
6
roles/radvd/handlers/main.yml
Normal file
6
roles/radvd/handlers/main.yml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Restart radvd
|
||||
ansible.builtin.systemd_service:
|
||||
name: radvd.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
52
roles/radvd/tasks/main.yml
Normal file
52
roles/radvd/tasks/main.yml
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
- name: Set OS dependent variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}_{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
- "{{ ansible_system | lower }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
ignore_errors: True
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: OS is supported
|
||||
assert:
|
||||
that: __os_supported
|
||||
quiet: True
|
||||
vars:
|
||||
__os_supported: "{{ lookup('vars', '{}_os_supported'.format(role_name)) | bool }}"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Install required packages
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ lookup('vars', '{}_packages'.format(role_name)) | list }}"
|
||||
when: radvd_enabled
|
||||
tags:
|
||||
- install
|
||||
|
||||
- name: Install radvd configuration
|
||||
template:
|
||||
src: radvd.conf.j2
|
||||
dest: "/etc/radvd.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
tags:
|
||||
- configure
|
||||
no_log: false
|
||||
notify: Restart radvd
|
||||
|
||||
- name: Enable radvd service
|
||||
ansible.builtin.systemd_service:
|
||||
name: radvd.service
|
||||
state: started
|
||||
enabled: true
|
||||
24
roles/radvd/templates/radvd.conf.j2
Normal file
24
roles/radvd/templates/radvd.conf.j2
Normal file
@ -0,0 +1,24 @@
|
||||
# {{ ansible_managed }}
|
||||
{% for iface in radvd_interfaces | default([]) %}
|
||||
interface {{ iface.name }} {
|
||||
AdvSendAdvert on;
|
||||
MinRtrAdvInterval 5;
|
||||
MaxRtrAdvInterval 15;
|
||||
route ::/0 {};
|
||||
AdvManagedFlag off;
|
||||
AdvOtherConfigFlag off;
|
||||
prefix {{ iface.prefix }} {
|
||||
AdvOnLink on;
|
||||
AdvAutonomous on;
|
||||
AdvRouterAddr on;
|
||||
};
|
||||
{% if iface.source is defined %}
|
||||
AdvRASrcAddress {
|
||||
{{ iface.source }};
|
||||
};
|
||||
{% endif %}
|
||||
RDNSS {{ iface.dns | default(['2a00:1398::1', '2a00:1398::2']) | join(' ') }} {
|
||||
AdvRDNSSLifetime {{ iface.dns_lifetime | default(3600) }};
|
||||
};
|
||||
};
|
||||
{% endfor %}
|
||||
3
roles/radvd/vars/debian.yml
Normal file
3
roles/radvd/vars/debian.yml
Normal file
@ -0,0 +1,3 @@
|
||||
radvd_os_supported: true
|
||||
radvd_packages:
|
||||
- radvd
|
||||
1
roles/radvd/vars/main.yml
Normal file
1
roles/radvd/vars/main.yml
Normal file
@ -0,0 +1 @@
|
||||
bird_os_supported: false
|
||||
193
roles/router_bundle/filter_plugins/router_filters.py
Normal file
193
roles/router_bundle/filter_plugins/router_filters.py
Normal file
@ -0,0 +1,193 @@
|
||||
import ipaddress
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'generate_radvd': self.generate_radvd,
|
||||
'generate_keepalived': self.generate_keepalived,
|
||||
'generate_interfaces': self.generate_interfaces,
|
||||
'generate_nftables': self.generate_nftables,
|
||||
'generate_dhcpd': self.generate_dhcpd,
|
||||
'generate_ospf': self.generate_ospf,
|
||||
}
|
||||
|
||||
def generate_radvd(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
if 'prefix' not in net:
|
||||
continue
|
||||
if 'dns' in net:
|
||||
dns = net['dns']
|
||||
else:
|
||||
dns = ['2a00:1398::64:1', '2a00:1398::64:2']
|
||||
radvd = {
|
||||
'name': net['interface'],
|
||||
'prefix': net['prefix'],
|
||||
'source': 'fe80::1',
|
||||
'dns': dns,
|
||||
}
|
||||
result.append(radvd)
|
||||
return result
|
||||
|
||||
def generate_keepalived(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
keepalived = {
|
||||
'name': net['interface'],
|
||||
'addresses': ['fe80::1/64'],
|
||||
}
|
||||
result.append(keepalived)
|
||||
if 'prefix_v4' in net:
|
||||
keepalived = {
|
||||
'name': net['interface'] + '_v4',
|
||||
'interface': net['interface'],
|
||||
'addresses': [self.nth_addr_v4(net['prefix_v4'], 1)],
|
||||
}
|
||||
result.append(keepalived)
|
||||
return result
|
||||
|
||||
def nth_addr(self, prefix: str, n:int) -> str:
|
||||
network = ipaddress.IPv6Network(prefix)
|
||||
# Check if n is within the valid range
|
||||
if n >= network.num_addresses or n < 0:
|
||||
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||
# Calculate the n-th address
|
||||
nth_address = network.network_address + n
|
||||
return str(nth_address) + '/' + str(network.prefixlen)
|
||||
|
||||
def nth_addr_v4(self, prefix: str, n:int, with_prefix: bool = True) -> str:
|
||||
network = ipaddress.IPv4Network(prefix)
|
||||
# Check if n is within the valid range
|
||||
if n >= network.num_addresses or n < 0:
|
||||
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||
# Calculate the n-th address
|
||||
nth_address = network.network_address + n
|
||||
if with_prefix:
|
||||
return str(nth_address) + '/' + str(network.prefixlen)
|
||||
else:
|
||||
return str(nth_address)
|
||||
|
||||
def generate_interfaces(self, cluster_networks, addr_num):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
interface = {
|
||||
'name': net['interface'],
|
||||
'mac': net['mac'],
|
||||
}
|
||||
if 'prefix' in net:
|
||||
inet6 = {
|
||||
'addr': [self.nth_addr(net['prefix'], addr_num)],
|
||||
'gateway': False,
|
||||
'custom': False,
|
||||
'routes': False,
|
||||
}
|
||||
interface['inet6'] = inet6
|
||||
if 'prefix_v4' in net:
|
||||
inet4 = {
|
||||
'addr': [self.nth_addr_v4(net['prefix_v4'], addr_num)],
|
||||
'gateway': False,
|
||||
'custom': False,
|
||||
'routes': False,
|
||||
}
|
||||
interface['inet'] = inet4
|
||||
result.append(interface)
|
||||
return result
|
||||
|
||||
def clean_port_range(self, port_range):
|
||||
if ('{' not in port_range) and ('-' in port_range or ',' in port_range):
|
||||
return '{{' + port_range + '}}'
|
||||
return port_range
|
||||
|
||||
def generate_nftables(self, cluster_networks, upstream_interface):
|
||||
result = []
|
||||
nat_rules = []
|
||||
default_policies = []
|
||||
for net in cluster_networks:
|
||||
if 'prefix' in net:
|
||||
if net.get('firewall', None) is not None and net['firewall'].get('enabled', False) == True:
|
||||
for rule in net['firewall'].get('rules', []):
|
||||
if rule.get('dst', None) is None:
|
||||
rule['dst'] = '%net'
|
||||
|
||||
nft_rule = 'oifname {} '.format(net['interface'])
|
||||
if rule.get('src', '') != '':
|
||||
nft_rule += 'ip6 saddr {} '.format(rule['src'].replace('%net', net['prefix']))
|
||||
if rule.get('dst', '') != '':
|
||||
nft_rule += 'ip6 daddr {} '.format(rule['dst'].replace('%net', net['prefix']))
|
||||
if rule.get('sport', '') != '':
|
||||
nft_rule += '{{proto}} sport {} '.format(self.clean_port_range(rule['sport']))
|
||||
if rule.get('dport', '') != '':
|
||||
nft_rule += '{{proto}} dport {} '.format(self.clean_port_range(rule['dport']))
|
||||
nft_rule += rule['action']
|
||||
for proto in rule.get('protocols', ['tcp', 'udp']):
|
||||
result.append(nft_rule.format(proto=proto))
|
||||
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], net['firewall']['policy']))
|
||||
else:
|
||||
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], 'accept'))
|
||||
# NAT
|
||||
if net.get('nat', None) is not None and net['nat'].get('enabled', False) == True:
|
||||
if net['nat'].get('source', None) is not None and net.get('prefix_v4', None) is not None:
|
||||
nat_rules.append('ip saddr {prefix} oif {dest_iface} snat to {source}'.format(prefix=net['prefix_v4'], dest_iface=upstream_interface, source=net['nat']['source']))
|
||||
|
||||
default_policies.append('oifname {} accept'.format(upstream_interface))
|
||||
|
||||
full_rules = "\n".join(result + default_policies)
|
||||
|
||||
full_nat = "\n".join(nat_rules)
|
||||
|
||||
ret = [
|
||||
{
|
||||
'name': 'cluster_router_bundle',
|
||||
'rule': full_rules,
|
||||
'chain': 'forward',
|
||||
},
|
||||
{
|
||||
'name': 'cluster_router_bundle',
|
||||
'rule': full_nat,
|
||||
'chain': 'postrouting',
|
||||
}
|
||||
]
|
||||
return ret
|
||||
|
||||
def is_v4(self, addr: str) -> bool:
|
||||
try:
|
||||
ipaddress.IPv4Address(addr)
|
||||
return True
|
||||
except ipaddress.AddressValueError:
|
||||
return False
|
||||
|
||||
def generate_dhcpd(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
if net.get('prefix_v4', None) is None:
|
||||
continue
|
||||
if net.get('dhcp', None) is None or net['dhcp'].get('enabled', False) == False:
|
||||
continue
|
||||
router = self.nth_addr_v4(net['prefix_v4'], 1, False)
|
||||
subnet = self.nth_addr_v4(net['prefix_v4'], 0, False)
|
||||
netmask = str(ipaddress.IPv4Network(net['prefix_v4']).netmask)
|
||||
range_start = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_start', 20), False)
|
||||
range_end = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_end', 150), False)
|
||||
dhcpd = {
|
||||
'interface': net['interface'],
|
||||
'subnet': subnet,
|
||||
'router': router,
|
||||
'netmask': netmask,
|
||||
'range_start': range_start,
|
||||
'range_end': range_end,
|
||||
}
|
||||
dns = ', '.join(list(filter(lambda x: self.is_v4(x), net.get('dns', []))))
|
||||
if len(dns) > 0:
|
||||
dhcpd['dns'] = dns
|
||||
result.append(dhcpd)
|
||||
return result
|
||||
|
||||
def generate_ospf(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
ospf = {
|
||||
'name': net['interface'],
|
||||
'stub': True,
|
||||
}
|
||||
result.append(ospf)
|
||||
return result
|
||||
19
roles/router_bundle/tasks/main.yml
Normal file
19
roles/router_bundle/tasks/main.yml
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Generate radvd config
|
||||
set_fact:
|
||||
radvd_interfaces: "{{ cluster_networks | generate_radvd | union(radvd_interfaces | default([])) }}"
|
||||
- name: Generate keepalived config
|
||||
set_fact:
|
||||
keepalived_interfaces: "{{ cluster_networks | generate_keepalived | union(keepalived_interfaces | default([])) }}"
|
||||
- name: Generate interfaces config
|
||||
set_fact:
|
||||
net_interfaces: "{{ cluster_networks | generate_interfaces(cluster_v6_end) | union(net_interfaces | default([])) }}"
|
||||
- name: Generate firewall config
|
||||
set_fact:
|
||||
nftables_rules: "{{ cluster_networks | generate_nftables(cluster_upstream_interface) | union(nftables_rules | default([])) }}"
|
||||
- name: Generate dhcp config
|
||||
set_fact:
|
||||
isc_dhcp_networks: "{{ cluster_networks | generate_dhcpd | union(isc_dhcp_networks | default([])) }}"
|
||||
# - name: Generate ospf config
|
||||
# set_fact:
|
||||
# bird_ospf_interfaces: "{{ cluster_networks | generate_ospf | union(bird_ospf_interfaces | default([])) }}"
|
||||
Loading…
x
Reference in New Issue
Block a user