]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
qa/tasks/dnsmasq: preserve nameserver for future use
authorKyr Shatskyy <kyrylo.shatskyy@clyso.com>
Tue, 9 Dec 2025 22:42:08 +0000 (23:42 +0100)
committerKyr Shatskyy <kyrylo.shatskyy@clyso.com>
Mon, 12 Jan 2026 09:48:18 +0000 (10:48 +0100)
While backing the resolv.conf up, we preserve nameserver address
for future use when it's needed to compose a dnsmasq config,
so current host resolution remains working.

Fixes: https://tracker.ceph.com/issues/74211
Fixes: https://tracker.ceph.com/issues/43744
Signed-off-by: Kyr Shatskyy <kyrylo.shatskyy@clyso.com>
qa/tasks/dnsmasq.py

index df8ccecb1f53cc05432f9f1362474617caf3155e..ea6b0bbf5ac4881fbfc5e7f050d1e59acea37816 100644 (file)
@@ -3,6 +3,7 @@ Task for dnsmasq configuration
 """
 import contextlib
 import logging
+import re
 
 from teuthology import misc
 from teuthology.exceptions import ConfigError
@@ -31,11 +32,16 @@ def install_dnsmasq(remote):
             packaging.remove_package('dnsmasq', remote)
 
 @contextlib.contextmanager
-def backup_resolv(remote, path):
+def backup_resolv(remote, path, table):
     """
     Store a backup of resolv.conf in the testdir and restore it after the task.
+    Reserve 'nameserver' values per remote name in lookup 'table' for future use.
     """
-    remote.run(args=['cp', '/etc/resolv.conf', path])
+    resolv_conf = remote.sh(f"cat /etc/resolv.conf | tee {path}")
+    nameserver_re = re.compile(r'\s*nameserver\s+([^\s]*)')
+    for line in resolv_conf.split('\n'):
+        if m := nameserver_re.match(line):
+            table.setdefault(remote.name, {}).setdefault('nameserver', []).append(m[1])
     try:
         yield
     finally:
@@ -44,7 +50,7 @@ def backup_resolv(remote, path):
         remote.run(args=['rm', path])
 
 @contextlib.contextmanager
-def replace_resolv(remote, path):
+def replace_resolv(remote, path, table):
     """
     Update resolv.conf to point the nameserver at localhost.
     """
@@ -60,28 +66,36 @@ def replace_resolv(remote, path):
         remote.run(args=['rm', path])
 
 @contextlib.contextmanager
-def setup_dnsmasq(remote, testdir, cnames):
+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"])
 
-    # add address entries for each cname
-    dnsmasq = "server=8.8.8.8\nserver=8.8.4.4\n"
-    address_template = "address=/{cname}/{ip_address}\n"
     for cname, ip_address in cnames.items():
-        dnsmasq += address_template.format(cname=cname, ip_address=ip_address)
+        dnsmasq.append(f"address=/{cname}/{ip_address}")
 
-    # write to temporary dnsmasq file
-    dnsmasq_tmp = '/'.join((testdir, 'ceph.tmp'))
-    remote.write_file(dnsmasq_tmp, dnsmasq)
-
-    # move into /etc/dnsmasq.d/
+    dnsmasq.append('')
+    dnsmasq_data = "\n".join(dnsmasq)
     dnsmasq_path = '/etc/dnsmasq.d/ceph'
-    remote.run(args=['sudo', 'mv', dnsmasq_tmp, dnsmasq_path])
+    remote.sudo_write_file(dnsmasq_path, dnsmasq_data)
     # restore selinux context if necessary
     remote.run(args=['sudo', 'restorecon', dnsmasq_path], check_status=False)
 
     # 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()))])
 
@@ -157,14 +171,15 @@ def task(ctx, config):
     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) ])
-        subtasks.extend([ lambda r=remote: replace_resolv(r, resolv_tmp) ])
-        subtasks.extend([ lambda r=remote, cn=cnames: setup_dnsmasq(r, testdir, cn) ])
+        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) ])
 
     with contextutil.nested(*subtasks):
         yield