"""
This is some of the code behind 'cobbler sync'.
"""
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright 2006-2009, Red Hat, Inc and Others
# SPDX-FileCopyrightText: Michael DeHaan <michael.dehaan AT gmail>
# SPDX-FileCopyrightText: John Eckersberg <jeckersb@redhat.com>
import time
from cobbler.utils import process_management
from cobbler.manager import ManagerModule
MANAGER = None
[docs]def register() -> str:
"""
The mandatory Cobbler modules registration hook.
:return: Always "manage".
"""
return "manage"
class _DnsmasqManager(ManagerModule):
"""
Handles conversion of internal state to the tftpboot tree layout.
"""
@staticmethod
def what() -> str:
"""
This identifies the module.
:return: Will always return ``dnsmasq``.
"""
return "dnsmasq"
def write_configs(self):
"""
DHCP files are written when ``manage_dhcp`` is set in our settings.
:raises OSError
"""
settings_file = "/etc/dnsmasq.conf"
template_file = "/etc/cobbler/dnsmasq.template"
try:
f2 = open(template_file, "r")
except Exception:
raise OSError("error writing template to file: %s" % template_file)
template_data = f2.read()
f2.close()
system_definitions = {}
# we used to just loop through each system, but now we must loop
# through each network interface of each system.
for system in self.systems:
if not system.is_management_supported(cidr_ok=False):
continue
profile = system.get_conceptual_parent()
distro = profile.get_conceptual_parent()
for interface in system.interfaces.values():
mac = interface.mac_address
ip = interface.ip_address
host = interface.dns_name
ipv6 = interface.ipv6_address
if not mac:
# can't write a DHCP entry for this system
continue
# In many reallife situations there is a need to control the IP address and hostname for a specific
# client when only the MAC address is available. In addition to that in some scenarios there is a need
# to explicitly label a host with the applicable architecture in order to correctly handle situations
# where we need something other than ``pxelinux.0``. So we always write a dhcp-host entry with as much
# info as possible to allow maximum control and flexibility within the dnsmasq config.
systxt = "dhcp-host=net:" + distro.arch.value.lower() + "," + mac
if host != "":
systxt += "," + host
if ip != "":
systxt += "," + ip
if ipv6 != "":
systxt += ",[%s]" % ipv6
systxt += "\n"
dhcp_tag = interface.dhcp_tag
if dhcp_tag == "":
dhcp_tag = "default"
if dhcp_tag not in system_definitions:
system_definitions[dhcp_tag] = ""
system_definitions[dhcp_tag] = system_definitions[dhcp_tag] + systxt
# We are now done with the looping through each interface of each system.
metadata = {
"insert_cobbler_system_definitions": system_definitions.get("default", ""),
"date": time.asctime(time.gmtime()),
"cobbler_server": self.settings.server,
"next_server_v4": self.settings.next_server_v4,
"next_server_v6": self.settings.next_server_v6,
}
# now add in other DHCP expansions that are not tagged with "default"
for x in list(system_definitions.keys()):
if x == "default":
continue
metadata["insert_cobbler_system_definitions_%s" % x] = system_definitions[x]
self.templar.render(template_data, metadata, settings_file)
def regen_ethers(self):
"""
This function regenerates the ethers file. To get more information please read ``man ethers``, the format is
also in there described.
"""
# dnsmasq knows how to read this database of MACs -> IPs, so we'll keep it up to date every time we add a
# system.
fh = open("/etc/ethers", "w+")
for system in self.systems:
if not system.is_management_supported(cidr_ok=False):
continue
for interface in system.interfaces.values():
mac = interface.mac_address
ip = interface.ip_address
if not mac:
# can't write this w/o a MAC address
continue
if ip is not None and ip != "":
fh.write(mac.upper() + "\t" + ip + "\n")
fh.close()
def regen_hosts(self):
"""
This rewrites the hosts file and thus also rewrites the dns config.
"""
# dnsmasq knows how to read this database for host info (other things may also make use of this later)
fh = open("/var/lib/cobbler/cobbler_hosts", "w+")
for system in self.systems:
if not system.is_management_supported(cidr_ok=False):
continue
for (_, interface) in system.interfaces.items():
mac = interface.mac_address
host = interface.dns_name
ip = interface.ip_address
ipv6 = interface.ipv6_address
if not mac:
continue
if host is not None and host != "" and ipv6 is not None and ipv6 != "":
fh.write(ipv6 + "\t" + host + "\n")
elif host is not None and host != "" and ip is not None and ip != "":
fh.write(ip + "\t" + host + "\n")
fh.close()
def restart_service(self):
"""
This restarts the dhcp server and thus applied the newly written config files.
"""
service_name = "dnsmasq"
if self.settings.restart_dhcp:
return_code_service_restart = process_management.service_restart(
service_name
)
if return_code_service_restart != 0:
self.logger.error("%s service failed", service_name)
return return_code_service_restart
[docs]def get_manager(api):
"""
Creates a manager object to manage a dnsmasq server.
:param api: The API to resolve all information with.
:return: The object generated from the class.
"""
# Singleton used, therefore ignoring 'global'
global MANAGER # pylint: disable=global-statement
if not MANAGER:
MANAGER = _DnsmasqManager(api)
return MANAGER