Allow modules to be categorized, and also sort them when generating the documentation.

This commit is contained in:
Michael DeHaan
2013-04-28 15:03:45 -04:00
parent f46bdb6343
commit 391fb98ee2
87 changed files with 118 additions and 67 deletions

299
library/packaging/apt Normal file
View File

@@ -0,0 +1,299 @@
#!/usr/bin/python
# -*- 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/>.
#
DOCUMENTATION = '''
---
module: apt
short_description: Manages apt-packages
description:
- Manages I(apt) packages (such as for Debian/Ubuntu).
version_added: "0.0.2"
options:
pkg:
description:
- A package name or package specifier with version, like C(foo) or C(foo=1.0)
required: true
default: null
state:
description:
- Indicates the desired package state
required: false
default: present
choices: [ "latest", "absent", "present" ]
update_cache:
description:
- Run the equivalent of C(apt-get update) before the operation. Can be run as part of the package installation or as a separate step
required: false
default: "no"
choices: [ "yes", "no" ]
purge:
description:
- Will force purging of configuration files if the module state is set to I(absent).
required: false
default: "no"
choices: [ "yes", "no" ]
default_release:
description:
- Corresponds to the C(-t) option for I(apt) and sets pin priorities
required: false
default: null
install_recommends:
description:
- Corresponds to the C(--no-install-recommends) option for I(apt), default behavior works as apt's default behavior, C(no) does not install recommended packages. Suggested packages are never installed.
required: false
default: "yes"
choices: [ "yes", "no" ]
force:
description:
- If C(yes), force installs/removes.
required: false
default: "no"
choices: [ "yes", "no" ]
upgrade:
description:
- 'If yes or safe, performs an aptitude safe-upgrade.'
- 'If full, performs an aptitude full-upgrade.'
- 'If dist, performs an apt-get dist-upgrade.'
- 'Note: This does not upgrade a specific package, use state=latest for that.'
version_added: "1.1"
required: false
default: "yes"
choices: [ "yes", "safe", "full", "dist"]
author: Matthew Williams
notes: []
examples:
- code: "apt: pkg=foo update_cache=yes"
description: Update repositories cache and install C(foo) package
- code: "apt: pkg=foo state=removed"
description: Remove C(foo) package
- code: "apt: pkg=foo state=installed"
description: Install the package C(foo)
- code: "apt: pkg=foo=1.00 state=installed"
description: Install the version '1.00' of package C(foo)
- code: "apt: pkg=nginx state=latest default_release=squeeze-backports update_cache=yes"
description: Update the repository cache and update package C(ngnix) to latest version using default release C(squeeze-backport)
- code: "apt: pkg=openjdk-6-jdk state=latest install_recommends=no"
description: Install latest version of C(openjdk-6-jdk) ignoring C(install-reccomends)
- code: "apt: upgrade=dist"
description: Update all packages to the latest version
- code: "apt: update_cache=yes"
description: Run the equivalent of C(apt-get update) as a separate step
requirements: [ python-apt, aptitude ]
'''
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
APTITUDE_CMD = "aptitude"
APT_GET_CMD = "apt-get"
APT_ENVVARS = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical"
DPKG_OPTIONS = '-o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold"'
APT_GET_ZERO = "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded."
APTITUDE_ZERO = "0 packages upgraded, 0 newly installed, 0 to remove and 0 not upgraded."
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, state):
try:
pkg = cache[pkgname]
except KeyError:
if state == 'install':
m.fail_json(msg="No package matching '%s' is available" % pkgname)
else:
return False, False
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, state='install')
if not installed or (upgrade and upgradable):
packages += "'%s' " % package
if len(packages) != 0:
if force:
force_yes = '--force-yes'
else:
force_yes = ''
if m.check_mode:
check_arg = '--simulate'
else:
check_arg = ''
cmd = "%s %s -y %s %s %s install %s" % (APT_ENVVARS, APT_GET_CMD, DPKG_OPTIONS, force_yes, check_arg, packages)
if default_release:
cmd += " -t '%s'" % (default_release,)
if not install_recommends:
cmd += " --no-install-recommends"
rc, out, err = m.run_command(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, state='remove')
if installed:
packages += "'%s' " % package
if len(packages) == 0:
m.exit_json(changed=False)
else:
purge = ''
if purge:
purge = '--purge'
cmd = "%s -q -y %s remove %s" % (APT_GET_CMD, purge,packages)
if m.check_mode:
m.exit_json(changed=True)
rc, out, err = m.run_command(cmd)
if rc:
m.fail_json(msg="'apt-get remove %s' failed: %s" % (packages, err))
m.exit_json(changed=True)
def upgrade(m, mode="yes"):
if m.check_mode:
check_arg = '--simulate'
else:
check_arg = ''
if mode == "dist":
# apt-get dist-upgrade
apt_cmd = APT_GET_CMD
upgrade_command = "dist-upgrade"
elif mode == "full":
# aptitude full-upgrade
apt_cmd = APTITUDE_CMD
upgrade_command = "full-upgrade"
else:
# aptitude safe-upgrade # mode=yes # default
apt_cmd = APTITUDE_CMD
upgrade_command = "safe-upgrade"
apt_cmd_path = m.get_bin_path(apt_cmd, required=True)
cmd = '%s %s -y %s %s %s' % (APT_ENVVARS, apt_cmd_path, DPKG_OPTIONS,
check_arg, upgrade_command)
rc, out, err = m.run_command(cmd)
if rc:
m.fail_json(msg="'%s %s' failed: %s" % (apt_cmd, upgrade_command, err))
m.fail_json(msg="'aptitude safe-upgrade' failed: %s" % err)
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)
m.exit_json(changed=True, msg=out)
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default='installed', choices=['installed', 'latest', 'removed', 'absent', 'present']),
update_cache = dict(aliases=['update-cache'], type='bool'),
purge = dict(default='no', type='bool'),
package = dict(default=None, aliases=['pkg', 'name']),
default_release = dict(default=None, aliases=['default-release']),
install_recommends = dict(default='yes', aliases=['install-recommends'], type='bool'),
force = dict(default='no', type='bool'),
upgrade = dict(choices=['yes', 'safe', 'full', 'dist'])
),
mutually_exclusive = [['package', 'upgrade']],
required_one_of = [['package', 'upgrade', 'update_cache']],
supports_check_mode = True
)
try:
import apt
import apt_pkg
except:
module.fail_json(msg="Could not import python modules: apt, apt_pkg. Please install python-apt package.")
p = module.params
install_recommends = p['install_recommends']
try:
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 p['update_cache']:
cache.update()
cache.open(progress=None)
if not p['package'] and not p['upgrade']:
module.exit_json(changed=False)
force_yes = p['force']
if p['upgrade']:
upgrade(module, p['upgrade'])
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'] in [ 'installed', 'present' ]:
install(module, packages, cache, default_release=p['default_release'],
install_recommends=install_recommends,force=force_yes)
elif p['state'] in [ 'removed', 'absent' ]:
remove(module, packages, cache, p['purge'])
except apt.cache.LockFailedException:
module.fail_json(msg="Failed to lock apt for exclusive operation")
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

183
library/packaging/apt_key Normal file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# (c) 2012, Jayson Vantuyl <jayson@aggressive.ly>
#
# 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/>.
DOCUMENTATION = '''
---
module: apt_key
author: Jayson Vantuyl & others
version_added: "1.0"
short_description: Add or remove an apt key
description:
- Add or remove an I(apt) key, optionally downloading it
notes:
- doesn't download the key unless it really needs it
- as a sanity check, downloaded key id must match the one specified
- best practice is to specify the key id and the url
options:
id:
required: false
default: none
description:
- identifier of key
url:
required: false
default: none
description:
- url to retrieve key from.
state:
required: false
choices: [ absent, present ]
default: present
description:
- used to specify if key is being added or revoked
examples:
- code: "apt_key: url=https://ftp-master.debian.org/keys/archive-key-6.0.asc state=present"
description: Add an Apt signing key, uses whichever key is at the URL
- code: "apt_key: id=473041FA url=https://ftp-master.debian.org/keys/archive-key-6.0.asc state=present"
description: Add an Apt signing key, will not download if present
- code: "apt_key: url=https://ftp-master.debian.org/keys/archive-key-6.0.asc state=absent"
description: Remove an Apt signing key, uses whichever key is at the URL
- code: "apt_key: id=473041FA state=absent"
description: Remove a Apt specific signing key
'''
# FIXME: standardize into module_common
from urllib2 import urlopen, URLError
from traceback import format_exc
from re import compile as re_compile
# FIXME: standardize into module_common
from distutils.spawn import find_executable
from os import environ
from sys import exc_info
import traceback
match_key = re_compile("^gpg:.*key ([0-9a-fA-F]+):.*$")
REQUIRED_EXECUTABLES=['gpg', 'grep', 'apt-key']
def check_missing_binaries(module):
missing = [e for e in REQUIRED_EXECUTABLES if not find_executable(e)]
if len(missing):
module.fail_json(msg="binaries are missing", names=all)
def all_keys(module):
(rc, out, err) = module.run_command("apt-key list")
results = []
lines = out.split('\n')
for line in lines:
if line.startswith("pub"):
tokens = line.split()
code = tokens[1]
(len_type, real_code) = code.split("/")
results.append(real_code)
return results
def key_present(module, key_id):
(rc, out, err) = module.run_command("apt-key list | 2>&1 grep -q %s" % key_id)
return rc == 0
def download_key(module, url):
# FIXME: move get_url code to common, allow for in-memory D/L, support proxies
# and reuse here
if url is None:
module.fail_json(msg="needed a URL but was not specified")
try:
connection = urlopen(url)
if connection is None:
module.fail_json("error connecting to download key from url")
data = connection.read()
return data
except:
module.fail_json(msg="error getting key id from url", traceback=format_exc())
def add_key(module, key):
cmd = "apt-key add -"
(rc, out, err) = module.run_command(cmd, data=key, check_rc=True)
return True
def remove_key(module, key_id):
# FIXME: use module.run_command, fail at point of error and don't discard useful stdin/stdout
cmd = 'apt-key del %s' % key_id
(rc, out, err) = module.run_command(cmd, check_rc=True)
return True
def main():
module = AnsibleModule(
argument_spec=dict(
id=dict(required=False, default=None),
url=dict(required=False),
data=dict(required=False),
key=dict(required=False),
state=dict(required=False, choices=['present', 'absent'], default='present')
),
supports_check_mode=True
)
key_id = module.params['id']
url = module.params['url']
data = module.params['data']
state = module.params['state']
changed = False
# FIXME: I think we have a common facility for this, if not, want
check_missing_binaries(module)
keys = all_keys(module)
return_values = {}
if state == 'present':
if key_id and key_id in keys:
module.exit_json(changed=False)
else:
if not data:
data = download_key(module, url)
if key_id and key_id in keys:
module.exit_json(changed=False)
else:
if module.check_mode:
module.exit_json(changed=True)
add_key(module, data)
changed=False
keys2 = all_keys(module)
if len(keys) != len(keys2):
changed=True
if key_id and not key_id in keys2:
module.fail_json(msg="key does not seem to have been added", id=key_id)
module.exit_json(changed=changed)
elif state == 'absent':
if not key_id:
module.fail_json(msg="key is required")
if key_id in keys:
if module.check_mode:
module.exit_json(changed=True)
if remove_key(module, key_id):
changed=True
else:
# FIXME: module.fail_json or exit-json immediately at point of failure
module.fail_json(msg="error removing key_id", **return_values)
module.exit_json(changed=changed, **return_values)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View File

