]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
qa/dnsmasq: use managed dnsmasq instead of editing resolv.conf
authorCasey Bodley <cbodley@redhat.com>
Tue, 9 Jun 2026 18:04:53 +0000 (14:04 -0400)
committerCasey Bodley <cbodley@redhat.com>
Mon, 22 Jun 2026 14:34:50 +0000 (10:34 -0400)
this task was manually editing /etc/resolv.conf to configure dns names.
on ubuntu 24, these changes were getting clobbered by NetworkManager or
systemd service restarts

instead, manage dnsmasq depending on host distro. rpm-based distros use
NetworkManager's dnsmasq plugin, while deb-based distros use
systemd-resolved to point at the local dnsmasq

Fixes: https://tracker.ceph.com/issues/76961
Signed-off-by: Casey Bodley <cbodley@redhat.com>
qa/tasks/dnsmasq.py

index ea6b0bbf5ac4881fbfc5e7f050d1e59acea37816..13e44bbfe1f99cf4da85018832ab7033c2fc96a9 100644 (file)
@@ -66,47 +66,75 @@ def replace_resolv(remote, path, table):
         remote.run(args=['rm', path])
 
 @contextlib.contextmanager
-def setup_dnsmasq(remote, testdir, cnames, table):
-    """ configure dnsmasq on the given remote, adding each cname given """
-    log.info('Configuring dnsmasq on remote %s..', remote.name)
-    # banner first
-    dnsmasq = ["# This file is generated by dnsmasq teuthology task", ""]
-
-    # lookup previously used nameservers for the remote in the table
-    servers = table.get(remote.name, {}).get('nameserver', None)
-    if servers:
-        log.info('Reusing nameservers: %s', servers)
-        dnsmasq.extend(f"server={a}" for a in servers)
-    else:
-        log.info('No predefined nameservers found, using 8.8.x.x')
-        dnsmasq.extend(["server=8.8.8.8", "server=8.8.4.4"])
-
+def configure_dnsmasq(remote, path, cnames):
+    """
+    configure dnsmasq on the given remote, adding each cname given
+    """
+    data = """# This file is generated by dnsmasq teuthology task
+"""
     for cname, ip_address in cnames.items():
-        dnsmasq.append(f"address=/{cname}/{ip_address}")
+        data += f"address=/.{cname}/{ip_address}\n"
 
-    dnsmasq.append('')
-    dnsmasq_data = "\n".join(dnsmasq)
-    dnsmasq_path = '/etc/dnsmasq.d/ceph'
-    remote.sudo_write_file(dnsmasq_path, dnsmasq_data)
-    # restore selinux context if necessary
-    remote.run(args=['sudo', 'restorecon', dnsmasq_path], check_status=False)
+    remote.sudo_write_file(path, data)
+    remote.run(args=['sudo', 'restorecon', path], check_status=False)
+    remote.sh(f'cat {path}')
+    try:
+        yield
+    finally:
+        remote.run(args=['sudo', 'rm', path])
 
-    # restart dnsmasq
-    remote.run(args=['sudo', 'systemctl', 'restart', 'dnsmasq'])
-    # some pause is required for dnsmasq to become operational,
-    # though, trying to just print out the config now instead
-    remote.sh(f'cat {dnsmasq_path}')
-    # verify dns name is set
-    remote.run(args=['ping', '-c', '4', next(iter(cnames.keys()))])
+@contextlib.contextmanager
+def configure_network_manager(remote):
+    """
+    enable NetworkManager's dnsmasq plugin
+    """
+    path = '/etc/NetworkManager/conf.d/dnsmasq.conf'
+    data = """# This file is generated by dnsmasq teuthology task
+[main]
+dns=dnsmasq
+"""
+    remote.sudo_write_file(path, data)
+    remote.run(args=['sudo', 'restorecon', path], check_status=False)
+    remote.run(args=['sudo', 'systemctl', 'restart', 'NetworkManager'])
+    remote.sh(f'cat {path}')
+    try:
+        yield
+    finally:
+        remote.run(args=['sudo', 'rm', path])
+        remote.run(args=['sudo', 'systemctl', 'restart', 'NetworkManager'])
 
+@contextlib.contextmanager
+def configure_systemd_resolved(remote, cnames):
+    """
+    configure systemd-resolved to search for listed cnames through dnsmasq at 127.0.0.1
+    """
+    dir = '/etc/systemd/resolved.conf.d'
+    remote.sh(f'sudo mkdir {dir}')
+
+    path = dir + '/dnsmasq.conf'
+    domains = ' '.join([f'~{cname}' for cname in cnames.keys()])
+    data = f"""# This file is generated by dnsmasq teuthology task
+[Resolve]
+DNS=127.0.0.1
+Domains={domains}
+"""
+    remote.sudo_write_file(path, data)
+    remote.run(args=['sudo', 'restorecon', path], check_status=False)
+    remote.run(args=['sudo', 'systemctl', 'restart', 'dnsmasq'])
+    remote.run(args=['sudo', 'systemctl', 'restart', 'systemd-resolved'])
+    remote.sh(f'cat {path}')
     try:
         yield
     finally:
-        log.info('Removing dnsmasq configuration from remote %s..', remote.name)
-        # remove /etc/dnsmasq.d/ceph
-        remote.run(args=['sudo', 'rm', dnsmasq_path])
-        # restart dnsmasq
-        remote.run(args=['sudo', 'systemctl', 'restart', 'dnsmasq'])
+        remote.run(args=['sudo', 'rm', path])
+        remote.run(args=['sudo', 'systemctl', 'restart', 'systemd-resolved'])
+
+@contextlib.contextmanager
+def test_dns_names(remote, cnames):
+    """ verify that ping can resolve the configured names """
+    for cname in cnames.keys():
+        remote.run(args=['ping', '-c', '4', cname])
+    yield
 
 @contextlib.contextmanager
 def task(ctx, config):
@@ -168,18 +196,19 @@ def task(ctx, config):
 
         remote_names[remote] = names
 
-    testdir = misc.get_testdir(ctx)
-    resolv_bak = '/'.join((testdir, 'resolv.bak'))
-    resolv_tmp = '/'.join((testdir, 'resolv.tmp'))
-    resolv_tbl = dict() # { remote.name -> nameserver list } lookup table
-
     # run subtasks for each unique remote
     subtasks = []
     for remote, cnames in remote_names.items():
         subtasks.extend([ lambda r=remote: install_dnsmasq(r) ])
-        subtasks.extend([ lambda r=remote: backup_resolv(r, resolv_bak, resolv_tbl) ])
-        subtasks.extend([ lambda r=remote: replace_resolv(r, resolv_tmp, resolv_tbl) ])
-        subtasks.extend([ lambda r=remote, cn=cnames: setup_dnsmasq(r, testdir, cn, resolv_tbl) ])
+        if remote.os.package_type == 'rpm':
+            path = '/etc/NetworkManager/dnsmasq.d/ceph.conf'
+            subtasks.extend([ lambda r=remote, p=path, cn=cnames: configure_dnsmasq(r, p, cn) ])
+            subtasks.extend([ lambda r=remote: configure_network_manager(r) ])
+        else:
+            path = '/etc/dnsmasq.d/ceph.conf'
+            subtasks.extend([ lambda r=remote, p=path, cn=cnames: configure_dnsmasq(r, p, cn) ])
+            subtasks.extend([ lambda r=remote, cn=cnames: configure_systemd_resolved(r, cn) ])
+        subtasks.extend([ lambda r=remote, cn=cnames: test_dns_names(r, cn) ])
 
     with contextutil.nested(*subtasks):
         yield