ansible/library/apt
Michael DeHaan 7e9e29011e Add encoding lines to python modules such that they can take unicode options if they are fed them, since the
AnsibleModule stuff no longer base64 encodes for simplicity and speed reasons.
2012-08-02 21:29:10 -04:00

184 lines
6.4 KiB
Python
Executable file

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# (c) 2012, Flowroute LLC
# Written by Matthew Williams <matthew@flowroute.com>
# Based on yum module written by Seth Vidal <skvidal at fedoraproject.org>
#
# This module 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.
#
# This software 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 this software. If not, see <http://www.gnu.org/licenses/>.
#
import traceback
# added to stave off future warnings about apt api
import warnings;
warnings.filterwarnings('ignore', "apt API not stable yet", FutureWarning)
# APT related constants
APT_PATH = "/usr/bin/apt-get"
APT = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical %s" % APT_PATH
def run_apt(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 = ''
else:
rc = cmd.returncode
return rc, out, err
def package_split(pkgspec):
parts = pkgspec.split('=')
if len(parts) > 1:
return parts[0], parts[1]
else:
return parts[0], None
def package_status(m, pkgname, version, cache):
try:
pkg = cache[pkgname]
except KeyError:
m.fail_json(msg="No package matching '%s' is available" % pkgname)
if version:
try :
return pkg.is_installed and pkg.installed.version == version, False
except AttributeError:
#assume older version of python-apt is installed
return pkg.isInstalled and pkg.installedVersion == version, False
else:
try :
return pkg.is_installed, pkg.is_upgradable
except AttributeError:
#assume older version of python-apt is installed
return pkg.isInstalled, pkg.isUpgradable
def install(m, pkgspec, cache, upgrade=False, default_release=None, install_recommends=True, force=False):
packages = ""
for package in pkgspec:
name, version = package_split(package)
installed, upgradable = package_status(m, name, version, cache)
if not installed or (upgrade and upgradable):
packages += "'%s' " % package
if len(packages) != 0:
if force:
force_yes = '--force-yes'
else:
force_yes = ''
cmd = "%s --option Dpkg::Options::=--force-confold -q -y %s install %s" % (APT, force_yes,packages)
if default_release:
cmd += " -t '%s'" % (default_release,)
if not install_recommends:
cmd += " --no-install-recommends"
rc, out, err = run_apt(cmd)
if rc:
m.fail_json(msg="'apt-get install %s' failed: %s" % (packages, err))
else:
m.exit_json(changed=True)
else:
m.exit_json(changed=False)
def remove(m, pkgspec, cache, purge=False):
packages = ""
for package in pkgspec:
name, version = package_split(package)
installed, upgradable = package_status(m, name, version, cache)
if installed:
packages += "'%s' " % package
if len(packages) == 0:
m.exit_json(changed=False)
else:
purge = '--purge' if purge else ''
cmd = "%s -q -y %s remove %s" % (APT, purge,packages)
rc, out, err = run_apt(cmd)
if rc:
m.fail_json(msg="'apt-get remove %s' failed: %s" % (packages, err))
m.exit_json(changed=True)
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default='installed', choices=['installed', 'latest', 'removed']),
update_cache = dict(default='no', choices=['yes', 'no'], aliases=['update-cache']),
purge = dict(default='no', choices=['yes', 'no']),
package = dict(default=None, aliases=['pkg', 'name']),
default_release = dict(default=None, aliases=['default-release']),
install_recommends = dict(default='yes', aliases=['install-recommends'], choices=['yes', 'no']),
force = dict(default='no', choices=['yes', 'no'])
)
)
try:
import apt, apt_pkg
except:
module.fail_json("Could not import python modules: apt, apt_pkg. Please install python-apt package.")
if not os.path.exists(APT_PATH):
module.fail_json(msg="Cannot find apt-get")
p = module.params
if p['package'] is None and p['update_cache'] != 'yes':
module.fail_json(msg='pkg=name and/or update_cache=yes is required')
install_recommends = module.boolean(p['install_recommends'])
cache = apt.Cache()
if p['default_release']:
apt_pkg.config['APT::Default-Release'] = p['default_release']
# reopen cache w/ modified config
cache.open(progress=None)
if module.boolean(p['update_cache']):
cache.update()
cache.open(progress=None)
if p['package'] == None:
module.exit_json(changed=False)
force_yes = module.boolean(p['force'])
packages = p['package'].split(',')
latest = p['state'] == 'latest'
for package in packages:
if package.count('=') > 1:
module.fail_json(msg="invalid package spec: %s" % package)
if latest and '=' in package:
module.fail_json(msg='version number inconsistent with state=latest: %s' % package)
if p['state'] == 'latest':
install(module, packages, cache, upgrade=True,
default_release=p['default_release'],
install_recommends=install_recommends,
force=force_yes)
elif p['state'] == 'installed':
install(module, packages, cache, default_release=p['default_release'],
install_recommends=install_recommends,force=force_yes)
elif p['state'] == 'removed':
remove(module, packages, cache, purge = module.boolean(p['purge']))
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()