@@ -0,0 +1,148 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Matt Wright <matt@nobien.net>
#
# 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/>.
#
# Example:
# - name: add nginx repo
# action: apt_repository repo=ppa:nginx/stable state=present
#
DOCUMENTATION = '''
---
module: apt_repository
short_description: Manages apt repositores
description:
- Manages apt repositories (such as for Debian/Ubuntu).
version_added: "0.7"
options:
repo:
description:
- The repository name/value
required: true
default: null
state:
description:
- The repository state
required: false
default: present
choices: [ "present", "absent" ]
notes:
- This module works on Debian and Ubuntu only and requires C(apt-add-repository) be available on the destination server. To ensure this package is available use the M(apt) module and install the C(python-software-properties) package before using this module.
- This module cannot be used on Debian Squeeze (Version 6) as there is no C(add-apt-repository) in C(python-software-properties)
- A bug in C(apt-add-repository) always adds C(deb) and C(deb-src) types for repositories (see the issue on Launchpad U(https://bugs.launchpad.net/ubuntu/+source/software-properties/+bug/987264)), if a repo doesn't have source information (eg MongoDB repo from 10gen) the system will fail while updating repositories.
author: Matt Wright
examples:
- code: "apt_repository: repo=ppa:nginx/stable"
description: Add nginx stable repository from PPA
- code: "apt_repository: repo='deb http://archive.canonical.com/ubuntu hardy partner'"
description: Add specified repository into sources.
requirements: [ python-apt ]
'''
import platform
try:
import apt
import apt_pkg
HAVE_PYAPT = True
except ImportError:
HAVE_PYAPT = False
APT = "/usr/bin/apt-get"
ADD_APT_REPO = 'add-apt-repository'
def check_cmd_needs_y():
if platform.dist()[0] == 'debian' or float(platform.dist()[1]) >= 11.10:
return True
return False
def repo_exists(module, repo):
configured = False
slist = apt_pkg.SourceList()
if not slist.read_main_list():
module.fail_json(msg="Failed to parse sources.list")
for metaindex in slist.list:
if repo in metaindex.uri:
configured = True
return configured
def main():
add_apt_repository = None
arg_spec = dict(
repo=dict(required=True),
state=dict(default='present', choices=['present', 'absent'])
)
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
if not HAVE_PYAPT:
module.fail_json(msg="Could not import python modules: apt, apt_pkg. Please install python-apt package.")
add_apt_repository = module.get_bin_path(ADD_APT_REPO, True)
if check_cmd_needs_y():
add_apt_repository += ' -y'
repo = module.params['repo']
state = module.params['state']
repo_url = repo
if 'ppa' in repo_url:
# looks like ppa:nginx/stable
repo_url = repo.split(':')[1]
elif len(repo_url.split(' ')) > 1:
# could be:
# http://myserver/path/to/repo free non-free
# deb http://myserver/path/to/repo free non-free
for i in repo_url.split():
if 'http' in i:
repo_url = i
exists = repo_exists(module, repo_url)
rc = 0
out = ''
err = ''
if state == 'absent' and exists:
if module.check_mode:
module.exit_json(changed=True)
cmd = '%s "%s" --remove' % (add_apt_repository, repo)
rc, out, err = module.run_command(cmd)
elif state == 'present' and not exists:
if module.check_mode:
module.exit_json(changed=True)
cmd = '%s "%s"' % (add_apt_repository, repo)
rc, out, err = module.run_command(cmd)
else:
module.exit_json(changed=False, repo=repo, state=state)
if rc != 0:
module.fail_json(msg=err)
else:
changed = True
if state == 'present' and changed:
rc, out, err = module.run_command('%s update' % APT)
module.exit_json(changed=changed, repo=repo, state=state)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View File

@@ -0,0 +1,142 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Matt Wright <matt@nobien.net>
#
# 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/>.
#
DOCUMENTATION = '''
---
module: easy_install
short_description: Installs Python libraries
description:
- Installs Python libraries, optionally in a I(virtualenv)
version_added: "0.7"
options:
name:
description:
- A Python library name
required: true
default: null
aliases: []
virtualenv:
description:
- an optional I(virtualenv) directory path to install into. If the
I(virtualenv) does not exist, it is created automatically
required: false
default: null
virtualenv_site_packages:
version_added: "1.1"
description:
- Whether the virtual environment will inherit packages from the
global site-packages directory. Note that if this setting is
changed on an already existing virtual environment it will not
have any effect, the environment must be deleted and newly
created.
required: false
default: "no"
choices: [ "yes", "no" ]
virtualenv_command:
version_added: "1.1"
description:
- The command to create the virtual environment with. For example
C(pyvenv), C(virtualenv), C(virtualenv2).
required: false
default: virtualenv
examples:
- code: "easy_install: name=pip"
description: "Examples from Ansible Playbooks"
- code: "easy_install: name=flask virtualenv=/webapps/myapp/venv"
description: "Install I(Flask) (U(http://flask.pocoo.org/)) into the specified I(virtualenv)"
notes:
- Please note that the M(easy_install) module can only install Python
libraries. Thus this module is not able to remove libraries. It is
generally recommended to use the M(pip) module which you can first install
using M(easy_install).
- Also note that I(virtualenv) must be installed on the remote host if the
C(virtualenv) parameter is specified.
requirements: [ "virtualenv" ]
author: Matt Wright
'''
def _is_package_installed(module, name, easy_install):
cmd = '%s --dry-run %s' % (easy_install, name)
rc, status_stdout, status_stderr = module.run_command(cmd)
return not ('Reading' in status_stdout or 'Downloading' in status_stdout)
def main():
arg_spec = dict(
name=dict(required=True),
virtualenv=dict(default=None, required=False),
virtualenv_site_packages=dict(default='no', type='bool'),
virtualenv_command=dict(default='virtualenv', required=False),
)
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
name = module.params['name']
env = module.params['virtualenv']
easy_install = module.get_bin_path('easy_install', True, ['%s/bin' % env])
site_packages = module.params['virtualenv_site_packages']
virtualenv_command = module.params['virtualenv_command']
rc = 0
err = ''
out = ''
if env:
virtualenv = module.get_bin_path(virtualenv_command, True)
if not os.path.exists(os.path.join(env, 'bin', 'activate')):
if module.check_mode:
module.exit_json(changed=True)
command = '%s %s' % (virtualenv, env)
if site_packages:
command += ' --system-site-packages'
rc_venv, out_venv, err_venv = module.run_command('%s %s' % (virtualenv, env))
rc += rc_venv
out += out_venv
err += err_venv
cmd = None
changed = False
installed = _is_package_installed(module, name, easy_install)
if not installed:
if module.check_mode:
module.exit_json(changed=True)
cmd = '%s %s' % (easy_install, name)
rc_pip, out_pip, err_pip = module.run_command(cmd)
rc += rc_pip
out += out_pip
err += err_pip
changed = True
if rc != 0:
module.fail_json(msg=err, cmd=cmd)
module.exit_json(changed=changed, binary=easy_install,
name=name, virtualenv=env)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

204
library/packaging/gem Normal file
View File

