From fb5a91d9d164976ea7c03a3696c7340ac3fb65af Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Mon, 18 Apr 2016 16:18:07 +0200 Subject: [PATCH] Add diff to apt.py (#2944) for install, remove, deb_install, and upgrade. Since apt has very commonly familiar output, just use the normal output from apt-get or aptitude -- trimmed to the interesting parts -- to show to the user if she specified --diff on the CLI. Uses the recent support for the `diff['prepared]` key. Fixes ansible/ansible#10239 --- lib/ansible/modules/packaging/os/apt.py | 56 +++++++++++++++++++++---- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/ansible/modules/packaging/os/apt.py b/lib/ansible/modules/packaging/os/apt.py index 53045fdb4c2..7f1f7b65882 100644 --- a/lib/ansible/modules/packaging/os/apt.py +++ b/lib/ansible/modules/packaging/os/apt.py @@ -370,6 +370,27 @@ def expand_pkgspec_from_fnmatches(m, pkgspec, cache): new_pkgspec.append(pkgspec_pattern) return new_pkgspec +def parse_diff(output): + diff = output.splitlines() + try: + # check for start marker from aptitude + diff_start = diff.index('Resolving dependencies...') + except ValueError: + try: + # check for start marker from apt-get + diff_start = diff.index('Reading state information...') + except ValueError: + diff_start = -1 + diff.insert(0, 'Unexpected apt output for --diff. Showing everything:') + try: + # check for end marker line from both apt-get and aptitude + diff_end = (i for i, item in enumerate(diff) if re.match('[0-9]+ (packages )?upgraded', item)).next() + except StopIteration: + diff_end = len(diff) + diff_start += 1 + diff_end += 1 + return {'prepared': '\n'.join(diff[diff_start:diff_end])} + def install(m, pkgspec, cache, upgrade=False, default_release=None, install_recommends=None, force=False, dpkg_options=expand_dpkg_options(DPKG_OPTIONS), @@ -436,10 +457,14 @@ def install(m, pkgspec, cache, upgrade=False, default_release=None, cmd += " --allow-unauthenticated" rc, out, err = m.run_command(cmd) + if m._diff: + diff = parse_diff(out) + else: + diff = {} if rc: return (False, dict(msg="'%s' failed: %s" % (cmd, err), stdout=out, stderr=err)) else: - return (True, dict(changed=True, stdout=out, stderr=err)) + return (True, dict(changed=True, stdout=out, stderr=err, diff=diff)) else: return (True, dict(changed=False)) @@ -491,17 +516,22 @@ def install_deb(m, debs, cache, force, install_recommends, allow_unauthenticated stdout = retvals["stdout"] + out else: stdout = out + if "diff" in retvals: + diff = retvals["diff"] + diff["prepared"] += '\n\n' + out + else: + diff = out if "stderr" in retvals: stderr = retvals["stderr"] + err else: stderr = err if rc == 0: - m.exit_json(changed=True, stdout=stdout, stderr=stderr) + m.exit_json(changed=True, stdout=stdout, stderr=stderr, diff=diff) else: m.fail_json(msg="%s failed" % cmd, stdout=stdout, stderr=stderr) else: - m.exit_json(changed=changed, stdout=retvals.get('stdout',''), stderr=retvals.get('stderr','')) + m.exit_json(changed=changed, stdout=retvals.get('stdout',''), stderr=retvals.get('stderr',''), diff=retvals.get('diff', '')) def remove(m, pkgspec, cache, purge=False, dpkg_options=expand_dpkg_options(DPKG_OPTIONS), autoremove=False): @@ -527,15 +557,21 @@ def remove(m, pkgspec, cache, purge=False, else: autoremove = '' - cmd = "%s -q -y %s %s %s remove %s" % (APT_GET_CMD, dpkg_options, purge, autoremove, packages) - if m.check_mode: - m.exit_json(changed=True) + check_arg = '--simulate' + else: + check_arg = '' + + cmd = "%s -q -y %s %s %s remove %s" % (APT_GET_CMD, dpkg_options, purge, autoremove, check_arg, packages) rc, out, err = m.run_command(cmd) + if m._diff: + diff = parse_diff(out) + else: + diff = {} if rc: m.fail_json(msg="'apt-get remove %s' failed: %s" % (packages, err), stdout=out, stderr=err) - m.exit_json(changed=True, stdout=out, stderr=err) + m.exit_json(changed=True, stdout=out, stderr=err, diff=diff) def upgrade(m, mode="yes", force=False, default_release=None, dpkg_options=expand_dpkg_options(DPKG_OPTIONS)): @@ -577,11 +613,15 @@ def upgrade(m, mode="yes", force=False, default_release=None, cmd += " -t '%s'" % (default_release,) rc, out, err = m.run_command(cmd, prompt_regex=prompt_regex) + if m._diff: + diff = parse_diff(out) + else: + diff = {} if rc: m.fail_json(msg="'%s %s' failed: %s" % (apt_cmd, upgrade_command, err), stdout=out) if (apt_cmd == APT_GET_CMD and APT_GET_ZERO in out) or (apt_cmd == APTITUDE_CMD and APTITUDE_ZERO in out): m.exit_json(changed=False, msg=out, stdout=out, stderr=err) - m.exit_json(changed=True, msg=out, stdout=out, stderr=err) + m.exit_json(changed=True, msg=out, stdout=out, stderr=err, diff=diff) def download(module, deb): tempdir = os.path.dirname(__file__)