#!/usr/bin/env python3 # dnsmasq.py - PVC Cluster Auto-bootstrap DNSMasq instance # Part of the Parallel Virtual Cluster (PVC) system # # Copyright (C) 2018-2021 Joshua M. Boniface # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 3. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # ############################################################################### import os import subprocess import signal from threading import Thread class DNSMasq: """ Implementes a daemonized instance of DNSMasq for providing DHCP and TFTP services The DNSMasq instance listens on the configured 'dhcp_address', and instead of a "real" leases database forwards requests to the 'dnsmasq-lease.py' script. This script will then hit the pvcbootstrapd '/checkin' API endpoint to perform actions. TFTP is provided to automate the bootstrap of a node, providing the pvc-installer over TFTP as well as a seed configuration which is created by the API. """ def __init__(self, config): self.environment = { "API_URI": f"http://{config['api_address']}:{config['api_port']}/checkin/dnsmasq" } self.dnsmasq_cmd = [ "/usr/sbin/dnsmasq", "--bogus-priv", "--no-hosts", "--dhcp-authoritative", "--filterwin2k", "--expand-hosts", "--domain-needed", f"--domain={config['dhcp_domain']}", f"--local=/{config['dhcp_domain']}/", "--log-facility=-", "--log-dhcp", "--keep-in-foreground", f"--dhcp-script={os.getcwd()}/pvcbootstrapd/dnsmasq-lease.py", "--bind-interfaces", f"--listen-address={config['dhcp_address']}", f"--dhcp-option=3,{config['dhcp_gateway']}", f"--dhcp-range={config['dhcp_lease_start']},{config['dhcp_lease_end']},{config['dhcp_lease_time']}", "--enable-tftp", f"--tftp-root={config['tftp_root_path']}/", # This block of dhcp-match, tag-if, and dhcp-boot statements sets the following TFTP setup: # If the machine sends client-arch 0, and is not tagged iPXE, load undionly.kpxe (chainload) # If the machine sends client-arch 7 or 9, and is not tagged iPXE, load ipxe.efi (chainload) # If the machine sends the iPXE option, load boot.ipxe (iPXE boot configuration) "--dhcp-match=set:o_bios,option:client-arch,0", "--dhcp-match=set:o_uefi,option:client-arch,7", "--dhcp-match=set:o_uefi,option:client-arch,9", "--dhcp-match=set:ipxe,175", "--tag-if=set:bios,tag:!ipxe,tag:o_bios", "--tag-if=set:uefi,tag:!ipxe,tag:o_uefi", "--dhcp-boot=tag:bios,undionly.kpxe", "--dhcp-boot=tag:uefi,ipxe.efi", "--dhcp-boot=tag:ipxe,boot.ipxe", ] if config["debug"]: self.dnsmasq_cmd.append("--leasefile-ro") print(self.dnsmasq_cmd) self.stdout = subprocess.PIPE def execute(self): self.proc = subprocess.Popen( self.dnsmasq_cmd, env=self.environment, ) def start(self): self.thread = Thread(target=self.execute, args=()) self.thread.start() def stop(self): self.proc.send_signal(signal.SIGTERM) def reload(self): self.proc.send_signal(signal.SIGHUP)