@@ -0,0 +1,204 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Johan Wiren <johan.wiren.se@gmail.com>
#
# 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/>.
#
DOCUMENTATION = '''
---
module: gem
short_description: Manage Ruby gems
description:
- Manage installation and uninstallation of Ruby gems.
version_added: "1.1"
options:
name:
description:
- The name of the gem to be managed.
required: true
state:
description:
- The desired state of the gem. C(latest) ensures that the latest version is installed.
required: true
choices: [present, absent, latest]
gem_source:
description:
- The path to a local gem used as installation source.
required: false
include_dependencies:
description:
- Wheter to include dependencies or not.
required: false
choices: [ "yes", "no" ]
default: "yes"
repository:
description:
- The repository from which the gem will be installed
required: false
aliases: [source]
version:
description:
- Version of the gem to be installed/removed.
required: false
author: Johan Wiren
'''
EXAMPLES = '''
# Installs version 1.0 of vagrant.
gem: name=vagrant version=1.0 state=present
# Installs latest available version of rake.
gem: name=rake state=latest
# Installs rake version 1.0 from a local gem on disk.
gem: name=rake gem_source=/path/to/gems/rake-1.0.gem state=present
'''
import re
def get_rubygems_version(module):
cmd = [module.get_bin_path('gem', True), '--version']
(rc, out, err) = module.run_command(cmd, check_rc=True)
match = re.match(r'^(\d+)\.(\d+)\.(\d+)', out)
if not match:
return None
return tuple(int(x) for x in match.groups())
def get_installed_versions(module, remote=False):
cmd = [ module.get_bin_path('gem', True) ]
cmd.append('query')
if remote:
cmd.append('--remote')
if module.params['repository']:
cmd.extend([ '--source', module.params['repository'] ])
cmd.append('-n')
cmd.append('^%s$' % module.params['name'])
(rc, out, err) = module.run_command(cmd, check_rc=True)
installed_versions = []
for line in out.splitlines():
match = re.match(r"\S+\s+\((.+)\)", line)
if match:
versions = match.group(1)
for version in versions.split(', '):
installed_versions.append(version)
return installed_versions
def exists(module):
if module.params['state'] == 'latest':
remoteversions = get_installed_versions(module, remote=True)
if remoteversions:
module.params['version'] = remoteversions[0]
installed_versions = get_installed_versions(module)
if module.params['version']:
if module.params['version'] in installed_versions:
return True
else:
if installed_versions:
return True
return False
def uninstall(module):
if module.check_mode:
return
cmd = [ module.get_bin_path('gem', True) ]
cmd.append('uninstall')
if module.params['version']:
cmd.extend([ '--version', module.params['version'] ])
else:
cmd.append('--all')
cmd.append(module.params['name'])
module.run_command(cmd, check_rc=True)
def install(module):
if module.check_mode:
return
ver = get_rubygems_version(module)
if ver:
major = ver[0]
else:
major = None
cmd = [ module.get_bin_path('gem', True) ]
cmd.append('install')
if module.params['version']:
cmd.extend([ '--version', module.params['version'] ])
if module.params['repository']:
cmd.extend([ '--source', module.params['repository'] ])
if not module.params['include_dependencies']:
cmd.append('--ignore-dependencies')
else:
if major and major < 2:
cmd.append('--include-dependencies')
cmd.append('--no-rdoc')
cmd.append('--no-ri')
cmd.append(module.params['gem_source'])
module.run_command(cmd, check_rc=True)
def main():
module = AnsibleModule(
argument_spec = dict(
gem_source = dict(required=False, type='str'),
include_dependencies = dict(required=False, default=True, type='bool'),
name = dict(required=True, type='str'),
repository = dict(required=False, aliases=['source'], type='str'),
state = dict(required=False, choices=['present','absent','latest'], type='str'),
version = dict(required=False, type='str'),
),
supports_check_mode = True,
mutually_exclusive = [ ['gem_source','repository'], ['gem_source','version'] ],
)
if module.params['version'] and module.params['state'] == 'latest':
module.fail_json(msg="Cannot specify version when state=latest")
if module.params['gem_source'] and module.params['state'] == 'latest':
module.fail_json(msg="Cannot maintain state=latest when installing from local source")
if not module.params['gem_source']:
module.params['gem_source'] = module.params['name']
changed = False
if module.params['state'] in [ 'present', 'latest']:
if not exists(module):
install(module)
changed = True
elif module.params['state'] == 'absent':
if exists(module):
uninstall(module)
changed = True
result = {}
result['name'] = module.params['name']
result['state'] = module.params['state']
if module.params['version']:
result['version'] = module.params['version']
result['changed'] = changed
module.exit_json(**result)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

153
library/packaging/homebrew Normal file
View File

@@ -0,0 +1,153 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Andrew Dunham <andrew@du.nham.ca>
# Based on macports (Jimmy Tang <jcftang@gmail.com>)
#
# 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/>.
DOCUMENTATION = '''
---
module: homebrew
author: Andrew Dunham
short_description: Package manager for Homebrew
description:
- Manages Homebrew packages
version_added: "1.1"
options:
name:
description:
- name of package to install/remove
required: true
state:
description:
- state of the package
choices: [ 'present', 'absent' ]
required: false
default: present
update_homebrew:
description:
- update homebrew itself first
required: false
default: "no"
choices: [ "yes", "no" ]
notes: []
'''
EXAMPLES = '''
homebrew: name=foo state=present
homebrew: name=foo state=present update_homebrew=yes
homebrew: name=foo state=absent
homebrew: name=foo,bar state=absent
'''
def update_homebrew(module, brew_path):
""" Updates packages list. """
rc, out, err = module.run_command("%s update" % brew_path)
if rc != 0:
module.fail_json(msg="could not update homebrew")
def query_package(module, brew_path, name, state="present"):
""" Returns whether a package is installed or not. """
if state == "present":
rc, out, err = module.run_command("%s list -m1 | grep -q '^%s$'" % (brew_path, name))
if rc == 0:
return True
return False
def remove_packages(module, brew_path, packages):
""" Uninstalls one or more packages if installed. """
removed_count = 0
# Using a for loop incase of error, we can report the package that failed
for package in packages:
# Query the package first, to see if we even need to remove.
if not query_package(module, brew_path, package):
continue
if module.check_mode:
module.exit_json(changed=True)
rc, out, err = module.run_command([brew_path, 'remove', package])
if query_package(module, brew_path, package):
module.fail_json(msg="failed to remove %s: %s" % (package, out.strip()))
removed_count += 1
if removed_count > 0:
module.exit_json(changed=True, msg="removed %d package(s)" % removed_count)
module.exit_json(changed=False, msg="package(s) already absent")
def install_packages(module, brew_path, packages):
""" Installs one or more packages if not already installed. """
installed_count = 0
for package in packages:
if query_package(module, brew_path, package):
continue
if module.check_mode:
module.exit_json(changed=True)
rc, out, err = module.run_command([brew_path, 'install', package])
if not query_package(module, brew_path, package):
module.fail_json(msg="failed to install %s: %s" % (package, out.strip()))
installed_count += 1
if installed_count > 0:
module.exit_json(changed=True, msg="installed %d package(s)" % (installed_count,))
module.exit_json(changed=False, msg="package(s) already present")
def main():
module = AnsibleModule(
argument_spec = dict(
name = dict(aliases=["pkg"], required=True),
state = dict(default="present", choices=["present", "installed", "absent", "removed"]),
update_homebrew = dict(default="no", aliases=["update-brew"], type='bool')
),
supports_check_mode=True
)
brew_path = module.get_bin_path('brew', True, ['/usr/local/bin'])
p = module.params
if p["update_homebrew"]:
update_homebrew(module, brew_path)
pkgs = p["name"].split(",")
if p["state"] in ["present", "installed"]:
install_packages(module, brew_path, pkgs)
elif p["state"] in ["absent", "removed"]:
remove_packages(module, brew_path, pkgs)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

215
library/packaging/macports Normal file
View File

@@ -0,0 +1,215 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Jimmy Tang <jcftang@gmail.com>
# Based on okpg (Patrick Pelletier <pp.pelletier@gmail.com>), pacman
# (Afterburn) and pkgin (Shaun Zinck) modules
#
# 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/>.
DOCUMENTATION = '''
---
module: macports
author: Jimmy Tang
short_description: Package manager for MacPorts
description:
- Manages MacPorts packages
version_added: "1.1"
options:
name:
description:
- name of package to install/remove
required: true
state:
description:
- state of the package
choices: [ 'present', 'absent', 'active', 'inactive' ]
required: false
default: present
update_cache:
description:
- update the package db first
required: false
default: "no"
choices: [ "yes", "no" ]
notes: []
'''
EXAMPLES = '''
macports: name=foo state=present
macports: name=foo state=present update_cache=yes
macports: name=foo state=absent
macports: name=foo state=active
macports: name=foo state=inactive
'''
def update_package_db(module, port_path):
""" Updates packages list. """
rc, out, err = module.run_command("%s sync" % port_path)
if rc != 0:
module.fail_json(msg="could not update package db")
def query_package(module, port_path, name, state="present"):
""" Returns whether a package is installed or not. """
if state == "present":
rc, out, err = module.run_command("%s installed | grep -q ^.*%s" % (port_path, name))
if rc == 0:
return True
return False
elif state == "active":
rc, out, err = module.run_command("%s installed %s | grep -q active" % (port_path, name))
if rc == 0:
return True
return False
def remove_packages(module, port_path, packages):
""" Uninstalls one or more packages if installed. """
remove_c = 0
# Using a for loop incase of error, we can report the package that failed
for package in packages:
# Query the package first, to see if we even need to remove
if not query_package(module, port_path, package):
continue
rc, out, err = module.run_command("%s uninstall %s" % (port_path, package))
if query_package(module, port_path, package):
module.fail_json(msg="failed to remove %s: %s" % (package, out))
remove_c += 1
if remove_c > 0:
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
module.exit_json(changed=False, msg="package(s) already absent")
def install_packages(module, port_path, packages):
""" Installs one or more packages if not already installed. """
install_c = 0
for package in packages:
if query_package(module, port_path, package):
continue
rc, out, err = module.run_command("%s install %s" % (port_path, package))
if not query_package(module, port_path, package):
module.fail_json(msg="failed to install %s: %s" % (package, out))
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="installed %s package(s)" % (install_c))
module.exit_json(changed=False, msg="package(s) already present")
def activate_packages(module, port_path, packages):
""" Activate a package if it's inactive. """
activate_c = 0
for package in packages:
if not query_package(module, port_path, package):
module.fail_json(msg="failed to activate %s, package(s) not present" % (package))
if query_package(module, port_path, package, state="active"):
continue
rc, out, err = module.run_command("%s activate %s" % (port_path, package))
if not query_package(module, port_path, package, state="active"):
module.fail_json(msg="failed to activate %s: %s" % (package, out))
activate_c += 1
if activate_c > 0:
module.exit_json(changed=True, msg="activated %s package(s)" % (activate_c))
module.exit_json(changed=False, msg="package(s) already active")
def deactivate_packages(module, port_path, packages):
""" Deactivate a package if it's active. """
deactivated_c = 0
for package in packages:
if not query_package(module, port_path, package):
module.fail_json(msg="failed to activate %s, package(s) not present" % (package))
if not query_package(module, port_path, package, state="active"):
continue
rc, out, err = module.run_command("%s deactivate %s" % (port_path, package))
if query_package(module, port_path, package, state="active"):
module.fail_json(msg="failed to deactivated %s: %s" % (package, out))
deactivated_c += 1
if deactivated_c > 0:
module.exit_json(changed=True, msg="deactivated %s package(s)" % (deactivated_c))
module.exit_json(changed=False, msg="package(s) already inactive")
def main():
module = AnsibleModule(
argument_spec = dict(
name = dict(aliases=["pkg"], required=True),
state = dict(default="present", choices=["present", "installed", "absent", "removed", "active", "inactive"]),
update_cache = dict(default="no", aliases=["update-cache"], type='bool')
)
)
port_path = module.get_bin_path('port', True, ['/opt/local/bin'])
p = module.params
if p["update_cache"]:
update_package_db(module, port_path)
pkgs = p["name"].split(",")
if p["state"] in ["present", "installed"]:
install_packages(module, port_path, pkgs)
elif p["state"] in ["absent", "removed"]:
remove_packages(module, port_path, pkgs)
elif p["state"] == "active":
activate_packages(module, port_path, pkgs)
elif p["state"] == "inactive":
deactivate_packages(module, port_path, pkgs)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

