import ipaddress
import os
from paradrop.lib.utils import pdosq
from .base import ConfigObject, ConfigOption
from .command import Command, KillCommand, FunctionCommand
[docs]class ConfigDhcp(ConfigObject):
typename = "dhcp"
options = [
ConfigOption(name="interface", required=True),
ConfigOption(name="leasetime"),
ConfigOption(name="limit", type=int),
ConfigOption(name="start", type=int),
ConfigOption(name="dhcp_option", type=list, default=[]),
# Non-standard options:
#
# relay: comma-separated string, specifying a DHCP relay, as with
# "<local address>,<server address>[,<interface>]".
# Refer to --dhcp-relay in man dnsmasq.
ConfigOption(name="relay", type=list, default=[])
]
[docs]class ConfigDomain(ConfigObject):
typename = "domain"
options = [
ConfigOption(name="name", type=str),
ConfigOption(name="ip", type=str)
]
[docs] def getName(self):
return self.name
[docs]class ConfigDnsmasq(ConfigObject):
typename = "dnsmasq"
options = [
ConfigOption(name="authoritative", type=bool, default=True),
ConfigOption(name="cachesize", type=int, default=150),
ConfigOption(name="dhcp_boot"),
ConfigOption(name="dhcpleasemax", type=int, default=1000),
ConfigOption(name="domain", type=str),
ConfigOption(name="enable_tftp", type=bool, default=False),
ConfigOption(name="expandhosts", type=bool, default=True),
ConfigOption(name="interface", type=list),
ConfigOption(name="leasefile", type=str),
ConfigOption(name="noresolv", type=bool, default=False),
ConfigOption(name="server", type=list),
ConfigOption(name="tftp_root")
]
[docs] def apply(self, allConfigs):
commands = list()
# visibleName will be used in choosing file names for this dnsmasq
# instance, must be unique if there are multiple dnsmasq instances
visibleName = self.internalName
if self.interface is None:
interfaces = []
for section in self.findByType(allConfigs, "dhcp", "dhcp"):
interfaces.append(section.interface)
else:
interfaces = self.interface
self.__leasefile = self.leasefile
if self.__leasefile is None:
self.__leasefile = "{}/dnsmasq-{}.leases".format(
self.manager.writeDir, visibleName)
pdosq.makedirs(os.path.dirname(self.__leasefile))
pidFile = "{}/dnsmasq-{}.pid".format(
self.manager.writeDir, visibleName)
pdosq.makedirs(os.path.dirname(pidFile))
outputPath = "{}/dnsmasq-{}.conf".format(
self.manager.writeDir, visibleName)
pdosq.makedirs(os.path.dirname(outputPath))
with open(outputPath, "w") as outputFile:
outputFile.write("#" * 80 + "\n")
outputFile.write("# dnsmasq configuration file generated by "
"pdconfd\n")
outputFile.write("# Source: {}\n".format(self.source))
outputFile.write("# Section: {}\n".format(str(self)))
outputFile.write("#" * 80 + "\n")
outputFile.write("\n")
outputFile.write("dhcp-leasefile={}\n".format(self.__leasefile))
if self.authoritative:
outputFile.write("dhcp-authoritative\n")
outputFile.write("cache-size={}\n".format(self.cachesize))
if self.dhcp_boot is not None:
outputFile.write("dhcp-boot={}\n".format(self.dhcp_boot))
outputFile.write("dhcp-lease-max={}\n".format(self.dhcpleasemax))
if self.domain:
outputFile.write("domain={}\n".format(self.domain))
if self.enable_tftp:
outputFile.write("enable-tftp\n")
if self.expandhosts:
outputFile.write("expand-hosts\n")
if self.noresolv:
outputFile.write("no-resolv\n")
if self.tftp_root is not None:
outputFile.write("tftp-root={}\n".format(self.tftp_root))
if self.server:
for server in self.server:
outputFile.write("server={}\n".format(server))
# TODO: Bind interfaces allows us to have multiple instances of
# dnsmasq running, but it would probably be better to have one
# running and reconfigure it when we want to add or remove
# interfaces. It is not very disruptive to reconfigure and restart
# dnsmasq.
outputFile.write("\n")
outputFile.write("except-interface=lo\n")
outputFile.write("bind-interfaces\n")
for intfName in interfaces:
interface = self.lookup(allConfigs, "network", "interface", intfName)
outputFile.write("\n")
outputFile.write("# Options for section interface {}\n".
format(interface.name))
outputFile.write("interface={}\n".format(
interface.config_ifname))
network = ipaddress.IPv4Network(u"{}/{}".format(
interface.ipaddr, interface.netmask), strict=False)
dhcp = self.lookup(allConfigs, "dhcp", "dhcp", intfName)
outputFile.write("\n")
outputFile.write("# Options for section dhcp {}\n".
format(interface.name))
if None not in [dhcp.start, dhcp.limit, dhcp.leasetime]:
# TODO: Error checking!
firstAddress = network.network_address + dhcp.start
lastAddress = firstAddress + dhcp.limit
outputFile.write("dhcp-range={},{},{},{}\n".format(
str(firstAddress), str(lastAddress), interface.netmask,
dhcp.leasetime))
# Write options sections to the config file.
for option in dhcp.dhcp_option:
outputFile.write("dhcp-option={}\n".format(option))
for relay in dhcp.relay:
outputFile.write("dhcp-relay={}\n".format(relay))
if dhcp.relay:
outputFile.write("dhcp-proxy\n")
outputFile.write("\n")
for domain in self.findByType(allConfigs, "dhcp", "domain"):
outputFile.write("address=/{}/{}\n".format(domain.name, domain.ip))
cmd = ["dnsmasq", "--conf-file={}".format(outputPath),
"--pid-file={}".format(pidFile)]
commands.append((self.PRIO_START_DAEMON, Command(cmd, self)))
self.pidFile = pidFile
return commands
[docs] def revert(self, allConfigs):
commands = list()
commands.append((-self.PRIO_START_DAEMON,
KillCommand(self.pidFile, self)))
# Clean up leases and pid files.
commands.append((-self.PRIO_START_DAEMON,
FunctionCommand(self, pdosq.safe_remove, self.__leasefile)))
commands.append((-self.PRIO_START_DAEMON,
FunctionCommand(self, pdosq.safe_remove, self.pidFile)))
return commands