From e5e0a70fc1e7a4b3f1a89be376bdd1178ff3c988 Mon Sep 17 00:00:00 2001 From: queridiculo Date: Wed, 1 Jul 2015 17:14:55 -0400 Subject: [PATCH 1/2] yum: improved check_mode handling and package update flow. --- packaging/os/yum.py | 167 ++++++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 53 deletions(-) diff --git a/packaging/os/yum.py b/packaging/os/yum.py index 14339b4c18b..cf321b31d13 100644 --- a/packaging/os/yum.py +++ b/packaging/os/yum.py @@ -212,7 +212,7 @@ def is_installed(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=None, di for rid in en_repos: my.repos.enableRepo(rid) - e,m,u = my.rpmdb.matchPackageNames([pkgspec]) + e, m, u = my.rpmdb.matchPackageNames([pkgspec]) pkgs = e + m if not pkgs: pkgs.extend(my.returnInstalledPackagesByDep(pkgspec)) @@ -224,16 +224,16 @@ def is_installed(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=None, di else: cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, pkgspec] - rc,out,err = module.run_command(cmd) + rc, out, err = module.run_command(cmd) if not is_pkg: cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, "--whatprovides", pkgspec] - rc2,out2,err2 = module.run_command(cmd) + rc2, out2, err2 = module.run_command(cmd) else: - rc2,out2,err2 = (0, '', '') + rc2, out2, err2 = (0, '', '') if rc == 0 and rc2 == 0: out += out2 - return [ p for p in out.split('\n') if p.strip() ] + return [p for p in out.split('\n') if p.strip()] else: module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2)) @@ -541,7 +541,7 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): module.fail_json(msg="Failure downloading %s, %s" % (spec, e)) #groups :( - elif spec.startswith('@'): + elif spec.startswith('@'): # complete wild ass guess b/c it's a group pkg = spec @@ -608,7 +608,11 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): shutil.rmtree(tempdir) except Exception, e: module.fail_json(msg="Failure deleting temp directory %s, %s" % (tempdir, e)) - module.exit_json(changed=True) + + for p in pkgs: + # take note of which packages are getting installed + res['results'].append('%s will be installed' % p) + module.exit_json(changed=True, results=res['results']) changed = True @@ -676,7 +680,10 @@ def remove(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): cmd = yum_basecmd + ["remove"] + pkgs if module.check_mode: - module.exit_json(changed=True) + # take note of which packages are getting removed + for p in pkgs: + res['results'].append('%s will be removed' % p) + module.exit_json(changed=True, results=res['results']) rc, out, err = module.run_command(cmd) @@ -711,47 +718,69 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): res['msg'] = '' res['changed'] = False res['rc'] = 0 + pkgs = {} + pkgs['update'] = [] + pkgs['install'] = [] + updates = {} + update_all = False + cmd = None - for spec in items: + # determine if we're doing an update all + if '*' in items: + update_all = True - pkg = None - basecmd = 'update' - cmd = '' - # groups, again - if spec.startswith('@'): - pkg = spec - - elif spec == '*': #update all - # use check-update to see if there is any need - rc,out,err = module.run_command(yum_basecmd + ['check-update']) - if rc == 100: - cmd = yum_basecmd + [basecmd] - else: - res['results'].append('All packages up to date') + # run check-update to see if we have packages pending + rc, out, err = module.run_command(yum_basecmd + ['check-update']) + if rc == 0 and update_all: + res['results'].append('Nothing to do here, all packages are up to date') + return res + elif rc == 100: + available_updates = out.split('\n') + # build update dictionary + for line in available_updates: + line = line.split() + # ignore irrelevant lines + # FIXME... revisit for something less kludgy + if '*' in line or len(line) != 3 or '.' not in line[0]: continue - - # dep/pkgname - find it - else: - if is_installed(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos): - basecmd = 'update' else: - basecmd = 'install' + pkg, version, repo = line + name, dist = pkg.split('.') + updates.update({name: {'version': version, 'dist': dist, 'repo': repo}}) + elif rc == 1: + res['msg'] = err + res['rc'] = rc + module.fail_json(**res) + if update_all: + cmd = yum_basecmd + ['update'] + else: + for spec in items: + # some guess work involved with groups. update @ will install the group if missing + if spec.startswith('@'): + pkgs['update'].append(spec) + continue + # dep/pkgname - find it + else: + if is_installed(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos): + pkgs['update'].append(spec) + else: + pkgs['install'].append(spec) pkglist = what_provides(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos) + # FIXME..? may not be desirable to throw an exception here if a single package is missing if not pkglist: res['msg'] += "No Package matching '%s' found available, installed or updated" % spec module.fail_json(**res) - + nothing_to_do = True for this in pkglist: - if basecmd == 'install' and is_available(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos): + if spec in pkgs['install'] and is_available(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos): nothing_to_do = False break - - if basecmd == 'update' and is_update(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos): - nothing_to_do = False - break - + + if spec in pkgs['update'] and spec in updates.keys(): + nothing_to_do = False + if nothing_to_do: res['results'].append("All packages providing %s are up to date" % spec) continue @@ -763,27 +792,60 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts) module.fail_json(**res) - pkg = spec - if not cmd: - cmd = yum_basecmd + [basecmd, pkg] + # list of package updates + if update_all: + will_update = updates.keys() + else: + will_update = [u for u in pkgs['update'] if u in updates.keys() or u.startswith('@')] - if module.check_mode: - return module.exit_json(changed=True) + # check_mode output + if module.check_mode: + for w in will_update: + if w.startswith('@'): + msg = '%s will be updated' % w + else: + msg = '%s will be updated with %s-%s.%s from %s' % (w, w, updates[w]['version'], updates[w]['dist'], updates[w]['repo']) + res['results'].append(msg) - rc, out, err = module.run_command(cmd) + for p in pkgs['install']: + res['results'].append('%s will be installed' % p) - res['rc'] += rc - res['results'].append(out) - res['msg'] += err - - # FIXME if it is - update it and check to see if it applied - # check to see if there is no longer an update available for the pkgspec - - if rc: - res['failed'] = True - else: + if len(will_update) > 0 or len(pkgs['install']) > 0: res['changed'] = True + return res + + # run commands + if cmd: # update all + rc, out, err = module.run_command(cmd) + res['changed'] = True + else: + if len(pkgs['install']) > 0: # install missing + cmd = yum_basecmd + ['install'] + pkgs['install'] + rc, out, err = module.run_command(cmd) + res['changed'] = True + else: + rc, out, err = [0, '', ''] + + if len(will_update) > 0: # update present + cmd = yum_basecmd + ['update'] + pkgs['update'] + rc2, out2, err2 = module.run_command(cmd) + res['changed'] = True + else: + rc2, out2, err2 = [0, '', ''] + + if not update_all: + rc += rc2 + out += out2 + err += err2 + + res['rc'] += rc + res['msg'] += err + res['results'].append(out) + + if rc: + res['failed'] = True + return res def ensure(module, state, pkgs, conf_file, enablerepo, disablerepo, @@ -927,4 +989,3 @@ from ansible.module_utils.basic import * from ansible.module_utils.urls import * if __name__ == '__main__': main() - From 9d4694122deeb3a5f5f193dcf32851f36decd73c Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 20 Aug 2015 13:02:29 -0700 Subject: [PATCH 2/2] Return change results in a dictionary listing the package names. Fix a parsing problem when package names contain a dot. --- packaging/os/yum.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packaging/os/yum.py b/packaging/os/yum.py index cf321b31d13..5778bc38c3c 100644 --- a/packaging/os/yum.py +++ b/packaging/os/yum.py @@ -609,10 +609,7 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): except Exception, e: module.fail_json(msg="Failure deleting temp directory %s, %s" % (tempdir, e)) - for p in pkgs: - # take note of which packages are getting installed - res['results'].append('%s will be installed' % p) - module.exit_json(changed=True, results=res['results']) + module.exit_json(changed=True, results=res['results'], changes=dict(installed=pkgs)) changed = True @@ -680,10 +677,7 @@ def remove(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): cmd = yum_basecmd + ["remove"] + pkgs if module.check_mode: - # take note of which packages are getting removed - for p in pkgs: - res['results'].append('%s will be removed' % p) - module.exit_json(changed=True, results=res['results']) + module.exit_json(changed=True, results=res['results'], changes=dict(removed=pkgs)) rc, out, err = module.run_command(cmd) @@ -745,7 +739,7 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): continue else: pkg, version, repo = line - name, dist = pkg.split('.') + name, dist = pkg.rsplit('.', 1) updates.update({name: {'version': version, 'dist': dist, 'repo': repo}}) elif rc == 1: res['msg'] = err @@ -800,15 +794,15 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos): # check_mode output if module.check_mode: + to_update = [] for w in will_update: if w.startswith('@'): + to_update.append((w, None)) msg = '%s will be updated' % w else: - msg = '%s will be updated with %s-%s.%s from %s' % (w, w, updates[w]['version'], updates[w]['dist'], updates[w]['repo']) - res['results'].append(msg) + to_update.append((w, '%s.%s from %s' % (updates[w]['version'], updates[w]['dist'], updates[w]['repo']))) - for p in pkgs['install']: - res['results'].append('%s will be installed' % p) + res['changes'] = dict(installed=pkgs['install'], updated=to_update) if len(will_update) > 0 or len(pkgs['install']) > 0: res['changed'] = True