222
library/packaging/npm Normal file
View File

@@ -0,0 +1,222 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Chris Hoffman <christopher.hoffman@gmail.com>
#
# 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/>.
DOCUMENTATION = '''
---
module: npm
short_description: Manage node.js packages with npm
description:
- Manage node.js packages with Node Package Manager (npm)
version_added: 1.2
author: Chris Hoffman
options:
name:
description:
- The name of a node.js library to install
requires: false
default: null
path:
description:
- The base path where to install the node.js libraries
required: false
default: null
version:
description:
- The version to be installed
required: false
default: null
global:
descrition:
- Install the node.js library globally
required: false
default: no
choices: [ "yes", "no" ]
executable:
description:
- The executable location for npm.
- This is useful if you are using a version manager, such as nvm
required: false
default: nvm
production:
description:
- Install dependencies in production mode, excluding devDependencies
required: false
default: no
state:
description:
- The state of the node.js library
required: false
default: present
choices: [ "present", "absent", "latest" ]
examples:
- code: "npm: name=coffee-script path=/app/location"
description: Install I(coffee-script) node.js package.
- code: "npm: name=coffee-script version=1.6.1 path=/app/location"
description: Install I(coffee-script) node.js package on version 1.6.1.
- code: "npm: name=coffee-script global=yes"
description: Install I(coffee-script) node.js package globally.
- code: "npm: name=coffee-script global=yes state=absent"
description: Remove the globally package I(coffee-script).
- code: "npm: path=/app/location"
description: Install packages based on package.json.
- code: "npm: path=/app/location state=latest"
description: Update packages based on package.json to their latest version.
- code: "npm: path=/app/location executable=/opt/nvm/v0.10.1/bin/npm state=present"
description: Install packages based on package.json using the npm installed with nvm v0.10.1.
'''
import os
try:
import json
except ImportError:
import simplejson as json
class Npm(object):
def __init__(self, module, **kwargs):
self.module = module
self.glbl = kwargs['glbl']
self.name = kwargs['name']
self.version = kwargs['version']
self.path = kwargs['path']
self.production = kwargs['production']
if kwargs['executable']:
self.executable = kwargs['executable']
else:
self.executable = module.get_bin_path('npm', True)
if kwargs['version']:
self.name_version = self.name + '@' + self.version
else:
self.name_version = self.name
def _exec(self, args, run_in_check_mode=False, check_rc=True):
if not self.module.check_mode or (self.module.check_mode and run_in_check_mode):
cmd = [self.executable] + args
if self.glbl:
cmd.append('--global')
if self.production:
cmd.append('--production')
if self.name:
cmd.append(self.name_version)
#If path is specified, cd into that path and run the command.
if self.path:
os.chdir(self.path)
rc, out, err = self.module.run_command(cmd, check_rc=check_rc)
return out
return ''
def list(self):
cmd = ['list', '--json']
installed = list()
missing = list()
data = json.loads(self._exec(cmd, True, False))
if 'dependencies' in data:
for dep in data['dependencies']:
if 'missing' in data['dependencies'][dep] and data['dependencies'][dep]['missing']:
missing.append(dep)
else:
installed.append(dep)
#Named dependency not installed
else:
missing.append(self.name)
return installed, missing
def install(self):
return self._exec(['install'])
def update(self):
return self._exec(['update'])
def uninstall(self):
return self._exec(['uninstall'])
def list_outdated(self):
outdated = list()
data = self._exec(['outdated'], True, False)
for dep in data.splitlines():
if dep:
pkg, other = dep.split('@', 1)
outdated.append(pkg)
return outdated
def main():
arg_spec = dict(
name=dict(default=None),
path=dict(default=None),
version=dict(default=None),
production=dict(default='no', type='bool'),
executable=dict(default=None),
state=dict(default='present', choices=['present', 'absent', 'latest'])
)
arg_spec['global']=dict(default='no', type='bool')
module = AnsibleModule(
argument_spec=arg_spec,
supports_check_mode=True
)
name = module.params['name']
path = module.params['path']
version = module.params['version']
glbl = module.params['global']
production = module.params['production']
executable = module.params['executable']
state = module.params['state']
if not path and not glbl:
module.fail_json(msg='path must be specified when not using global')
if state == 'absent' and not name:
module.fail_json(msg='uninstalling a package is only available for named packages')
npm = Npm(module, name=name, path=path, version=version, glbl=glbl, production=production, \
executable=executable)
changed = False
if state == 'present':
installed, missing = npm.list()
if len(missing):
changed = True
npm.install()
elif state == 'latest':
installed, missing = npm.list()
outdated = npm.list_outdated()
if len(missing) or len(outdated):
changed = True
npm.install()
npm.update()
else: #absent
installed, missing = npm.list()
if name in installed:
changed = True
npm.uninstall()
module.exit_json(changed=changed)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View File

@@ -0,0 +1,222 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Patrik Lundin <patrik.lundin.swe@gmail.com>
#
# 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 re
DOCUMENTATION = '''
---
module: openbsd_pkg
author: Patrik Lundin
version_added: "1.1"
short_description: Manage packages on OpenBSD.
description:
- Manage packages on OpenBSD using the pkg tools.
options:
name:
required: true
description:
- Name of the package.
state:
required: true
choices: [ present, latest, absent ]
description:
- C(present) will make sure the package is installed.
C(latest) will make sure the latest version of the package is installed.
C(absent) will make sure the specified package is not installed.
examples:
- description: Make sure nmap is installed
code: "openbsd_pkg: name=nmap state=present"
- description: Make sure nmap is the latest version
code: "openbsd_pkg: name=nmap state=latest"
- description: Make sure nmap is not installed
code: "openbsd_pkg: name=nmap state=absent"
'''
# select whether we dump additional debug info through syslog
syslogging = False
# Function used for executing commands.
def execute_command(cmd, syslogging):
if syslogging:
syslog.openlog('ansible-%s' % os.path.basename(__file__))
syslog.syslog(syslog.LOG_NOTICE, 'Command %s' % '|'.join(cmd))
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = p.communicate()
rc = p.returncode
return (rc, out, err)
# Function used for getting the name of a currently installed package.
def get_current_name(name, specific_version):
info_cmd = 'pkg_info'
(rc, stdout, stderr) = execute_command("%s" % (info_cmd), syslogging)
if rc != 0:
return (rc, stdout, stderr)
if specific_version:
syntax = "%s"
else:
syntax = "%s-"
for line in stdout.splitlines():
if syntax % name in line:
current_name = line.split()[0]
return current_name
# Function used to find out if a package is currently installed.
def get_package_state(name, specific_version):
info_cmd = 'pkg_info -e'
if specific_version:
syntax = "%s %s"
else:
syntax = "%s %s-*"
rc, stdout, stderr = execute_command(syntax % (info_cmd, name), syslogging)
if rc == 0:
return True
else:
return False
# Function used to make sure a package is present.
def package_present(name, installed_state):
install_cmd = 'pkg_add -I'
if installed_state is False:
rc, stdout, stderr = execute_command("%s %s" % (install_cmd, name), syslogging)
# pkg_add returns 0 even if the package does not exist
# so depend on stderr instead if something bad happened.
if stderr:
rc = 1
changed=False
else:
changed=True
else:
rc = 0
stdout = ''
stderr = ''
changed=False
return (rc, stdout, stderr, changed)
# Function used to make sure a package is the latest available version.
def package_latest(name, installed_state, specific_version):
upgrade_cmd = 'pkg_add -u'
pre_upgrade_name = ''
post_upgrade_name = ''
if installed_state is True:
# pkg_add -u exits 0 even if no update was needed, so compare the
# installed package before and after to know if we changed anything.
pre_upgrade_name = get_current_name(name, specific_version)
(rc, stdout, stderr) = execute_command("%s %s" % (upgrade_cmd, name), syslogging)
# 'pkg_add -u' returns 0 even when something strange happened, stdout
# should be empty if everything went fine.
if stdout:
rc=1
post_upgrade_name = get_current_name(name, specific_version)
if pre_upgrade_name == post_upgrade_name:
changed = False
else:
changed = True
return (rc, stdout, stderr, changed)
else:
# If package was not installed at all just make it present.
return package_present(name, installed_state)
# Function used to make sure a package is not installed.
def package_absent(name, installed_state):
remove_cmd = 'pkg_delete -I'
if installed_state is True:
rc, stdout, stderr = execute_command("%s %s" % (remove_cmd, name), syslogging)
if rc == 0:
changed=True
else:
changed=False
else:
rc = 0
stdout = ''
stderr = ''
changed=False
return (rc, stdout, stderr, changed)
# ===========================================
# Main control flow
def main():
module = AnsibleModule(
argument_spec = dict(
name = dict(required=True),
state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']),
)
)
name = module.params['name']
state = module.params['state']
rc = 0
stdout = ''
stderr = ''
result = {}
result['name'] = name
result['state'] = state
# Decide if the name contains a version number.
# This regex is based on packages-specs(7).
match = re.search("-[0-9]", name)
if match:
specific_version = True
else:
specific_version = False
# Get package state
installed_state = get_package_state(name, specific_version)
# Perform requested action
if state in ['installed', 'present']:
(rc, stdout, stderr, changed) = package_present(name, installed_state)
elif state in ['absent', 'removed']:
(rc, stdout, stderr, changed) = package_absent(name, installed_state)
elif state == 'latest':
(rc, stdout, stderr, changed) = package_latest(name, installed_state, specific_version)
if rc != 0:
if stderr:
module.fail_json(msg=stderr)
else:
module.fail_json(msg=stdout)
result['changed'] = changed
module.exit_json(**result)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

