Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions dnsconfd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
from .system_manager import SystemManager
from .network_manager import NetworkManager
from .cli_commands import CLI_Commands
from .dbus import RESOLVED_NAME

DEFAULT_DBUS_NAME = RESOLVED_NAME

__all__ = [ DEFAULT_DBUS_NAME, SystemManager, NetworkManager, CLI_Commands ]
116 changes: 72 additions & 44 deletions dnsconfd/cli_commands.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
from dnsconfd import NetworkManager
from dnsconfd import SystemManager
import dnsconfd.dbus

from sys import exit
from dbus import DBusException
import dbus
import typing
import sys
import json

import enum
import json.decoder

class Codes(enum.Enum):
"""Return codes on command line commands."""
SUCCESS = 0
ERROR_GENERAL = 1
ERROR_DBUS_NAME = 2
ERROR_DBUS_OTHER = 3
ERROR_JSON = 4

def print_status(js):
print("Service: {service}".format(service=js.get('service')))
config = js.get('config')
if config:
for k in config:
servers = ' '.join(config[k])
print("Domain {domain}: {servers}".format(domain=k, servers=servers))

class CLI_Commands:
"""Command line small action helpers."""

@staticmethod
def _fatal(message, code=Codes.ERROR_GENERAL):
sys.stderr.write(message+"\n")
if isinstance(code, Codes):
code = code.value
exit(code)

@staticmethod
def _get_object(dbus_name, api_choice):
if api_choice == "resolve1":
object_path = dnsconfd.dbus.PATH_RESOLVED
int_name = dnsconfd.dbus.INT_DNSCONFD
else:
object_path = "/com/redhat/dnsconfd"
int_name = "com.redhat.dnsconfd.Manager"
bus = dbus.SystemBus()
dnsconfd_object = bus.get_object(dbus_name,
object_path)
return (dnsconfd_object, int_name)

@staticmethod
def print_status(status: object):
print(status)

