diff --git a/partition/roles/metal-core/test/__init__.py b/partition/roles/metal-core/test/__init__.py new file mode 100644 index 000000000..4795a2386 --- /dev/null +++ b/partition/roles/metal-core/test/__init__.py @@ -0,0 +1,6 @@ +import os + + +def read_template_file(name): + with open(os.path.join(os.path.dirname(__file__), "..", "templates", name), 'r') as f: + return f.read() diff --git a/partition/roles/metal-core/test/template_test.py b/partition/roles/metal-core/test/template_test.py new file mode 100644 index 000000000..88992332c --- /dev/null +++ b/partition/roles/metal-core/test/template_test.py @@ -0,0 +1,159 @@ +import unittest + +import yaml +import os +from textwrap import dedent + +from ansible.template import Templar + +from test import read_template_file + + +def read_defaults(): + with open(os.path.join(os.path.dirname(__file__), "..", "defaults", "main.yaml"), "r") as stream: + return yaml.safe_load(stream) + + +def read_partition_defaults(): + with open(os.path.join(os.path.dirname(__file__), "..", "..", "defaults", "defaults", "main.yaml"), "r") as stream: + return yaml.safe_load(stream) + + +def base_vars(): + return read_partition_defaults() | read_defaults() | dict( + metal_partition_timezone="Europe/Berlin", + metal_partition_id="fra-equ01", + metal_partition_metal_api_addr="api.metal-stack.dev", + lo="10.0.0.11", + asn=4200000011, + metal_core_cidr="10.255.255.2/24", + metal_core_rack_id="rack01", + ) + + +class MetalCoreEnv(unittest.TestCase): + maxDiff = None + + def test_env_template(self): + t = read_template_file("metal-core-env.j2") + + templar = Templar(loader=None, variables=base_vars()) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + TZ: "Europe/Berlin" + METAL_CORE_LOOPBACK_IP: "10.0.0.11" + METAL_CORE_ASN: "4200000011" + METAL_CORE_CIDR: "10.255.255.2/24" + METAL_CORE_PARTITION_ID: "fra-equ01" + METAL_CORE_RACK_ID: "rack01" + METAL_CORE_BIND_ADDRESS: 0.0.0.0 + METAL_CORE_METAL_API_IP: "api.metal-stack.dev" + METAL_CORE_METAL_API_PORT: "443" + METAL_CORE_METAL_API_PROTOCOL: "https" + METAL_CORE_METAL_API_BASEPATH: "/" + METAL_CORE_HMAC_KEY: "change-me" + METAL_CORE_LOG_LEVEL: "warn" + METAL_CORE_RECONFIGURE_SWITCH: "True" + METAL_CORE_RECONFIGURE_SWITCH_INTERVAL: "10s" + METAL_CORE_TIMEOUT: "15s" + METAL_CORE_SET_SRC_LOOPBACK: "True" + METAL_CORE_GRPC_ADDRESS: "api.metal-stack.dev:50051" + METAL_CORE_GRPC_CA_CERT_FILE: "/certs/grpc/ca.pem" + METAL_CORE_GRPC_CLIENT_CERT_FILE: "/certs/grpc/client.pem" + METAL_CORE_GRPC_CLIENT_KEY_FILE: "/certs/grpc/client-key.pem" + METAL_CORE_ADDITIONAL_BRIDGE_VIDS: "" + METAL_CORE_ADDITIONAL_BRIDGE_PORTS: "" + METAL_CORE_INTERFACES_TPL_FILE: "" + METAL_CORE_FRR_TPL_FILE: "" + METAL_CORE_PXE_VLAN_ID: "4000" + """), result) + + def test_env_template_with_room_id_mgmt_gateway_and_spine_uplinks(self): + t = read_template_file("metal-core-env.j2") + + templar = Templar(loader=None, variables=base_vars() | dict( + metal_core_room_id="room01", + metal_partition_mgmt_gateway="10.255.255.1", + metal_core_spine_uplinks=["Ethernet0", "Ethernet4"], + metal_core_additional_bridge_vids=["201-256"], + metal_core_additional_bridge_ports=["Ethernet96", "Ethernet100"], + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + TZ: "Europe/Berlin" + METAL_CORE_LOOPBACK_IP: "10.0.0.11" + METAL_CORE_ASN: "4200000011" + METAL_CORE_CIDR: "10.255.255.2/24" + METAL_CORE_PARTITION_ID: "fra-equ01" + METAL_CORE_RACK_ID: "rack01" + METAL_CORE_ROOM_ID: "room01" + METAL_CORE_BIND_ADDRESS: 0.0.0.0 + METAL_CORE_METAL_API_IP: "api.metal-stack.dev" + METAL_CORE_METAL_API_PORT: "443" + METAL_CORE_METAL_API_PROTOCOL: "https" + METAL_CORE_METAL_API_BASEPATH: "/" + METAL_CORE_HMAC_KEY: "change-me" + METAL_CORE_LOG_LEVEL: "warn" + METAL_CORE_RECONFIGURE_SWITCH: "True" + METAL_CORE_RECONFIGURE_SWITCH_INTERVAL: "10s" + METAL_CORE_TIMEOUT: "15s" + METAL_CORE_SET_SRC_LOOPBACK: "True" + METAL_CORE_MANAGEMENT_GATEWAY: "10.255.255.1" + METAL_CORE_GRPC_ADDRESS: "api.metal-stack.dev:50051" + METAL_CORE_GRPC_CA_CERT_FILE: "/certs/grpc/ca.pem" + METAL_CORE_GRPC_CLIENT_CERT_FILE: "/certs/grpc/client.pem" + METAL_CORE_GRPC_CLIENT_KEY_FILE: "/certs/grpc/client-key.pem" + METAL_CORE_ADDITIONAL_BRIDGE_VIDS: "201-256" + METAL_CORE_ADDITIONAL_BRIDGE_PORTS: "Ethernet96,Ethernet100" + METAL_CORE_SPINE_UPLINKS: "Ethernet0,Ethernet4" + METAL_CORE_INTERFACES_TPL_FILE: "" + METAL_CORE_FRR_TPL_FILE: "" + METAL_CORE_PXE_VLAN_ID: "4000" + """), result) + + +class MetalCoreVolumes(unittest.TestCase): + maxDiff = None + + def test_volumes_template_sonic(self): + t = read_template_file("metal-core-volumes.j2") + + templar = Templar(loader=None, variables=base_vars() | dict( + metal_stack_switch_os_is_sonic=True, + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + - /etc/sonic/:/etc/sonic + - /var/run/redis/:/var/run/redis + - /var/run/dbus:/var/run/dbus + - /run/systemd/private:/run/systemd/private + - "/certs/grpc:/certs/grpc:ro" + - /var/run/bgp-neighbors:/var/run/bgp-neighbors"""), result) + + def test_volumes_template_cumulus_with_hosts_file_resolution_and_additional_mounts(self): + t = read_template_file("metal-core-volumes.j2") + + templar = Templar(loader=None, variables=base_vars() | dict( + metal_stack_switch_os_is_sonic=False, + metal_core_consider_hosts_file_resolution=True, + metal_core_additional_volume_mounts=["/etc/motd:/etc/motd:ro"], + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + - /etc/network/:/etc/network + - /etc/frr/:/etc/frr + - /etc/lsb-release:/etc/lsb-release:ro + - /var/run/dbus:/var/run/dbus + - /run/systemd/private:/run/systemd/private + - /etc/nsswitch.conf:/etc/nsswitch.conf + - "/certs/grpc:/certs/grpc:ro" + - /etc/motd:/etc/motd:ro + - /var/run/bgp-neighbors:/var/run/bgp-neighbors"""), result) diff --git a/partition/roles/mgmt-server/test/__init__.py b/partition/roles/mgmt-server/test/__init__.py new file mode 100644 index 000000000..4795a2386 --- /dev/null +++ b/partition/roles/mgmt-server/test/__init__.py @@ -0,0 +1,6 @@ +import os + + +def read_template_file(name): + with open(os.path.join(os.path.dirname(__file__), "..", "templates", name), 'r') as f: + return f.read() diff --git a/partition/roles/mgmt-server/test/template_test.py b/partition/roles/mgmt-server/test/template_test.py new file mode 100644 index 000000000..4264167d7 --- /dev/null +++ b/partition/roles/mgmt-server/test/template_test.py @@ -0,0 +1,208 @@ +import unittest + +import yaml +import os +from textwrap import dedent + +from ansible.template import Templar + +from test import read_template_file + + +def read_defaults(): + with open(os.path.join(os.path.dirname(__file__), "..", "defaults", "main.yaml"), "r") as stream: + return yaml.safe_load(stream) + + +class MgmtServerFrrConf(unittest.TestCase): + maxDiff = None + + def test_frr_conf_template(self): + t = read_template_file("frr.conf.j2") + + templar = Templar(loader=None, variables=read_defaults() | dict( + ansible_hostname="mgmt01", + mgmt_server_asn=4200000000, + mgmt_server_router_id="10.1.0.1", + mgmt_server_spine_facing_interface="lan0", + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + frr defaults datacenter + hostname mgmt01 + ! + service integrated-vtysh-config + ! + log syslog debugging + debug bgp updates + debug bgp nht + debug bgp update-groups + debug bgp zebra + ! + interface lan0 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra + ! + router bgp 4200000000 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + bgp network import-check + neighbor FABRIC peer-group + neighbor FABRIC remote-as external + neighbor FABRIC timers 1 3 + neighbor lan0 interface peer-group FABRIC + ! + address-family ipv4 unicast + redistribute connected route-map LOCAL_INTERFACES + exit-address-family + ! + route-map LOCAL_INTERFACES permit 10 + match interface lo + ! + line vty + ! + """), result) + + def test_frr_conf_template_with_firewall_static_routes_and_default_route(self): + t = read_template_file("frr.conf.j2") + + templar = Templar(loader=None, variables=read_defaults() | dict( + ansible_hostname="mgmt01", + mgmt_server_asn=4200000000, + mgmt_server_router_id="10.1.0.1", + mgmt_server_spine_facing_interface="lan0", + mgmt_server_firewall_ip="10.1.0.2", + mgmt_server_provide_default_route=True, + mgmt_server_frr_static_routes=[ + "10.100.0.0/22 10.1.0.254", + "192.168.0.0/16 10.1.0.253", + ], + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + frr defaults datacenter + hostname mgmt01 + ! + service integrated-vtysh-config + ! + log syslog debugging + debug bgp updates + debug bgp nht + debug bgp update-groups + debug bgp zebra + ! + ip route 10.100.0.0/22 10.1.0.254 + ip route 192.168.0.0/16 10.1.0.253 + ! + interface lan0 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra + ! + router bgp 4200000000 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + bgp network import-check + neighbor FABRIC peer-group + neighbor FABRIC remote-as external + neighbor FABRIC timers 1 3 + neighbor lan0 interface peer-group FABRIC + neighbor 10.1.0.2 peer-group FABRIC + neighbor 10.1.0.2 disable-connected-check + ! + address-family ipv4 unicast + redistribute connected route-map LOCAL_INTERFACES + network 0.0.0.0/0 + exit-address-family + ! + route-map LOCAL_INTERFACES permit 10 + match interface lo + ! + line vty + ! + """), result) + + +class MgmtServerSSHConfig(unittest.TestCase): + maxDiff = None + + def test_ssh_config_template(self): + t = read_template_file("ssh_config.j2") + + # leaf02 comes before leaf01 to prove hosts are rendered in sorted + # order, mgmt01 has no ansible_user and must be skipped entirely + templar = Templar(loader=None, variables=read_defaults() | dict( + mgmt_server_metal_ssh_options=["StrictHostKeyChecking no"], + mgmt_server_metal_ssh_groups=["leaf02", "mgmt01", "leaf01"], + hostvars=dict( + leaf01=dict(ansible_host="10.1.0.10", ansible_user="admin", host_alias="exit01"), + leaf02=dict(ansible_host="10.1.0.11", ansible_user="admin"), + mgmt01=dict(ansible_host="10.1.0.1"), + ), + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + StrictHostKeyChecking no + Host leaf01 exit01 + User admin + Host leaf02{trailing_space} + User admin + """).format(trailing_space=" "), result) + + +class MgmtServerResolvedConf(unittest.TestCase): + maxDiff = None + + def test_resolved_conf_template_with_defaults(self): + t = read_template_file("resolved.conf.j2") + + templar = Templar(loader=None, variables=read_defaults()) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + [Resolve] + DNS=193.110.81.0#dns0.eu + DNS=2a0f:fc80::#dns0.eu + DNS=185.253.5.0#dns0.eu + DNS=2a0f:fc81::#dns0.eu + + DNSOverTLS=yes + """), result) + + def test_resolved_conf_template_without_dns_over_tls(self): + t = read_template_file("resolved.conf.j2") + + templar = Templar(loader=None, variables=read_defaults() | dict( + mgmt_server_nameservers=["10.1.0.53"], + mgmt_server_dns_over_tls=False, + )) + + result = templar.template(t) + + self.assertEqual(dedent("""\ + [Resolve] + DNS=10.1.0.53 + + """), result) + + +class MgmtServerDockerDaemonJson(unittest.TestCase): + maxDiff = None + + def test_daemon_json_template(self): + t = read_template_file("daemon.json.j2") + + templar = Templar(loader=None, variables=read_defaults()) + + result = templar.template(t, convert_data=False) + + self.assertEqual(dedent("""\ + { + "registry-mirrors": ["https://mirror.gcr.io"] + }"""), result)