149
library/packaging/opkg Normal file
View File

@@ -0,0 +1,149 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Patrick Pelletier <pp.pelletier@gmail.com>
# Based on pacman (Afterburn) and pkgin (Shaun Zinck) modules
#
# 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/>.
DOCUMENTATION = '''
---
module: opkg
author: Patrick Pelletier
short_description: Package manager for OpenWrt
description:
- Manages OpenWrt packages
version_added: "1.1"
options:
name:
description:
- name of package to install/remove
required: true
state:
description:
- state of the package
choices: [ 'present', 'absent' ]
required: false
default: present
update_cache:
description:
- update the package db first
required: false
default: "no"
choices: [ "yes", "no" ]
notes: []
'''
EXAMPLES = '''
opkg: name=foo state=present
opkg: name=foo state=present update_cache=yes
opkg: name=foo state=absent
opkg: name=foo,bar state=absent
'''
def update_package_db(module, opkg_path):
""" Updates packages list. """
rc, out, err = module.run_command("%s update" % opkg_path)
if rc != 0:
module.fail_json(msg="could not update package db")
def query_package(module, opkg_path, name, state="present"):
""" Returns whether a package is installed or not. """
if state == "present":
rc, out, err = module.run_command("%s list-installed | grep -q ^%s" % (opkg_path, name))
if rc == 0:
return True
return False
def remove_packages(module, opkg_path, packages):
""" Uninstalls one or more packages if installed. """
remove_c = 0
# Using a for loop incase of error, we can report the package that failed
for package in packages:
# Query the package first, to see if we even need to remove
if not query_package(module, opkg_path, package):
continue
rc, out, err = module.run_command("%s remove %s" % (opkg_path, package))
if query_package(module, opkg_path, package):
module.fail_json(msg="failed to remove %s: %s" % (package, out))
remove_c += 1
if remove_c > 0:
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
module.exit_json(changed=False, msg="package(s) already absent")
def install_packages(module, opkg_path, packages):
""" Installs one or more packages if not already installed. """
install_c = 0
for package in packages:
if query_package(module, opkg_path, package):
continue
rc, out, err = module.run_command("%s install %s" % (opkg_path, package))
if not query_package(module, opkg_path, package):
module.fail_json(msg="failed to install %s: %s" % (package, out))
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="installed %s package(s)" % (install_c))
module.exit_json(changed=False, msg="package(s) already present")
def main():
module = AnsibleModule(
argument_spec = dict(
name = dict(aliases=["pkg"], required=True),
state = dict(default="present", choices=["present", "installed", "absent", "removed"]),
update_cache = dict(default="no", aliases=["update-cache"], type='bool')
)
)
opkg_path = module.get_bin_path('opkg', True, ['/bin'])
p = module.params
if p["update_cache"]:
update_package_db(module, opkg_path)
pkgs = p["name"].split(",")
if p["state"] in ["present", "installed"]:
install_packages(module, opkg_path, pkgs)
elif p["state"] in ["absent", "removed"]:
remove_packages(module, opkg_path, pkgs)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

163
library/packaging/pacman Normal file
View File

@@ -0,0 +1,163 @@
#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# (c) 2012, Afterburn
# Written by Afterburn <http://github.com/afterburn>
# Based on apt module written by Matthew Williams <matthew@flowroute.com>
#
# 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/>.
DOCUMENTATION = '''
---
module: pacman
short_description: Package manager for Archlinux
description:
- Manages Archlinux packages
version_added: "1.0"
options:
name:
description:
- name of package to install, upgrade or remove.
required: true
state:
description:
- state of the package (installed or absent).
required: false
update_cache:
description:
- update the package database first (pacman -Syy).
required: false
default: "no"
choices: [ "yes", "no" ]
author: Afterburn
notes: []
examples:
- code: "pacman: name=foo state=installed"
description: install package foo
- code: "pacman: name=foo state=absent"
description: remove package foo
- code: "pacman: name=foo,bar state=absent"
description: remove packages foo and bar
- code: "pacman: name=bar, state=installed, update_cache=yes"
description: update the package database (pacman -Syy) and install bar (bar will be the updated if a newer version exists)
'''
import json
import shlex
import os
import sys
PACMAN_PATH = "/usr/bin/pacman"
def query_package(module, name, state="installed"):
# pacman -Q returns 0 if the package is installed,
# 1 if it is not installed
if state == "installed":
rc = os.system("pacman -Q %s" % (name))
if rc == 0:
return True
return False
def update_package_db(module):
rc = os.system("pacman -Syy > /dev/null")
if rc != 0:
module.fail_json(msg="could not update package db")
def remove_packages(module, packages):
remove_c = 0
# Using a for loop incase of error, we can report the package that failed
for package in packages:
# Query the package first, to see if we even need to remove
if not query_package(module, package):
continue
rc = os.system("pacman -R %s --noconfirm > /dev/null" % (package))
if rc != 0:
module.fail_json(msg="failed to remove %s" % (package))
remove_c += 1
if remove_c > 0:
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
module.exit_json(changed=False, msg="package(s) already absent")
def install_packages(module, packages):
install_c = 0
for package in packages:
if query_package(module, package):
continue
rc = os.system("pacman -S %s --noconfirm > /dev/null" % (package))
if rc != 0:
module.fail_json(msg="failed to install %s" % (package))
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="installed %s package(s)" % (install_c))
module.exit_json(changed=False, msg="package(s) already installed")
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default="installed", choices=["installed","absent"]),
update_cache = dict(default="no", aliases=["update-cache"], type='bool'),
name = dict(aliases=["pkg"], required=True)))
if not os.path.exists(PACMAN_PATH):
module.fail_json(msg="cannot find pacman, looking for %s" % (PACMAN_PATH))
p = module.params
if p["update_cache"]:
update_package_db(module)
pkgs = p["name"].split(",")
if p["state"] == "installed":
install_packages(module, pkgs)
elif p["state"] == "absent":
remove_packages(module, pkgs)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

242
library/packaging/pip Normal file
View File

@@ -0,0 +1,242 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Matt Wright <matt@nobien.net>
#
# 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 tempfile
DOCUMENTATION = '''
---
module: pip
short_description: Manages Python library dependencies.
description:
- Manage Python library dependencies.
version_added: "0.7"
options:
name:
description:
- The name of a Python library to install
required: false
default: null
version:
description:
- The version number to install of the Python library specified in the I(name) parameter
required: false
default: null
requirements:
description:
- The path to a pip requirements file
required: false
default: null
virtualenv:
description:
- An optional path to a I(virtualenv) directory to install into
required: false
default: null
virtualenv_site_packages:
version_added: "1.0"
description:
- Whether the virtual environment will inherit packages from the
global site-packages directory. Note that if this setting is
changed on an already existing virtual environment it will not
have any effect, the environment must be deleted and newly
created.
required: false
default: "no"
choices: [ "yes", "no" ]
virtualenv_command:
version_aded: "1.1"
description:
- The command to create the virtual environment with. For example
C(pyvenv), C(virtualenv), C(virtualenv2).
required: false
default: virtualenv
use_mirrors:
description:
- Whether to use mirrors when installing python libraries. If using
an older version of pip (< 1.0), you should set this to no because
older versions of pip do not support I(--use-mirrors).
required: false
default: "yes"
choices: [ "yes", "no" ]
version_added: "1.0"
state:
description:
- The state of module
required: false
default: present
choices: [ "present", "absent", "latest" ]
extra_args:
description:
- Extra arguments passed to pip.
required: false
default: null
version_added: "1.0"
examples:
- code: "pip: name=flask"
description: Install I(flask) python package.
- code: "pip: name=flask version=0.8"
description: Install I(flask) python package on version 0.8.
- code: "pip: name=flask virtualenv=/my_app/venv"
description: "Install I(Flask) (U(http://flask.pocoo.org/)) into the specified I(virtualenv), inheriting none of the globally installed modules"
- code: "pip: name=flask virtualenv=/my_app/venv virtualenv_site_packages=yes"
description: "Install I(Flask) (U(http://flask.pocoo.org/)) into the specified I(virtualenv), inheriting globally installed modules"
- code: "pip: name=flask virtualenv=/my_app/venv virtualenv_command=virtualenv-2.7"
description: "Install I(Flask) (U(http://flask.pocoo.org/)) into the specified I(virtualenv), using Python 2.7"
- code: "pip: requirements=/my_app/requirements.txt"
description: Install specified python requirements.
- code: "pip: requirements=/my_app/requirements.txt virtualenv=/my_app/venv"
description: Install specified python requirements in indicated I(virtualenv).
- code: "pip: requirements=/my_app/requirements.txt extra_args='-i https://example.com/pypi/simple'"
description: Install specified python requirements and custom Index URL.
notes:
- Please note that virtualenv (U(http://www.virtualenv.org/)) must be installed on the remote host if the virtualenv parameter is specified.
requirements: [ "virtualenv", "pip" ]
author: Matt Wright
'''
def _get_full_name(name, version=None):
if version is None:
resp = name
else:
resp = name + '==' + version
return resp
def _get_pip(module, env):
# On Debian and Ubuntu, pip is pip.
# On Fedora18 and up, pip is python-pip.
# On Fedora17 and below, CentOS and RedHat 6 and 5, pip is pip-python.
# On Fedora, CentOS, and RedHat, the exception is in the virtualenv.
# There, pip is just pip.
# Try pip with the virtualenv directory first.
pip = module.get_bin_path('pip', False, ['%s/bin' % env])
for p in ['python-pip', 'pip-python']:
if not pip:
pip = module.get_bin_path(p, False, ['%s/bin' % env])
# pip should have been found by now. The final call to get_bin_path
# will trigger fail_json.
if not pip:
pip = module.get_bin_path('pip', True, ['%s/bin' % env])
return pip
def _fail(module, cmd, out, err):
msg = ''
if out:
msg += "stdout: %s" % (out, )
if err:
msg += "\n:stderr: %s" % (err, )
module.fail_json(cmd=cmd, msg=msg)
def main():
state_map = dict(
present='install',
absent='uninstall -y',
latest='install -U',
)
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=state_map.keys()),
name=dict(default=None, required=False),
version=dict(default=None, required=False),
requirements=dict(default=None, required=False),
virtualenv=dict(default=None, required=False),
virtualenv_site_packages=dict(default='no', type='bool'),
virtualenv_command=dict(default='virtualenv', required=False),
use_mirrors=dict(default='yes', type='bool'),
extra_args=dict(default=None, required=False),
),
required_one_of=[['name', 'requirements']],
mutually_exclusive=[['name', 'requirements']],
supports_check_mode=True
)
state = module.params['state']
name = module.params['name']
version = module.params['version']
requirements = module.params['requirements']
use_mirrors = module.params['use_mirrors']
extra_args = module.params['extra_args']
if state == 'latest' and version is not None:
module.fail_json(msg='version is incompatible with state=latest')
if name and '=' in name:
module.fail_json(msg='version must be specified in the version parameter')
err = ''
out = ''
env = module.params['virtualenv']
virtualenv_command = module.params['virtualenv_command']
if env:
env = os.path.expanduser(env)
virtualenv = module.get_bin_path(virtualenv_command, True)
if not os.path.exists(os.path.join(env, 'bin', 'activate')):
if module.check_mode:
module.exit_json(changed=True)
if module.params['virtualenv_site_packages']:
cmd = '%s --system-site-packages %s' % (virtualenv, env)
else:
cmd = '%s %s' % (virtualenv, env)
rc, out_venv, err_venv = module.run_command(cmd)
out += out_venv
err += err_venv
if rc != 0:
_fail(module, cmd, out, err)
pip = _get_pip(module, env)
cmd = '%s %s' % (pip, state_map[state])
if state != 'absent' and use_mirrors:
cmd += ' --use-mirrors'
if extra_args:
cmd += ' %s' % extra_args
if name:
cmd += ' %s' % _get_full_name(name, version)
elif requirements:
cmd += ' -r %s' % requirements
if module.check_mode:
module.exit_json(changed=True)
os.chdir(tempfile.gettempdir())
rc, out_pip, err_pip = module.run_command(cmd)
out += out_pip
err += err_pip
if rc == 1 and state == 'absent' and 'not installed' in out_pip:
pass # rc is 1 when attempting to uninstall non-installed package
elif rc != 0:
_fail(module, cmd, out, err)
if state == 'absent':
changed = 'Successfully uninstalled' in out_pip
else:
changed = 'Successfully installed' in out_pip
module.exit_json(changed=changed, cmd=cmd, name=name, version=version,
state=state, requirements=requirements, virtualenv=env, stdout=out, stderr=err)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

