#!/usr/bin/python3 import pathlib from typing import List, Union from ansible.module_utils.generic import SYSTEMD_NETWORK_CONFIG as SYSTEMD_PATH from ansible.module_utils.generic import Types from ansible.module_utils.module import SystemdUnitModule def boolconvert(b: Union[bool, str]) -> str: if b is True: return "yes" elif b is False: return "no" return b class Module(SystemdUnitModule): """Sets up the systemd network unit""" name = "systemd_network" module_spec = dict( argument_spec=dict( mac=Types.str(help="The MAC-Address of the device"), device=Types.str(help="The name of the network device"), name=Types.str(required=True, help="name of the unit"), description=Types.str(help="An optional description"), dot=Types.bool(help="if DNS-over-TLS should be required or disabled. If it is unset, it will used if the server supports it"), dnssec=Types.bool("if the Domainqueries should require DNSSEC or not. If its missing, domains that have DNSSEC enabled will be validated, all others it will be assumed to be okay."), dns=Types.list(elements=str, help="List of DNS-Servers"), domain=Types.list(elements=str, help="List of domains that are on this device"), defaultdns=Types.bool(help="If the DNS-Server(s) on this device should be used for all domains that are not set on other devices"), address=Types.list(elements=str, required=True, help="IP-Addresses of this networkdevice"), route=Types.list(elements=str, help="Routes of networks that can be reached with this device"), ), required_if=(("defaultdns", True, ("dns",), False),), required_one_of=(("mac", "device"),), ) def prepare(self): self.unitfile = SYSTEMD_PATH.joinpath(self.get("name")).with_suffix(".network") self.__unit = None def unit(self) -> str: if self.__unit is None: self.__unit = "\n".join( ( self.match(), self.network(), self.addresses(), self.routes(), ) ) return self.__unit def match(self) -> str: matches = [] if self.get("mac", False): matches.append("MACAddress={}\n".format(self.get("mac"))) if self.get("device", False): matches.append("Name={}\n".format(self.get("device"))) return "[Match]\n" + "".join(matches) def network(self) -> str: output = "[Network]\n" options = [] try: options.append("Description={}".format(self.get("description"))) except KeyError: pass try: for server in self.get("dns", []): options.append(f"DNS={server}") options.append("DNSDefaultRoute={}".format(self.get("defaultdns", False))) except KeyError: pass try: domain = self.get("domain") self.set("domainlog", str(domain)) options.append("Domains={}".format(" ".join(domain))) options.append( "DNSOverTLS={}".format(boolconvert(self.get("dot", "opportunistic"))) ) options.append( "DNSSEC={}".format(boolconvert(self.get("dnssec", "allow-downgrade"))) ) except KeyError: pass output += "\n".join(options) return output def addresses(self) -> str: output = [] for address in self.get("address"): output.append(f"[Address]\nAddress={address}\n") return "\n".join(output) def routes(self) -> str: output = [] routes = self.get("route", []) self.set("routes", routes) for gw in routes: output.append(f"[Route]\nGateway={gw}\nGatewayOnLink=yes\nQuickAck=yes\n") self.set("routes", output) return "\n".join(output) DOCUMENTATION = """--- description: - Sets up the systemd network unit module: systemd_network options: address: description: - IP-Addresses of this networkdevice elements: str required: true type: list defaultdns: description: - If the DNS-Server(s) on this device should be used for all domains that are not set on other devices required: false type: bool description: description: - An optional description required: false type: str device: description: - The name of the network device required: false type: str dns: default: [] description: - List of DNS-Servers elements: str required: false type: list dnssec: required: true type: bool domain: default: [] description: - List of domains that are on this device elements: str required: false type: list dot: description: - if DNS-over-TLS should be required or disabled. If it is unset, it will used if the server supports it required: false type: bool mac: description: - The MAC-Address of the device required: false type: str name: description: - name of the unit required: true type: str route: default: [] description: - Routes of networks that can be reached with this device elements: str required: false type: list short_description: Sets up the systemd network unit """ if __name__ == "__main__": Module()()