@staticmethod
def status(dbus_name: str,
json_format: bool,
Expand All @@ -21,27 +66,23 @@ def status(dbus_name: str,
:type json_format: bool
:return: No return
"""
bus = dbus.SystemBus()
try:
if api_choice == "resolve1":
object_path = "/org/freedesktop/resolve1"
int_name = "org.freedesktop.resolve1.Dnsconfd"
else:
object_path = "/com/redhat/dnsconfd"
int_name = "com.redhat.dnsconfd.Manager"
dnsconfd_object = bus.get_object(dbus_name,
object_path)
(dnsconfd_object, int_name) = CLI_Commands._get_object(dbus_name, api_choice)
except DBusException as e:
print(f"Dnsconfd is not listening on name {dbus_name}, {e}")
exit(1)
CLI_Commands._fatal(f"Dnsconfd is not listening on name {dbus_name}: {e.get_dbus_message()}",
Codes.ERROR_DBUS_NAME)

try:
print(dnsconfd_object.Status(json_format,
dbus_interface=int_name))
status_str = object.Status(dbus_interface=dnsconfd.dbus.DNSCONFD_IFACE)
decoder = json.decoder.JSONDecoder()
status = decoder.decode(status_str)
print_status(status)
exit(Codes.SUCCESS.value)
except json.decoder.JSONDecodeError as e:
CLI_Commands._fatal(f"Failed to decode JSON: {e}", Codes.ERROR_JSON)
except DBusException as e:
print("Was not able to call Status method, check your DBus policy:"
+ f"{e}")
exit(1)
exit(0)
CLI_Commands._fatal(f"Error calling Status method: {e}",
Codes.ERROR_DBUS_OTHER)

@staticmethod
def nm_config(enable: bool) -> typing.NoReturn:
Expand All @@ -52,17 +93,19 @@ def nm_config(enable: bool) -> typing.NoReturn:
"""
if enable:
success = NetworkManager().enable()
use = 'use'
else:
success = NetworkManager().disable()
use = 'not use'
if not success:
print("Dnsconfd was unable to configure Network Manager")
exit(1)
print(f"Network Manager will {'use' if enable else 'not use'}"
+ " dnsconfd now")
exit(0)
CLI_Commands._fatal(f"Dnsconfd was unable to configure Network Manager: {str(e)}",
Codes.ERROR_DBUS_OTHER)
else:
print(f"Network Manager will {use} dnsconfd now")
exit(Codes.SUCCESS.value)

@staticmethod
def reload(dbus_name: str,
def reload(dbus_name=dnsconfd.DEFAULT_DBUS_NAME,
api_choice: str) -> typing.NoReturn:
""" Call Dnsconfd reload method through DBUS

Expand All @@ -72,28 +115,15 @@ def reload(dbus_name: str,
:type dbus_name: str
:return: No return
"""
bus = dbus.SystemBus()
try:
if api_choice == "resolve1":
object_path = "/org/freedesktop/resolve1"
int_name = "org.freedesktop.resolve1.Dnsconfd"
else:
object_path = "/com/redhat/dnsconfd"
int_name = "com.redhat.dnsconfd.Manager"
dnsconfd_object = bus.get_object(dbus_name,
object_path)
except DBusException as e:
print(f"Dnsconfd is not listening on name {dbus_name}, {e}")
exit(1)
try:
(dnsconfd_object, int_name) = CLI_Commands._get_object(dbus_name, api_choice)

all_ok, msg = dnsconfd_object.Reload(dbus_interface=int_name)
print(msg)
exit(0 if all_ok else 1)
exit(not all_ok)
except DBusException as e:
print("Was not able to call Status method, check your DBus policy:"
+ f"{e}")
exit(1)

CLI_Commands._fatal(f"Error calling Reload method: {e}",
Codes.ERROR_DBUS_OTHER)
@staticmethod
def chown_resolvconf(config: dict, user: str) -> typing.NoReturn:
""" Change ownership resolv.conf
Expand All @@ -116,8 +146,6 @@ def install(config: dict) -> typing.NoReturn:
"""
if (not NetworkManager().enable() or
not SystemManager(config).chown_resolvconf("dnsconfd")):
exit(1)
exit(0)

@staticmethod
def uninstall(config: dict) -> typing.NoReturn:
Expand Down
3 changes: 2 additions & 1 deletion dnsconfd/configuration/dnsconfd_argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import yaml.scanner
import logging
import sys
import dnsconfd.dbus


class DnsconfdArgumentParser(ArgumentParser):
Expand All @@ -30,7 +31,7 @@ def __init__(self, *args, **kwargs) -> None:
validation=r"DEBUG|INFO|WARNING|ERROR|CRITICAL"),
StringOption("dbus_name",
"DBUS name that dnsconfd should use",
"org.freedesktop.resolve1",
dnsconfd.dbus.RESOLVED_NAME,
validation=dbus_re),
Option("resolv_conf_path",
"Path to resolv.conf that the dnsconfd should manage",
Expand Down
27 changes: 27 additions & 0 deletions dnsconfd/dbus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""DBus constants module."""

# Our future dbus service name. Not yet used.
DNSCONFD_NAME = 'com.redhat.dnsconfd'

# Implements systemd-resolved interfaces defined at:
# https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.resolve1.html
# or man 5 org.freedesktop.resolve1
RESOLVED_NAME = 'org.freedesktop.resolve1'
RESOLVED_MANAGER_IFACE = RESOLVED_NAME+'.Manager'
RESOLVED_DNSCONFD_IFACE = RESOLVED_NAME+'.Dnsconfd'
RESOLVED_PATH = "/org/freedesktop/resolve1"

# https://networkmanager.dev/docs/api/latest/spec.html
NM_NAME = "org.freedesktop.NetworkManager"
NM_PATH = "/org/freedesktop/NetworkManager"
NM_IFACE = NM_NAME
NM_DNS_PATH = "/org/freedesktop/NetworkManager/DnsManager"
NM_DNS_IFACE = "org.freedesktop.NetworkManager.DnsManager"
NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device"
NM_IP4CONFIG_IFACE = "org.freedesktop.NetworkManager.IP4Config"
NM_IP6CONFIG_IFACE = "org.freedesktop.NetworkManager.IP6Config"
__all__ = [ DNSCONFD_NAME,
RESOLVED_NAME, RESOLVED_MANAGER_IFACE, RESOLVED_DNSCONFD_IFACE,
RESOLVED_PATH,
NM_NAME, NM_PATH, NM_IFACE, NM_DNS_PATH, NM_DNS_IFACE, NM_DEVICE_IFACE,
NM_IP4CONFIG_IFACE, NM_IP6CONFIG_IFACE ]
4 changes: 4 additions & 0 deletions dnsconfd/dns_managers/dns_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ def get_status(self) -> dict[str, list[str]]:
:rtype: dict[str, list[str]]
"""
raise NotImplementedError

def flush_cache(self, domain="."):
""" Flush cache tree under domain """
raise NotImplementedError
3 changes: 3 additions & 0 deletions dnsconfd/dns_managers/unbound_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,6 @@ def get_status(self) -> dict[str, list[str]]:
for zone, servers in self.zones_to_servers.items():
status[zone] = [str(srv) for srv in servers]
return status

def flush_cache(self, domain="."):
return self._execute_cmd("flush "+domain) == 0
33 changes: 15 additions & 18 deletions dnsconfd/fsm/dnsconfd_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from dnsconfd.fsm import ContextState
from dnsconfd import SystemManager
from dnsconfd.fsm import ExitCode
import dnsconfd.dbus

from gi.repository import GLib
from typing import Callable, Any
Expand Down Expand Up @@ -486,14 +487,13 @@ def _get_nm_device_config(self, index):
try:
device_path = self._nm_interface.GetDeviceByIpIface(int_name)
self.lgr.debug(f"Device path is {device_path}")
device_object = self.bus.get_object("org.freedesktop"
".NetworkManager",
device_object = self.bus.get_object(dnsconfd.dbus.NM_NAME,
device_path)

device_properties = dbus.Interface(device_object,
"org.freedesktop"
".DBus.Properties").GetAll(
"org.freedesktop.NetworkManager.Device")
dnsconfd.dbus.NM_DEVICE_IFACE)
if not 80 <= device_properties["State"] <= 100:
self.lgr.info(f"Interface {int_name} is not yet activated, "
f"state: {device_properties["State"]}, "
Expand All @@ -503,30 +503,30 @@ def _get_nm_device_config(self, index):
lambda: self.transition_function(upd))
return [], [], None
prop_interface = "org.freedesktop.DBus.Properties"
ip4_object = self.bus.get_object('org.freedesktop.NetworkManager',
ip4_object = self.bus.get_object(dnsconfd.dbus.NM_NAME,
device_properties["Ip4Config"])
ip6_object = self.bus.get_object('org.freedesktop.NetworkManager',
ip6_object = self.bus.get_object(dnsconfd.dbus.NM_NAME,
device_properties["Ip6Config"])
ip4_routes = dbus.Interface(ip4_object,
prop_interface).Get(
"org.freedesktop.NetworkManager.IP4Config", "RouteData")
dnsconfd.dbus.NM_IP4CONFIG_IFACE, "RouteData")
self.lgr.info(f"ipv4 Route data is {ip4_routes}")
ip6_routes = dbus.Interface(ip6_object,
prop_interface).Get(
"org.freedesktop.NetworkManager.IP6Config", "RouteData")
dnsconfd.dbus.NM_IP6CONFIG_IFACE, "RouteData")
self.lgr.info(f"ipv6 Route data is {ip6_routes}")
ip4_addresses = dbus.Interface(ip4_object,
prop_interface).Get(
"org.freedesktop.NetworkManager.IP4Config", "Addresses")
dnsconfd.dbus.NM_IP4CONFIG_IFACE, "Addresses")
ip6_addresses = dbus.Interface(ip6_object,
prop_interface).Get(
"org.freedesktop.NetworkManager.IP6Config", "Addresses")
dnsconfd.dbus.NM_IP6CONFIG_IFACE, "Addresses")
if len(ip4_addresses) == 0 and len(ip6_addresses) == 0:
self.lgr.info(f"interface {int_name} has no address "
"and thus we will not handle its routing")
return [], [], None
dev_int = dbus.Interface(device_object,
"org.freedesktop.NetworkManager.Device")
dnsconfd.dbus.NM_DEVICE_IFACE)
applied = dev_int.GetAppliedConnection(0)
self.lgr.debug(f"Applied connection is {applied}")
except dbus.DBusException as e:
Expand Down Expand Up @@ -584,20 +584,17 @@ def _get_nm_device_interface(self, ifname):
"""Get DBus proxy object of Device identified by ifname."""
device_path = self._nm_interface.GetDeviceByIpIface(ifname)
self.lgr.debug(f"Device path is {device_path}")
nm_int_str = "org.freedesktop.NetworkManager"
dev_int_str = "org.freedesktop.NetworkManager.Device"
device_object = self.bus.get_object(nm_int_str,
device_object = self.bus.get_object(dnsconfd.dbus.NM_NAME,
device_path)
dev_int = dbus.Interface(device_object,
dev_int_str)
dnsconfd.dbus.NM_DEVICE_IFACE)
return dev_int

def _get_nm_interface(self):
nm_dbus_name = "org.freedesktop.NetworkManager"
nm_object = self.bus.get_object(nm_dbus_name,
'/org/freedesktop/NetworkManager')
nm_object = self.bus.get_object(dnsconfd.dbus.NM_NAME,
dnsconfd.dbus.NM_PATH)
self._nm_interface = dbus.Interface(nm_object,
nm_dbus_name)
dnsconfd.dbus.NM_IFACE)
return self._nm_interface

def _reapply_routes(self, ifname, connection, cver):
Expand Down
Loading