136
library/packaging/pkgin Normal file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Shaun Zinck
# Written by Shaun Zinck <shaun.zinck at gmail.com>
# Based on pacman module written by Afterburn <http://github.com/afterburn>
# that was based on apt module written by Matthew Williams <matthew@flowroute.com>
#
# 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/>.
DOCUMENTATION = '''
---
module: pkgin
short_description: Package manager for SmartOS
description:
- Manages SmartOS packages
version_added: "1.0"
options:
name:
description:
- name of package to install/remove
required: true
state:
description:
- state of the package
choices: [ 'present', 'absent' ]
required: false
default: present
author: Shaun Zinck
notes: []
examples:
- code: "pkgin: name=foo state=present"
description: install package foo"
- code: "pkgin: name=foo state=absent"
description: remove package foo
- code: "pkgin: name=foo,bar state=absent"
description: remove packages foo and bar
'''
import json
import shlex
import os
import sys
def query_package(module, pkgin_path, name, state="present"):
if state == "present":
rc, out, err = module.run_command("%s list | grep ^%s" % (pkgin_path, name))
if rc == 0:
return True
return False
def remove_packages(module, pkgin_path, packages):
remove_c = 0
# Using a for loop incase of error, we can report the package that failed
for package in packages:
# Query the package first, to see if we even need to remove
if not query_package(module, pkgin_path, package):
continue
rc, out, err = module.run_command("%s -y remove %s" % (pkgin_path, package))
if query_package(module, pkgin_path, package):
module.fail_json(msg="failed to remove %s: %s" % (package, out))
remove_c += 1
if remove_c > 0:
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)
module.exit_json(changed=False, msg="package(s) already absent")
def install_packages(module, pkgin_path, packages):
install_c = 0
for package in packages:
if query_package(module, pkgin_path, package):
continue
rc, out, err = module.run_command("%s -y install %s" % (pkgin_path, package))
if not query_package(module, pkgin_path, package):
module.fail_json(msg="failed to install %s: %s" % (package, out))
install_c += 1
if install_c > 0:
module.exit_json(changed=True, msg="present %s package(s)" % (install_c))
module.exit_json(changed=False, msg="package(s) already present")
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default="present", choices=["present","absent"]),
name = dict(aliases=["pkg"], required=True)))
pkgin_path = module.get_bin_path('pkgin', True, ['/opt/local/bin'])
p = module.params
pkgs = p["name"].split(",")
if p["state"] == "present":
install_packages(module, pkgin_path, pkgs)
elif p["state"] == "absent":
remove_packages(module, pkgin_path, pkgs)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View File

@@ -0,0 +1,149 @@
#!/usr/bin/python
DOCUMENTATION = '''
---
module: rhn_channel
short_description: Adds or removes Red Hat software channels
description:
- Adds or removes Red Hat software channels
version_added: "1.1"
author: Vincent Van der Kussen
notes:
- this module fetches the system id from RHN.
requirements:
- none
options:
name:
description:
- name of the software channel
required: true
default: null
sysname:
description:
- name of the system as it is known in RHN/Satellite
required: true
default: null
state:
description:
- whether the channel should be present or not
required: false
default: present
url:
description:
- The full url to the RHN/Satellite api
required: true
user:
description:
- RHN/Satellite user
required: true
password:
description:
- "the user's password"
required: true
'''
EXAMPLES = '''
rhn_channel: name=rhel-x86_64-server-v2vwin-6 sysname=server01 url=https://rhn.redhat.com/rpc/api user=rhnuser password=guessme
'''
import xmlrpclib
from operator import itemgetter
import re
# ------------------------------------------------------- #
def get_systemid(client, session, sysname):
systems = client.system.listUserSystems(session)
for system in systems:
if system.get('name') == sysname:
idres = system.get('id')
idd = int(idres)
return idd
# ------------------------------------------------------- #
# unused:
#
#def get_localsystemid():
# f = open("/etc/sysconfig/rhn/systemid", "r")
# content = f.read()
# loc_id = re.search(r'\b(ID-)(\d{10})' ,content)
# return loc_id.group(2)
# ------------------------------------------------------- #
def subscribe_channels(channels, client, session, sysname, sys_id):
c = base_channels(client, session, sys_id)
c.append(channels)
return client.channel.software.setSystemChannels(session, sys_id, c)
# ------------------------------------------------------- #
def unsubscribe_channels(channels, client, session, sysname, sys_id):
c = base_channels(client, session, sys_id)
c.remove(channels)
return client.channel.software.setSystemChannels(session, sys_id, c)
# ------------------------------------------------------- #
def base_channels(client, session, sys_id):
basechan = client.channel.software.listSystemChannels(session, sys_id)
chans = [item['channel_label'] for item in basechan]
return chans
# ------------------------------------------------------- #
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default='present', choices=['present', 'absent']),
name = dict(required=True),
sysname = dict(required=True),
url = dict(required=True),
user = dict(required=True),
password = dict(required=True, aliases=['pwd']),
)
# supports_check_mode=True
)
state = module.params['state']
channelname = module.params['name']
systname = module.params['sysname']
saturl = module.params['url']
user = module.params['user']
password = module.params['password']
#initialize connection
client = xmlrpclib.Server(saturl, verbose=0)
session = client.auth.login(user, password)
# get systemid
sys_id = get_systemid(client, session, systname)
# get channels for system
chans = base_channels(client, session, sys_id)
if state == 'present':
if channelname in chans:
module.exit_json(changed=False, msg="Channel %s already exists" % channelname)
else:
subscribe_channels(channelname, client, session, systname, sys_id)
module.exit_json(changed=True, msg="Channel %s added" % channelname)
if state == 'absent':
if not channelname in chans:
module.exit_json(changed=False, msg="Not subscribed to channel %s." % channelname)
else:
unsubscribe_channels(channelname, client, session, systname, sys_id)
module.exit_json(changed=True, msg="Channel %s removed" % channelname)
client.auth.logout(session)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

