diff --git a/_modules/node.py b/_modules/node.py --- a/_modules/node.py +++ b/_modules/node.py @@ -11,6 +11,7 @@ from salt.exceptions import CommandExecutionError, SaltCloudConfigError +from salt._compat import ipaddress def _get_all_nodes(): @@ -218,3 +219,43 @@ ipv6 = __grains__.get("ipv6") return " ".join(["[" + ip + "]" for ip in ipv6]) + + +def resolve_network(): + """ + A function to determine canonical properties of networks + from the nodes pillar. + + CLI Example: + salt * node.resolve_network + """ + network = { + "ipv4_address": "", + "ipv4_gateway": "", + } + private_network = network.copy() + + interfaces = _get_property("network:interfaces", __grains__["id"], {}) + for interface_name, interface in interfaces.items(): + if "ipv4" not in interface: + continue + + ipv4 = interface["ipv4"]["address"] + if ipaddress.ip_address(ipv4).is_private: + target = private_network + else: + target = network + + if target["ipv4_address"] != "": + continue + + target["ipv4_address"] = ipv4 + try: + target["ipv4_gateway"] = interface["ipv4"]["gateway"] + except KeyError: + pass + + if network["ipv4_address"] == "": + return private_network + + return network diff --git a/_tests/data/forests.yaml b/_tests/data/forests.yaml --- a/_tests/data/forests.yaml +++ b/_tests/data/forests.yaml @@ -9,11 +9,28 @@ hostname: egladil.lothlorien.forest roles: - treecity + network: + interfaces: + net01: + ipv4: + address: 1.2.3.4 #public + gateway: 1.2.3.254 + net02: + ipv4: + address: 10.100.0.4 #private + gateway: 10.100.0.1 + entwash: forest: fangorn hostname: entwash.node roles: - border + network: + interfaces: + net02: + ipv4: + address: 10.100.0.5 #private + gateway: 10.100.0.1 items_by_role: treecity: diff --git a/_tests/modules/test_node.py b/_tests/modules/test_node.py --- a/_tests/modules/test_node.py +++ b/_tests/modules/test_node.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from importlib.machinery import SourceFileLoader +from unittest_data_provider import data_provider import unittest @@ -104,6 +105,52 @@ node.get_ipv6_list(), ) + resolved_networks = lambda: ( + ( + "egladil", + { + "ipv4_address": "1.2.3.4", + "ipv4_gateway": "1.2.3.254", + }, + ), + ( + "entwash", + { + "ipv4_address": "10.100.0.5", + "ipv4_gateway": "10.100.0.1", + }, + ), + ) + + @data_provider(resolved_networks) + def test_resolve_network(self, id, expected): + self.grains["id"] = id + self.assertEqual(expected, node.resolve_network()) + + def test_resolve_network_without_gateway(self): + expected = { + "ipv4_address": "10.100.0.5", + "ipv4_gateway": "", + } + + self.grains["id"] = "entwash" + del self.pillar["nodes"]["entwash"]["network"]["interfaces"]["net02"]["ipv4"][ + "gateway" + ] + + self.assertEqual(expected, node.resolve_network()) + + def test_resolve_network_without_any_network(self): + expected = { + "ipv4_address": "", + "ipv4_gateway": "", + } + + self.grains["id"] = "entwash" + del self.pillar["nodes"]["entwash"]["network"] + + self.assertEqual(expected, node.resolve_network()) + if __name__ == "__main__": unittest.main() diff --git a/pillar/nodes/nodes.sls b/pillar/nodes/nodes.sls --- a/pillar/nodes/nodes.sls +++ b/pillar/nodes/nodes.sls @@ -19,18 +19,23 @@ roles: - opensearch network: - ipv4_interface: eno1 - ipv4_address: 188.165.200.229 - ipv4_gateway: 188.165.200.254 - - ipv6_interface: eno1 - ipv6_address: fe80::ec4:7aff:fe6a:36e8 - ipv6_gateway: fe80::ee30:91ff:fee0:df80 - ipv6_prefix: 64 ipv6_native: True - ipv6_tunnel: False + canonical_public_ipv4: 188.165.200.229 + + interfaces: + eno1: + device: eno1 + ipv4: + address: 188.165.200.229 + gateway: 188.165.200.254 + ipv6: + address: fe80::ec4:7aff:fe6a:36e8 + prefix: 64 + gateway: fe80::ee30:91ff:fee0:df80 + + dwellers: forest: nasqueron-infra hostname: dwellers.nasqueron.org @@ -41,16 +46,25 @@ flags: install_docker_devel_tools: True network: - ipv4_address: 51.255.124.11 - ipv4_gateway: 91.121.86.254 + ipv6_tunnel: True - private_interface: - uuid: 8e8ca793-b2eb-46d8-9266-125aba6d06c4 - device: ens224 - address: 172.27.27.4 - netmask: 255.255.255.0 + canonical_public_ipv4: 51.255.124.11 - ipv6_tunnel: True + interfaces: + ens192: + device: ens192 + uuid: 6e05ebea-f2fd-4ca1-a21f-78a778664d8c + ipv4: + address: 51.255.124.11 + gateway: 91.121.86.254 + + ens224: + device: ens224 + uuid: 8e8ca793-b2eb-46d8-9266-125aba6d06c4 + ipv4: + address: 172.27.27.4 + netmask: &drake_netmask 255.255.255.0 + gateway: 172.27.27.1 docker-001: forest: nasqueron-infra @@ -58,16 +72,25 @@ roles: - paas-docker network: - ipv4_address: 51.255.124.9 - ipv4_gateway: 91.121.86.254 + ipv6_tunnel: False - private_interface: - uuid: 3fd0b9f8-ecc3-400d-bc61-3ba21d0b6337 - device: ens224 - address: 172.27.27.6 - netmask: 255.255.255.0 + canonical_public_ipv4: 51.255.124.9 - ipv6_tunnel: False + interfaces: + ens192: + device: ens192 + uuid: ef7370c5-5060-4d89-82bb-dbeabf4a35f6 + ipv4: + address: 51.255.124.9 + gateway: 91.121.86.254 + + ens224: + device: ens224 + uuid: 3fd0b9f8-ecc3-400d-bc61-3ba21d0b6337 + ipv4: + address: 172.27.27.6 + netmask: *drake_netmask + gateway: 172.27.27.1 router-001: forest: nasqueron-infra @@ -75,18 +98,24 @@ roles: - router network: - ipv4_interface: vmx0 - ipv4_address: 51.255.124.8 - ipv4_netmask: 255.255.255.255 - ipv4_gateway: 91.121.86.254 ipv4_ovh_failover: True + ipv6_tunnel: False - private_interface: - device: vmx1 - address: 172.27.27.1 - netmask: 255.255.255.0 + canonical_public_ipv4: 51.255.124.8 - ipv6_tunnel: False + interfaces: + vmx0: + device: vmx0 + ipv4: + address: 51.255.124.8 + netmask: 255.255.255.255 + gateway: 91.121.86.254 + + vmx1: + device: vmx1 + ipv4: + address: 172.27.27.1 + netmask: &intranought_netmask 255.255.255.240 ysul: forest: nasqueron-dev @@ -99,15 +128,20 @@ zfs: pool: arcology network: - ipv4_interface: igb0 - ipv4_address: 163.172.49.16 - ipv4_gateway: 163.172.49.1 - ipv4_aliases: - - 212.83.187.132 - ipv6_tunnel: True ipv6_gateway: 2001:470:1f12:9e1::1 + canonical_public_ipv4: 212.83.187.132 + + interfaces: + igb0: + device: igb0 + ipv4: + address: 163.172.49.16 + gateway: 163.172.49.1 + aliases: + - 212.83.187.132 + windriver: forest: nasqueron-dev hostname: windriver.nasqueron.org @@ -119,18 +153,22 @@ zfs: pool: arcology network: - ipv4_interface: igb0 - ipv4_address: 51.159.18.59 - ipv4_gateway: 51.159.18.1 - - ipv6_interface: igb0 - ipv6_address: 2001:0bc8:6005:0005:aa1e:84ff:fef3:5d9c - ipv6_gateway: fe80::a293:51ff:feb7:5073 - ipv6_prefix: 128 ipv6_native: True - ipv6_tunnel: False + canonical_public_ipv4: 51.159.18.59 + + interfaces: + igb0: + device: igb0 + ipv4: + address: 51.159.18.59 + gateway: 51.159.18.1 + ipv6: + address: 2001:0bc8:6005:0005:aa1e:84ff:fef3:5d9c + gateway: fe80::a293:51ff:feb7:5073 + prefix: 128 + ## ## Forest: Eglide ## Semantic field: ? (P27 used for "Eglide" too) @@ -145,10 +183,19 @@ roles: - shellserver network: - ipv4_interface: ens2 - ipv4_address: 51.159.150.221 - ipv4_gateway: "" - ipv6_tunnel: True + + canonical_public_ipv4: 51.159.150.221 + + interfaces: + ens2: + device: ens2 + ipv4: + address: 51.159.150.221 + gateway: "" + flags: + # This interface is configured by cloud-init + - skip_interface_configuration + fixes: rsyslog_xconsole: True diff --git a/roles/core/motd/init.sls b/roles/core/motd/init.sls --- a/roles/core/motd/init.sls +++ b/roles/core/motd/init.sls @@ -7,7 +7,7 @@ # ------------------------------------------------------------- {% set motd_path = salt['motd.get_path']() %} -{% set network = salt['node.get']('network') %} +{% set network = salt['node.resolve_network()'] %} motd: file.managed: diff --git a/roles/core/network/files/FreeBSD/netif_ipv4.rc b/roles/core/network/files/Debian/10-net.jinja copy from roles/core/network/files/FreeBSD/netif_ipv4.rc copy to roles/core/network/files/Debian/10-net.jinja --- a/roles/core/network/files/FreeBSD/netif_ipv4.rc +++ b/roles/core/network/files/Debian/10-net.jinja @@ -1,9 +1,9 @@ # ------------------------------------------------------------- -# Network — rc configuration +# Network — interfaces configuration # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Project: Nasqueron # License: Trivial work, not eligible to copyright -# Source file: roles/core/network/files/FreeBSD/netif_ipv4.rc +# Source file: roles/core/network/files/Debian/10-net.jinja # ------------------------------------------------------------- # # <auto-generated> @@ -13,7 +13,10 @@ # and will be lost if the state is redeployed. # </auto-generated> -ifconfig_{{ interface }}="inet {{ ipv4_address }} netmask {{ ipv4_netmask }}" -{%- for ip in ipv4_aliases %} -ifconfig_{{ interface }}_alias{{ loop.index0 }}="inet {{ ip }} netmask 255.255.255.255" -{% endfor -%} +auto {{ interface.device }} +iface {{ interface.device }} inet static + address {{ interface.ipv4.address }} + netmask {{ interface.ipv4.netmask }} + {% if "gateway" in interface.ipv4 %} + gateway {{ interface.ipv4.gateway }} + {% endif %} diff --git a/roles/core/network/files/FreeBSD/netif_ipv4.rc b/roles/core/network/files/FreeBSD/netif_ipv4.rc --- a/roles/core/network/files/FreeBSD/netif_ipv4.rc +++ b/roles/core/network/files/FreeBSD/netif_ipv4.rc @@ -13,7 +13,7 @@ # and will be lost if the state is redeployed. # </auto-generated> -ifconfig_{{ interface }}="inet {{ ipv4_address }} netmask {{ ipv4_netmask }}" -{%- for ip in ipv4_aliases %} -ifconfig_{{ interface }}_alias{{ loop.index0 }}="inet {{ ip }} netmask 255.255.255.255" +ifconfig_{{ interface.device }}="inet {{ interface.address }} netmask {{ interface.netmask }}" +{%- for ip in interface.get("aliases", []) %} +ifconfig_{{ interface.device }}_alias{{ loop.index0 }}="inet {{ ip }} netmask 255.255.255.255" {% endfor -%} diff --git a/roles/core/network/files/RedHat/ifcfg-private b/roles/core/network/files/RedHat/ifcfg rename from roles/core/network/files/RedHat/ifcfg-private rename to roles/core/network/files/RedHat/ifcfg --- a/roles/core/network/files/RedHat/ifcfg-private +++ b/roles/core/network/files/RedHat/ifcfg @@ -3,7 +3,7 @@ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Project: Nasqueron # License: Trivial work, not eligible to copyright -# Source file: roles/core/network/files/RedHat/ifcfg-private +# Source file: roles/core/network/files/RedHat/ifcfg # ------------------------------------------------------------- # # <auto-generated> @@ -18,7 +18,7 @@ BROWSER_ONLY=no BOOTPROTO=none DEFROUTE=yes -IPV4_FAILURE_FATAL=no +IPV4_FAILURE_FATAL=yes IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes @@ -28,5 +28,5 @@ UUID={{ interface.uuid }} DEVICE={{ interface.device }} ONBOOT=yes +IPADDR={{ interface.ipv4.address }} PREFIX={{ prefix }} -IPADDR={{ interface.address }} diff --git a/roles/core/network/init.sls b/roles/core/network/init.sls --- a/roles/core/network/init.sls +++ b/roles/core/network/init.sls @@ -9,12 +9,11 @@ include: - .ipv4 - .ipv6 - - .private - .gre # Drake can be configured as: # -# - private (e.g. IntraNought network cards on EXSi hypervisor VMs) +# - ipv4 (e.g. IntraNought network cards on EXSi hypervisor VMs) # - gre (e.g. isolated servers needing a tunnel) # # Both are needed for servers with router role. diff --git a/roles/core/network/ipv4.sls b/roles/core/network/ipv4.sls --- a/roles/core/network/ipv4.sls +++ b/roles/core/network/ipv4.sls @@ -6,24 +6,34 @@ # License: Trivial work, not eligible to copyright # ------------------------------------------------------------- +{% from "roles/core/network/map.jinja" import interface_config with context %} {% set network = salt['node.get']('network') %} # ------------------------------------------------------------- # Interface # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{% if grains['os'] == 'FreeBSD' %} -/etc/rc.conf.d/netif/ipv4_{{ network['ipv4_interface'] }}: +{% for interface_name, interface in network["interfaces"] %} + +{% if "skip_interface_configuration" not in interface.get("flags", []) %} +network_ipv4_{{ interface_name }}: file.managed: - - source: salt://roles/core/network/files/FreeBSD/netif_ipv4.rc + {% if interface_config["suffix"] == "interface" %} + - name : {{ interface_config["config_path"] }}{{ interface_name }} + {% else %} + - name : {{ interface_config["config_path"] }}{{ interface["device"] }} + {% endif %} + - source: salt://roles/core/network/files/{{ interface_config["source_path"] }} - makedirs: True - template: jinja - - context: - interface: {{ network['ipv4_interface'] }} - ipv4_address: {{ network['ipv4_address'] }} - ipv4_netmask: {{ network['ipv4_netmask'] | default('255.255.255.0') }} - ipv4_aliases: {{ salt['node.get_list']('network:ipv4_aliases') }} + - defaults: + interface: {{ interface }} +{% if grains['os_family'] == 'RedHat' %} + prefix: {{ salt['network_utils.netmask_to_cidr_prefix'](interface['netmask']) }} {% endif %} +{% endif %} + +{% endfor %} # ------------------------------------------------------------- # Routes diff --git a/roles/core/network/ipv6.sls b/roles/core/network/ipv6.sls --- a/roles/core/network/ipv6.sls +++ b/roles/core/network/ipv6.sls @@ -26,19 +26,27 @@ {% if salt['node.has']('network:ipv6_native') %} +{% for interface_name, interface in network["interfaces"] %} + +{% if "gateway" in interface["ipv6"] %} +{% set ipv6_gateway = interface["ipv6"]["gateway"] %} +{% endif %} + {% if grains['os'] == 'FreeBSD' %} -/etc/rc.conf.d/netif/ipv6_{{ network['ipv6_interface'] }}: +/etc/rc.conf.d/netif/ipv6_{{ interface['device'] }}: file.managed: - source: salt://roles/core/network/files/FreeBSD/netif_ipv6.rc - makedirs: True - template: jinja - context: - interface: {{ network['ipv6_interface'] }} - ipv6_address: {{ network['ipv6_address'] }} - ipv6_prefix: {{ network['ipv6_prefix'] | default(64) }} + interface: {{ interface['device'] }} + ipv6_address: {{ interface['ipv6']['address'] }} + ipv6_prefix: {{ interface['ipv6']['prefix'] | default(64) }} has_native_ipv6: True {% endif %} +{% endfor %} + {% endif %} # ------------------------------------------------------------- @@ -67,7 +75,7 @@ - makedirs: True - template: jinja - context: - ipv6_gateway: {{ network['ipv6_gateway'] }} + ipv6_gateway: {{ ipv6_gateway }} {% endif %} {% endif %} diff --git a/roles/core/network/map.jinja b/roles/core/network/map.jinja new file mode 100644 --- /dev/null +++ b/roles/core/network/map.jinja @@ -0,0 +1,34 @@ +# ------------------------------------------------------------- +# Salt — Network +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# License: Trivial work, not eligible to copyright +# ------------------------------------------------------------- + +# ------------------------------------------------------------- +# Interface configuration by OS/distro +# +# config_path: the configuration file to write in OS +# source_path: in this repo, roles/core/network/files/<source_path> +# +# Don't set default value, so we MUST define them +# for EACH os/distro. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +{% set interface_config = salt['grains.filter_by']({ + 'FreeBSD': { + "config_path": "/etc/rc.conf.d/netif/ipv4_", + "source_path": "FreeBSD/netif_ipv4.rc", + "suffix": "interface", + }, + 'RedHat': { + "config_path": "/etc/sysconfig/network-scripts/ifcfg-", + "source_path": "RedHat/ifcfg", + "suffix": "device", + }, + 'Debian': { + "config_path": "/etc/network/interfaces.d/10-net-", + "source_path": "Debian/10-net.jinja", + "suffix": "device", + }, +}) %} diff --git a/roles/core/network/private.sls b/roles/core/network/private.sls deleted file mode 100644 --- a/roles/core/network/private.sls +++ /dev/null @@ -1,41 +0,0 @@ -# ------------------------------------------------------------- -# Salt — Network -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Project: Nasqueron -# Created: 2020-09-24 -# License: Trivial work, not eligible to copyright -# ------------------------------------------------------------- - -{% set network = salt['node.get']('network') %} - -# ------------------------------------------------------------- -# Interface -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{% if salt['node.has']('network:private_interface') %} -{% set interface = network['private_interface'] %} - -{% if grains['os'] == 'FreeBSD' %} -/etc/rc.conf.d/netif/ipv4_{{ network['private_interface'] }}: - file.managed: - - source: salt://roles/core/network/files/FreeBSD/netif_ipv4.rc - - makedirs: True - - template: jinja - - context: - interface: {{ interface['device'] }} - ipv4_address: {{ interface['address'] }} - ipv4_netmask: {{ interface['netmask'] | default('255.255.255.0') }} - ipv4_aliases: {{ salt['node.get_list']('network:private_interface:aliases') }} -{% endif %} - -{% if grains['os_family'] == 'RedHat' %} -/etc/sysconfig/network-scripts/ifcfg-{{ interface['device'] }}: - file.managed: - - source: salt://roles/core/network/files/RedHat/ifcfg-private - - template: jinja - - context: - interface: {{ interface }} - prefix: {{ salt['network_utils.netmask_to_cidr_prefix'](interface['netmask']) }} -{% endif %} - -{% endif %}