From: Casey Bodley Date: Mon, 19 Feb 2018 18:36:05 +0000 (-0500) Subject: qa: clean up dnsmasq task and fix EPERM error X-Git-Tag: v13.0.2~106^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=11570fab7313aa8b65078d5049b45c71951063de;p=ceph.git qa: clean up dnsmasq task and fix EPERM error Signed-off-by: Casey Bodley --- diff --git a/qa/tasks/dnsmasq.py b/qa/tasks/dnsmasq.py index ee01b17955e6..7f675f3bc29f 100644 --- a/qa/tasks/dnsmasq.py +++ b/qa/tasks/dnsmasq.py @@ -7,42 +7,89 @@ import logging from teuthology import misc from teuthology.exceptions import ConfigError from teuthology import contextutil +from teuthology import packaging from util import get_remote_for_role log = logging.getLogger(__name__) @contextlib.contextmanager -def setup_dnsmasq(remote, cnames): +def install_dnsmasq(remote): + """ + If dnsmasq is not installed, install it for the duration of the task. + """ + try: + existing = packaging.get_package_version(remote, 'dnsmasq') + except: + existing = None + + if existing is None: + packaging.install_package('dnsmasq', remote) + try: + yield + finally: + if existing is None: + packaging.remove_package('dnsmasq', remote) + +@contextlib.contextmanager +def backup_resolv(remote, path): + """ + Store a backup of resolv.conf in the testdir and restore it after the task. + """ + remote.run(args=['cp', '/etc/resolv.conf', path]) + try: + yield + finally: + # restore with 'cp' to avoid overwriting its security context + remote.run(args=['sudo', 'cp', path, '/etc/resolv.conf']) + remote.run(args=['rm', path]) + +@contextlib.contextmanager +def replace_resolv(remote, path): + """ + Update resolv.conf to point the nameserver at localhost. + """ + misc.write_file(remote, path, "nameserver 127.0.0.1\n") + try: + # install it + remote.run(args=['sudo', 'cp', path, '/etc/resolv.conf']) + yield + finally: + remote.run(args=['rm', path]) + +@contextlib.contextmanager +def setup_dnsmasq(remote, testdir, cnames): """ configure dnsmasq on the given remote, adding each cname given """ log.info('Configuring dnsmasq on remote %s..', remote.name) - # back up existing resolv.conf - resolv_conf = misc.get_file(remote, '/etc/resolv.conf') - # point resolv.conf to local dnsmasq - misc.sudo_write_file(remote, '/etc/resolv.conf', - "nameserver 127.0.0.1\n") - - # add address entries to /etc/dnsmasq.d/ceph + # 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.iteritems(): dnsmasq += address_template.format(cname=cname, ip_address=ip_address) - misc.sudo_write_file(remote, '/etc/dnsmasq.d/ceph', dnsmasq) - remote.run(args=['cat', '/etc/dnsmasq.d/ceph']) + # write to temporary dnsmasq file + dnsmasq_tmp = '/'.join((testdir, 'ceph.tmp')) + misc.write_file(remote, dnsmasq_tmp, dnsmasq) + + # move into /etc/dnsmasq.d/ + dnsmasq_path = '/etc/dnsmasq.d/ceph' + remote.run(args=['sudo', 'mv', dnsmasq_tmp, dnsmasq_path]) + # restore selinux context if necessary + remote.run(args=['sudo', 'restorecon', dnsmasq_path], check_status=False) + # restart dnsmasq remote.run(args=['sudo', 'systemctl', 'restart', 'dnsmasq']) - remote.run(args=['sudo', 'systemctl', 'status', 'dnsmasq']) # verify dns name is set remote.run(args=['ping', '-c', '4', cnames.keys()[0]]) - yield - - log.info('Removing dnsmasq configuration from remote %s..', remote.name) - # restore resolv.conf - misc.sudo_write_file(remote, '/etc/resolv.conf', resolv_conf) - # restart dnsmasq - remote.run(args=['sudo', 'systemctl', 'restart', 'dnsmasq']) + 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']) @contextlib.contextmanager def task(ctx, config): @@ -65,6 +112,13 @@ def task(ctx, config): client.0: client.0.example.com: client.0 client.1.example.com: client.1 + + Cnames that end with a . are treated as prefix for the existing hostname. + For example, if the remote for client.0 has a hostname of 'example.com', + this task will add cnames for dev.example.com and test.example.com: + + - dnsmasq: + client.0: [dev., test.] """ # apply overrides overrides = config.get('overrides', {}) @@ -82,6 +136,8 @@ def task(ctx, config): if isinstance(cnames, list): # when given a list of cnames, point to local ip for cname in cnames: + if cname.endswith('.'): + cname += remote.hostname names[cname] = remote.ip_address elif isinstance(cnames, dict): # when given a dict, look up the remote ip for each @@ -89,14 +145,23 @@ def task(ctx, config): r = get_remote_for_role(ctx, client) if r is None: raise ConfigError('no remote for role %s' % client) + if cname.endswith('.'): + cname += r.hostname names[cname] = r.ip_address remote_names[remote] = names - # run a subtask for each unique remote + testdir = misc.get_testdir(ctx) + resolv_bak = '/'.join((testdir, 'resolv.bak')) + resolv_tmp = '/'.join((testdir, 'resolv.tmp')) + + # run subtasks for each unique remote subtasks = [] for remote, cnames in remote_names.iteritems(): - subtasks.extend([ lambda r=remote, cn=cnames: setup_dnsmasq(r, cn) ]) + 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) ]) with contextutil.nested(*subtasks): yield