179
library/packaging/svr4pkg Normal file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Boyd Adamson <boyd () boydadamson.com>
#
# 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/>.
#
DOCUMENTATION = '''
---
module: svr4pkg
short_description: Manage Solaris SVR4 packages
description:
- Manages SVR4 packages on Solaris 10 and 11.
- These were the native packages on Solaris <= 10 and are available
as a legacy feature in Solaris 11.
- Note that this is a very basic packaging system. It will not enforce
dependencies on install or remove.
version_added: "0.9"
author: Boyd Adamson
options:
name:
description:
- Package name, e.g. C(SUNWcsr)
required: true
state:
description:
- Whether to install (C(present)), or remove (C(absent)) a package.
- If the package is to be installed, then I(src) is required.
- The SVR4 package system doesn't provide an upgrade operation. You need to uninstall the old, then install the new package.
required: true
choices: ["present", "absent"]
src:
description:
- Specifies the location to install the package from. Required when C(state=present).
- "Can be any path acceptable to the C(pkgadd) command's C(-d) option. e.g.: C(somefile.pkg), C(/dir/with/pkgs), C(http:/server/mypkgs.pkg)."
- If using a file or directory, they must already be accessible by the host. See the M(copy) module for a way to get them there.
proxy:
description:
- HTTP[s] proxy to be used if C(src) is a URL.
examples:
- code: svr4pkg name=CSWcommon src=/tmp/cswpkgs.pkg state=present
description: Install a package from an already copied file
- code: 'svr4pkg name=CSWpkgutil src=http://get.opencsw.org/now state=present'
description: Install a package directly from an http site
- code: svr4pkg name=SUNWgnome-sound-recorder state=absent
description: Ensure that a package is not installed.
'''
import os
import tempfile
def package_installed(module, name):
cmd = [module.get_bin_path('pkginfo', True)]
cmd.append('-q')
cmd.append(name)
rc, out, err = module.run_command(' '.join(cmd), shell=False)
if rc == 0:
return True
else:
return False
def create_admin_file():
(desc, filename) = tempfile.mkstemp(prefix='ansible_svr4pkg', text=True)
fullauto = '''
mail=
instance=unique
partial=nocheck
runlevel=quit
idepend=nocheck
rdepend=nocheck
space=quit
setuid=nocheck
conflict=nocheck
action=nocheck
networktimeout=60
networkretries=3
authentication=quit
keystore=/var/sadm/security
proxy=
basedir=default
'''
os.write(desc, fullauto)
os.close(desc)
return filename
def run_command(module, cmd):
progname = cmd[0]
cmd[0] = module.get_bin_path(progname, True)
return module.run_command(cmd)
def package_install(module, name, src, proxy):
adminfile = create_admin_file()
cmd = [ 'pkgadd', '-na', adminfile, '-d', src ]
if proxy is not None:
cmd += [ '-x', proxy ]
cmd.append(name)
(rc, out, err) = run_command(module, cmd)
os.unlink(adminfile)
return (rc, out, err)
def package_uninstall(module, name, src):
adminfile = create_admin_file()
cmd = [ 'pkgrm', '-na', adminfile, name]
(rc, out, err) = run_command(module, cmd)
os.unlink(adminfile)
return (rc, out, err)
def main():
module = AnsibleModule(
argument_spec = dict(
name = dict(required = True),
state = dict(required = True, choices=['present', 'absent']),
src = dict(default = None),
proxy = dict(default = None)
),
supports_check_mode=True
)
state = module.params['state']
name = module.params['name']
src = module.params['src']
proxy = module.params['proxy']
rc = None
out = ''
err = ''
result = {}
result['name'] = name
result['state'] = state
if state == 'present':
if src is None:
module.fail_json(name=name,
msg="src is required when state=present")
if not package_installed(module, name):
if module.check_mode:
module.exit_json(changed=True)
(rc, out, err) = package_install(module, name, src, proxy)
# Stdout is normally empty but for some packages can be
# very long and is not often useful
if len(out) > 75:
out = out[:75] + '...'
elif state == 'absent':
if package_installed(module, name):
if module.check_mode:
module.exit_json(changed=True)
(rc, out, err) = package_uninstall(module, name, src)
out = out[:75]
if rc is None:
result['changed'] = False
else:
result['changed'] = True
if out:
result['stdout'] = out
if err:
result['stderr'] = err
module.exit_json(**result)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

700
library/packaging/yum Normal file
View File

