ansible/yum
Michael DeHaan 948e357766 Tweak alias consistency a bit. 'package' has never been documented, but since it works, make it work in both places.
'name' is the preferred form.  Similarly, take 'name' for the 'guest' argument to the 'virt' module.
2012-07-28 09:22:13 -04:00

320 lines
9.5 KiB
Python
Executable file

#!/usr/bin/python -tt
# (c) 2012, Red Hat, Inc
# Written by Seth Vidal <skvidal at fedoraproject.org>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
import yum
import datetime
import traceback
def yum_base(conf_file=None, cachedir=False):
my = yum.YumBase()
my.preconf.debuglevel=0
if conf_file and os.path.exists(conf_file):
my.preconf.fn = conf_file
if cachedir:
if hasattr(my, 'setCacheDir'):
my.setCacheDir()
else:
cachedir = yum.misc.getCacheDir()
my.repos.setCacheDir(cachedir)
my.conf.cache = 0
return my
def pkg_to_dict(po):
d = {
'name':po.name,
'arch':po.arch,
'epoch':po.epoch,
'release':po.release,
'version':po.version,
}
if type(po) == yum.rpmsack.RPMInstalledPackage:
d['yumstate'] = 'installed'
d['repo'] = 'installed'
else:
d['yumstate'] = 'available'
d['repo'] = po.repoid
if hasattr(po, 'ui_from_repo'):
d['repo'] = po.ui_from_repo
if hasattr(po, 'ui_nevra'):
d['_nevra'] = po.ui_nevra
else:
d['_nevra'] = '%s-%s-%s.%s' % (po.name, po.version, po.release, po.arch)
return d
def list_stuff(my, stuff):
# FIXME - there are potential tracebacks that could occur here
# need some more catching for them so we can see what happened
if stuff == 'installed':
return [ pkg_to_dict(po) for po in my.rpmdb ]
elif stuff == 'updates':
return [ pkg_to_dict(po) for
po in my.doPackageLists(pkgnarrow='updates').updates ]
elif stuff == 'available':
return [ pkg_to_dict(po) for po in my.pkgSack ]
elif stuff == 'repos':
r = []
for repo in my.repos.repos.values():
t = {}
s = 'disabled'
if repo.enabled:
s = 'enabled'
t[repo.id] = s
r.append(t)
return r
else:
e,m,u = my.rpmdb.matchPackageNames([stuff])
p = e + m
e,m,u = my.pkgSack.matchPackageNames([stuff])
p = p + e + m
return [ pkg_to_dict(po) for po in p ]
def run_yum(command):
try:
cmd = subprocess.Popen(command, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = cmd.communicate()
except (OSError, IOError), e:
rc = 1
err = str(e)
out = ''
except:
rc = 1
err = traceback.format_exc()
out = ''
if out is None:
out = ''
if err is None:
err = ''
else:
rc = cmd.returncode
return rc, out, err
def ensure(my, state, pkgspec):
yumconf = my.conf.config_file_path
res = {}
if state == 'installed':
# check if pkgspec is installed
if re.search('[></=]',pkgspec):
try:
pkgs = my.returnInstalledPackagesByDep(pkgspec)
except yum.Errors.YumBaseError:
pkgs = None
else:
e,m,u = my.rpmdb.matchPackageNames([pkgspec])
pkgs = e +m
if pkgs:
return { 'changed':False }
# if not see if it is available
if re.search('[></=]',pkgspec):
try:
pkgs = my.returnPackagesByDep(pkgspec)
except yum.Errors.YumBaseError:
pkgs = None
else:
e,m,u = my.pkgSack.matchPackageNames([pkgspec])
pkgs = e +m
if not pkgs:
msg = "No Package matching %s available" % pkgspec
return { 'changed':False, 'failed': True, 'msg':msg }
my.close()
del my
cmd = "yum -c %s -d1 -y install '%s'" % (yumconf, pkgspec)
rc, out, err = run_yum(cmd)
# FIXME - if we did an install - go and check the rpmdb to see if it actually installed
# look for the pkg in rpmdb
# look for the pkg via obsoletes
if rc:
changed = False
failed = True
else:
changed = True
failed = False
res['changed'] = changed
if failed:
res['failed'] = failed
res['msg'] = err
res['results'] = out
return res
if state == 'removed':
# check if pkgspec is installed
# if not return {changed: False, failed=False }
# if so try to remove it:
# if successfull {changed:True, failed=False, results=stdout, errors=stderr }
# if fail {'changed':False, failed='True', results=stdout, errors=stderr }
yumconf = my.conf.config_file_path
if re.search('[></=]',pkgspec):
try:
pkgs = my.returnInstalledPackagesByDep(pkgspec)
except yum.Errors.YumBaseError:
pkgs = None
else:
e,m,u = my.rpmdb.matchPackageNames([pkgspec])
pkgs = e +m
if not pkgs:
return { 'changed':False }
my.close()
del my
cmd = "yum -c %s -d1 -y remove '%s'" % (yumconf, pkgspec)
rc, out, err = run_yum(cmd)
# FIXME if we ran the remove - check to make sure it actually removed :(
# look for the pkg in the rpmdb
if rc:
changed = False
failed = True
else:
changed = True
failed = False
res['changed'] = changed
if failed:
res['failed'] = failed
res['msg'] = err
res['results'] = out
return res
if state == 'latest':
updates = my.doPackageLists(pkgnarrow='updates', patterns=[pkgspec]).updates
# sucks but this is for rhel5 - won't matter for rhel6 or fedora or whatnot
e,m,u = yum.parsePackages(updates, [pkgspec], casematch=True)
updates = e + m
avail = my.doPackageLists(pkgnarrow='available', patterns=[pkgspec]).available
if not updates and not avail:
if not my.doPackageLists(pkgnarrow='installed', patterns=[pkgspec]).installed:
msg = "No Package matching '%s' found available, installed or updated" % pkgspec
return { 'changed':False, 'failed':True, 'msg': msg }
return { 'changed':False,}
# we have something in updates or available
if not updates:
cmd = "yum -c %s -d1 -y install '%s'" % (yumconf, pkgspec)
else:
cmd = "yum -c %s -d1 -y update '%s'" % (yumconf, pkgspec)
rc, out, err = run_yum(cmd)
# 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:
changed = False
failed = True
else:
changed = True
failed = False
res['changed'] = changed
if failed:
res['failed'] = failed
res['msg'] = err
res['results'] = out
return res
# should be caught by AnsibleModule argument_spec
return dict(changed=False, failed=True, results='', errors='unexpected state')
def update(args):
#generic update routine
pass
def remove_only(pkgspec):
# remove this pkg and only this pkg - fail if it will require more to remove
pass
def main():
# state=installed pkg=pkgspec
# state=removed pkg=pkgspec
# state=latest pkg=pkgspec
#
# informational commands:
# list=installed
# list=updates
# list=available
# list=repos
# list=pkgspec
module = AnsibleModule(
argument_spec = dict(
pkg=dict(aliases=['name','package']),
# removed==absent, installed==present, these are accepted as aliases
state=dict(default='installed', choices=['absent','present','installed','removed','latest']),
list=dict(choices=['installed','updates','available','repos','pkgspec']),
)
)
params = module.params
if 'conf_file' not in params:
params['conf_file'] = None
if 'list' in params and 'pkg' in params:
module.fail_json(msg="expected 'list=' or 'name=', but not both")
if 'list' in params:
try:
my = yum_base(conf_file=params['conf_file'], cachedir=True)
results = dict(results=list_stuff(my, params['list']))
except Exception, e:
module.fail_json(msg=str(e))
module.exit_json(**results)
else:
pkg = params['pkg']
if 'pkg' is None:
module.fail_json(msg="expected 'list=' or 'name='")
else:
try:
my = yum_base(conf_file=params['conf_file'], cachedir=True)
state = params['state']
results = ensure(my, state, pkg)
except Exception, e:
module.fail_json(msg=str(e))
module.exit_json(**results)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()