@@ -0,0 +1,700 @@
#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# (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 traceback
import os
import yum
DOCUMENTATION = '''
---
module: yum
short_description: Manages packages with the I(yum) package manager
description:
- Will install, upgrade, remove, and list packages with the I(yum) package manager.
options:
name:
description:
- package name, or package specifier with version, like C(name-1.0).
required: true
default: null
aliases: []
list:
description:
- various non-idempotent commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples.
required: false
default: null
state:
description:
- whether to install (C(present), C(latest)), or remove (C(absent)) a package.
required: false
choices: [ "present", "latest", "absent" ]
default: "present"
enablerepo:
description:
- Repoid of repositories to enable for the install/update operation.
These repos will not persist beyond the transaction
multiple repos separated with a ','
required: false
version_added: "0.9"
default: null
aliases: []
disablerepo:
description:
- I(repoid) of repositories to disable for the install/update operation
These repos will not persist beyond the transaction
Multiple repos separated with a ','
required: false
version_added: "0.9"
default: null
aliases: []
conf_file:
description:
- The remote yum configuration file to use for the transaction.
required: false
version_added: "0.6"
default: null
aliases: []
disable_gpg_check:
description:
- Whether to disable the GPG checking of signatures of packages being
installed. Has an effect only if state is I(present) or I(latest).
required: false
version_added: "1.2"
default: "no"
choices: ["yes", "no"]
aliases: []
examples:
- code: yum name=httpd state=latest
- code: yum name=httpd state=removed
- code: yum name=httpd enablerepo=testing state=installed
notes: []
# informational: requirements for nodes
requirements: [ yum, rpm ]
author: Seth Vidal
'''
def_qf = "%{name}-%{version}-%{release}.%{arch}"
repoquery='/usr/bin/repoquery'
if not os.path.exists(repoquery):
repoquery = None
yumbin='/usr/bin/yum'
def yum_base(conf_file=None, cachedir=False):
my = yum.YumBase()
my.preconf.debuglevel=0
my.preconf.errorlevel=0
if conf_file and os.path.exists(conf_file):
my.preconf.fn = conf_file
if cachedir or os.geteuid() != 0:
if hasattr(my, 'setCacheDir'):
my.setCacheDir()
else:
cachedir = yum.misc.getCacheDir()
my.repos.setCacheDir(cachedir)
my.conf.cache = 0
return my
def po_to_nevra(po):
if hasattr(po, 'ui_nevra'):
return po.ui_nevra
else:
return '%s-%s-%s.%s' % (po.name, po.version, po.release, po.arch)
def is_installed(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=[], dis_repos=[]):
if not repoq:
pkgs = []
try:
my = yum_base(conf_file)
for rid in en_repos:
my.repos.enableRepo(rid)
for rid in dis_repos:
my.repos.disableRepo(rid)
e,m,u = my.rpmdb.matchPackageNames([pkgspec])
pkgs = e + m
if not pkgs:
pkgs.extend(my.returnInstalledPackagesByDep(pkgspec))
except Exception, e:
module.fail_json(msg="Failure talking to yum: %s" % e)
return [ po_to_nevra(p) for p in pkgs ]
else:
cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, pkgspec]
rc,out,err = module.run_command(cmd)
cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, "--whatprovides", pkgspec]
rc2,out2,err2 = module.run_command(cmd)
if rc == 0 and rc2 == 0:
out += out2
return [ p for p in out.split('\n') if p.strip() ]
else:
module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2))
return []
def is_available(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=[], dis_repos=[]):
if not repoq:
pkgs = []
try:
my = yum_base(conf_file)
for rid in en_repos:
my.repos.enableRepo(rid)
for rid in dis_repos:
my.repos.disableRepo(rid)
e,m,u = my.pkgSack.matchPackageNames([pkgspec])
pkgs = e + m
if not pkgs:
pkgs.extend(my.returnPackagesByDep(pkgspec))
except Exception, e:
module.fail_json(msg="Failure talking to yum: %s" % e)
return [ po_to_nevra(p) for p in pkgs ]
else:
myrepoq = list(repoq)
for repoid in en_repos:
r_cmd = ['--enablerepo', repoid]
myrepoq.extend(r_cmd)
for repoid in dis_repos:
r_cmd = ['--disablerepo', repoid]
myrepoq.extend(r_cmd)
cmd = myrepoq + ["--qf", qf, pkgspec]
rc,out,err = module.run_command(cmd)
if rc == 0:
return [ p for p in out.split('\n') if p.strip() ]
else:
module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err))
return []
def is_update(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=[], dis_repos=[]):
if not repoq:
retpkgs = []
pkgs = []
updates = []
try:
my = yum_base(conf_file)
for rid in en_repos:
my.repos.enableRepo(rid)
for rid in dis_repos:
my.repos.disableRepo(rid)
pkgs = my.returnPackagesByDep(pkgspec) + my.returnInstalledPackagesByDep(pkgspec)
if not pkgs:
e,m,u = my.pkgSack.matchPackageNames([pkgspec])
pkgs = e + m
updates = my.doPackageLists(pkgnarrow='updates').updates
except Exception, e:
module.fail_json(msg="Failure talking to yum: %s" % e)
for pkg in pkgs:
if pkg in updates:
retpkgs.append(pkg)
return set([ po_to_nevra(p) for p in retpkgs ])
else:
myrepoq = list(repoq)
for repoid in en_repos:
r_cmd = ['--enablerepo', repoid]
myrepoq.extend(r_cmd)
for repoid in dis_repos:
r_cmd = ['--disablerepo', repoid]
myrepoq.extend(r_cmd)
cmd = myrepoq + ["--pkgnarrow=updates", "--qf", qf, pkgspec]
rc,out,err = module.run_command(cmd)
if rc == 0:
return set([ p for p in out.split('\n') if p.strip() ])
else:
module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err))
return []
def what_provides(module, repoq, req_spec, conf_file, qf=def_qf, en_repos=[], dis_repos=[]):
if not repoq:
pkgs = []
try:
my = yum_base(conf_file)
for rid in en_repos:
my.repos.enableRepo(rid)
for rid in dis_repos:
my.repos.disableRepo(rid)
pkgs = my.returnPackagesByDep(req_spec) + my.returnInstalledPackagesByDep(req_spec)
if not pkgs:
e,m,u = my.pkgSack.matchPackageNames([req_spec])
pkgs.extend(e)
pkgs.extend(m)
e,m,u = my.rpmdb.matchPackageNames([req_spec])
pkgs.extend(e)
pkgs.extend(m)
except Exception, e:
module.fail_json(msg="Failure talking to yum: %s" % e)
return set([ po_to_nevra(p) for p in pkgs ])
else:
myrepoq = list(repoq)
for repoid in en_repos:
r_cmd = ['--enablerepo', repoid]
myrepoq.extend(r_cmd)
for repoid in dis_repos:
r_cmd = ['--disablerepo', repoid]
myrepoq.extend(r_cmd)
cmd = myrepoq + ["--qf", qf, "--whatprovides", req_spec]
rc,out,err = module.run_command(cmd)
cmd = myrepoq + ["--qf", qf, req_spec]
rc2,out2,err2 = module.run_command(cmd)
if rc == 0 and rc2 == 0:
out += out2
pkgs = set([ p for p in out.split('\n') if p.strip() ])
if not pkgs:
pkgs = is_installed(module, repoq, req_spec, conf_file, qf=qf)
return pkgs
else:
module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2))
return []
def local_nvra(module, path):
"""return nvra of a local rpm passed in"""
cmd = ['/bin/rpm', '-qp' ,'--qf',
'%{name}-%{version}-%{release}.%{arch}\n', path ]
rc, out, err = module.run_command(cmd)
if rc != 0:
return None
nvra = out.split('\n')[0]
return nvra
def pkg_to_dict(pkgstr):
if pkgstr.strip():
n,e,v,r,a,repo = pkgstr.split('|')
else:
return {'error_parsing': pkgstr}
d = {
'name':n,
'arch':a,
'epoch':e,
'release':r,
'version':v,
'repo':repo,
'nevra': '%s:%s-%s-%s.%s' % (e,n,v,r,a)
}
if repo == 'installed':
d['yumstate'] = 'installed'
else:
d['yumstate'] = 'available'
return d
def repolist(module, repoq, qf="%{repoid}"):
cmd = repoq + ["--qf", qf, "-a"]
rc,out,err = module.run_command(cmd)
ret = []
if rc == 0:
ret = set([ p for p in out.split('\n') if p.strip() ])
return ret
def list_stuff(module, conf_file, stuff):
qf = "%{name}|%{epoch}|%{version}|%{release}|%{arch}|%{repoid}"
repoq = [repoquery, '--show-duplicates', '--plugins', '--quiet', '-q']
if conf_file and os.path.exists(conf_file):
repoq += ['-c', conf_file]
if stuff == 'installed':
return [ pkg_to_dict(p) for p in is_installed(module, repoq, '-a', conf_file, qf=qf) if p.strip() ]
elif stuff == 'updates':
return [ pkg_to_dict(p) for p in is_update(module, repoq, '-a', conf_file, qf=qf) if p.strip() ]
elif stuff == 'available':
return [ pkg_to_dict(p) for p in is_available(module, repoq, '-a', conf_file, qf=qf) if p.strip() ]
elif stuff == 'repos':
return [ dict(repoid=name, state='enabled') for name in repolist(module, repoq) if name.strip() ]
else:
return [ pkg_to_dict(p) for p in is_installed(module, repoq, stuff, conf_file, qf=qf) + is_available(module, repoq, stuff, conf_file, qf=qf) if p.strip() ]
def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
res = {}
res['results'] = []
res['msg'] = ''
res['rc'] = 0
res['changed'] = False
for spec in items:
pkg = None
# check if pkgspec is installed (if possible for idempotence)
# localpkg
if spec.endswith('.rpm') and '://' not in spec:
# get the pkg name-v-r.arch
if not os.path.exists(spec):
res['msg'] += "No Package file matching '%s' found on system" % spec
module.fail_json(**res)
nvra = local_nvra(module, spec)
# look for them in the rpmdb
if is_installed(module, repoq, nvra, conf_file, en_repos=en_repos, dis_repos=dis_repos):
# if they are there, skip it
continue
pkg = spec
# URL
elif '://' in spec:
pkg = spec
#groups :(
elif spec.startswith('@'):
# complete wild ass guess b/c it's a group
pkg = spec
# range requires or file-requires or pkgname :(
else:
# look up what pkgs provide this
pkglist = what_provides(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos)
if not pkglist:
res['msg'] += "No Package matching '%s' found available, installed or updated" % spec
module.fail_json(**res)
# if any of them are installed
# then nothing to do
found = False
for this in pkglist:
if is_installed(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos):
found = True
res['results'].append('%s providing %s is already installed' % (this, spec))
break
# if the version of the pkg you have installed is not in ANY repo, but there are
# other versions in the repos (both higher and lower) then the previous checks won't work.
# so we check one more time. This really only works for pkgname - not for file provides or virt provides
# but virt provides should be all caught in what_provides on its own.
# highly irritating
if not found:
if is_installed(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos):
found = True
res['results'].append('package providing %s is already installed' % (spec))
if found:
continue
# if not - then pass in the spec as what to install
# we could get here if nothing provides it but that's not
# the error we're catching here
pkg = spec
cmd = yum_basecmd + ['install', pkg]
if module.check_mode:
module.exit_json(changed=True)
changed = True
rc, out, err = module.run_command(cmd)
if rc != 0 and 'Nothing to do' in err:
# avoid failing in the 'Nothing To Do' case
# this may happen with an URL spec
rc = 0
err = ''
out = '%s: Nothing to do' % spec
changed = False
res['rc'] += rc
res['results'].append(out)
res['msg'] += err
# 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
# accumulate any changes
res['changed'] |= changed
module.exit_json(**res)
def remove(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
res = {}
res['results'] = []
res['msg'] = ''
res['changed'] = False
res['rc'] = 0
for pkg in items:
is_group = False
# group remove - this is doom on a stick
if pkg.startswith('@'):
is_group = True
else:
if not is_installed(module, repoq, pkg, conf_file, en_repos=en_repos, dis_repos=dis_repos):
res['results'].append('%s is not installed' % pkg)
continue
# run an actual yum transaction
cmd = yum_basecmd + ["remove", pkg]
if module.check_mode:
module.exit_json(changed=True)
rc, out, err = module.run_command(cmd)
res['rc'] += rc
res['results'].append(out)
res['msg'] += err
# compile the results into one batch. If anything is changed
# then mark changed
# at the end - if we've end up failed then fail out of the rest
# of the process
# at this point we should check to see if the pkg is no longer present
if not is_group: # we can't sensibly check for a group being uninstalled reliably
# look to see if the pkg shows up from is_installed. If it doesn't
if not is_installed(module, repoq, pkg, conf_file, en_repos=en_repos, dis_repos=dis_repos):
res['changed'] = True
else:
module.fail_json(**res)
if rc != 0:
module.fail_json(**res)
module.exit_json(**res)
def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
res = {}
res['results'] = []
res['msg'] = ''
res['changed'] = False
res['rc'] = 0
for spec in items:
pkg = None
basecmd = 'update'
# groups, again
if spec.startswith('@'):
pkg = spec
# 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'
pkglist = what_provides(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos)
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):
nothing_to_do = False
break
if basecmd == 'update' and is_update(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=en_repos):
nothing_to_do = False
break
if nothing_to_do:
res['results'].append("All packages providing %s are up to date" % spec)
continue
pkg = spec
cmd = yum_basecmd + [basecmd, pkg]
if module.check_mode:
return module.exit_json(changed=True)
rc, out, err = module.run_command(cmd)
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:
res['changed'] = True
module.exit_json(**res)
def ensure(module, state, pkgspec, conf_file, enablerepo, disablerepo,
disable_gpg_check):
# take multiple args comma separated
items = pkgspec.split(',')
yum_basecmd = [yumbin, '-d', '1', '-y']
if not repoquery:
repoq = None
else:
repoq = [repoquery, '--show-duplicates', '--plugins', '--quiet', '-q']
if conf_file and os.path.exists(conf_file):
yum_basecmd += ['-c', conf_file]
if repoq:
repoq += ['-c', conf_file]
dis_repos =[]
en_repos = []
if disablerepo:
dis_repos = disablerepo.split(',')
if enablerepo:
en_repos = enablerepo.split(',')
for repoid in en_repos:
r_cmd = ['--enablerepo', repoid]
yum_basecmd.extend(r_cmd)
for repoid in dis_repos:
r_cmd = ['--disablerepo', repoid]
yum_basecmd.extend(r_cmd)
if state in ['installed', 'present', 'latest']:
my = yum_base(conf_file)
try:
for r in dis_repos:
my.repos.disableRepo(r)
for r in en_repos:
try:
my.repos.enableRepo(r)
rid = my.repos.getRepo(r)
a = rid.repoXML.repoid
except yum.Errors.YumBaseError, e:
module.fail_json(msg="Error setting/accessing repo %s: %s" % (r, e))
except yum.Errors.YumBaseError, e:
module.fail_json(msg="Error accessing repos: %s" % e)
if state in ['installed', 'present']:
if disable_gpg_check:
yum_basecmd.append('--nogpgcheck')
install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
elif state in ['removed', 'absent']:
remove(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
elif state == 'latest':
if disable_gpg_check:
yum_basecmd.append('--nogpgcheck')
latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
# should be caught by AnsibleModule argument_spec
return dict(changed=False, failed=True, results='', errors='unexpected state')
def main():
# state=installed name=pkgspec
# state=removed name=pkgspec
# state=latest name=pkgspec
#
# informational commands:
# list=installed
# list=updates
# list=available
# list=repos
# list=pkgspec
module = AnsibleModule(
argument_spec = dict(
name=dict(aliases=['pkg']),
# removed==absent, installed==present, these are accepted as aliases
state=dict(default='installed', choices=['absent','present','installed','removed','latest']),
enablerepo=dict(),
disablerepo=dict(),
list=dict(),
conf_file=dict(default=None),
disable_gpg_check=dict(required=False, default="no",
choices=BOOLEANS, type='bool'),
),
required_one_of = [['name','list']],
mutually_exclusive = [['name','list']],
supports_check_mode = True
)
params = module.params
if params['list']:
if not repoquery:
module.fail_json(msg="repoquery is required to use list= with this module. Please install the yum-utils package.")
results = dict(results=list_stuff(module, params['conf_file'], params['list']))
module.exit_json(**results)
else:
pkg = params['name']
state = params['state']
enablerepo = params.get('enablerepo', '')
disablerepo = params.get('disablerepo', '')
disable_gpg_check = params['disable_gpg_check']
res = ensure(module, state, pkg, params['conf_file'], enablerepo,
disablerepo, disable_gpg_check)
module.fail_json(msg="we should never get here unless this all failed", **res)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()