mirror of https://github.com/ansible/ansible.git
Core/extras split WIP.
parent
7e94a05dd6
commit
8cdea3edf0
@ -1,164 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Dimitrios Tydeas Mengidis <tydeas.dr@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: composer
|
||||
author: Dimitrios Tydeas Mengidis
|
||||
short_description: Dependency Manager for PHP
|
||||
version_added: "1.6"
|
||||
description:
|
||||
- Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you
|
||||
options:
|
||||
command:
|
||||
version_added: "1.8"
|
||||
description:
|
||||
- Composer command like "install", "update" and so on
|
||||
required: false
|
||||
default: install
|
||||
working_dir:
|
||||
description:
|
||||
- Directory of your project ( see --working-dir )
|
||||
required: true
|
||||
default: null
|
||||
aliases: [ "working-dir" ]
|
||||
prefer_source:
|
||||
description:
|
||||
- Forces installation from package sources when possible ( see --prefer-source )
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "prefer-source" ]
|
||||
prefer_dist:
|
||||
description:
|
||||
- Forces installation from package dist even for de versions ( see --prefer-dist )
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "prefer-dist" ]
|
||||
no_dev:
|
||||
description:
|
||||
- Disables installation of require-dev packages ( see --no-dev )
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "no-dev" ]
|
||||
no_scripts:
|
||||
description:
|
||||
- Skips the execution of all scripts defined in composer.json ( see --no-scripts )
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "no-scripts" ]
|
||||
no_plugins:
|
||||
description:
|
||||
- Disables all plugins ( see --no-plugins )
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "no-plugins" ]
|
||||
optimize_autoloader:
|
||||
description:
|
||||
- Optimize autoloader during autoloader dump ( see --optimize-autoloader ). Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default.
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: [ "optimize-autoloader" ]
|
||||
requirements:
|
||||
- php
|
||||
- composer installed in bin path (recommended /usr/local/bin)
|
||||
notes:
|
||||
- Default options that are always appended in each execution are --no-ansi, --no-progress, and --no-interaction
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Downloads and installs all the libs and dependencies outlined in the /path/to/project/composer.lock
|
||||
- composer: command=install working_dir=/path/to/project
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
def parse_out(string):
|
||||
return re.sub("\s+", " ", string).strip()
|
||||
|
||||
def has_changed(string):
|
||||
return (re.match("Nothing to install or update", string) != None)
|
||||
|
||||
def composer_install(module, command, options):
|
||||
php_path = module.get_bin_path("php", True, ["/usr/local/bin"])
|
||||
composer_path = module.get_bin_path("composer", True, ["/usr/local/bin"])
|
||||
cmd = "%s %s %s %s" % (php_path, composer_path, command, " ".join(options))
|
||||
|
||||
return module.run_command(cmd)
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
command = dict(default="install", type="str", required=False),
|
||||
working_dir = dict(aliases=["working-dir"], required=True),
|
||||
prefer_source = dict(default="no", type="bool", aliases=["prefer-source"]),
|
||||
prefer_dist = dict(default="no", type="bool", aliases=["prefer-dist"]),
|
||||
no_dev = dict(default="yes", type="bool", aliases=["no-dev"]),
|
||||
no_scripts = dict(default="no", type="bool", aliases=["no-scripts"]),
|
||||
no_plugins = dict(default="no", type="bool", aliases=["no-plugins"]),
|
||||
optimize_autoloader = dict(default="yes", type="bool", aliases=["optimize-autoloader"]),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
module.params["working_dir"] = os.path.abspath(module.params["working_dir"])
|
||||
|
||||
options = set([])
|
||||
# Default options
|
||||
options.add("--no-ansi")
|
||||
options.add("--no-progress")
|
||||
options.add("--no-interaction")
|
||||
|
||||
if module.check_mode:
|
||||
options.add("--dry-run")
|
||||
|
||||
# Get composer command with fallback to default
|
||||
command = module.params['command']
|
||||
del module.params['command'];
|
||||
|
||||
# Prepare options
|
||||
for i in module.params:
|
||||
opt = "--%s" % i.replace("_","-")
|
||||
p = module.params[i]
|
||||
if isinstance(p, (bool)) and p:
|
||||
options.add(opt)
|
||||
elif isinstance(p, (str)):
|
||||
options.add("%s=%s" % (opt, p))
|
||||
|
||||
rc, out, err = composer_install(module, command, options)
|
||||
|
||||
if rc != 0:
|
||||
output = parse_out(err)
|
||||
module.fail_json(msg=output)
|
||||
else:
|
||||
output = parse_out(out)
|
||||
module.exit_json(changed=has_changed(output), msg=output)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,145 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2012, Franck Cuny <franck@lumberjaph.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: cpanm
|
||||
short_description: Manages Perl library dependencies.
|
||||
description:
|
||||
- Manage Perl library dependencies.
|
||||
version_added: "1.6"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the Perl library to install
|
||||
required: false
|
||||
default: null
|
||||
aliases: ["pkg"]
|
||||
from_path:
|
||||
description:
|
||||
- The local directory from where to install
|
||||
required: false
|
||||
default: null
|
||||
notest:
|
||||
description:
|
||||
- Do not run unit tests
|
||||
required: false
|
||||
default: false
|
||||
locallib:
|
||||
description:
|
||||
- Specify the install base to install modules
|
||||
required: false
|
||||
default: false
|
||||
mirror:
|
||||
description:
|
||||
- Specifies the base URL for the CPAN mirror to use
|
||||
required: false
|
||||
default: false
|
||||
examples:
|
||||
- code: "cpanm: name=Dancer"
|
||||
description: Install I(Dancer) perl package.
|
||||
- code: "cpanm: name=Dancer locallib=/srv/webapps/my_app/extlib"
|
||||
description: "Install I(Dancer) (U(http://perldancer.org/)) into the specified I(locallib)"
|
||||
- code: "cpanm: from_path=/srv/webapps/my_app/src/"
|
||||
description: Install perl dependencies from local directory.
|
||||
- code: "cpanm: name=Dancer notest=True locallib=/srv/webapps/my_app/extlib"
|
||||
description: Install I(Dancer) perl package without running the unit tests in indicated I(locallib).
|
||||
- code: "cpanm: name=Dancer mirror=http://cpan.cpantesters.org/"
|
||||
description: Install I(Dancer) perl package from a specific mirror
|
||||
notes:
|
||||
- Please note that U(http://search.cpan.org/dist/App-cpanminus/bin/cpanm, cpanm) must be installed on the remote host.
|
||||
author: Franck Cuny
|
||||
'''
|
||||
|
||||
def _is_package_installed(module, name, locallib, cpanm):
|
||||
cmd = ""
|
||||
if locallib:
|
||||
os.environ["PERL5LIB"] = "%s/lib/perl5" % locallib
|
||||
cmd = "%s perl -M%s -e '1'" % (cmd, name)
|
||||
res, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
if res == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _build_cmd_line(name, from_path, notest, locallib, mirror, cpanm):
|
||||
# this code should use "%s" like everything else and just return early but not fixing all of it now.
|
||||
# don't copy stuff like this
|
||||
if from_path:
|
||||
cmd = "{cpanm} {path}".format(cpanm=cpanm, path=from_path)
|
||||
else:
|
||||
cmd = "{cpanm} {name}".format(cpanm=cpanm, name=name)
|
||||
|
||||
if notest is True:
|
||||
cmd = "{cmd} -n".format(cmd=cmd)
|
||||
|
||||
if locallib is not None:
|
||||
cmd = "{cmd} -l {locallib}".format(cmd=cmd, locallib=locallib)
|
||||
|
||||
if mirror is not None:
|
||||
cmd = "{cmd} --mirror {mirror}".format(cmd=cmd, mirror=mirror)
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def main():
|
||||
arg_spec = dict(
|
||||
name=dict(default=None, required=False, aliases=['pkg']),
|
||||
from_path=dict(default=None, required=False),
|
||||
notest=dict(default=False, type='bool'),
|
||||
locallib=dict(default=None, required=False),
|
||||
mirror=dict(default=None, required=False)
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=arg_spec,
|
||||
required_one_of=[['name', 'from_path']],
|
||||
)
|
||||
|
||||
cpanm = module.get_bin_path('cpanm', True)
|
||||
name = module.params['name']
|
||||
from_path = module.params['from_path']
|
||||
notest = module.boolean(module.params.get('notest', False))
|
||||
locallib = module.params['locallib']
|
||||
mirror = module.params['mirror']
|
||||
|
||||
changed = False
|
||||
|
||||
installed = _is_package_installed(module, name, locallib, cpanm)
|
||||
|
||||
if not installed:
|
||||
out_cpanm = err_cpanm = ''
|
||||
cmd = _build_cmd_line(name, from_path, notest, locallib, mirror, cpanm)
|
||||
|
||||
rc_cpanm, out_cpanm, err_cpanm = module.run_command(cmd, check_rc=False)
|
||||
|
||||
if rc_cpanm != 0:
|
||||
module.fail_json(msg=err_cpanm, cmd=cmd)
|
||||
|
||||
if err_cpanm and 'is up to date' not in err_cpanm:
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed, binary=cpanm, name=name)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,835 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Andrew Dunham <andrew@du.nham.ca>
|
||||
# (c) 2013, Daniel Jaouen <dcj24@cornell.edu>
|
||||
#
|
||||
# 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 and Daniel Jaouen
|
||||
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: [ 'head', 'latest', 'present', 'absent', 'linked', 'unlinked' ]
|
||||
required: false
|
||||
default: present
|
||||
update_homebrew:
|
||||
description:
|
||||
- update homebrew itself first
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
upgrade_all:
|
||||
description:
|
||||
- upgrade all homebrew packages
|
||||
required: false
|
||||
default: no
|
||||
choices: [ "yes", "no" ]
|
||||
install_options:
|
||||
description:
|
||||
- options flags to install a package
|
||||
required: false
|
||||
default: null
|
||||
version_added: "1.4"
|
||||
notes: []
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
- homebrew: name=foo state=present
|
||||
- homebrew: name=foo state=present update_homebrew=yes
|
||||
- homebrew: name=foo state=latest update_homebrew=yes
|
||||
- homebrew: update_homebrew=yes upgrade_all=yes
|
||||
- homebrew: name=foo state=head
|
||||
- homebrew: name=foo state=linked
|
||||
- homebrew: name=foo state=absent
|
||||
- homebrew: name=foo,bar state=absent
|
||||
- homebrew: name=foo state=present install_options=with-baz,enable-debug
|
||||
'''
|
||||
|
||||
import os.path
|
||||
import re
|
||||
|
||||
|
||||
# exceptions -------------------------------------------------------------- {{{
|
||||
class HomebrewException(Exception):
|
||||
pass
|
||||
# /exceptions ------------------------------------------------------------- }}}
|
||||
|
||||
|
||||
# utils ------------------------------------------------------------------- {{{
|
||||
def _create_regex_group(s):
|
||||
lines = (line.strip() for line in s.split('\n') if line.strip())
|
||||
chars = filter(None, (line.split('#')[0].strip() for line in lines))
|
||||
group = r'[^' + r''.join(chars) + r']'
|
||||
return re.compile(group)
|
||||
# /utils ------------------------------------------------------------------ }}}
|
||||
|
||||
|
||||
class Homebrew(object):
|
||||
'''A class to manage Homebrew packages.'''
|
||||
|
||||
# class regexes ------------------------------------------------ {{{
|
||||
VALID_PATH_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
\s # spaces
|
||||
: # colons
|
||||
{sep} # the OS-specific path separator
|
||||
. # dots
|
||||
- # dashes
|
||||
'''.format(sep=os.path.sep)
|
||||
|
||||
VALID_BREW_PATH_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
\s # spaces
|
||||
{sep} # the OS-specific path separator
|
||||
. # dots
|
||||
- # dashes
|
||||
'''.format(sep=os.path.sep)
|
||||
|
||||
VALID_PACKAGE_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
. # dots
|
||||
\+ # plusses
|
||||
- # dashes
|
||||
'''
|
||||
|
||||
INVALID_PATH_REGEX = _create_regex_group(VALID_PATH_CHARS)
|
||||
INVALID_BREW_PATH_REGEX = _create_regex_group(VALID_BREW_PATH_CHARS)
|
||||
INVALID_PACKAGE_REGEX = _create_regex_group(VALID_PACKAGE_CHARS)
|
||||
# /class regexes ----------------------------------------------- }}}
|
||||
|
||||
# class validations -------------------------------------------- {{{
|
||||
@classmethod
|
||||
def valid_path(cls, path):
|
||||
'''
|
||||
`path` must be one of:
|
||||
- list of paths
|
||||
- a string containing only:
|
||||
- alphanumeric characters
|
||||
- dashes
|
||||
- dots
|
||||
- spaces
|
||||
- colons
|
||||
- os.path.sep
|
||||
'''
|
||||
|
||||
if isinstance(path, basestring):
|
||||
return not cls.INVALID_PATH_REGEX.search(path)
|
||||
|
||||
try:
|
||||
iter(path)
|
||||
except TypeError:
|
||||
return False
|
||||
else:
|
||||
paths = path
|
||||
return all(cls.valid_brew_path(path_) for path_ in paths)
|
||||
|
||||
@classmethod
|
||||
def valid_brew_path(cls, brew_path):
|
||||
'''
|
||||
`brew_path` must be one of:
|
||||
- None
|
||||
- a string containing only:
|
||||
- alphanumeric characters
|
||||
- dashes
|
||||
- dots
|
||||
- spaces
|
||||
- os.path.sep
|
||||
'''
|
||||
|
||||
if brew_path is None:
|
||||
return True
|
||||
|
||||
return (
|
||||
isinstance(brew_path, basestring)
|
||||
and not cls.INVALID_BREW_PATH_REGEX.search(brew_path)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_package(cls, package):
|
||||
'''A valid package is either None or alphanumeric.'''
|
||||
|
||||
if package is None:
|
||||
return True
|
||||
|
||||
return (
|
||||
isinstance(package, basestring)
|
||||
and not cls.INVALID_PACKAGE_REGEX.search(package)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_state(cls, state):
|
||||
'''
|
||||
A valid state is one of:
|
||||
- None
|
||||
- installed
|
||||
- upgraded
|
||||
- head
|
||||
- linked
|
||||
- unlinked
|
||||
- absent
|
||||
'''
|
||||
|
||||
if state is None:
|
||||
return True
|
||||
else:
|
||||
return (
|
||||
isinstance(state, basestring)
|
||||
and state.lower() in (
|
||||
'installed',
|
||||
'upgraded',
|
||||
'head',
|
||||
'linked',
|
||||
'unlinked',
|
||||
'absent',
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_module(cls, module):
|
||||
'''A valid module is an instance of AnsibleModule.'''
|
||||
|
||||
return isinstance(module, AnsibleModule)
|
||||
|
||||
# /class validations ------------------------------------------- }}}
|
||||
|
||||
# class properties --------------------------------------------- {{{
|
||||
@property
|
||||
def module(self):
|
||||
return self._module
|
||||
|
||||
@module.setter
|
||||
def module(self, module):
|
||||
if not self.valid_module(module):
|
||||
self._module = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid module: {0}.'.format(module)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
else:
|
||||
self._module = module
|
||||
return module
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
if not self.valid_path(path):
|
||||
self._path = []
|
||||
self.failed = True
|
||||
self.message = 'Invalid path: {0}.'.format(path)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
else:
|
||||
if isinstance(path, basestring):
|
||||
self._path = path.split(':')
|
||||
else:
|
||||
self._path = path
|
||||
|
||||
return path
|
||||
|
||||
@property
|
||||
def brew_path(self):
|
||||
return self._brew_path
|
||||
|
||||
@brew_path.setter
|
||||
def brew_path(self, brew_path):
|
||||
if not self.valid_brew_path(brew_path):
|
||||
self._brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid brew_path: {0}.'.format(brew_path)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
else:
|
||||
self._brew_path = brew_path
|
||||
return brew_path
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self._params
|
||||
|
||||
@params.setter
|
||||
def params(self, params):
|
||||
self._params = self.module.params
|
||||
return self._params
|
||||
|
||||
@property
|
||||
def current_package(self):
|
||||
return self._current_package
|
||||
|
||||
@current_package.setter
|
||||
def current_package(self, package):
|
||||
if not self.valid_package(package):
|
||||
self._current_package = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
else:
|
||||
self._current_package = package
|
||||
return package
|
||||
# /class properties -------------------------------------------- }}}
|
||||
|
||||
def __init__(self, module, path=None, packages=None, state=None,
|
||||
update_homebrew=False, upgrade_all=False,
|
||||
install_options=None):
|
||||
if not install_options:
|
||||
install_options = list()
|
||||
self._setup_status_vars()
|
||||
self._setup_instance_vars(module=module, path=path, packages=packages,
|
||||
state=state, update_homebrew=update_homebrew,
|
||||
upgrade_all=upgrade_all,
|
||||
install_options=install_options, )
|
||||
|
||||
self._prep()
|
||||
|
||||
# prep --------------------------------------------------------- {{{
|
||||
def _setup_status_vars(self):
|
||||
self.failed = False
|
||||
self.changed = False
|
||||
self.changed_count = 0
|
||||
self.unchanged_count = 0
|
||||
self.message = ''
|
||||
|
||||
def _setup_instance_vars(self, **kwargs):
|
||||
for key, val in kwargs.iteritems():
|
||||
setattr(self, key, val)
|
||||
|
||||
def _prep(self):
|
||||
self._prep_path()
|
||||
self._prep_brew_path()
|
||||
|
||||
def _prep_path(self):
|
||||
if not self.path:
|
||||
self.path = ['/usr/local/bin']
|
||||
|
||||
def _prep_brew_path(self):
|
||||
if not self.module:
|
||||
self.brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'AnsibleModule not set.'
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
self.brew_path = self.module.get_bin_path(
|
||||
'brew',
|
||||
required=True,
|
||||
opt_dirs=self.path,
|
||||
)
|
||||
if not self.brew_path:
|
||||
self.brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'Unable to locate homebrew executable.'
|
||||
raise HomebrewException('Unable to locate homebrew executable.')
|
||||
|
||||
return self.brew_path
|
||||
|
||||
def _status(self):
|
||||
return (self.failed, self.changed, self.message)
|
||||
# /prep -------------------------------------------------------- }}}
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self._run()
|
||||
except HomebrewException:
|
||||
pass
|
||||
|
||||
if not self.failed and (self.changed_count + self.unchanged_count > 1):
|
||||
self.message = "Changed: %d, Unchanged: %d" % (
|
||||
self.changed_count,
|
||||
self.unchanged_count,
|
||||
)
|
||||
(failed, changed, message) = self._status()
|
||||
|
||||
return (failed, changed, message)
|
||||
|
||||
# checks ------------------------------------------------------- {{{
|
||||
def _current_package_is_installed(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
cmd = [
|
||||
"{brew_path}".format(brew_path=self.brew_path),
|
||||
"info",
|
||||
self.current_package,
|
||||
]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
for line in out.split('\n'):
|
||||
if (
|
||||
re.search(r'Built from source', line)
|
||||
or re.search(r'Poured from bottle', line)
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _outdated_packages(self):
|
||||
rc, out, err = self.module.run_command([
|
||||
self.brew_path,
|
||||
'outdated',
|
||||
])
|
||||
return [line.split(' ')[0].strip() for line in out.split('\n') if line]
|
||||
|
||||
def _current_package_is_outdated(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
return False
|
||||
|
||||
return self.current_package in self._outdated_packages()
|
||||
|
||||
def _current_package_is_installed_from_head(self):
|
||||
if not Homebrew.valid_package(self.current_package):
|
||||
return False
|
||||
elif not self._current_package_is_installed():
|
||||
return False
|
||||
|
||||
rc, out, err = self.module.run_command([
|
||||
self.brew_path,
|
||||
'info',
|
||||
self.current_package,
|
||||
])
|
||||
|
||||
try:
|
||||
version_info = [line for line in out.split('\n') if line][0]
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
return version_info.split(' ')[-1] == 'HEAD'
|
||||
# /checks ------------------------------------------------------ }}}
|
||||
|
||||
# commands ----------------------------------------------------- {{{
|
||||
def _run(self):
|
||||
if self.update_homebrew:
|
||||
self._update_homebrew()
|
||||
|
||||
if self.upgrade_all:
|
||||
self._upgrade_all()
|
||||
|
||||
if self.packages:
|
||||
if self.state == 'installed':
|
||||
return self._install_packages()
|
||||
elif self.state == 'upgraded':
|
||||
return self._upgrade_packages()
|
||||
elif self.state == 'head':
|
||||
return self._install_packages()
|
||||
elif self.state == 'linked':
|
||||
return self._link_packages()
|
||||
elif self.state == 'unlinked':
|
||||
return self._unlink_packages()
|
||||
elif self.state == 'absent':
|
||||
return self._uninstall_packages()
|
||||
|
||||
# updated -------------------------------- {{{
|
||||
def _update_homebrew(self):
|
||||
rc, out, err = self.module.run_command([
|
||||
self.brew_path,
|
||||
'update',
|
||||
])
|
||||
if rc == 0:
|
||||
if out and isinstance(out, basestring):
|
||||
already_updated = any(
|
||||
re.search(r'Already up-to-date.', s.strip(), re.IGNORECASE)
|
||||
for s in out.split('\n')
|
||||
if s
|
||||
)
|
||||
if not already_updated:
|
||||
self.changed = True
|
||||
self.message = 'Homebrew updated successfully.'
|
||||
else:
|
||||
self.message = 'Homebrew already up-to-date.'
|
||||
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
# /updated ------------------------------- }}}
|
||||
|
||||
# _upgrade_all --------------------------- {{{
|
||||
def _upgrade_all(self):
|
||||
rc, out, err = self.module.run_command([
|
||||
self.brew_path,
|
||||
'upgrade',
|
||||
])
|
||||
if rc == 0:
|
||||
if not out:
|
||||
self.message = 'Homebrew packages already upgraded.'
|
||||
|
||||
else:
|
||||
self.changed = True
|
||||
self.message = 'Homebrew upgraded.'
|
||||
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
# /_upgrade_all -------------------------- }}}
|
||||
|
||||
# installed ------------------------------ {{{
|
||||
def _install_current_package(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if self._current_package_is_installed():
|
||||
self.unchanged_count += 1
|
||||
self.message = 'Package already installed: {0}'.format(
|
||||
self.current_package,
|
||||
)
|
||||
return True
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Package would be installed: {0}'.format(
|
||||
self.current_package
|
||||
)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if self.state == 'head':
|
||||
head = '--HEAD'
|
||||
else:
|
||||
head = None
|
||||
|
||||
opts = (
|
||||
[self.brew_path, 'install']
|
||||
+ self.install_options
|
||||
+ [self.current_package, head]
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if self._current_package_is_installed():
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Package installed: {0}'.format(self.current_package)
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _install_packages(self):
|
||||
for package in self.packages:
|
||||
self.current_package = package
|
||||
self._install_current_package()
|
||||
|
||||
return True
|
||||
# /installed ----------------------------- }}}
|
||||
|
||||
# upgraded ------------------------------- {{{
|
||||
def _upgrade_current_package(self):
|
||||
command = 'upgrade'
|
||||
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if not self._current_package_is_installed():
|
||||
command = 'install'
|
||||
|
||||
if self._current_package_is_installed() and not self._current_package_is_outdated():
|
||||
self.message = 'Package is already upgraded: {0}'.format(
|
||||
self.current_package,
|
||||
)
|
||||
self.unchanged_count += 1
|
||||
return True
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Package would be upgraded: {0}'.format(
|
||||
self.current_package
|
||||
)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
opts = (
|
||||
[self.brew_path, command]
|
||||
+ self.install_options
|
||||
+ [self.current_package]
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if self._current_package_is_installed() and not self._current_package_is_outdated():
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Package upgraded: {0}'.format(self.current_package)
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _upgrade_all_packages(self):
|
||||
opts = (
|
||||
[self.brew_path, 'upgrade']
|
||||
+ self.install_options
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
self.changed = True
|
||||
self.message = 'All packages upgraded.'
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _upgrade_packages(self):
|
||||
if not self.packages:
|
||||
self._upgrade_all_packages()
|
||||
else:
|
||||
for package in self.packages:
|
||||
self.current_package = package
|
||||
self._upgrade_current_package()
|
||||
return True
|
||||
# /upgraded ------------------------------ }}}
|
||||
|
||||
# uninstalled ---------------------------- {{{
|
||||
def _uninstall_current_package(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if not self._current_package_is_installed():
|
||||
self.unchanged_count += 1
|
||||
self.message = 'Package already uninstalled: {0}'.format(
|
||||
self.current_package,
|
||||
)
|
||||
return True
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Package would be uninstalled: {0}'.format(
|
||||
self.current_package
|
||||
)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
opts = (
|
||||
[self.brew_path, 'uninstall']
|
||||
+ self.install_options
|
||||
+ [self.current_package]
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if not self._current_package_is_installed():
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Package uninstalled: {0}'.format(self.current_package)
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _uninstall_packages(self):
|
||||
for package in self.packages:
|
||||
self.current_package = package
|
||||
self._uninstall_current_package()
|
||||
|
||||
return True
|
||||
# /uninstalled ----------------------------- }}}
|
||||
|
||||
# linked --------------------------------- {{{
|
||||
def _link_current_package(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if not self._current_package_is_installed():
|
||||
self.failed = True
|
||||
self.message = 'Package not installed: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Package would be linked: {0}'.format(
|
||||
self.current_package
|
||||
)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
opts = (
|
||||
[self.brew_path, 'link']
|
||||
+ self.install_options
|
||||
+ [self.current_package]
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Package linked: {0}'.format(self.current_package)
|
||||
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = 'Package could not be linked: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _link_packages(self):
|
||||
for package in self.packages:
|
||||
self.current_package = package
|
||||
self._link_current_package()
|
||||
|
||||
return True
|
||||
# /linked -------------------------------- }}}
|
||||
|
||||
# unlinked ------------------------------- {{{
|
||||
def _unlink_current_package(self):
|
||||
if not self.valid_package(self.current_package):
|
||||
self.failed = True
|
||||
self.message = 'Invalid package: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if not self._current_package_is_installed():
|
||||
self.failed = True
|
||||
self.message = 'Package not installed: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Package would be unlinked: {0}'.format(
|
||||
self.current_package
|
||||
)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
opts = (
|
||||
[self.brew_path, 'unlink']
|
||||
+ self.install_options
|
||||
+ [self.current_package]
|
||||
)
|
||||
cmd = [opt for opt in opts if opt]
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
if rc == 0:
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Package unlinked: {0}'.format(self.current_package)
|
||||
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = 'Package could not be unlinked: {0}.'.format(self.current_package)
|
||||
raise HomebrewException(self.message)
|
||||
|
||||
def _unlink_packages(self):
|
||||
for package in self.packages:
|
||||
self.current_package = package
|
||||
self._unlink_current_package()
|
||||
|
||||
return True
|
||||
# /unlinked ------------------------------ }}}
|
||||
# /commands ---------------------------------------------------- }}}
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(aliases=["pkg"], required=False),
|
||||
path=dict(required=False),
|
||||
state=dict(
|
||||
default="present",
|
||||
choices=[
|
||||
"present", "installed",
|
||||
"latest", "upgraded", "head",
|
||||
"linked", "unlinked",
|
||||
"absent", "removed", "uninstalled",
|
||||
],
|
||||
),
|
||||
update_homebrew=dict(
|
||||
default="no",
|
||||
aliases=["update-brew"],
|
||||
type='bool',
|
||||
),
|
||||
upgrade_all=dict(
|
||||
default="no",
|
||||
aliases=["upgrade"],
|
||||
type='bool',
|
||||
),
|
||||
install_options=dict(
|
||||
default=None,
|
||||
aliases=['options'],
|
||||
type='list',
|
||||
)
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
p = module.params
|
||||
|
||||
if p['name']:
|
||||
packages = p['name'].split(',')
|
||||
else:
|
||||
packages = None
|
||||
|
||||
path = p['path']
|
||||
if path:
|
||||
path = path.split(':')
|
||||
else:
|
||||
path = ['/usr/local/bin']
|
||||
|
||||
state = p['state']
|
||||
if state in ('present', 'installed'):
|
||||
state = 'installed'
|
||||
if state in ('head', ):
|
||||
state = 'head'
|
||||
if state in ('latest', 'upgraded'):
|
||||
state = 'upgraded'
|
||||
if state == 'linked':
|
||||
state = 'linked'
|
||||
if state == 'unlinked':
|
||||
state = 'unlinked'
|
||||
if state in ('absent', 'removed', 'uninstalled'):
|
||||
state = 'absent'
|
||||
|
||||
update_homebrew = p['update_homebrew']
|
||||
upgrade_all = p['upgrade_all']
|
||||
p['install_options'] = p['install_options'] or []
|
||||
install_options = ['--{0}'.format(install_option)
|
||||
for install_option in p['install_options']]
|
||||
|
||||
brew = Homebrew(module=module, path=path, packages=packages,
|
||||
state=state, update_homebrew=update_homebrew,
|
||||
upgrade_all=upgrade_all, install_options=install_options)
|
||||
(failed, changed, message) = brew.run()
|
||||
if failed:
|
||||
module.fail_json(msg=message)
|
||||
else:
|
||||
module.exit_json(changed=changed, msg=message)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||
main()
|
@ -1,513 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Daniel Jaouen <dcj24@cornell.edu>
|
||||
#
|
||||
# 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_cask
|
||||
author: Daniel Jaouen
|
||||
short_description: Install/uninstall homebrew casks.
|
||||
description:
|
||||
- Manages Homebrew casks.
|
||||
version_added: "1.6"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- name of cask to install/remove
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- state of the cask
|
||||
choices: [ 'installed', 'uninstalled' ]
|
||||
required: false
|
||||
default: present
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
- homebrew_cask: name=alfred state=present
|
||||
- homebrew_cask: name=alfred state=absent
|
||||
'''
|
||||
|
||||
import os.path
|
||||
import re
|
||||
|
||||
|
||||
# exceptions -------------------------------------------------------------- {{{
|
||||
class HomebrewCaskException(Exception):
|
||||
pass
|
||||
# /exceptions ------------------------------------------------------------- }}}
|
||||
|
||||
|
||||
# utils ------------------------------------------------------------------- {{{
|
||||
def _create_regex_group(s):
|
||||
lines = (line.strip() for line in s.split('\n') if line.strip())
|
||||
chars = filter(None, (line.split('#')[0].strip() for line in lines))
|
||||
group = r'[^' + r''.join(chars) + r']'
|
||||
return re.compile(group)
|
||||
# /utils ------------------------------------------------------------------ }}}
|
||||
|
||||
|
||||
class HomebrewCask(object):
|
||||
'''A class to manage Homebrew casks.'''
|
||||
|
||||
# class regexes ------------------------------------------------ {{{
|
||||
VALID_PATH_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
\s # spaces
|
||||
: # colons
|
||||
{sep} # the OS-specific path separator
|
||||
- # dashes
|
||||
'''.format(sep=os.path.sep)
|
||||
|
||||
VALID_BREW_PATH_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
\s # spaces
|
||||
{sep} # the OS-specific path separator
|
||||
- # dashes
|
||||
'''.format(sep=os.path.sep)
|
||||
|
||||
VALID_CASK_CHARS = r'''
|
||||
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
|
||||
- # dashes
|
||||
'''
|
||||
|
||||
INVALID_PATH_REGEX = _create_regex_group(VALID_PATH_CHARS)
|
||||
INVALID_BREW_PATH_REGEX = _create_regex_group(VALID_BREW_PATH_CHARS)
|
||||
INVALID_CASK_REGEX = _create_regex_group(VALID_CASK_CHARS)
|
||||
# /class regexes ----------------------------------------------- }}}
|
||||
|
||||
# class validations -------------------------------------------- {{{
|
||||
@classmethod
|
||||
def valid_path(cls, path):
|
||||
'''
|
||||
`path` must be one of:
|
||||
- list of paths
|
||||
- a string containing only:
|
||||
- alphanumeric characters
|
||||
- dashes
|
||||
- spaces
|
||||
- colons
|
||||
- os.path.sep
|
||||
'''
|
||||
|
||||
if isinstance(path, basestring):
|
||||
return not cls.INVALID_PATH_REGEX.search(path)
|
||||
|
||||
try:
|
||||
iter(path)
|
||||
except TypeError:
|
||||
return False
|
||||
else:
|
||||
paths = path
|
||||
return all(cls.valid_brew_path(path_) for path_ in paths)
|
||||
|
||||
@classmethod
|
||||
def valid_brew_path(cls, brew_path):
|
||||
'''
|
||||
`brew_path` must be one of:
|
||||
- None
|
||||
- a string containing only:
|
||||
- alphanumeric characters
|
||||
- dashes
|
||||
- spaces
|
||||
- os.path.sep
|
||||
'''
|
||||
|
||||
if brew_path is None:
|
||||
return True
|
||||
|
||||
return (
|
||||
isinstance(brew_path, basestring)
|
||||
and not cls.INVALID_BREW_PATH_REGEX.search(brew_path)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_cask(cls, cask):
|
||||
'''A valid cask is either None or alphanumeric + backslashes.'''
|
||||
|
||||
if cask is None:
|
||||
return True
|
||||
|
||||
return (
|
||||
isinstance(cask, basestring)
|
||||
and not cls.INVALID_CASK_REGEX.search(cask)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_state(cls, state):
|
||||
'''
|
||||
A valid state is one of:
|
||||
- installed
|
||||
- absent
|
||||
'''
|
||||
|
||||
if state is None:
|
||||
return True
|
||||
else:
|
||||
return (
|
||||
isinstance(state, basestring)
|
||||
and state.lower() in (
|
||||
'installed',
|
||||
'absent',
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def valid_module(cls, module):
|
||||
'''A valid module is an instance of AnsibleModule.'''
|
||||
|
||||
return isinstance(module, AnsibleModule)
|
||||
# /class validations ------------------------------------------- }}}
|
||||
|
||||
# class properties --------------------------------------------- {{{
|
||||
@property
|
||||
def module(self):
|
||||
return self._module
|
||||
|
||||
@module.setter
|
||||
def module(self, module):
|
||||
if not self.valid_module(module):
|
||||
self._module = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid module: {0}.'.format(module)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
else:
|
||||
self._module = module
|
||||
return module
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
if not self.valid_path(path):
|
||||
self._path = []
|
||||
self.failed = True
|
||||
self.message = 'Invalid path: {0}.'.format(path)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
else:
|
||||
if isinstance(path, basestring):
|
||||
self._path = path.split(':')
|
||||
else:
|
||||
self._path = path
|
||||
|
||||
return path
|
||||
|
||||
@property
|
||||
def brew_path(self):
|
||||
return self._brew_path
|
||||
|
||||
@brew_path.setter
|
||||
def brew_path(self, brew_path):
|
||||
if not self.valid_brew_path(brew_path):
|
||||
self._brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid brew_path: {0}.'.format(brew_path)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
else:
|
||||
self._brew_path = brew_path
|
||||
return brew_path
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self._params
|
||||
|
||||
@params.setter
|
||||
def params(self, params):
|
||||
self._params = self.module.params
|
||||
return self._params
|
||||
|
||||
@property
|
||||
def current_cask(self):
|
||||
return self._current_cask
|
||||
|
||||
@current_cask.setter
|
||||
def current_cask(self, cask):
|
||||
if not self.valid_cask(cask):
|
||||
self._current_cask = None
|
||||
self.failed = True
|
||||
self.message = 'Invalid cask: {0}.'.format(cask)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
else:
|
||||
self._current_cask = cask
|
||||
return cask
|
||||
# /class properties -------------------------------------------- }}}
|
||||
|
||||
def __init__(self, module, path=None, casks=None, state=None):
|
||||
self._setup_status_vars()
|
||||
self._setup_instance_vars(module=module, path=path, casks=casks,
|
||||
state=state)
|
||||
|
||||
self._prep()
|
||||
|
||||
# prep --------------------------------------------------------- {{{
|
||||
def _setup_status_vars(self):
|
||||
self.failed = False
|
||||
self.changed = False
|
||||
self.changed_count = 0
|
||||
self.unchanged_count = 0
|
||||
self.message = ''
|
||||
|
||||
def _setup_instance_vars(self, **kwargs):
|
||||
for key, val in kwargs.iteritems():
|
||||
setattr(self, key, val)
|
||||
|
||||
def _prep(self):
|
||||
self._prep_path()
|
||||
self._prep_brew_path()
|
||||
|
||||
def _prep_path(self):
|
||||
if not self.path:
|
||||
self.path = ['/usr/local/bin']
|
||||
|
||||
def _prep_brew_path(self):
|
||||
if not self.module:
|
||||
self.brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'AnsibleModule not set.'
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
self.brew_path = self.module.get_bin_path(
|
||||
'brew',
|
||||
required=True,
|
||||
opt_dirs=self.path,
|
||||
)
|
||||
if not self.brew_path:
|
||||
self.brew_path = None
|
||||
self.failed = True
|
||||
self.message = 'Unable to locate homebrew executable.'
|
||||
raise HomebrewCaskException('Unable to locate homebrew executable.')
|
||||
|
||||
return self.brew_path
|
||||
|
||||
def _status(self):
|
||||
return (self.failed, self.changed, self.message)
|
||||
# /prep -------------------------------------------------------- }}}
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self._run()
|
||||
except HomebrewCaskException:
|
||||
pass
|
||||
|
||||
if not self.failed and (self.changed_count + self.unchanged_count > 1):
|
||||
self.message = "Changed: %d, Unchanged: %d" % (
|
||||
self.changed_count,
|
||||
self.unchanged_count,
|
||||
)
|
||||
(failed, changed, message) = self._status()
|
||||
|
||||
return (failed, changed, message)
|
||||
|
||||
# checks ------------------------------------------------------- {{{
|
||||
def _current_cask_is_installed(self):
|
||||
if not self.valid_cask(self.current_cask):
|
||||
self.failed = True
|
||||
self.message = 'Invalid cask: {0}.'.format(self.current_cask)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
cmd = [self.brew_path, 'cask', 'list']
|
||||
rc, out, err = self.module.run_command(cmd, path_prefix=self.path[0])
|
||||
|
||||
if 'nothing to list' in err:
|
||||
return False
|
||||
elif rc == 0:
|
||||
casks = [cask_.strip() for cask_ in out.split('\n') if cask_.strip()]
|
||||
return self.current_cask in casks
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewCaskException(self.message)
|
||||
# /checks ------------------------------------------------------ }}}
|
||||
|
||||
# commands ----------------------------------------------------- {{{
|
||||
def _run(self):
|
||||
if self.state == 'installed':
|
||||
return self._install_casks()
|
||||
elif self.state == 'absent':
|
||||
return self._uninstall_casks()
|
||||
|
||||
if self.command:
|
||||
return self._command()
|
||||
|
||||
# updated -------------------------------- {{{
|
||||
def _update_homebrew(self):
|
||||
rc, out, err = self.module.run_command([
|
||||
self.brew_path,
|
||||
'update',
|
||||
], path_prefix=self.path[0])
|
||||
if rc == 0:
|
||||
if out and isinstance(out, basestring):
|
||||
already_updated = any(
|
||||
re.search(r'Already up-to-date.', s.strip(), re.IGNORECASE)
|
||||
for s in out.split('\n')
|
||||
if s
|
||||
)
|
||||
if not already_updated:
|
||||
self.changed = True
|
||||
self.message = 'Homebrew updated successfully.'
|
||||
else:
|
||||
self.message = 'Homebrew already up-to-date.'
|
||||
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewCaskException(self.message)
|
||||
# /updated ------------------------------- }}}
|
||||
|
||||
# installed ------------------------------ {{{
|
||||
def _install_current_cask(self):
|
||||
if not self.valid_cask(self.current_cask):
|
||||
self.failed = True
|
||||
self.message = 'Invalid cask: {0}.'.format(self.current_cask)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
if self._current_cask_is_installed():
|
||||
self.unchanged_count += 1
|
||||
self.message = 'Cask already installed: {0}'.format(
|
||||
self.current_cask,
|
||||
)
|
||||
return True
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Cask would be installed: {0}'.format(
|
||||
self.current_cask
|
||||
)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
cmd = [opt
|
||||
for opt in (self.brew_path, 'cask', 'install', self.current_cask)
|
||||
if opt]
|
||||
|
||||
rc, out, err = self.module.run_command(cmd, path_prefix=self.path[0])
|
||||
|
||||
if self._current_cask_is_installed():
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Cask installed: {0}'.format(self.current_cask)
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
def _install_casks(self):
|
||||
for cask in self.casks:
|
||||
self.current_cask = cask
|
||||
self._install_current_cask()
|
||||
|
||||
return True
|
||||
# /installed ----------------------------- }}}
|
||||
|
||||
# uninstalled ---------------------------- {{{
|
||||
def _uninstall_current_cask(self):
|
||||
if not self.valid_cask(self.current_cask):
|
||||
self.failed = True
|
||||
self.message = 'Invalid cask: {0}.'.format(self.current_cask)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
if not self._current_cask_is_installed():
|
||||
self.unchanged_count += 1
|
||||
self.message = 'Cask already uninstalled: {0}'.format(
|
||||
self.current_cask,
|
||||
)
|
||||
return True
|
||||
|
||||
if self.module.check_mode:
|
||||
self.changed = True
|
||||
self.message = 'Cask would be uninstalled: {0}'.format(
|
||||
self.current_cask
|
||||
)
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
cmd = [opt
|
||||
for opt in (self.brew_path, 'cask', 'uninstall', self.current_cask)
|
||||
if opt]
|
||||
|
||||
rc, out, err = self.module.run_command(cmd, path_prefix=self.path[0])
|
||||
|
||||
if not self._current_cask_is_installed():
|
||||
self.changed_count += 1
|
||||
self.changed = True
|
||||
self.message = 'Cask uninstalled: {0}'.format(self.current_cask)
|
||||
return True
|
||||
else:
|
||||
self.failed = True
|
||||
self.message = err.strip()
|
||||
raise HomebrewCaskException(self.message)
|
||||
|
||||
def _uninstall_casks(self):
|
||||
for cask in self.casks:
|
||||
self.current_cask = cask
|
||||
self._uninstall_current_cask()
|
||||
|
||||
return True
|
||||
# /uninstalled ----------------------------- }}}
|
||||
# /commands ---------------------------------------------------- }}}
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(aliases=["cask"], required=False),
|
||||
path=dict(required=False),
|
||||
state=dict(
|
||||
default="present",
|
||||
choices=[
|
||||
"present", "installed",
|
||||
"absent", "removed", "uninstalled",
|
||||
],
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
p = module.params
|
||||
|
||||
if p['name']:
|
||||
casks = p['name'].split(',')
|
||||
else:
|
||||
casks = None
|
||||
|
||||
path = p['path']
|
||||
if path:
|
||||
path = path.split(':')
|
||||
else:
|
||||
path = ['/usr/local/bin']
|
||||
|
||||
state = p['state']
|
||||
if state in ('present', 'installed'):
|
||||
state = 'installed'
|
||||
if state in ('absent', 'removed', 'uninstalled'):
|
||||
state = 'absent'
|
||||
|
||||
brew_cask = HomebrewCask(module=module, path=path, casks=casks,
|
||||
state=state)
|
||||
(failed, changed, message) = brew_cask.run()
|
||||
if failed:
|
||||
module.fail_json(msg=message)
|
||||
else:
|
||||
module.exit_json(changed=changed, msg=message)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||
main()
|
@ -1,215 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Daniel Jaouen <dcj24@cornell.edu>
|
||||
# Based on homebrew (Andrew Dunham <andrew@du.nham.ca>)
|
||||
#
|
||||
# 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: homebrew_tap
|
||||
author: Daniel Jaouen
|
||||
short_description: Tap a Homebrew repository.
|
||||
description:
|
||||
- Tap external Homebrew repositories.
|
||||
version_added: "1.6"
|
||||
options:
|
||||
tap:
|
||||
description:
|
||||
- The repository to tap.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- state of the repository.
|
||||
choices: [ 'present', 'absent' ]
|
||||
required: false
|
||||
default: 'present'
|
||||
requirements: [ homebrew ]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
homebrew_tap: tap=homebrew/dupes state=present
|
||||
homebrew_tap: tap=homebrew/dupes state=absent
|
||||
homebrew_tap: tap=homebrew/dupes,homebrew/science state=present
|
||||
'''
|
||||
|
||||
|
||||
def a_valid_tap(tap):
|
||||
'''Returns True if the tap is valid.'''
|
||||
regex = re.compile(r'^(\S+)/(homebrew-)?(\w+)$')
|
||||
return regex.match(tap)
|
||||
|
||||
|
||||
def already_tapped(module, brew_path, tap):
|
||||
'''Returns True if already tapped.'''
|
||||
|
||||
rc, out, err = module.run_command([
|
||||
brew_path,
|
||||
'tap',
|
||||
])
|
||||
taps = [tap_.strip().lower() for tap_ in out.split('\n') if tap_]
|
||||
return tap.lower() in taps
|
||||
|
||||
|
||||
def add_tap(module, brew_path, tap):
|
||||
'''Adds a single tap.'''
|
||||
failed, changed, msg = False, False, ''
|
||||
|
||||
if not a_valid_tap(tap):
|
||||
failed = True
|
||||
msg = 'not a valid tap: %s' % tap
|
||||
|
||||
elif not already_tapped(module, brew_path, tap):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
rc, out, err = module.run_command([
|
||||
brew_path,
|
||||
'tap',
|
||||
tap,
|
||||
])
|
||||
if already_tapped(module, brew_path, tap):
|
||||
changed = True
|
||||
msg = 'successfully tapped: %s' % tap
|
||||
else:
|
||||
failed = True
|
||||
msg = 'failed to tap: %s' % tap
|
||||
|
||||
else:
|
||||
msg = 'already tapped: %s' % tap
|
||||
|
||||
return (failed, changed, msg)
|
||||
|
||||
|
||||
def add_taps(module, brew_path, taps):
|
||||
'''Adds one or more taps.'''
|
||||
failed, unchanged, added, msg = False, 0, 0, ''
|
||||
|
||||
for tap in taps:
|
||||
(failed, changed, msg) = add_tap(module, brew_path, tap)
|
||||
if failed:
|
||||
break
|
||||
if changed:
|
||||
added += 1
|
||||
else:
|
||||
unchanged += 1
|
||||
|
||||
if failed:
|
||||
msg = 'added: %d, unchanged: %d, error: ' + msg
|
||||
msg = msg % (added, unchanged)
|
||||
elif added:
|
||||
changed = True
|
||||
msg = 'added: %d, unchanged: %d' % (added, unchanged)
|
||||
else:
|
||||
msg = 'added: %d, unchanged: %d' % (added, unchanged)
|
||||
|
||||
return (failed, changed, msg)
|
||||
|
||||
|
||||
def remove_tap(module, brew_path, tap):
|
||||
'''Removes a single tap.'''
|
||||
failed, changed, msg = False, False, ''
|
||||
|
||||
if not a_valid_tap(tap):
|
||||
failed = True
|
||||
msg = 'not a valid tap: %s' % tap
|
||||
|
||||
elif already_tapped(module, brew_path, tap):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
rc, out, err = module.run_command([
|
||||
brew_path,
|
||||
'untap',
|
||||
tap,
|
||||
])
|
||||
if not already_tapped(module, brew_path, tap):
|
||||
changed = True
|
||||
msg = 'successfully untapped: %s' % tap
|
||||
else:
|
||||
failed = True
|
||||
msg = 'failed to untap: %s' % tap
|
||||
|
||||
else:
|
||||
msg = 'already untapped: %s' % tap
|
||||
|
||||
return (failed, changed, msg)
|
||||
|
||||
|
||||
def remove_taps(module, brew_path, taps):
|
||||
'''Removes one or more taps.'''
|
||||
failed, unchanged, removed, msg = False, 0, 0, ''
|
||||
|
||||
for tap in taps:
|
||||
(failed, changed, msg) = remove_tap(module, brew_path, tap)
|
||||
if failed:
|
||||
break
|
||||
if changed:
|
||||
removed += 1
|
||||
else:
|
||||
unchanged += 1
|
||||
|
||||
if failed:
|
||||
msg = 'removed: %d, unchanged: %d, error: ' + msg
|
||||
msg = msg % (removed, unchanged)
|
||||
elif removed:
|
||||
changed = True
|
||||
msg = 'removed: %d, unchanged: %d' % (removed, unchanged)
|
||||
else:
|
||||
msg = 'removed: %d, unchanged: %d' % (removed, unchanged)
|
||||
|
||||
return (failed, changed, msg)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(aliases=['tap'], required=True),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
brew_path = module.get_bin_path(
|
||||
'brew',
|
||||
required=True,
|
||||
opt_dirs=['/usr/local/bin']
|
||||
)
|
||||
|
||||
taps = module.params['name'].split(',')
|
||||
|
||||
if module.params['state'] == 'present':
|
||||
failed, changed, msg = add_taps(module, brew_path, taps)
|
||||
|
||||
if failed:
|
||||
module.fail_json(msg=msg)
|
||||
else:
|
||||
module.exit_json(changed=changed, msg=msg)
|
||||
|
||||
elif module.params['state'] == 'absent':
|
||||
failed, changed, msg = remove_taps(module, brew_path, taps)
|
||||
|
||||
if failed:
|
||||
module.fail_json(msg=msg)
|
||||
else:
|
||||
module.exit_json(changed=changed, msg=msg)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||
main()
|
@ -1,236 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Jakub Jirutka <jakub@jirutka.cz>
|
||||
#
|
||||
# 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 shutil
|
||||
from os import path
|
||||
from urllib2 import Request, urlopen, URLError
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: layman
|
||||
author: Jakub Jirutka
|
||||
version_added: "1.6"
|
||||
short_description: Manage Gentoo overlays
|
||||
description:
|
||||
- Uses Layman to manage an additional repositories for the Portage package manager on Gentoo Linux.
|
||||
Please note that Layman must be installed on a managed node prior using this module.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The overlay id to install, synchronize, or uninstall.
|
||||
Use 'ALL' to sync all of the installed overlays (can be used only when C(state=updated)).
|
||||
required: true
|
||||
list_url:
|
||||
description:
|
||||
- An URL of the alternative overlays list that defines the overlay to install.
|
||||
This list will be fetched and saved under C(${overlay_defs})/${name}.xml), where
|
||||
C(overlay_defs) is readed from the Layman's configuration.
|
||||
required: false
|
||||
state:
|
||||
description:
|
||||
- Whether to install (C(present)), sync (C(updated)), or uninstall (C(absent)) the overlay.
|
||||
required: false
|
||||
default: present
|
||||
choices: [present, absent, updated]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install the overlay 'mozilla' which is on the central overlays list.
|
||||
- layman: name=mozilla
|
||||
|
||||
# Install the overlay 'cvut' from the specified alternative list.
|
||||
- layman: name=cvut list_url=http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml
|
||||
|
||||
# Update (sync) the overlay 'cvut', or install if not installed yet.
|
||||
- layman: name=cvut list_url=http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml state=updated
|
||||
|
||||
# Update (sync) all of the installed overlays.
|
||||
- layman: name=ALL state=updated
|
||||
|
||||
# Uninstall the overlay 'cvut'.
|
||||
- layman: name=cvut state=absent
|
||||
'''
|
||||
|
||||
USERAGENT = 'ansible-httpget'
|
||||
|
||||
try:
|
||||
from layman.api import LaymanAPI
|
||||
from layman.config import BareConfig
|
||||
HAS_LAYMAN_API = True
|
||||
except ImportError:
|
||||
HAS_LAYMAN_API = False
|
||||
|
||||
|
||||
class ModuleError(Exception): pass
|
||||
|
||||
|
||||
def init_layman(config=None):
|
||||
'''Returns the initialized ``LaymanAPI``.
|
||||
|
||||
:param config: the layman's configuration to use (optional)
|
||||
'''
|
||||
if config is None: config = BareConfig(read_configfile=True, quietness=1)
|
||||
return LaymanAPI(config)
|
||||
|
||||
|
||||
def download_url(url, dest):
|
||||
'''
|
||||
:param url: the URL to download
|
||||
:param dest: the absolute path of where to save the downloaded content to;
|
||||
it must be writable and not a directory
|
||||
|
||||
:raises ModuleError
|
||||
'''
|
||||
request = Request(url)
|
||||
request.add_header('User-agent', USERAGENT)
|
||||
|
||||
try:
|
||||
response = urlopen(request)
|
||||
except URLError, e:
|
||||
raise ModuleError("Failed to get %s: %s" % (url, str(e)))
|
||||
|
||||
try:
|
||||
with open(dest, 'w') as f:
|
||||
shutil.copyfileobj(response, f)
|
||||
except IOError, e:
|
||||
raise ModuleError("Failed to write: %s" % str(e))
|
||||
|
||||
|
||||
def install_overlay(name, list_url=None):
|
||||
'''Installs the overlay repository. If not on the central overlays list,
|
||||
then :list_url of an alternative list must be provided. The list will be
|
||||
fetched and saved under ``%(overlay_defs)/%(name.xml)`` (location of the
|
||||
``overlay_defs`` is read from the Layman's configuration).
|
||||
|
||||
:param name: the overlay id
|
||||
:param list_url: the URL of the remote repositories list to look for the overlay
|
||||
definition (optional, default: None)
|
||||
|
||||
:returns: True if the overlay was installed, or False if already exists
|
||||
(i.e. nothing has changed)
|
||||
:raises ModuleError
|
||||
'''
|
||||
# read Layman configuration
|
||||
layman_conf = BareConfig(read_configfile=True)
|
||||
layman = init_layman(layman_conf)
|
||||
|
||||
if layman.is_installed(name):
|
||||
return False
|
||||
|
||||
if not layman.is_repo(name):
|
||||
if not list_url: raise ModuleError("Overlay '%s' is not on the list of known " \
|
||||
"overlays and URL of the remote list was not provided." % name)
|
||||
|
||||
overlay_defs = layman_conf.get_option('overlay_defs')
|
||||
dest = path.join(overlay_defs, name + '.xml')
|
||||
|
||||
download_url(list_url, dest)
|
||||
|
||||
# reload config
|
||||
layman = init_layman()
|
||||
|
||||
if not layman.add_repos(name): raise ModuleError(layman.get_errors())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def uninstall_overlay(name):
|
||||
'''Uninstalls the given overlay repository from the system.
|
||||
|
||||
:param name: the overlay id to uninstall
|
||||
|
||||
:returns: True if the overlay was uninstalled, or False if doesn't exist
|
||||
(i.e. nothing has changed)
|
||||
:raises ModuleError
|
||||
'''
|
||||
layman = init_layman()
|
||||
|
||||
if not layman.is_installed(name):
|
||||
return False
|
||||
|
||||
layman.delete_repos(name)
|
||||
if layman.get_errors(): raise ModuleError(layman.get_errors())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def sync_overlay(name):
|
||||
'''Synchronizes the specified overlay repository.
|
||||
|
||||
:param name: the overlay repository id to sync
|
||||
:raises ModuleError
|
||||
'''
|
||||
layman = init_layman()
|
||||
|
||||
if not layman.sync(name):
|
||||
messages = [ str(item[1]) for item in layman.sync_results[2] ]
|
||||
raise ModuleError(messages)
|
||||
|
||||
|
||||
def sync_overlays():
|
||||
'''Synchronize all of the installed overlays.
|
||||
|
||||
:raises ModuleError
|
||||
'''
|
||||
layman = init_layman()
|
||||
|
||||
for name in layman.get_installed():
|
||||
sync_overlay(name)
|
||||
|
||||
|
||||
def main():
|
||||
# define module
|
||||
module = AnsibleModule(
|
||||
argument_spec = {
|
||||
'name': { 'required': True },
|
||||
'list_url': { 'aliases': ['url'] },
|
||||
'state': { 'default': "present", 'choices': ['present', 'absent', 'updated'] },
|
||||
}
|
||||
)
|
||||
|
||||
if not HAS_LAYMAN_API:
|
||||
module.fail_json(msg='Layman is not installed')
|
||||
|
||||
state, name, url = (module.params[key] for key in ['state', 'name', 'list_url'])
|
||||
|
||||
changed = False
|
||||
try:
|
||||
if state == 'present':
|
||||
changed = install_overlay(name, url)
|
||||
|
||||
elif state == 'updated':
|
||||
if name == 'ALL':
|
||||
sync_overlays()
|
||||
elif install_overlay(name, url):
|
||||
changed = True
|
||||
else:
|
||||
sync_overlay(name)
|
||||
else:
|
||||
changed = uninstall_overlay(name)
|
||||
|
||||
except ModuleError, e:
|
||||
module.fail_json(msg=e.message)
|
||||
else:
|
||||
module.exit_json(changed=changed, name=name)
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,217 +0,0 @@
|
||||
#!/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
|
||||
'''
|
||||
|
||||
import pipes
|
||||
|
||||
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" % (pipes.quote(port_path), pipes.quote(name)), use_unsafe_shell=True)
|
||||
if rc == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
elif state == "active":
|
||||
|
||||
rc, out, err = module.run_command("%s installed %s | grep -q active" % (pipes.quote(port_path), pipes.quote(name)), use_unsafe_shell=True)
|
||||
|
||||
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)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,263 +0,0 @@
|
||||
#!/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
|
||||
required: false
|
||||
path:
|
||||
description:
|
||||
- The base path where to install the node.js libraries
|
||||
required: false
|
||||
version:
|
||||
description:
|
||||
- The version to be installed
|
||||
required: false
|
||||
global:
|
||||
description:
|
||||
- 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
|
||||
ignore_scripts:
|
||||
description:
|
||||
- Use the --ignore-scripts flag when installing.
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: no
|
||||
version_added: "1.8"
|
||||
production:
|
||||
description:
|
||||
- Install dependencies in production mode, excluding devDependencies
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: no
|
||||
registry:
|
||||
description:
|
||||
- The registry to install modules from.
|
||||
required: false
|
||||
version_added: "1.6"
|
||||
state:
|
||||
description:
|
||||
- The state of the node.js library
|
||||
required: false
|
||||
default: present
|
||||
choices: [ "present", "absent", "latest" ]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
description: Install "coffee-script" node.js package.
|
||||
- npm: name=coffee-script path=/app/location
|
||||
|
||||
description: Install "coffee-script" node.js package on version 1.6.1.
|
||||
- npm: name=coffee-script version=1.6.1 path=/app/location
|
||||
|
||||
description: Install "coffee-script" node.js package globally.
|
||||
- npm: name=coffee-script global=yes
|
||||
|
||||
description: Remove the globally package "coffee-script".
|
||||
- npm: name=coffee-script global=yes state=absent
|
||||
|
||||
description: Install "coffee-script" node.js package from custom registry.
|
||||
- npm: name=coffee-script registry=http://registry.mysite.com
|
||||
|
||||
description: Install packages based on package.json.
|
||||
- npm: path=/app/location
|
||||
|
||||
description: Update packages based on package.json to their latest version.
|
||||
- npm: path=/app/location state=latest
|
||||
|
||||
description: Install packages based on package.json using the npm installed with nvm v0.10.1.
|
||||
- npm: path=/app/location executable=/opt/nvm/v0.10.1/bin/npm state=present
|
||||
'''
|
||||
|
||||
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.registry = kwargs['registry']
|
||||
self.production = kwargs['production']
|
||||
self.ignore_scripts = kwargs['ignore_scripts']
|
||||
|
||||
if kwargs['executable']:
|
||||
self.executable = kwargs['executable'].split(' ')
|
||||
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.ignore_scripts:
|
||||
cmd.append('--ignore-scripts')
|
||||
if self.name:
|
||||
cmd.append(self.name_version)
|
||||
if self.registry:
|
||||
cmd.append('--registry')
|
||||
cmd.append(self.registry)
|
||||
|
||||
#If path is specified, cd into that path and run the command.
|
||||
cwd = None
|
||||
if self.path:
|
||||
if not os.path.exists(self.path):
|
||||
os.makedirs(self.path)
|
||||
if not os.path.isdir(self.path):
|
||||
self.module.fail_json(msg="path %s is not a directory" % self.path)
|
||||
cwd = self.path
|
||||
|
||||
rc, out, err = self.module.run_command(cmd, check_rc=check_rc, cwd=cwd)
|
||||
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)
|
||||
elif 'invalid' in data['dependencies'][dep] and data['dependencies'][dep]['invalid']:
|
||||
missing.append(dep)
|
||||
else:
|
||||
installed.append(dep)
|
||||
if self.name and self.name not in installed:
|
||||
missing.append(self.name)
|
||||
#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:
|
||||
# node.js v0.10.22 changed the `npm outdated` module separator
|
||||
# from "@" to " ". Split on both for backwards compatibility.
|
||||
pkg, other = re.split('\s|@', dep, 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),
|
||||
registry=dict(default=None),
|
||||
state=dict(default='present', choices=['present', 'absent', 'latest']),
|
||||
ignore_scripts=dict(default=False, type='bool'),
|
||||
)
|
||||
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']
|
||||
registry = module.params['registry']
|
||||
state = module.params['state']
|
||||
ignore_scripts = module.params['ignore_scripts']
|
||||
|
||||
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, registry=registry, ignore_scripts=ignore_scripts)
|
||||
|
||||
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()
|
||||
else: #absent
|
||||
installed, missing = npm.list()
|
||||
if name in installed:
|
||||
changed = True
|
||||
npm.uninstall()
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,373 +0,0 @@
|
||||
#!/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
|
||||
import shlex
|
||||
import syslog
|
||||
|
||||
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 = '''
|
||||
# Make sure nmap is installed
|
||||
- openbsd_pkg: name=nmap state=present
|
||||
|
||||
# Make sure nmap is the latest version
|
||||
- openbsd_pkg: name=nmap state=latest
|
||||
|
||||
# Make sure nmap is not installed
|
||||
- openbsd_pkg: name=nmap state=absent
|
||||
|
||||
# Specify a pkg flavour with '--'
|
||||
- openbsd_pkg: name=vim--nox11 state=present
|
||||
|
||||
# Specify the default flavour to avoid ambiguity errors
|
||||
- openbsd_pkg: name=vim-- state=present
|
||||
'''
|
||||
|
||||
# Control if we write debug information to syslog.
|
||||
debug = False
|
||||
|
||||
# Function used for executing commands.
|
||||
def execute_command(cmd, module):
|
||||
if debug:
|
||||
syslog.syslog("execute_command(): cmd = %s" % cmd)
|
||||
# Break command line into arguments.
|
||||
# This makes run_command() use shell=False which we need to not cause shell
|
||||
# expansion of special characters like '*'.
|
||||
cmd_args = shlex.split(cmd)
|
||||
return module.run_command(cmd_args)
|
||||
|
||||
# Function used for getting the name of a currently installed package.
|
||||
def get_current_name(name, pkg_spec, module):
|
||||
info_cmd = 'pkg_info'
|
||||
(rc, stdout, stderr) = execute_command("%s" % (info_cmd), module)
|
||||
if rc != 0:
|
||||
return (rc, stdout, stderr)
|
||||
|
||||
if pkg_spec['version']:
|
||||
pattern = "^%s" % name
|
||||
elif pkg_spec['flavor']:
|
||||
pattern = "^%s-.*-%s\s" % (pkg_spec['stem'], pkg_spec['flavor'])
|
||||
else:
|
||||
pattern = "^%s-" % pkg_spec['stem']
|
||||
|
||||
if debug:
|
||||
syslog.syslog("get_current_name(): pattern = %s" % pattern)
|
||||
|
||||
for line in stdout.splitlines():
|
||||
if debug:
|
||||
syslog.syslog("get_current_name: line = %s" % line)
|
||||
match = re.search(pattern, line)
|
||||
if match:
|
||||
current_name = line.split()[0]
|
||||
|
||||
return current_name
|
||||
|
||||
# Function used to find out if a package is currently installed.
|
||||
def get_package_state(name, pkg_spec, module):
|
||||
info_cmd = 'pkg_info -e'
|
||||
|
||||
if pkg_spec['version']:
|
||||
command = "%s %s" % (info_cmd, name)
|
||||
elif pkg_spec['flavor']:
|
||||
command = "%s %s-*-%s" % (info_cmd, pkg_spec['stem'], pkg_spec['flavor'])
|
||||
else:
|
||||
command = "%s %s-*" % (info_cmd, pkg_spec['stem'])
|
||||
|
||||
rc, stdout, stderr = execute_command(command, module)
|
||||
|
||||
if (stderr):
|
||||
module.fail_json(msg="failed in get_package_state(): " + stderr)
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Function used to make sure a package is present.
|
||||
def package_present(name, installed_state, pkg_spec, module):
|
||||
if module.check_mode:
|
||||
install_cmd = 'pkg_add -Imn'
|
||||
else:
|
||||
install_cmd = 'pkg_add -Im'
|
||||
|
||||
if installed_state is False:
|
||||
|
||||
# Attempt to install the package
|
||||
(rc, stdout, stderr) = execute_command("%s %s" % (install_cmd, name), module)
|
||||
|
||||
# The behaviour of pkg_add is a bit different depending on if a
|
||||
# specific version is supplied or not.
|
||||
#
|
||||
# When a specific version is supplied the return code will be 0 when
|
||||
# a package is found and 1 when it is not, if a version is not
|
||||
# supplied the tool will exit 0 in both cases:
|
||||
if pkg_spec['version']:
|
||||
# Depend on the return code.
|
||||
if debug:
|
||||
syslog.syslog("package_present(): depending on return code")
|
||||
if rc:
|
||||
changed=False
|
||||
else:
|
||||
# Depend on stderr instead.
|
||||
if debug:
|
||||
syslog.syslog("package_present(): depending on stderr")
|
||||
if stderr:
|
||||
# There is a corner case where having an empty directory in
|
||||
# installpath prior to the right location will result in a
|
||||
# "file:/local/package/directory/ is empty" message on stderr
|
||||
# while still installing the package, so we need to look for
|
||||
# for a message like "packagename-1.0: ok" just in case.
|
||||
match = re.search("\W%s-[^:]+: ok\W" % name, stdout)
|
||||
if match:
|
||||
# It turns out we were able to install the package.
|
||||
if debug:
|
||||
syslog.syslog("package_present(): we were able to install package")
|
||||
pass
|
||||
else:
|
||||
# We really did fail, fake the return code.
|
||||
if debug:
|
||||
syslog.syslog("package_present(): we really did fail")
|
||||
rc = 1
|
||||
changed=False
|
||||
else:
|
||||
if debug:
|
||||
syslog.syslog("package_present(): stderr was not set")
|
||||
|
||||
if rc == 0:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
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, pkg_spec, module):
|
||||
if module.check_mode:
|
||||
upgrade_cmd = 'pkg_add -umn'
|
||||
else:
|
||||
upgrade_cmd = 'pkg_add -um'
|
||||
|
||||
pre_upgrade_name = ''
|
||||
|
||||
if installed_state is True:
|
||||
|
||||
# Fetch name of currently installed package.
|
||||
pre_upgrade_name = get_current_name(name, pkg_spec, module)
|
||||
|
||||
if debug:
|
||||
syslog.syslog("package_latest(): pre_upgrade_name = %s" % pre_upgrade_name)
|
||||
|
||||
# Attempt to upgrade the package.
|
||||
(rc, stdout, stderr) = execute_command("%s %s" % (upgrade_cmd, name), module)
|
||||
|
||||
# Look for output looking something like "nmap-6.01->6.25: ok" to see if
|
||||
# something changed (or would have changed). Use \W to delimit the match
|
||||
# from progress meter output.
|
||||
match = re.search("\W%s->.+: ok\W" % pre_upgrade_name, stdout)
|
||||
if match:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
|
||||
# FIXME: This part is problematic. Based on the issues mentioned (and
|
||||
# handled) in package_present() it is not safe to blindly trust stderr
|
||||
# as an indicator that the command failed, and in the case with
|
||||
# empty installpath directories this will break.
|
||||
#
|
||||
# For now keep this safeguard here, but ignore it if we managed to
|
||||
# parse out a successful update above. This way we will report a
|
||||
# successful run when we actually modify something but fail
|
||||
# otherwise.
|
||||
if changed != True:
|
||||
if stderr:
|
||||
rc=1
|
||||
|
||||
return (rc, stdout, stderr, changed)
|
||||
|
||||
else:
|
||||
# If package was not installed at all just make it present.
|
||||
if debug:
|
||||
syslog.syslog("package_latest(): package is not installed, calling package_present()")
|
||||
return package_present(name, installed_state, pkg_spec, module)
|
||||
|
||||
# Function used to make sure a package is not installed.
|
||||
def package_absent(name, installed_state, module):
|
||||
if module.check_mode:
|
||||
remove_cmd = 'pkg_delete -In'
|
||||
else:
|
||||
remove_cmd = 'pkg_delete -I'
|
||||
|
||||
if installed_state is True:
|
||||
|
||||
# Attempt to remove the package.
|
||||
rc, stdout, stderr = execute_command("%s %s" % (remove_cmd, name), module)
|
||||
|
||||
if rc == 0:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
changed=True
|
||||
else:
|
||||
changed=False
|
||||
|
||||
else:
|
||||
rc = 0
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
changed=False
|
||||
|
||||
return (rc, stdout, stderr, changed)
|
||||
|
||||
# Function used to parse the package name based on packages-specs(7)
|
||||
# The general name structure is "stem-version[-flavors]"
|
||||
def parse_package_name(name, pkg_spec, module):
|
||||
# Do some initial matches so we can base the more advanced regex on that.
|
||||
version_match = re.search("-[0-9]", name)
|
||||
versionless_match = re.search("--", name)
|
||||
|
||||
# Stop if someone is giving us a name that both has a version and is
|
||||
# version-less at the same time.
|
||||
if version_match and versionless_match:
|
||||
module.fail_json(msg="Package name both has a version and is version-less: " + name)
|
||||
|
||||
# If name includes a version.
|
||||
if version_match:
|
||||
match = re.search("^(?P<stem>.*)-(?P<version>[0-9][^-]*)(?P<flavor_separator>-)?(?P<flavor>[a-z].*)?$", name)
|
||||
if match:
|
||||
pkg_spec['stem'] = match.group('stem')
|
||||
pkg_spec['version_separator'] = '-'
|
||||
pkg_spec['version'] = match.group('version')
|
||||
pkg_spec['flavor_separator'] = match.group('flavor_separator')
|
||||
pkg_spec['flavor'] = match.group('flavor')
|
||||
else:
|
||||
module.fail_json(msg="Unable to parse package name at version_match: " + name)
|
||||
|
||||
# If name includes no version but is version-less ("--").
|
||||
elif versionless_match:
|
||||
match = re.search("^(?P<stem>.*)--(?P<flavor>[a-z].*)?$", name)
|
||||
if match:
|
||||
pkg_spec['stem'] = match.group('stem')
|
||||
pkg_spec['version_separator'] = '-'
|
||||
pkg_spec['version'] = None
|
||||
pkg_spec['flavor_separator'] = '-'
|
||||
pkg_spec['flavor'] = match.group('flavor')
|
||||
else:
|
||||
module.fail_json(msg="Unable to parse package name at versionless_match: " + name)
|
||||
|
||||
# If name includes no version, and is not version-less, it is all a stem.
|
||||
else:
|
||||
match = re.search("^(?P<stem>.*)$", name)
|
||||
if match:
|
||||
pkg_spec['stem'] = match.group('stem')
|
||||
pkg_spec['version_separator'] = None
|
||||
pkg_spec['version'] = None
|
||||
pkg_spec['flavor_separator'] = None
|
||||
pkg_spec['flavor'] = None
|
||||
else:
|
||||
module.fail_json(msg="Unable to parse package name at else: " + name)
|
||||
|
||||
# Sanity check that there are no trailing dashes in flavor.
|
||||
# Try to stop strange stuff early so we can be strict later.
|
||||
if pkg_spec['flavor']:
|
||||
match = re.search("-$", pkg_spec['flavor'])
|
||||
if match:
|
||||
module.fail_json(msg="Trailing dash in flavor: " + pkg_spec['flavor'])
|
||||
|
||||
# ===========================================
|
||||
# Main control flow
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True),
|
||||
state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']),
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
|
||||
rc = 0
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
result = {}
|
||||
result['name'] = name
|
||||
result['state'] = state
|
||||
|
||||
# Parse package name and put results in the pkg_spec dictionary.
|
||||
pkg_spec = {}
|
||||
parse_package_name(name, pkg_spec, module)
|
||||
|
||||
# Get package state.
|
||||
installed_state = get_package_state(name, pkg_spec, module)
|
||||
|
||||
# Perform requested action.
|
||||
if state in ['installed', 'present']:
|
||||
(rc, stdout, stderr, changed) = package_present(name, installed_state, pkg_spec, module)
|
||||
elif state in ['absent', 'removed']:
|
||||
(rc, stdout, stderr, changed) = package_absent(name, installed_state, module)
|
||||
elif state == 'latest':
|
||||
(rc, stdout, stderr, changed) = package_latest(name, installed_state, pkg_spec, module)
|
||||
|
||||
if rc != 0:
|
||||
if stderr:
|
||||
module.fail_json(msg=stderr)
|
||||
else:
|
||||
module.fail_json(msg=stdout)
|
||||
|
||||
result['changed'] = changed
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,150 +0,0 @@
|
||||
#!/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
|
||||
'''
|
||||
|
||||
import pipes
|
||||
|
||||
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" % (pipes.quote(opkg_path), pipes.quote(name)), use_unsafe_shell=True)
|
||||
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)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,234 +0,0 @@
|
||||
#!/usr/bin/python -tt
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2012, Afterburn <http://github.com/afterburn>
|
||||
# (c) 2013, Aaron Bull Schaefer <aaron@elasticdog.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: pacman
|
||||
short_description: Manage packages with I(pacman)
|
||||
description:
|
||||
- Manage packages with the I(pacman) package manager, which is used by
|
||||
Arch Linux and its variants.
|
||||
version_added: "1.0"
|
||||
author: Afterburn
|
||||
notes: []
|
||||
requirements: []
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the package to install, upgrade, or remove.
|
||||
required: false
|
||||
default: null
|
||||
|
||||
state:
|
||||
description:
|
||||
- Desired state of the package.
|
||||
required: false
|
||||
default: "present"
|
||||
choices: ["present", "absent"]
|
||||
|
||||
recurse:
|
||||
description:
|
||||
- When removing a package, also remove its dependencies, provided
|
||||
that they are not required by other packages and were not
|
||||
explicitly installed by a user.
|
||||
required: false
|
||||
default: "no"
|
||||
choices: ["yes", "no"]
|
||||
version_added: "1.3"
|
||||
|
||||
update_cache:
|
||||
description:
|
||||
- Whether or not to refresh the master package lists. This can be
|
||||
run as part of a package installation or as a separate step.
|
||||
required: false
|
||||
default: "no"
|
||||
choices: ["yes", "no"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install package foo
|
||||
- pacman: name=foo state=present
|
||||
|
||||
# Remove packages foo and bar
|
||||
- pacman: name=foo,bar state=absent
|
||||
|
||||
# Recursively remove package baz
|
||||
- pacman: name=baz state=absent recurse=yes
|
||||
|
||||
# Run the equivalent of "pacman -Syy" as a separate step
|
||||
- pacman: update_cache=yes
|
||||
'''
|
||||
|
||||
import json
|
||||
import shlex
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
PACMAN_PATH = "/usr/bin/pacman"
|
||||
|
||||
def query_package(module, name, state="present"):
|
||||
# pacman -Q returns 0 if the package is installed,
|
||||
# 1 if it is not installed
|
||||
if state == "present":
|
||||
cmd = "pacman -Q %s" % (name)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def update_package_db(module):
|
||||
cmd = "pacman -Syy"
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
module.fail_json(msg="could not update package db")
|
||||
|
||||
|
||||
def remove_packages(module, packages):
|
||||
if module.params["recurse"]:
|
||||
args = "Rs"
|
||||
else:
|
||||
args = "R"
|
||||
|
||||
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
|
||||
|
||||
cmd = "pacman -%s %s --noconfirm" % (args, package)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
|
||||
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, package_files):
|
||||
install_c = 0
|
||||
|
||||
for i, package in enumerate(packages):
|
||||
if query_package(module, package):
|
||||
continue
|
||||
|
||||
if package_files[i]:
|
||||
params = '-U %s' % package_files[i]
|
||||
else:
|
||||
params = '-S %s' % package
|
||||
|
||||
cmd = "pacman %s --noconfirm" % (params)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
|
||||
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 check_packages(module, packages, state):
|
||||
would_be_changed = []
|
||||
for package in packages:
|
||||
installed = query_package(module, package)
|
||||
if ((state == "present" and not installed) or
|
||||
(state == "absent" and installed)):
|
||||
would_be_changed.append(package)
|
||||
if would_be_changed:
|
||||
if state == "absent":
|
||||
state = "removed"
|
||||
module.exit_json(changed=True, msg="%s package(s) would be %s" % (
|
||||
len(would_be_changed), state))
|
||||
else:
|
||||
module.exit_json(change=False, msg="package(s) already %s" % state)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(aliases=['pkg']),
|
||||
state = dict(default='present', choices=['present', 'installed', 'absent', 'removed']),
|
||||
recurse = dict(default='no', choices=BOOLEANS, type='bool'),
|
||||
update_cache = dict(default='no', aliases=['update-cache'], choices=BOOLEANS, type='bool')),
|
||||
required_one_of = [['name', 'update_cache']],
|
||||
supports_check_mode = True)
|
||||
|
||||
if not os.path.exists(PACMAN_PATH):
|
||||
module.fail_json(msg="cannot find pacman, looking for %s" % (PACMAN_PATH))
|
||||
|
||||
p = module.params
|
||||
|
||||
# normalize the state parameter
|
||||
if p['state'] in ['present', 'installed']:
|
||||
p['state'] = 'present'
|
||||
elif p['state'] in ['absent', 'removed']:
|
||||
p['state'] = 'absent'
|
||||
|
||||
if p["update_cache"] and not module.check_mode:
|
||||
update_package_db(module)
|
||||
if not p['name']:
|
||||
module.exit_json(changed=True, msg='updated the package master lists')
|
||||
|
||||
if p['update_cache'] and module.check_mode and not p['name']:
|
||||
module.exit_json(changed=True, msg='Would have updated the package cache')
|
||||
|
||||
if p['name']:
|
||||
pkgs = p['name'].split(',')
|
||||
|
||||
pkg_files = []
|
||||
for i, pkg in enumerate(pkgs):
|
||||
if pkg.endswith('.pkg.tar.xz'):
|
||||
# The package given is a filename, extract the raw pkg name from
|
||||
# it and store the filename
|
||||
pkg_files.append(pkg)
|
||||
pkgs[i] = re.sub('-[0-9].*$', '', pkgs[i].split('/')[-1])
|
||||
else:
|
||||
pkg_files.append(None)
|
||||
|
||||
if module.check_mode:
|
||||
check_packages(module, pkgs, p['state'])
|
||||
|
||||
if p['state'] == 'present':
|
||||
install_packages(module, pkgs, pkg_files)
|
||||
elif p['state'] == 'absent':
|
||||
remove_packages(module, pkgs)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,168 +0,0 @@
|
||||
#!/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 = '''
|
||||
# install package foo"
|
||||
- pkgin: name=foo state=present
|
||||
|
||||
# remove package foo
|
||||
- pkgin: name=foo state=absent
|
||||
|
||||
# remove packages foo and bar
|
||||
- pkgin: name=foo,bar state=absent
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
import shlex
|
||||
import os
|
||||
import sys
|
||||
import pipes
|
||||
|
||||
def query_package(module, pkgin_path, name, state="present"):
|
||||
|
||||
if state == "present":
|
||||
|
||||
rc, out, err = module.run_command("%s -y list | grep ^%s" % (pipes.quote(pkgin_path), pipes.quote(name)), use_unsafe_shell=True)
|
||||
|
||||
if rc == 0:
|
||||
# At least one package with a package name that starts with ``name``
|
||||
# is installed. For some cases this is not sufficient to determine
|
||||
# wether the queried package is installed.
|
||||
#
|
||||
# E.g. for ``name='gcc47'``, ``gcc47`` not being installed, but
|
||||
# ``gcc47-libs`` being installed, ``out`` would be:
|
||||
#
|
||||
# gcc47-libs-4.7.2nb4 The GNU Compiler Collection (GCC) support shared libraries.
|
||||
#
|
||||
# Multiline output is also possible, for example with the same query
|
||||
# and bot ``gcc47`` and ``gcc47-libs`` being installed:
|
||||
#
|
||||
# gcc47-libs-4.7.2nb4 The GNU Compiler Collection (GCC) support shared libraries.
|
||||
# gcc47-4.7.2nb3 The GNU Compiler Collection (GCC) - 4.7 Release Series
|
||||
|
||||
# Loop over lines in ``out``
|
||||
for line in out.split('\n'):
|
||||
|
||||
# Strip description
|
||||
# (results in sth. like 'gcc47-libs-4.7.2nb4')
|
||||
pkgname_with_version = out.split(' ')[0]
|
||||
|
||||
# Strip version
|
||||
# (results in sth like 'gcc47-libs')
|
||||
pkgname_without_version = '-'.join(pkgname_with_version.split('-')[:-1])
|
||||
|
||||
if name == pkgname_without_version:
|
||||
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)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,301 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, bleader
|
||||
# Written by bleader <bleader@ratonland.org>
|
||||
# Based on pkgin module written by Shaun Zinck <shaun.zinck at gmail.com>
|
||||
# that was 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: pkgng
|
||||
short_description: Package manager for FreeBSD >= 9.0
|
||||
description:
|
||||
- Manage binary packages for FreeBSD using 'pkgng' which
|
||||
is available in versions after 9.0.
|
||||
version_added: "1.2"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- name of package to install/remove
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- state of the package
|
||||
choices: [ 'present', 'absent' ]
|
||||
required: false
|
||||
default: present
|
||||
cached:
|
||||
description:
|
||||
- use local package base or try to fetch an updated one
|
||||
choices: [ 'yes', 'no' ]
|
||||
required: false
|
||||
default: no
|
||||
annotation:
|
||||
description:
|
||||
- a comma-separated list of keyvalue-pairs of the form
|
||||
<+/-/:><key>[=<value>]. A '+' denotes adding an annotation, a
|
||||
'-' denotes removing an annotation, and ':' denotes modifying an
|
||||
annotation.
|
||||
If setting or modifying annotations, a value must be provided.
|
||||
required: false
|
||||
version_added: "1.6"
|
||||
pkgsite:
|
||||
description:
|
||||
- for pkgng versions before 1.1.4, specify packagesite to use
|
||||
for downloading packages, if not specified, use settings from
|
||||
/usr/local/etc/pkg.conf
|
||||
for newer pkgng versions, specify a the name of a repository
|
||||
configured in /usr/local/etc/pkg/repos
|
||||
required: false
|
||||
author: bleader
|
||||
notes:
|
||||
- When using pkgsite, be careful that already in cache packages won't be downloaded again.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install package foo
|
||||
- pkgng: name=foo state=present
|
||||
|
||||
# Annotate package foo and bar
|
||||
- pkgng: name=foo,bar annotation=+test1=baz,-test2,:test3=foobar
|
||||
|
||||
# Remove packages foo and bar
|
||||
- pkgng: name=foo,bar state=absent
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
import shlex
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
def query_package(module, pkgng_path, name):
|
||||
|
||||
rc, out, err = module.run_command("%s info -g -e %s" % (pkgng_path, name))
|
||||
|
||||
if rc == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def pkgng_older_than(module, pkgng_path, compare_version):
|
||||
|
||||
rc, out, err = module.run_command("%s -v" % pkgng_path)
|
||||
version = map(lambda x: int(x), re.split(r'[\._]', out))
|
||||
|
||||
i = 0
|
||||
new_pkgng = True
|
||||
while compare_version[i] == version[i]:
|
||||
i += 1
|
||||
if i == min(len(compare_version), len(version)):
|
||||
break
|
||||
else:
|
||||
if compare_version[i] > version[i]:
|
||||
new_pkgng = False
|
||||
return not new_pkgng
|
||||
|
||||
|
||||
def remove_packages(module, pkgng_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, pkgng_path, package):
|
||||
continue
|
||||
|
||||
if not module.check_mode:
|
||||
rc, out, err = module.run_command("%s delete -y %s" % (pkgng_path, package))
|
||||
|
||||
if not module.check_mode and query_package(module, pkgng_path, package):
|
||||
module.fail_json(msg="failed to remove %s: %s" % (package, out))
|
||||
|
||||
remove_c += 1
|
||||
|
||||
if remove_c > 0:
|
||||
|
||||
return (True, "removed %s package(s)" % remove_c)
|
||||
|
||||
return (False, "package(s) already absent")
|
||||
|
||||
|
||||
def install_packages(module, pkgng_path, packages, cached, pkgsite):
|
||||
|
||||
install_c = 0
|
||||
|
||||
# as of pkg-1.1.4, PACKAGESITE is deprecated in favor of repository definitions
|
||||
# in /usr/local/etc/pkg/repos
|
||||
old_pkgng = pkgng_older_than(module, pkgng_path, [1, 1, 4])
|
||||
if pkgsite != "":
|
||||
if old_pkgng:
|
||||
pkgsite = "PACKAGESITE=%s" % (pkgsite)
|
||||
else:
|
||||
pkgsite = "-r %s" % (pkgsite)
|
||||
|
||||
if not module.check_mode and not cached:
|
||||
if old_pkgng:
|
||||
rc, out, err = module.run_command("%s %s update" % (pkgsite, pkgng_path))
|
||||
else:
|
||||
rc, out, err = module.run_command("%s update" % (pkgng_path))
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Could not update catalogue")
|
||||
|
||||
for package in packages:
|
||||
if query_package(module, pkgng_path, package):
|
||||
continue
|
||||
|
||||
if not module.check_mode:
|
||||
if old_pkgng:
|
||||
rc, out, err = module.run_command("%s %s install -g -U -y %s" % (pkgsite, pkgng_path, package))
|
||||
else:
|
||||
rc, out, err = module.run_command("%s install %s -g -U -y %s" % (pkgng_path, pkgsite, package))
|
||||
|
||||
if not module.check_mode and not query_package(module, pkgng_path, package):
|
||||
module.fail_json(msg="failed to install %s: %s" % (package, out), stderr=err)
|
||||
|
||||
install_c += 1
|
||||
|
||||
if install_c > 0:
|
||||
return (True, "added %s package(s)" % (install_c))
|
||||
|
||||
return (False, "package(s) already present")
|
||||
|
||||
def annotation_query(module, pkgng_path, package, tag):
|
||||
rc, out, err = module.run_command("%s info -g -A %s" % (pkgng_path, package))
|
||||
match = re.search(r'^\s*(?P<tag>%s)\s*:\s*(?P<value>\w+)' % tag, out, flags=re.MULTILINE)
|
||||
if match:
|
||||
return match.group('value')
|
||||
return False
|
||||
|
||||
|
||||
def annotation_add(module, pkgng_path, package, tag, value):
|
||||
_value = annotation_query(module, pkgng_path, package, tag)
|
||||
if not _value:
|
||||
# Annotation does not exist, add it.
|
||||
rc, out, err = module.run_command('%s annotate -y -A %s %s "%s"'
|
||||
% (pkgng_path, package, tag, value))
|
||||
if rc != 0:
|
||||
module.fail_json("could not annotate %s: %s"
|
||||
% (package, out), stderr=err)
|
||||
return True
|
||||
elif _value != value:
|
||||
# Annotation exists, but value differs
|
||||
module.fail_json(
|
||||
mgs="failed to annotate %s, because %s is already set to %s, but should be set to %s"
|
||||
% (package, tag, _value, value))
|
||||
return False
|
||||
else:
|
||||
# Annotation exists, nothing to do
|
||||
return False
|
||||
|
||||
def annotation_delete(module, pkgng_path, package, tag, value):
|
||||
_value = annotation_query(module, pkgng_path, package, tag)
|
||||
if _value:
|
||||
rc, out, err = module.run_command('%s annotate -y -D %s %s'
|
||||
% (pkgng_path, package, tag))
|
||||
if rc != 0:
|
||||
module.fail_json("could not delete annotation to %s: %s"
|
||||
% (package, out), stderr=err)
|
||||
return True
|
||||
return False
|
||||
|
||||
def annotation_modify(module, pkgng_path, package, tag, value):
|
||||
_value = annotation_query(module, pkgng_path, package, tag)
|
||||
if not value:
|
||||
# No such tag
|
||||
module.fail_json("could not change annotation to %s: tag %s does not exist"
|
||||
% (package, tag))
|
||||
elif _value == value:
|
||||
# No change in value
|
||||
return False
|
||||
else:
|
||||
rc,out,err = module.run_command('%s annotate -y -M %s %s "%s"'
|
||||
% (pkgng_path, package, tag, value))
|
||||
if rc != 0:
|
||||
module.fail_json("could not change annotation annotation to %s: %s"
|
||||
% (package, out), stderr=err)
|
||||
return True
|
||||
|
||||
|
||||
def annotate_packages(module, pkgng_path, packages, annotation):
|
||||
annotate_c = 0
|
||||
annotations = map(lambda _annotation:
|
||||
re.match(r'(?P<operation>[\+-:])(?P<tag>\w+)(=(?P<value>\w+))?',
|
||||
_annotation).groupdict(),
|
||||
re.split(r',', annotation))
|
||||
|
||||
operation = {
|
||||
'+': annotation_add,
|
||||
'-': annotation_delete,
|
||||
':': annotation_modify
|
||||
}
|
||||
|
||||
for package in packages:
|
||||
for _annotation in annotations:
|
||||
annotate_c += ( 1 if operation[_annotation['operation']](
|
||||
module, pkgng_path, package,
|
||||
_annotation['tag'], _annotation['value']) else 0 )
|
||||
|
||||
if annotate_c > 0:
|
||||
return (True, "added %s annotations." % annotate_c)
|
||||
return (False, "changed no annotations")
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default="present", choices=["present","absent"], required=False),
|
||||
name = dict(aliases=["pkg"], required=True),
|
||||
cached = dict(default=False, type='bool'),
|
||||
annotation = dict(default="", required=False),
|
||||
pkgsite = dict(default="", required=False)),
|
||||
supports_check_mode = True)
|
||||
|
||||
pkgng_path = module.get_bin_path('pkg', True)
|
||||
|
||||
p = module.params
|
||||
|
||||
pkgs = p["name"].split(",")
|
||||
|
||||
changed = False
|
||||
msgs = []
|
||||
|
||||
if p["state"] == "present":
|
||||
_changed, _msg = install_packages(module, pkgng_path, pkgs, p["cached"], p["pkgsite"])
|
||||
changed = changed or _changed
|
||||
msgs.append(_msg)
|
||||
|
||||
elif p["state"] == "absent":
|
||||
_changed, _msg = remove_packages(module, pkgng_path, pkgs)
|
||||
changed = changed or _changed
|
||||
msgs.append(_msg)
|
||||
|
||||
if p["annotation"]:
|
||||
_changed, _msg = annotate_packages(module, pkgng_path, pkgs, p["annotation"])
|
||||
changed = changed or _changed
|
||||
msgs.append(_msg)
|
||||
|
||||
module.exit_json(changed=changed, msg=", ".join(msgs))
|
||||
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,179 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Alexander Winkler <mail () winkler-alexander.de>
|
||||
# based on svr4pkg by
|
||||
# Boyd Adamson <boyd () boydadamson.com> (2012)
|
||||
#
|
||||
# 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: pkgutil
|
||||
short_description: Manage CSW-Packages on Solaris
|
||||
description:
|
||||
- Manages CSW packages (SVR4 format) on Solaris 10 and 11.
|
||||
- These were the native packages on Solaris <= 10 and are available
|
||||
as a legacy feature in Solaris 11.
|
||||
- Pkgutil is an advanced packaging system, which resolves dependency on installation.
|
||||
It is designed for CSW packages.
|
||||
version_added: "1.3"
|
||||
author: Alexander Winkler
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Package name, e.g. (C(CSWnrpe))
|
||||
required: true
|
||||
site:
|
||||
description:
|
||||
- Specifies the repository path to install the package from.
|
||||
- Its global definition is done in C(/etc/opt/csw/pkgutil.conf).
|
||||
state:
|
||||
description:
|
||||
- Whether to install (C(present)), or remove (C(absent)) a package.
|
||||
- The upgrade (C(latest)) operation will update/install the package to the latest version available.
|
||||
- "Note: The module has a limitation that (C(latest)) only works for one package, not lists of them."
|
||||
required: true
|
||||
choices: ["present", "absent", "latest"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install a package
|
||||
pkgutil: name=CSWcommon state=present
|
||||
|
||||
# Install a package from a specific repository
|
||||
pkgutil: name=CSWnrpe site='ftp://myinternal.repo/opencsw/kiel state=latest'
|
||||
'''
|
||||
|
||||
import os
|
||||
import pipes
|
||||
|
||||
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))
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def package_latest(module, name, site):
|
||||
# Only supports one package
|
||||
cmd = [ 'pkgutil', '--single', '-c' ]
|
||||
if site is not None:
|
||||
cmd += [ '-t', pipes.quote(site) ]
|
||||
cmd.append(pipes.quote(name))
|
||||
cmd += [ '| tail -1 | grep -v SAME' ]
|
||||
rc, out, err = module.run_command(' '.join(cmd), use_unsafe_shell=True)
|
||||
if rc == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
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, state, name, site):
|
||||
cmd = [ 'pkgutil', '-iy' ]
|
||||
if site is not None:
|
||||
cmd += [ '-t', site ]
|
||||
if state == 'latest':
|
||||
cmd += [ '-f' ]
|
||||
cmd.append(name)
|
||||
(rc, out, err) = run_command(module, cmd)
|
||||
return (rc, out, err)
|
||||
|
||||
def package_upgrade(module, name, site):
|
||||
cmd = [ 'pkgutil', '-ufy' ]
|
||||
if site is not None:
|
||||
cmd += [ '-t', site ]
|
||||
cmd.append(name)
|
||||
(rc, out, err) = run_command(module, cmd)
|
||||
return (rc, out, err)
|
||||
|
||||
def package_uninstall(module, name):
|
||||
cmd = [ 'pkgutil', '-ry', name]
|
||||
(rc, out, err) = run_command(module, cmd)
|
||||
return (rc, out, err)
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required = True),
|
||||
state = dict(required = True, choices=['present', 'absent','latest']),
|
||||
site = dict(default = None),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
site = module.params['site']
|
||||
rc = None
|
||||
out = ''
|
||||
err = ''
|
||||
result = {}
|
||||
result['name'] = name
|
||||
result['state'] = state
|
||||
|
||||
if state == 'present':
|
||||
if not package_installed(module, name):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = package_install(module, state, name, site)
|
||||
# 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 == 'latest':
|
||||
if not package_installed(module, name):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = package_install(module, state, name, site)
|
||||
else:
|
||||
if not package_latest(module, name, site):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = package_upgrade(module, name, site)
|
||||
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)
|
||||
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)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,405 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Yap Sok Ann
|
||||
# Written by Yap Sok Ann <sokann@gmail.com>
|
||||
# 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: portage
|
||||
short_description: Package manager for Gentoo
|
||||
description:
|
||||
- Manages Gentoo packages
|
||||
version_added: "1.6"
|
||||
|
||||
options:
|
||||
package:
|
||||
description:
|
||||
- Package atom or set, e.g. C(sys-apps/foo) or C(>foo-2.13) or C(@world)
|
||||
required: false
|
||||
default: null
|
||||
|
||||
state:
|
||||
description:
|
||||
- State of the package atom
|
||||
required: false
|
||||
default: "present"
|
||||
choices: [ "present", "installed", "emerged", "absent", "removed", "unmerged" ]
|
||||
|
||||
update:
|
||||
description:
|
||||
- Update packages to the best version available (--update)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
deep:
|
||||
description:
|
||||
- Consider the entire dependency tree of packages (--deep)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
newuse:
|
||||
description:
|
||||
- Include installed packages where USE flags have changed (--newuse)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
changed_use:
|
||||
description:
|
||||
- Include installed packages where USE flags have changed, except when
|
||||
- flags that the user has not enabled are added or removed
|
||||
- (--changed-use)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
version_added: 1.8
|
||||
|
||||
oneshot:
|
||||
description:
|
||||
- Do not add the packages to the world file (--oneshot)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
noreplace:
|
||||
description:
|
||||
- Do not re-emerge installed packages (--noreplace)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
nodeps:
|
||||
description:
|
||||
- Only merge packages but not their dependencies (--nodeps)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
onlydeps:
|
||||
description:
|
||||
- Only merge packages' dependencies but not the packages (--onlydeps)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
depclean:
|
||||
description:
|
||||
- Remove packages not needed by explicitly merged packages (--depclean)
|
||||
- If no package is specified, clean up the world's dependencies
|
||||
- Otherwise, --depclean serves as a dependency aware version of --unmerge
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
quiet:
|
||||
description:
|
||||
- Run emerge in quiet mode (--quiet)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
verbose:
|
||||
description:
|
||||
- Run emerge in verbose mode (--verbose)
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes" ]
|
||||
|
||||
sync:
|
||||
description:
|
||||
- Sync package repositories first
|
||||
- If yes, perform "emerge --sync"
|
||||
- If web, perform "emerge-webrsync"
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "yes", "web" ]
|
||||
|
||||
requirements: [ gentoolkit ]
|
||||
author: Yap Sok Ann
|
||||
notes: []
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Make sure package foo is installed
|
||||
- portage: package=foo state=present
|
||||
|
||||
# Make sure package foo is not installed
|
||||
- portage: package=foo state=absent
|
||||
|
||||
# Update package foo to the "best" version
|
||||
- portage: package=foo update=yes
|
||||
|
||||
# Sync repositories and update world
|
||||
- portage: package=@world update=yes deep=yes sync=yes
|
||||
|
||||
# Remove unneeded packages
|
||||
- portage: depclean=yes
|
||||
|
||||
# Remove package foo if it is not explicitly needed
|
||||
- portage: package=foo state=absent depclean=yes
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
import pipes
|
||||
|
||||
|
||||
def query_package(module, package, action):
|
||||
if package.startswith('@'):
|
||||
return query_set(module, package, action)
|
||||
return query_atom(module, package, action)
|
||||
|
||||
|
||||
def query_atom(module, atom, action):
|
||||
cmd = '%s list %s' % (module.equery_path, atom)
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
return rc == 0
|
||||
|
||||
|
||||
def query_set(module, set, action):
|
||||
system_sets = [
|
||||
'@live-rebuild',
|
||||
'@module-rebuild',
|
||||
'@preserved-rebuild',
|
||||
'@security',
|
||||
'@selected',
|
||||
'@system',
|
||||
'@world',
|
||||
'@x11-module-rebuild',
|
||||
]
|
||||
|
||||
if set in system_sets:
|
||||
if action == 'unmerge':
|
||||
module.fail_json(msg='set %s cannot be removed' % set)
|
||||
return False
|
||||
|
||||
world_sets_path = '/var/lib/portage/world_sets'
|
||||
if not os.path.exists(world_sets_path):
|
||||
return False
|
||||
|
||||
cmd = 'grep %s %s' % (set, world_sets_path)
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
return rc == 0
|
||||
|
||||
|
||||
def sync_repositories(module, webrsync=False):
|
||||
if module.check_mode:
|
||||
module.exit_json(msg='check mode not supported by sync')
|
||||
|
||||
if webrsync:
|
||||
webrsync_path = module.get_bin_path('emerge-webrsync', required=True)
|
||||
cmd = '%s --quiet' % webrsync_path
|
||||
else:
|
||||
cmd = '%s --sync --quiet' % module.emerge_path
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
if rc != 0:
|
||||
module.fail_json(msg='could not sync package repositories')
|
||||
|
||||
|
||||
# Note: In the 3 functions below, equery is done one-by-one, but emerge is done
|
||||
# in one go. If that is not desirable, split the packages into multiple tasks
|
||||
# instead of joining them together with comma.
|
||||
|
||||
|
||||
def emerge_packages(module, packages):
|
||||
p = module.params
|
||||
|
||||
if not (p['update'] or p['noreplace']):
|
||||
for package in packages:
|
||||
if not query_package(module, package, 'emerge'):
|
||||
break
|
||||
else:
|
||||
module.exit_json(changed=False, msg='Packages already present.')
|
||||
|
||||
args = []
|
||||
emerge_flags = {
|
||||
'update': '--update',
|
||||
'deep': '--deep',
|
||||
'newuse': '--newuse',
|
||||
'changed_use': '--changed-use',
|
||||
'oneshot': '--oneshot',
|
||||
'noreplace': '--noreplace',
|
||||
'nodeps': '--nodeps',
|
||||
'onlydeps': '--onlydeps',
|
||||
'quiet': '--quiet',
|
||||
'verbose': '--verbose',
|
||||
}
|
||||
for flag, arg in emerge_flags.iteritems():
|
||||
if p[flag]:
|
||||
args.append(arg)
|
||||
|
||||
cmd, (rc, out, err) = run_emerge(module, packages, *args)
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
cmd=cmd, rc=rc, stdout=out, stderr=err,
|
||||
msg='Packages not installed.',
|
||||
)
|
||||
|
||||
changed = True
|
||||
for line in out.splitlines():
|
||||
if line.startswith('>>> Emerging (1 of'):
|
||||
break
|
||||
else:
|
||||
changed = False
|
||||
|
||||
module.exit_json(
|
||||
changed=changed, cmd=cmd, rc=rc, stdout=out, stderr=err,
|
||||
msg='Packages installed.',
|
||||
)
|
||||
|
||||
|
||||
def unmerge_packages(module, packages):
|
||||
p = module.params
|
||||
|
||||
for package in packages:
|
||||
if query_package(module, package, 'unmerge'):
|
||||
break
|
||||
else:
|
||||
module.exit_json(changed=False, msg='Packages already absent.')
|
||||
|
||||
args = ['--unmerge']
|
||||
|
||||
for flag in ['quiet', 'verbose']:
|
||||
if p[flag]:
|
||||
args.append('--%s' % flag)
|
||||
|
||||
cmd, (rc, out, err) = run_emerge(module, packages, *args)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(
|
||||
cmd=cmd, rc=rc, stdout=out, stderr=err,
|
||||
msg='Packages not removed.',
|
||||
)
|
||||
|
||||
module.exit_json(
|
||||
changed=True, cmd=cmd, rc=rc, stdout=out, stderr=err,
|
||||
msg='Packages removed.',
|
||||
)
|
||||
|
||||
|
||||
def cleanup_packages(module, packages):
|
||||
p = module.params
|
||||
|
||||
if packages:
|
||||
for package in packages:
|
||||
if query_package(module, package, 'unmerge'):
|
||||
break
|
||||
else:
|
||||
module.exit_json(changed=False, msg='Packages already absent.')
|
||||
|
||||
args = ['--depclean']
|
||||
|
||||
for flag in ['quiet', 'verbose']:
|
||||
if p[flag]:
|
||||
args.append('--%s' % flag)
|
||||
|
||||
cmd, (rc, out, err) = run_emerge(module, packages, *args)
|
||||
if rc != 0:
|
||||
module.fail_json(cmd=cmd, rc=rc, stdout=out, stderr=err)
|
||||
|
||||
removed = 0
|
||||
for line in out.splitlines():
|
||||
if not line.startswith('Number removed:'):
|
||||
continue
|
||||
parts = line.split(':')
|
||||
removed = int(parts[1].strip())
|
||||
changed = removed > 0
|
||||
|
||||
module.exit_json(
|
||||
changed=changed, cmd=cmd, rc=rc, stdout=out, stderr=err,
|
||||
msg='Depclean completed.',
|
||||
)
|
||||
|
||||
|
||||
def run_emerge(module, packages, *args):
|
||||
args = list(args)
|
||||
|
||||
if module.check_mode:
|
||||
args.append('--pretend')
|
||||
|
||||
cmd = [module.emerge_path] + args + packages
|
||||
return cmd, module.run_command(cmd)
|
||||
|
||||
|
||||
portage_present_states = ['present', 'emerged', 'installed']
|
||||
portage_absent_states = ['absent', 'unmerged', 'removed']
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
package=dict(default=None, aliases=['name']),
|
||||
state=dict(
|
||||
default=portage_present_states[0],
|
||||
choices=portage_present_states + portage_absent_states,
|
||||
),
|
||||
update=dict(default=None, choices=['yes']),
|
||||
deep=dict(default=None, choices=['yes']),
|
||||
newuse=dict(default=None, choices=['yes']),
|
||||
changed_use=dict(default=None, choices=['yes']),
|
||||
oneshot=dict(default=None, choices=['yes']),
|
||||
noreplace=dict(default=None, choices=['yes']),
|
||||
nodeps=dict(default=None, choices=['yes']),
|
||||
onlydeps=dict(default=None, choices=['yes']),
|
||||
depclean=dict(default=None, choices=['yes']),
|
||||
quiet=dict(default=None, choices=['yes']),
|
||||
verbose=dict(default=None, choices=['yes']),
|
||||
sync=dict(default=None, choices=['yes', 'web']),
|
||||
),
|
||||
required_one_of=[['package', 'sync', 'depclean']],
|
||||
mutually_exclusive=[['nodeps', 'onlydeps'], ['quiet', 'verbose']],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
module.emerge_path = module.get_bin_path('emerge', required=True)
|
||||
module.equery_path = module.get_bin_path('equery', required=True)
|
||||
|
||||
p = module.params
|
||||
|
||||
if p['sync']:
|
||||
sync_repositories(module, webrsync=(p['sync'] == 'web'))
|
||||
if not p['package']:
|
||||
module.exit_json(msg='Sync successfully finished.')
|
||||
|
||||
packages = p['package'].split(',') if p['package'] else []
|
||||
|
||||
if p['depclean']:
|
||||
if packages and p['state'] not in portage_absent_states:
|
||||
module.fail_json(
|
||||
msg='Depclean can only be used with package when the state is '
|
||||
'one of: %s' % portage_absent_states,
|
||||
)
|
||||
|
||||
cleanup_packages(module, packages)
|
||||
|
||||
elif p['state'] in portage_present_states:
|
||||
emerge_packages(module, packages)
|
||||
|
||||
elif p['state'] in portage_absent_states:
|
||||
unmerge_packages(module, packages)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,207 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, berenddeboer
|
||||
# Written by berenddeboer <berend@pobox.com>
|
||||
# Based on pkgng module written by bleader <bleader at ratonland.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: portinstall
|
||||
short_description: Installing packages from FreeBSD's ports system
|
||||
description:
|
||||
- Manage packages for FreeBSD using 'portinstall'.
|
||||
version_added: "1.3"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- name of package to install/remove
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- state of the package
|
||||
choices: [ 'present', 'absent' ]
|
||||
required: false
|
||||
default: present
|
||||
use_packages:
|
||||
description:
|
||||
- use packages instead of ports whenever available
|
||||
choices: [ 'yes', 'no' ]
|
||||
required: false
|
||||
default: yes
|
||||
author: berenddeboer
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install package foo
|
||||
- portinstall: name=foo state=present
|
||||
|
||||
# Install package security/cyrus-sasl2-saslauthd
|
||||
- portinstall: name=security/cyrus-sasl2-saslauthd state=present
|
||||
|
||||
# Remove packages foo and bar
|
||||
- portinstall: name=foo,bar state=absent
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
import shlex
|
||||
import os
|
||||
import sys
|
||||
|
||||
def query_package(module, name):
|
||||
|
||||
pkg_info_path = module.get_bin_path('pkg_info', False)
|
||||
|
||||
# Assume that if we have pkg_info, we haven't upgraded to pkgng
|
||||
if pkg_info_path:
|
||||
pkgng = False
|
||||
pkg_glob_path = module.get_bin_path('pkg_glob', True)
|
||||
rc, out, err = module.run_command("%s -e `pkg_glob %s`" % (pkg_info_path, pipes.quote(name)), use_unsafe_shell=True)
|
||||
else:
|
||||
pkgng = True
|
||||
pkg_info_path = module.get_bin_path('pkg', True)
|
||||
pkg_info_path = pkg_info_path + " info"
|
||||
rc, out, err = module.run_command("%s %s" % (pkg_info_path, name))
|
||||
|
||||
found = rc == 0
|
||||
|
||||
if not found:
|
||||
# databases/mysql55-client installs as mysql-client, so try solving
|
||||
# that the ugly way. Pity FreeBSD doesn't have a fool proof way of checking
|
||||
# some package is installed
|
||||
name_without_digits = re.sub('[0-9]', '', name)
|
||||
if name != name_without_digits:
|
||||
if pkgng:
|
||||
rc, out, err = module.run_command("%s %s" % (pkg_info_path, name_without_digits))
|
||||
else:
|
||||
rc, out, err = module.run_command("%s %s" % (pkg_info_path, name_without_digits))
|
||||
|
||||
found = rc == 0
|
||||
|
||||
return found
|
||||
|
||||
|
||||
def matching_packages(module, name):
|
||||
|
||||
ports_glob_path = module.get_bin_path('ports_glob', True)
|
||||
rc, out, err = module.run_command("%s %s" % (ports_glob_path, name))
|
||||
#counts the numer of packages found
|
||||
occurrences = out.count('\n')
|
||||
if occurrences == 0:
|
||||
name_without_digits = re.sub('[0-9]', '', name)
|
||||
if name != name_without_digits:
|
||||
rc, out, err = module.run_command("%s %s" % (ports_glob_path, name_without_digits))
|
||||
occurrences = out.count('\n')
|
||||
return occurrences
|
||||
|
||||
|
||||
def remove_packages(module, packages):
|
||||
|
||||
remove_c = 0
|
||||
pkg_glob_path = module.get_bin_path('pkg_glob', True)
|
||||
|
||||
# If pkg_delete not found, we assume pkgng
|
||||
pkg_delete_path = module.get_bin_path('pkg_delete', False)
|
||||
if not pkg_delete_path:
|
||||
pkg_delete_path = module.get_bin_path('pkg', True)
|
||||
pkg_delete_path = pkg_delete_path + " delete -y"
|
||||
|
||||
# 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, out, err = module.run_command("%s `%s %s`" % (pkg_delete_path, pkg_glob_path, pipes.quote(package)), use_unsafe_shell=True)
|
||||
|
||||
if query_package(module, package):
|
||||
name_without_digits = re.sub('[0-9]', '', package)
|
||||
rc, out, err = module.run_command("%s `%s %s`" % (pkg_delete_path, pkg_glob_path, pipes.quote(name_without_digits)),use_unsafe_shell=True)
|
||||
if query_package(module, 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, packages, use_packages):
|
||||
|
||||
install_c = 0
|
||||
|
||||
# If portinstall not found, automagically install
|
||||
portinstall_path = module.get_bin_path('portinstall', False)
|
||||
if not portinstall_path:
|
||||
pkg_path = module.get_bin_path('pkg', False)
|
||||
if pkg_path:
|
||||
module.run_command("pkg install -y portupgrade")
|
||||
portinstall_path = module.get_bin_path('portinstall', True)
|
||||
|
||||
if use_packages == "yes":
|
||||
portinstall_params="--use-packages"
|
||||
else:
|
||||
portinstall_params=""
|
||||
|
||||
for package in packages:
|
||||
if query_package(module, package):
|
||||
continue
|
||||
|
||||
# TODO: check how many match
|
||||
matches = matching_packages(module, package)
|
||||
if matches == 1:
|
||||
rc, out, err = module.run_command("%s --batch %s %s" % (portinstall_path, portinstall_params, package))
|
||||
if not query_package(module, package):
|
||||
module.fail_json(msg="failed to install %s: %s" % (package, out))
|
||||
elif matches == 0:
|
||||
module.fail_json(msg="no matches for package %s" % (package))
|
||||
else:
|
||||
module.fail_json(msg="%s matches found for package name %s" % (matches, package))
|
||||
|
||||
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),
|
||||
use_packages = dict(type='bool', default='yes')))
|
||||
|
||||
p = module.params
|
||||
|
||||
pkgs = p["name"].split(",")
|
||||
|
||||
if p["state"] == "present":
|
||||
install_packages(module, pkgs, p["use_packages"])
|
||||
|
||||
elif p["state"] == "absent":
|
||||
remove_packages(module, pkgs)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,234 +0,0 @@
|
||||
#!/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.
|
||||
response_file:
|
||||
description:
|
||||
- Specifies the location of a response file to be used if package expects input on install. (added in Ansible 1.4)
|
||||
required: false
|
||||
zone:
|
||||
description:
|
||||
- Whether to install the package only in the current zone, or install it into all zones.
|
||||
- The installation into all zones works only if you are working with the global zone.
|
||||
required: false
|
||||
default: "all"
|
||||
choices: ["current", "all"]
|
||||
version_added: "1.6"
|
||||
category:
|
||||
description:
|
||||
- Install/Remove category instead of a single package.
|
||||
required: false
|
||||
choices: ["true", "false"]
|
||||
version_added: "1.6"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install a package from an already copied file
|
||||
- svr4pkg: name=CSWcommon src=/tmp/cswpkgs.pkg state=present
|
||||
|
||||
# Install a package directly from an http site
|
||||
- svr4pkg: name=CSWpkgutil src=http://get.opencsw.org/now state=present zone=current
|
||||
|
||||
# Install a package with a response file
|
||||
- svr4pkg: name=CSWggrep src=/tmp/third-party.pkg response_file=/tmp/ggrep.response state=present
|
||||
|
||||
# Ensure that a package is not installed.
|
||||
- svr4pkg: name=SUNWgnome-sound-recorder state=absent
|
||||
|
||||
# Ensure that a category is not installed.
|
||||
- svr4pkg: name=FIREFOX state=absent category=true
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
def package_installed(module, name, category):
|
||||
cmd = [module.get_bin_path('pkginfo', True)]
|
||||
cmd.append('-q')
|
||||
if category:
|
||||
cmd.append('-c')
|
||||
cmd.append(name)
|
||||
rc, out, err = module.run_command(' '.join(cmd))
|
||||
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, response_file, zone, category):
|
||||
adminfile = create_admin_file()
|
||||
cmd = [ 'pkgadd', '-n']
|
||||
if zone == 'current':
|
||||
cmd += [ '-G' ]
|
||||
cmd += [ '-a', adminfile, '-d', src ]
|
||||
if proxy is not None:
|
||||
cmd += [ '-x', proxy ]
|
||||
if response_file is not None:
|
||||
cmd += [ '-r', response_file ]
|
||||
if category:
|
||||
cmd += [ '-Y' ]
|
||||
cmd.append(name)
|
||||
(rc, out, err) = run_command(module, cmd)
|
||||
os.unlink(adminfile)
|
||||
return (rc, out, err)
|
||||
|
||||
def package_uninstall(module, name, src, category):
|
||||
adminfile = create_admin_file()
|
||||
if category:
|
||||
cmd = [ 'pkgrm', '-na', adminfile, '-Y', name ]
|
||||
else:
|
||||
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),
|
||||
response_file = dict(default = None),
|
||||
zone = dict(required=False, default = 'all', choices=['current','all']),
|
||||
category = dict(default=False, type='bool')
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
state = module.params['state']
|
||||
name = module.params['name']
|
||||
src = module.params['src']
|
||||
proxy = module.params['proxy']
|
||||
response_file = module.params['response_file']
|
||||
zone = module.params['zone']
|
||||
category = module.params['category']
|
||||
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, category):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = package_install(module, name, src, proxy, response_file, zone, category)
|
||||
# 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, category):
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = package_uninstall(module, name, src, category)
|
||||
out = out[:75]
|
||||
|
||||
# Success, Warning, Interruption, Reboot all, Reboot this return codes
|
||||
if rc in (0, 2, 3, 10, 20):
|
||||
result['changed'] = True
|
||||
# no install nor uninstall, or failed
|
||||
else:
|
||||
result['changed'] = False
|
||||
|
||||
# Fatal error, Administration, Administration Interaction return codes
|
||||
if rc in (1, 4 , 5):
|
||||
result['failed'] = True
|
||||
else:
|
||||
result['failed'] = False
|
||||
|
||||
if out:
|
||||
result['stdout'] = out
|
||||
if err:
|
||||
result['stderr'] = err
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,196 +0,0 @@
|
||||
#!/usr/bin/python -tt
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Raul Melo
|
||||
# Written by Raul Melo <raulmelo@gmail.com>
|
||||
# Based on yum module written by Seth Vidal <skvidal at fedoraproject.org>
|
||||
#
|
||||
# This module is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import pipes
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: swdepot
|
||||
short_description: Manage packages with swdepot package manager (HP-UX)
|
||||
description:
|
||||
- Will install, upgrade and remove packages with swdepot package manager (HP-UX)
|
||||
version_added: "1.4"
|
||||
notes: []
|
||||
author: Raul Melo
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- package name.
|
||||
required: true
|
||||
default: null
|
||||
choices: []
|
||||
aliases: []
|
||||
version_added: 1.4
|
||||
state:
|
||||
description:
|
||||
- whether to install (C(present), C(latest)), or remove (C(absent)) a package.
|
||||
required: true
|
||||
default: null
|
||||
choices: [ 'present', 'latest', 'absent']
|
||||
aliases: []
|
||||
version_added: 1.4
|
||||
depot:
|
||||
description:
|
||||
- The source repository from which install or upgrade a package.
|
||||
required: false
|
||||
default: null
|
||||
choices: []
|
||||
aliases: []
|
||||
version_added: 1.4
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- swdepot: name=unzip-6.0 state=installed depot=repository:/path
|
||||
- swdepot: name=unzip state=latest depot=repository:/path
|
||||
- swdepot: name=unzip state=absent
|
||||
'''
|
||||
|
||||
def compare_package(version1, version2):
|
||||
""" Compare version packages.
|
||||
Return values:
|
||||
-1 first minor
|
||||
0 equal
|
||||
1 fisrt greater """
|
||||
|
||||
def normalize(v):
|
||||
return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")]
|
||||
return cmp(normalize(version1), normalize(version2))
|
||||
|
||||
def query_package(module, name, depot=None):
|
||||
""" Returns whether a package is installed or not and version. """
|
||||
|
||||
cmd_list = '/usr/sbin/swlist -a revision -l product'
|
||||
if depot:
|
||||
rc, stdout, stderr = module.run_command("%s -s %s %s | grep %s" % (cmd_list, pipes.quote(depot), pipes.quote(name), pipes.quote(name)), use_unsafe_shell=True)
|
||||
else:
|
||||
rc, stdout, stderr = module.run_command("%s %s | grep %s" % (cmd_list, pipes.quote(name), pipes.quote(name)), use_unsafe_shell=True)
|
||||
if rc == 0:
|
||||
version = re.sub("\s\s+|\t" , " ", stdout).strip().split()[1]
|
||||
else:
|
||||
version = None
|
||||
|
||||
return rc, version
|
||||
|
||||
def remove_package(module, name):
|
||||
""" Uninstall package if installed. """
|
||||
|
||||
cmd_remove = '/usr/sbin/swremove'
|
||||
rc, stdout, stderr = module.run_command("%s %s" % (cmd_remove, name))
|
||||
|
||||
if rc == 0:
|
||||
return rc, stdout
|
||||
else:
|
||||
return rc, stderr
|
||||
|
||||
def install_package(module, depot, name):
|
||||
""" Install package if not already installed """
|
||||
|
||||
cmd_install = '/usr/sbin/swinstall -x mount_all_filesystems=false'
|
||||
rc, stdout, stderr = module.run_command("%s -s %s %s" % (cmd_install, depot, name))
|
||||
if rc == 0:
|
||||
return rc, stdout
|
||||
else:
|
||||
return rc, stderr
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(aliases=['pkg'], required=True),
|
||||
state = dict(choices=['present', 'absent', 'latest'], required=True),
|
||||
depot = dict(default=None, required=False)
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
depot = module.params['depot']
|
||||
|
||||
changed = False
|
||||
msg = "No changed"
|
||||
rc = 0
|
||||
if ( state == 'present' or state == 'latest' ) and depot == None:
|
||||
output = "depot parameter is mandatory in present or latest task"
|
||||
module.fail_json(name=name, msg=output, rc=rc)
|
||||
|
||||
|
||||
#Check local version
|
||||
rc, version_installed = query_package(module, name)
|
||||
if not rc:
|
||||
installed = True
|
||||
msg = "Already installed"
|
||||
|
||||
else:
|
||||
installed = False
|
||||
|
||||
if ( state == 'present' or state == 'latest' ) and installed == False:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
rc, output = install_package(module, depot, name)
|
||||
|
||||
if not rc:
|
||||
changed = True
|
||||
msg = "Packaged installed"
|
||||
|
||||
else:
|
||||
module.fail_json(name=name, msg=output, rc=rc)
|
||||
|
||||
elif state == 'latest' and installed == True:
|
||||
#Check depot version
|
||||
rc, version_depot = query_package(module, name, depot)
|
||||
|
||||
if not rc:
|
||||
if compare_package(version_installed,version_depot) == -1:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
#Install new version
|
||||
rc, output = install_package(module, depot, name)
|
||||
|
||||
if not rc:
|
||||
msg = "Packge upgraded, Before " + version_installed + " Now " + version_depot
|
||||
changed = True
|
||||
|
||||
else:
|
||||
module.fail_json(name=name, msg=output, rc=rc)
|
||||
|
||||
else:
|
||||
output = "Software package not in repository " + depot
|
||||
module.fail_json(name=name, msg=output, rc=rc)
|
||||
|
||||
elif state == 'absent' and installed == True:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
rc, output = remove_package(module, name)
|
||||
if not rc:
|
||||
changed = True
|
||||
msg = "Package removed"
|
||||
else:
|
||||
module.fail_json(name=name, msg=output, rc=rc)
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
module.exit_json(changed=changed, name=name, state=state, msg=msg)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
||||
|
@ -1,200 +0,0 @@
|
||||
#!/usr/bin/python -tt
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Philippe Makowski
|
||||
# Written by Philippe Makowski <philippem@mageia.org>
|
||||
# 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: urpmi
|
||||
short_description: Urpmi manager
|
||||
description:
|
||||
- Manages packages with I(urpmi) (such as for Mageia or Mandriva)
|
||||
version_added: "1.3.4"
|
||||
options:
|
||||
pkg:
|
||||
description:
|
||||
- name of package to install, upgrade or remove.
|
||||
required: true
|
||||
default: null
|
||||
state:
|
||||
description:
|
||||
- Indicates the desired package state
|
||||
required: false
|
||||
default: present
|
||||
choices: [ "absent", "present" ]
|
||||
update_cache:
|
||||
description:
|
||||
- update the package database first C(urpmi.update -a).
|
||||
required: false
|
||||
default: no
|
||||
choices: [ "yes", "no" ]
|
||||
no-suggests:
|
||||
description:
|
||||
- Corresponds to the C(--no-suggests) option for I(urpmi).
|
||||
required: false
|
||||
default: yes
|
||||
choices: [ "yes", "no" ]
|
||||
force:
|
||||
description:
|
||||
- Corresponds to the C(--force) option for I(urpmi).
|
||||
required: false
|
||||
default: yes
|
||||
choices: [ "yes", "no" ]
|
||||
author: Philippe Makowski
|
||||
notes: []
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# install package foo
|
||||
- urpmi: pkg=foo state=present
|
||||
# remove package foo
|
||||
- urpmi: pkg=foo state=absent
|
||||
# description: remove packages foo and bar
|
||||
- urpmi: pkg=foo,bar state=absent
|
||||
# description: update the package database (urpmi.update -a -q) and install bar (bar will be the updated if a newer version exists)
|
||||
- urpmi: name=bar, state=present, update_cache=yes
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
import shlex
|
||||
import os
|
||||
import sys
|
||||
|
||||
URPMI_PATH = '/usr/sbin/urpmi'
|
||||
URPME_PATH = '/usr/sbin/urpme'
|
||||
|
||||
def query_package(module, name):
|
||||
# rpm -q returns 0 if the package is installed,
|
||||
# 1 if it is not installed
|
||||
cmd = "rpm -q %s" % (name)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
if rc == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def query_package_provides(module, name):
|
||||
# rpm -q returns 0 if the package is installed,
|
||||
# 1 if it is not installed
|
||||
cmd = "rpm -q --provides %s" % (name)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
return rc == 0
|
||||
|
||||
|
||||
def update_package_db(module):
|
||||
cmd = "urpmi.update -a -q"
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
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
|
||||
|
||||
cmd = "%s --auto %s" % (URPME_PATH, package)
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
|
||||
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, pkgspec, force=True, no_suggests=True):
|
||||
|
||||
packages = ""
|
||||
for package in pkgspec:
|
||||
if not query_package_provides(module, package):
|
||||
packages += "'%s' " % package
|
||||
|
||||
if len(packages) != 0:
|
||||
if no_suggests:
|
||||
no_suggests_yes = '--no-suggests'
|
||||
else:
|
||||
no_suggests_yes = ''
|
||||
|
||||
if force:
|
||||
force_yes = '--force'
|
||||
else:
|
||||
force_yes = ''
|
||||
|
||||
cmd = ("%s --auto %s --quiet %s %s" % (URPMI_PATH, force_yes, no_suggests_yes, packages))
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
|
||||
installed = True
|
||||
for packages in pkgspec:
|
||||
if not query_package_provides(module, package):
|
||||
installed = False
|
||||
|
||||
# urpmi always have 0 for exit code if --force is used
|
||||
if rc or not installed:
|
||||
module.fail_json(msg="'urpmi %s' failed: %s" % (packages, err))
|
||||
else:
|
||||
module.exit_json(changed=True, msg="%s present(s)" % packages)
|
||||
else:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='installed', choices=['installed', 'removed', 'absent', 'present']),
|
||||
update_cache = dict(default=False, aliases=['update-cache'], type='bool'),
|
||||
force = dict(default=True, type='bool'),
|
||||
no_suggests = dict(default=True, aliases=['no-suggests'], type='bool'),
|
||||
package = dict(aliases=['pkg', 'name'], required=True)))
|
||||
|
||||
|
||||
if not os.path.exists(URPMI_PATH):
|
||||
module.fail_json(msg="cannot find urpmi, looking for %s" % (URPMI_PATH))
|
||||
|
||||
p = module.params
|
||||
|
||||
force_yes = p['force']
|
||||
no_suggest_yes = p['no_suggests']
|
||||
|
||||
if p['update_cache']:
|
||||
update_package_db(module)
|
||||
|
||||
packages = p['package'].split(',')
|
||||
|
||||
if p['state'] in [ 'installed', 'present' ]:
|
||||
install_packages(module, packages, force_yes, no_suggest_yes)
|
||||
|
||||
elif p['state'] in [ 'removed', 'absent' ]:
|
||||
remove_packages(module, packages)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,260 +0,0 @@
|
||||
#!/usr/bin/python -tt
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Patrick Callahan <pmc@patrickcallahan.com>
|
||||
# based on
|
||||
# openbsd_pkg
|
||||
# (c) 2013
|
||||
# Patrik Lundin <patrik.lundin.swe@gmail.com>
|
||||
#
|
||||
# yum
|
||||
# (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 re
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: zypper
|
||||
author: Patrick Callahan
|
||||
version_added: "1.2"
|
||||
short_description: Manage packages on SuSE and openSuSE
|
||||
description:
|
||||
- Manage packages on SuSE and openSuSE using the zypper and rpm tools.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- package name or package specifier wth version C(name) or C(name-1.0).
|
||||
required: true
|
||||
aliases: [ 'pkg' ]
|
||||
state:
|
||||
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.
|
||||
required: false
|
||||
choices: [ present, latest, absent ]
|
||||
default: "present"
|
||||
disable_gpg_check:
|
||||
description:
|
||||
- Whether to disable to GPG signature checking of the package
|
||||
signature being installed. Has an effect only if state is
|
||||
I(present) or I(latest).
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: []
|
||||
disable_recommends:
|
||||
version_added: "1.8"
|
||||
description:
|
||||
- Corresponds to the C(--no-recommends) option for I(zypper). Default behavior (C(yes)) modifies zypper's default behavior; C(no) does install recommended packages.
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: [ "yes", "no" ]
|
||||
|
||||
notes: []
|
||||
# informational: requirements for nodes
|
||||
requirements: [ zypper, rpm ]
|
||||
author: Patrick Callahan
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Install "nmap"
|
||||
- zypper: name=nmap state=present
|
||||
|
||||
# Install apache2 with recommended packages
|
||||
- zypper: name=apache2 state=present disable_recommends=no
|
||||
|
||||
# Remove the "nmap" package
|
||||
- zypper: name=nmap state=absent
|
||||
'''
|
||||
|
||||
# Function used for getting versions of currently installed packages.
|
||||
def get_current_version(m, name):
|
||||
cmd = ['/bin/rpm', '-q', '--qf', '%{NAME} %{VERSION}-%{RELEASE}\n']
|
||||
cmd.extend(name)
|
||||
(rc, stdout, stderr) = m.run_command(cmd)
|
||||
|
||||
current_version = {}
|
||||
rpmoutput_re = re.compile('^(\S+) (\S+)$')
|
||||
for stdoutline, package in zip(stdout.splitlines(), name):
|
||||
m = rpmoutput_re.match(stdoutline)
|
||||
if m == None:
|
||||
return None
|
||||
rpmpackage = m.group(1)
|
||||
rpmversion = m.group(2)
|
||||
if package != rpmpackage:
|
||||
return None
|
||||
current_version[package] = rpmversion
|
||||
|
||||
return current_version
|
||||
|
||||
# Function used to find out if a package is currently installed.
|
||||
def get_package_state(m, packages):
|
||||
cmd = ['/bin/rpm', '--query', '--qf', 'package %{NAME} is installed\n']
|
||||
cmd.extend(packages)
|
||||
|
||||
rc, stdout, stderr = m.run_command(cmd, check_rc=False)
|
||||
|
||||
installed_state = {}
|
||||
rpmoutput_re = re.compile('^package (\S+) (.*)$')
|
||||
for stdoutline, name in zip(stdout.splitlines(), packages):
|
||||
m = rpmoutput_re.match(stdoutline)
|
||||
if m == None:
|
||||
return None
|
||||
package = m.group(1)
|
||||
result = m.group(2)
|
||||
if not name.startswith(package):
|
||||
print name + ':' + package + ':' + stdoutline + '\n'
|
||||
return None
|
||||
if result == 'is installed':
|
||||
installed_state[name] = True
|
||||
else:
|
||||
installed_state[name] = False
|
||||
|
||||
return installed_state
|
||||
|
||||
# Function used to make sure a package is present.
|
||||
def package_present(m, name, installed_state, disable_gpg_check, disable_recommends):
|
||||
packages = []
|
||||
for package in name:
|
||||
if installed_state[package] is False:
|
||||
packages.append(package)
|
||||
if len(packages) != 0:
|
||||
cmd = ['/usr/bin/zypper', '--non-interactive']
|
||||
# add global options before zypper command
|
||||
if disable_gpg_check:
|
||||
cmd.append('--no-gpg-check')
|
||||
|
||||
cmd.extend(['install', '--auto-agree-with-licenses'])
|
||||
# add install parameter
|
||||
if disable_recommends:
|
||||
cmd.append('--no-recommends')
|
||||
cmd.extend(packages)
|
||||
rc, stdout, stderr = m.run_command(cmd, check_rc=False)
|
||||
|
||||
if rc == 0:
|
||||
changed=True
|
||||
else:
|
||||
changed=False
|
||||
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(m, name, installed_state, disable_gpg_check, disable_recommends):
|
||||
|
||||
# first of all, make sure all the packages are installed
|
||||
(rc, stdout, stderr, changed) = package_present(m, name, installed_state, disable_gpg_check)
|
||||
|
||||
# if we've already made a change, we don't have to check whether a version changed
|
||||
if not changed:
|
||||
pre_upgrade_versions = get_current_version(m, name)
|
||||
|
||||
cmd = ['/usr/bin/zypper', '--non-interactive', 'update', '--auto-agree-with-licenses']
|
||||
cmd.extend(name)
|
||||
rc, stdout, stderr = m.run_command(cmd, check_rc=False)
|
||||
|
||||
# if we've already made a change, we don't have to check whether a version changed
|
||||
if not changed:
|
||||
post_upgrade_versions = get_current_version(m, name)
|
||||
if pre_upgrade_versions != post_upgrade_versions:
|
||||
changed = True
|
||||
|
||||
return (rc, stdout, stderr, changed)
|
||||
|
||||
# Function used to make sure a package is not installed.
|
||||
def package_absent(m, name, installed_state):
|
||||
packages = []
|
||||
for package in name:
|
||||
if installed_state[package] is True:
|
||||
packages.append(package)
|
||||
if len(packages) != 0:
|
||||
cmd = ['/usr/bin/zypper', '--non-interactive', 'remove']
|
||||
cmd.extend(packages)
|
||||
rc, stdout, stderr = m.run_command(cmd)
|
||||
|
||||
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, aliases=['pkg'], type='list'),
|
||||
state = dict(required=False, default='present', choices=['absent', 'installed', 'latest', 'present', 'removed']),
|
||||
disable_gpg_check = dict(required=False, default='no', type='bool'),
|
||||
disable_recommends = dict(required=False, default='yes', type='bool'),
|
||||
),
|
||||
supports_check_mode = False
|
||||
)
|
||||
|
||||
|
||||
params = module.params
|
||||
|
||||
name = params['name']
|
||||
state = params['state']
|
||||
disable_gpg_check = params['disable_gpg_check']
|
||||
disable_recommends = params['disable_recommends']
|
||||
|
||||
rc = 0
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
result = {}
|
||||
result['name'] = name
|
||||
result['state'] = state
|
||||
|
||||
# Get package state
|
||||
installed_state = get_package_state(module, name)
|
||||
|
||||
# Perform requested action
|
||||
if state in ['installed', 'present']:
|
||||
(rc, stdout, stderr, changed) = package_present(module, name, installed_state, disable_gpg_check, disable_recommends)
|
||||
elif state in ['absent', 'removed']:
|
||||
(rc, stdout, stderr, changed) = package_absent(module, name, installed_state)
|
||||
elif state == 'latest':
|
||||
(rc, stdout, stderr, changed) = package_latest(module, name, installed_state, disable_gpg_check, disable_recommends)
|
||||
|
||||
if rc != 0:
|
||||
if stderr:
|
||||
module.fail_json(msg=stderr)
|
||||
else:
|
||||
module.fail_json(msg=stdout)
|
||||
|
||||
result['changed'] = changed
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,221 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
|
||||
# (c) 2013, Matthias Vogelgesang <matthias.vogelgesang@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: zypper_repository
|
||||
author: Matthias Vogelgesang
|
||||
version_added: "1.4"
|
||||
short_description: Add and remove Zypper repositories
|
||||
description:
|
||||
- Add or remove Zypper repositories on SUSE and openSUSE
|
||||
options:
|
||||
name:
|
||||
required: false
|
||||
default: none
|
||||
description:
|
||||
- A name for the repository. Not required when adding repofiles.
|
||||
repo:
|
||||
required: false
|
||||
default: none
|
||||
description:
|
||||
- URI of the repository or .repo file. Required when state=present.
|
||||
state:
|
||||
required: false
|
||||
choices: [ "absent", "present" ]
|
||||
default: "present"
|
||||
description:
|
||||
- A source string state.
|
||||
description:
|
||||
required: false
|
||||
default: none
|
||||
description:
|
||||
- A description of the repository
|
||||
disable_gpg_check:
|
||||
description:
|
||||
- Whether to disable GPG signature checking of
|
||||
all packages. Has an effect only if state is
|
||||
I(present).
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
aliases: []
|
||||
notes: []
|
||||
requirements: [ zypper ]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Add NVIDIA repository for graphics drivers
|
||||
- zypper_repository: name=nvidia-repo repo='ftp://download.nvidia.com/opensuse/12.2' state=present
|
||||
|
||||
# Remove NVIDIA repository
|
||||
- zypper_repository: name=nvidia-repo repo='ftp://download.nvidia.com/opensuse/12.2' state=absent
|
||||
|
||||
# Add python development repository
|
||||
- zypper_repository: repo=http://download.opensuse.org/repositories/devel:/languages:/python/SLE_11_SP3/devel:languages:python.repo
|
||||
'''
|
||||
from xml.dom.minidom import parseString as parseXML
|
||||
|
||||
REPO_OPTS = ['alias', 'name', 'priority', 'enabled', 'autorefresh', 'gpgcheck']
|
||||
|
||||
|
||||
def _parse_repos(module):
|
||||
"""parses the output of zypper -x lr and returns a parse repo dictionary"""
|
||||
cmd = ['/usr/bin/zypper', '-x', 'lr']
|
||||
repos = []
|
||||
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=True)
|
||||
dom = parseXML(stdout)
|
||||
repo_list = dom.getElementsByTagName('repo')
|
||||
for repo in repo_list:
|
||||
opts = {}
|
||||
for o in REPO_OPTS:
|
||||
opts[o] = repo.getAttribute(o)
|
||||
opts['url'] = repo.getElementsByTagName('url')[0].firstChild.data
|
||||
# A repo can be uniquely identified by an alias + url
|
||||
repos.append(opts)
|
||||
|
||||
return repos
|
||||
|
||||
|
||||
def repo_exists(module, **kwargs):
|
||||
|
||||
def repo_subset(realrepo, repocmp):
|
||||
for k in repocmp:
|
||||
if k not in realrepo:
|
||||
return False
|
||||
|
||||
for k, v in realrepo.items():
|
||||
if k in repocmp:
|
||||
if v.rstrip("/") != repocmp[k].rstrip("/"):
|
||||
return False
|
||||
return True
|
||||
|
||||
repos = _parse_repos(module)
|
||||
|
||||
for repo in repos:
|
||||
if repo_subset(repo, kwargs):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def add_repo(module, repo, alias, description, disable_gpg_check):
|
||||
cmd = ['/usr/bin/zypper', 'ar', '--check', '--refresh']
|
||||
|
||||
if description:
|
||||
cmd.extend(['--name', description])
|
||||
|
||||
if disable_gpg_check:
|
||||
cmd.append('--no-gpgcheck')
|
||||
|
||||
cmd.append(repo)
|
||||
|
||||
if not repo.endswith('.repo'):
|
||||
cmd.append(alias)
|
||||
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
changed = rc == 0
|
||||
if rc == 0:
|
||||
changed = True
|
||||
elif 'already exists. Please use another alias' in stderr:
|
||||
changed = False
|
||||
else:
|
||||
module.fail_json(msg=stderr if stderr else stdout)
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
def remove_repo(module, repo, alias):
|
||||
|
||||
cmd = ['/usr/bin/zypper', 'rr']
|
||||
if alias:
|
||||
cmd.append(alias)
|
||||
else:
|
||||
cmd.append(repo)
|
||||
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=True)
|
||||
changed = rc == 0
|
||||
return changed
|
||||
|
||||
|
||||
def fail_if_rc_is_null(module, rc, stdout, stderr):
|
||||
if rc != 0:
|
||||
module.fail_json(msg=stderr if stderr else stdout)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=False),
|
||||
repo=dict(required=False),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
description=dict(required=False),
|
||||
disable_gpg_check = dict(required=False, default='no', type='bool'),
|
||||
),
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
repo = module.params['repo']
|
||||
state = module.params['state']
|
||||
name = module.params['name']
|
||||
description = module.params['description']
|
||||
disable_gpg_check = module.params['disable_gpg_check']
|
||||
|
||||
def exit_unchanged():
|
||||
module.exit_json(changed=False, repo=repo, state=state, name=name)
|
||||
|
||||
# Check run-time module parameters
|
||||
if state == 'present' and not repo:
|
||||
module.fail_json(msg='Module option state=present requires repo')
|
||||
if state == 'absent' and not repo and not name:
|
||||
module.fail_json(msg='Alias or repo parameter required when state=absent')
|
||||
|
||||
if repo and repo.endswith('.repo'):
|
||||
if name:
|
||||
module.fail_json(msg='Incompatible option: \'name\'. Do not use name when adding repo files')
|
||||
else:
|
||||
if not name and state == "present":
|
||||
module.fail_json(msg='Name required when adding non-repo files:')
|
||||
|
||||
if repo and repo.endswith('.repo'):
|
||||
exists = repo_exists(module, url=repo, alias=name)
|
||||
elif repo:
|
||||
exists = repo_exists(module, url=repo)
|
||||
else:
|
||||
exists = repo_exists(module, alias=name)
|
||||
|
||||
if state == 'present':
|
||||
if exists:
|
||||
exit_unchanged()
|
||||
|
||||
changed = add_repo(module, repo, name, description, disable_gpg_check)
|
||||
elif state == 'absent':
|
||||
if not exists:
|
||||
exit_unchanged()
|
||||
|
||||
changed = remove_repo(module, repo, name)
|
||||
|
||||
module.exit_json(changed=changed, repo=repo, state=state)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
@ -1,198 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, André Paramés <git@andreparames.com>
|
||||
# Based on the Git module by Michael DeHaan <michael.dehaan@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 = u'''
|
||||
---
|
||||
module: bzr
|
||||
author: André Paramés
|
||||
version_added: "1.1"
|
||||
short_description: Deploy software (or files) from bzr branches
|
||||
description:
|
||||
- Manage I(bzr) branches to deploy files or software.
|
||||
options:
|
||||
name:
|
||||
required: true
|
||||
aliases: [ 'parent' ]
|
||||
description:
|
||||
- SSH or HTTP protocol address of the parent branch.
|
||||
dest:
|
||||
required: true
|
||||
description:
|
||||
- Absolute path of where the branch should be cloned to.
|
||||
version:
|
||||
required: false
|
||||
default: "head"
|
||||
description:
|
||||
- What version of the branch to clone. This can be the
|
||||
bzr revno or revid.
|
||||
force:
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: [ 'yes', 'no' ]
|
||||
description:
|
||||
- If C(yes), any modified files in the working
|
||||
tree will be discarded.
|
||||
executable:
|
||||
required: false
|
||||
default: null
|
||||
version_added: "1.4"
|
||||
description:
|
||||
- Path to bzr executable to use. If not supplied,
|
||||
the normal mechanism for resolving binary paths will be used.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Example bzr checkout from Ansible Playbooks
|
||||
- bzr: name=bzr+ssh://foosball.example.org/path/to/branch dest=/srv/checkout version=22
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class Bzr(object):
|
||||
def __init__(self, module, parent, dest, version, bzr_path):
|
||||
self.module = module
|
||||
self.parent = parent
|
||||
self.dest = dest
|
||||
self.version = version
|
||||
self.bzr_path = bzr_path
|
||||
|
||||
def _command(self, args_list, cwd=None, **kwargs):
|
||||
(rc, out, err) = self.module.run_command([self.bzr_path] + args_list, cwd=cwd, **kwargs)
|
||||
return (rc, out, err)
|
||||
|
||||
def get_version(self):
|
||||
'''samples the version of the bzr branch'''
|
||||
|
||||
cmd = "%s revno" % self.bzr_path
|
||||
rc, stdout, stderr = self.module.run_command(cmd, cwd=self.dest)
|
||||
revno = stdout.strip()
|
||||
return revno
|
||||
|
||||
def clone(self):
|
||||
'''makes a new bzr branch if it does not already exist'''
|
||||
dest_dirname = os.path.dirname(self.dest)
|
||||
try:
|
||||
os.makedirs(dest_dirname)
|
||||
except:
|
||||
pass
|
||||
if self.version.lower() != 'head':
|
||||
args_list = ["branch", "-r", self.version, self.parent, self.dest]
|
||||
else:
|
||||
args_list = ["branch", self.parent, self.dest]
|
||||
return self._command(args_list, check_rc=True, cwd=dest_dirname)
|
||||
|
||||
def has_local_mods(self):
|
||||
|
||||
cmd = "%s status -S" % self.bzr_path
|
||||
rc, stdout, stderr = self.module.run_command(cmd, cwd=self.dest)
|
||||
lines = stdout.splitlines()
|
||||
|
||||
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
|
||||
return len(lines) > 0
|
||||
|
||||
def reset(self, force):
|
||||
'''
|
||||
Resets the index and working tree to head.
|
||||
Discards any changes to tracked files in the working
|
||||
tree since that commit.
|
||||
'''
|
||||
if not force and self.has_local_mods():
|
||||
self.module.fail_json(msg="Local modifications exist in branch (force=no).")
|
||||
return self._command(["revert"], check_rc=True, cwd=self.dest)
|
||||
|
||||
def fetch(self):
|
||||
'''updates branch from remote sources'''
|
||||
if self.version.lower() != 'head':
|
||||
(rc, out, err) = self._command(["pull", "-r", self.version], cwd=self.dest)
|
||||
else:
|
||||
(rc, out, err) = self._command(["pull"], cwd=self.dest)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Failed to pull")
|
||||
return (rc, out, err)
|
||||
|
||||
def switch_version(self):
|
||||
'''once pulled, switch to a particular revno or revid'''
|
||||
if self.version.lower() != 'head':
|
||||
args_list = ["revert", "-r", self.version]
|
||||
else:
|
||||
args_list = ["revert"]
|
||||
return self._command(args_list, check_rc=True, cwd=self.dest)
|
||||
|
||||
# ===========================================
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
dest=dict(required=True),
|
||||
name=dict(required=True, aliases=['parent']),
|
||||
version=dict(default='head'),
|
||||
force=dict(default='yes', type='bool'),
|
||||
executable=dict(default=None),
|
||||
)
|
||||
)
|
||||
|
||||
dest = os.path.abspath(os.path.expanduser(module.params['dest']))
|
||||
parent = module.params['name']
|
||||
version = module.params['version']
|
||||
force = module.params['force']
|
||||
bzr_path = module.params['executable'] or module.get_bin_path('bzr', True)
|
||||
|
||||
bzrconfig = os.path.join(dest, '.bzr', 'branch', 'branch.conf')
|
||||
|
||||
rc, out, err, status = (0, None, None, None)
|
||||
|
||||
bzr = Bzr(module, parent, dest, version, bzr_path)
|
||||
|
||||
# if there is no bzr configuration, do a branch operation
|
||||
# else pull and switch the version
|
||||
before = None
|
||||
local_mods = False
|
||||
if not os.path.exists(bzrconfig):
|
||||
(rc, out, err) = bzr.clone()
|
||||
|
||||
else:
|
||||
# else do a pull
|
||||
local_mods = bzr.has_local_mods()
|
||||
before = bzr.get_version()
|
||||
(rc, out, err) = bzr.reset(force)
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err)
|
||||
(rc, out, err) = bzr.fetch()
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err)
|
||||
|
||||
# switch to version specified regardless of whether
|
||||
# we cloned or pulled
|
||||
(rc, out, err) = bzr.switch_version()
|
||||
|
||||
# determine if we changed anything
|
||||
after = bzr.get_version()
|
||||
changed = False
|
||||
|
||||
if before != after or local_mods:
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed, before=before, after=after)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,178 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Phillip Gentry <phillip@cx.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 json
|
||||
import base64
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: github_hooks
|
||||
short_description: Manages github service hooks.
|
||||
description:
|
||||
- Adds service hooks and removes service hooks that have an error status.
|
||||
version_added: "1.4"
|
||||
options:
|
||||
user:
|
||||
description:
|
||||
- Github username.
|
||||
required: true
|
||||
oauthkey:
|
||||
description:
|
||||
- The oauth key provided by github. It can be found/generated on github under "Edit Your Profile" >> "Applications" >> "Personal Access Tokens"
|
||||
required: true
|
||||
repo:
|
||||
description:
|
||||
- "This is the API url for the repository you want to manage hooks for. It should be in the form of: https://api.github.com/repos/user:/repo:. Note this is different than the normal repo url."
|
||||
required: true
|
||||
hookurl:
|
||||
description:
|
||||
- When creating a new hook, this is the url that you want github to post to. It is only required when creating a new hook.
|
||||
required: false
|
||||
action:
|
||||
description:
|
||||
- This tells the githooks module what you want it to do.
|
||||
required: true
|
||||
choices: [ "create", "cleanall" ]
|
||||
validate_certs:
|
||||
description:
|
||||
- If C(no), SSL certificates for the target repo will not be validated. This should only be used
|
||||
on personally controlled sites using self-signed certificates.
|
||||
required: false
|
||||
default: 'yes'
|
||||
choices: ['yes', 'no']
|
||||
|
||||
author: Phillip Gentry, CX Inc
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Example creating a new service hook. It ignores duplicates.
|
||||
- github_hooks: action=create hookurl=http://11.111.111.111:2222 user={{ gituser }} oauthkey={{ oauthkey }} repo=https://api.github.com/repos/pcgentry/Github-Auto-Deploy
|
||||
|
||||
# Cleaning all hooks for this repo that had an error on the last update. Since this works for all hooks in a repo it is probably best that this would be called from a handler.
|
||||
- local_action: github_hooks action=cleanall user={{ gituser }} oauthkey={{ oauthkey }} repo={{ repo }}
|
||||
'''
|
||||
|
||||
def list(module, hookurl, oauthkey, repo, user):
|
||||
url = "%s/hooks" % repo
|
||||
auth = base64.encodestring('%s:%s' % (user, oauthkey)).replace('\n', '')
|
||||
headers = {
|
||||
'Authorization': 'Basic %s' % auth,
|
||||
}
|
||||
response, info = fetch_url(module, url, headers=headers)
|
||||
if info['status'] != 200:
|
||||
return False, ''
|
||||
else:
|
||||
return False, response.read()
|
||||
|
||||
def clean504(module, hookurl, oauthkey, repo, user):
|
||||
current_hooks = list(hookurl, oauthkey, repo, user)[1]
|
||||
decoded = json.loads(current_hooks)
|
||||
|
||||
for hook in decoded:
|
||||
if hook['last_response']['code'] == 504:
|
||||
# print "Last response was an ERROR for hook:"
|
||||
# print hook['id']
|
||||
delete(module, hookurl, oauthkey, repo, user, hook['id'])
|
||||
|
||||
return 0, current_hooks
|
||||
|
||||
def cleanall(module, hookurl, oauthkey, repo, user):
|
||||
current_hooks = list(hookurl, oauthkey, repo, user)[1]
|
||||
decoded = json.loads(current_hooks)
|
||||
|
||||
for hook in decoded:
|
||||
if hook['last_response']['code'] != 200:
|
||||
# print "Last response was an ERROR for hook:"
|
||||
# print hook['id']
|
||||
delete(module, hookurl, oauthkey, repo, user, hook['id'])
|
||||
|
||||
return 0, current_hooks
|
||||
|
||||
def create(module, hookurl, oauthkey, repo, user):
|
||||
url = "%s/hooks" % repo
|
||||
values = {
|
||||
"active": True,
|
||||
"name": "web",
|
||||
"config": {
|
||||
"url": "%s" % hookurl,
|
||||
"content_type": "json"
|
||||
}
|
||||
}
|
||||
data = json.dumps(values)
|
||||
auth = base64.encodestring('%s:%s' % (user, oauthkey)).replace('\n', '')
|
||||
headers = {
|
||||
'Authorization': 'Basic %s' % auth,
|
||||
}
|
||||
response, info = fetch_url(module, url, data=data, headers=headers)
|
||||
if info['status'] != 200:
|
||||
return 0, '[]'
|
||||
else:
|
||||
return 0, response.read()
|
||||
|
||||
def delete(module, hookurl, oauthkey, repo, user, hookid):
|
||||
url = "%s/hooks/%s" % (repo, hookid)
|
||||
auth = base64.encodestring('%s:%s' % (user, oauthkey)).replace('\n', '')
|
||||
headers = {
|
||||
'Authorization': 'Basic %s' % auth,
|
||||
}
|
||||
response, info = fetch_url(module, url, data=data, headers=headers, method='DELETE')
|
||||
return response.read()
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
action=dict(required=True),
|
||||
hookurl=dict(required=False),
|
||||
oauthkey=dict(required=True),
|
||||
repo=dict(required=True),
|
||||
user=dict(required=True),
|
||||
validate_certs=dict(default='yes', type='bool'),
|
||||
)
|
||||
)
|
||||
|
||||
action = module.params['action']
|
||||
hookurl = module.params['hookurl']
|
||||
oauthkey = module.params['oauthkey']
|
||||
repo = module.params['repo']
|
||||
user = module.params['user']
|
||||
|
||||
if action == "list":
|
||||
(rc, out) = list(module, hookurl, oauthkey, repo, user)
|
||||
|
||||
if action == "clean504":
|
||||
(rc, out) = clean504(module, hookurl, oauthkey, repo, user)
|
||||
|
||||
if action == "cleanall":
|
||||
(rc, out) = cleanall(module, hookurl, oauthkey, repo, user)
|
||||
|
||||
if action == "create":
|
||||
(rc, out) = create(module, hookurl, oauthkey, repo, user)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(msg="failed", result=out)
|
||||
|
||||
module.exit_json(msg="success", result=out)
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.urls import *
|
||||
|
||||
main()
|
@ -1,214 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013, Peter Sprygada <sprygada@gmail.com>
|
||||
#
|
||||
# This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ejabberd_user
|
||||
version_added: "1.5"
|
||||
author: Peter Sprygada
|
||||
short_description: Manages users for ejabberd servers
|
||||
requirements:
|
||||
- ejabberd with mod_admin_extra
|
||||
description:
|
||||
- This module provides user management for ejabberd servers
|
||||
options:
|
||||
username:
|
||||
description:
|
||||
- the name of the user to manage
|
||||
required: true
|
||||
host:
|
||||
description:
|
||||
- the ejabberd host associated with this username
|
||||
required: true
|
||||
password:
|
||||
description:
|
||||
- the password to assign to the username
|
||||
required: false
|
||||
logging:
|
||||
description:
|
||||
- enables or disables the local syslog facility for this module
|
||||
required: false
|
||||
default: false
|
||||
choices: [ 'true', 'false', 'yes', 'no' ]
|
||||
state:
|
||||
description:
|
||||
- describe the desired state of the user to be managed
|
||||
required: false
|
||||
default: 'present'
|
||||
choices: [ 'present', 'absent' ]
|
||||
notes:
|
||||
- Password parameter is required for state == present only
|
||||
- Passwords must be stored in clear text for this release
|
||||
- The ejabberd configuration file must include mod_admin_extra as a module.
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
Example playbook entries using the ejabberd_user module to manage users state.
|
||||
|
||||
tasks:
|
||||
|
||||
- name: create a user if it does not exists
|
||||
action: ejabberd_user username=test host=server password=password
|
||||
|
||||
- name: delete a user if it exists
|
||||
action: ejabberd_user username=test host=server state=absent
|
||||
'''
|
||||
import syslog
|
||||
|
||||
class EjabberdUserException(Exception):
|
||||
""" Base exeption for EjabberdUser class object """
|
||||
pass
|
||||
|
||||
class EjabberdUser(object):
|
||||
""" This object represents a user resource for an ejabberd server. The
|
||||
object manages user creation and deletion using ejabberdctl. The following
|
||||
commands are currently supported:
|
||||
* ejabberdctl register
|
||||
* ejabberdctl deregister
|
||||
"""
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.logging = module.params.get('logging')
|
||||
self.state = module.params.get('state')
|
||||
self.host = module.params.get('host')
|
||||
self.user = module.params.get('username')
|
||||
self.pwd = module.params.get('password')
|
||||
|
||||
@property
|
||||
def changed(self):
|
||||
""" This method will check the current user and see if the password has
|
||||
changed. It will return True if the user does not match the supplied
|
||||
credentials and False if it does not
|
||||
"""
|
||||
try:
|
||||
options = [self.user, self.host, self.pwd]
|
||||
(rc, out, err) = self.run_command('check_password', options)
|
||||
except EjabberdUserException, e:
|
||||
(rc, out, err) = (1, None, "required attribute(s) missing")
|
||||
return rc
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
""" This method will check to see if the supplied username exists for
|
||||
host specified. If the user exists True is returned, otherwise False
|
||||
is returned
|
||||
"""
|
||||
try:
|
||||
options = [self.user, self.host]
|
||||
(rc, out, err) = self.run_command('check_account', options)
|
||||
except EjabberdUserException, e:
|
||||
(rc, out, err) = (1, None, "required attribute(s) missing")
|
||||
return True if rc == 0 else False
|
||||
|
||||
def log(self, entry):
|
||||
""" This method will log information to the local syslog facility """
|
||||
if self.logging:
|
||||
syslog.openlog('ansible-%s' % os.path.basename(__file__))
|
||||
syslog.syslog(syslog.LOG_NOTICE, entry)
|
||||
|
||||
def run_command(self, cmd, options):
|
||||
""" This method will run the any command specified and return the
|
||||
returns using the Ansible common module
|
||||
"""
|
||||
if not all(options):
|
||||
raise EjabberdUserException
|
||||
|
||||
cmd = 'ejabberdctl %s ' % cmd
|
||||
cmd += " ".join(options)
|
||||
self.log('command: %s' % cmd)
|
||||
return self.module.run_command(cmd.split())
|
||||
|
||||
def update(self):
|
||||
""" The update method will update the credentials for the user provided
|
||||
"""
|
||||
try:
|
||||
options = [self.user, self.host, self.pwd]
|
||||
(rc, out, err) = self.run_command('change_password', options)
|
||||
except EjabberdUserException, e:
|
||||
(rc, out, err) = (1, None, "required attribute(s) missing")
|
||||
return (rc, out, err)
|
||||
|
||||
def create(self):
|
||||
""" The create method will create a new user on the host with the
|
||||
password provided
|
||||
"""
|
||||
try:
|
||||
options = [self.user, self.host, self.pwd]
|
||||
(rc, out, err) = self.run_command('register', options)
|
||||
except EjabberdUserException, e:
|
||||
(rc, out, err) = (1, None, "required attribute(s) missing")
|
||||
return (rc, out, err)
|
||||
|
||||
def delete(self):
|
||||
""" The delete method will delete the user from the host
|
||||
"""
|
||||
try:
|
||||
options = [self.user, self.host]
|
||||
(rc, out, err) = self.run_command('unregister', options)
|
||||
except EjabberdUserException, e:
|
||||
(rc, out, err) = (1, None, "required attribute(s) missing")
|
||||
return (rc, out, err)
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
host=dict(default=None, type='str'),
|
||||
username=dict(default=None, type='str'),
|
||||
password=dict(default=None, type='str'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
logging=dict(default=False, type='bool')
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
obj = EjabberdUser(module)
|
||||
|
||||
rc = None
|
||||
result = dict()
|
||||
|
||||
if obj.state == 'absent':
|
||||
if obj.exists:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = obj.delete()
|
||||
if rc != 0:
|
||||
module.fail_json(msg=err, rc=rc)
|
||||
|
||||
elif obj.state == 'present':
|
||||
if not obj.exists:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = obj.create()
|
||||
elif obj.changed:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = obj.update()
|
||||
if rc is not None and rc != 0:
|
||||
module.fail_json(msg=err, rc=rc)
|
||||
|
||||
if rc is None:
|
||||
result['changed'] = False
|
||||
else:
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,140 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2013, Jeroen Hoekx <jeroen.hoekx@dsquare.be>
|
||||
#
|
||||
# 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: jboss
|
||||
version_added: "1.4"
|
||||
short_description: deploy applications to JBoss
|
||||
description:
|
||||
- Deploy applications to JBoss standalone using the filesystem
|
||||
options:
|
||||
deployment:
|
||||
required: true
|
||||
description:
|
||||
- The name of the deployment
|
||||
src:
|
||||
required: false
|
||||
description:
|
||||
- The remote path of the application ear or war to deploy
|
||||
deploy_path:
|
||||
required: false
|
||||
default: /var/lib/jbossas/standalone/deployments
|
||||
description:
|
||||
- The location in the filesystem where the deployment scanner listens
|
||||
state:
|
||||
required: false
|
||||
choices: [ present, absent ]
|
||||
default: "present"
|
||||
description:
|
||||
- Whether the application should be deployed or undeployed
|
||||
notes:
|
||||
- "The JBoss standalone deployment-scanner has to be enabled in standalone.xml"
|
||||
- "Ensure no identically named application is deployed through the JBoss CLI"
|
||||
author: Jeroen Hoekx
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Deploy a hello world application
|
||||
- jboss: src=/tmp/hello-1.0-SNAPSHOT.war deployment=hello.war state=present
|
||||
# Update the hello world application
|
||||
- jboss: src=/tmp/hello-1.1-SNAPSHOT.war deployment=hello.war state=present
|
||||
# Undeploy the hello world application
|
||||
- jboss: deployment=hello.war state=absent
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
def is_deployed(deploy_path, deployment):
|
||||
return os.path.exists(os.path.join(deploy_path, "%s.deployed"%(deployment)))
|
||||
|
||||
def is_undeployed(deploy_path, deployment):
|
||||
return os.path.exists(os.path.join(deploy_path, "%s.undeployed"%(deployment)))
|
||||
|
||||
def is_failed(deploy_path, deployment):
|
||||
return os.path.exists(os.path.join(deploy_path, "%s.failed"%(deployment)))
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
src=dict(),
|
||||
deployment=dict(required=True),
|
||||
deploy_path=dict(default='/var/lib/jbossas/standalone/deployments'),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
),
|
||||
)
|
||||
|
||||
changed = False
|
||||
|
||||
src = module.params['src']
|
||||
deployment = module.params['deployment']
|
||||
deploy_path = module.params['deploy_path']
|
||||
state = module.params['state']
|
||||
|
||||
if state == 'present' and not src:
|
||||
module.fail_json(msg="Argument 'src' required.")
|
||||
|
||||
if not os.path.exists(deploy_path):
|
||||
module.fail_json(msg="deploy_path does not exist.")
|
||||
|
||||
deployed = is_deployed(deploy_path, deployment)
|
||||
|
||||
if state == 'present' and not deployed:
|
||||
if not os.path.exists(src):
|
||||
module.fail_json(msg='Source file %s does not exist.'%(src))
|
||||
if is_failed(deploy_path, deployment):
|
||||
### Clean up old failed deployment
|
||||
os.remove(os.path.join(deploy_path, "%s.failed"%(deployment)))
|
||||
|
||||
shutil.copyfile(src, os.path.join(deploy_path, deployment))
|
||||
while not deployed:
|
||||
deployed = is_deployed(deploy_path, deployment)
|
||||
if is_failed(deploy_path, deployment):
|
||||
module.fail_json(msg='Deploying %s failed.'%(deployment))
|
||||
time.sleep(1)
|
||||
changed = True
|
||||
|
||||
if state == 'present' and deployed:
|
||||
if module.md5(src) != module.md5(os.path.join(deploy_path, deployment)):
|
||||
os.remove(os.path.join(deploy_path, "%s.deployed"%(deployment)))
|
||||
shutil.copyfile(src, os.path.join(deploy_path, deployment))
|
||||
deployed = False
|
||||
while not deployed:
|
||||
deployed = is_deployed(deploy_path, deployment)
|
||||
if is_failed(deploy_path, deployment):
|
||||
module.fail_json(msg='Deploying %s failed.'%(deployment))
|
||||
time.sleep(1)
|
||||
changed = True
|
||||
|
||||
if state == 'absent' and deployed:
|
||||
os.remove(os.path.join(deploy_path, "%s.deployed"%(deployment)))
|
||||
while deployed:
|
||||
deployed = not is_undeployed(deploy_path, deployment)
|
||||
if is_failed(deploy_path, deployment):
|
||||
module.fail_json(msg='Undeploying %s failed.'%(deployment))
|
||||
time.sleep(1)
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -1,347 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Steve Smith <ssmith@atlassian.com>
|
||||
# Atlassian open-source approval reference OSR-76.
|
||||
#
|
||||
# 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: jira
|
||||
version_added: "1.6"
|
||||
short_description: create and modify issues in a JIRA instance
|
||||
description:
|
||||
- Create and modify issues in a JIRA instance.
|
||||
|
||||
options:
|
||||
uri:
|
||||
required: true
|
||||
description:
|
||||
- Base URI for the JIRA instance
|
||||
|
||||
operation:
|
||||
required: true
|
||||
aliases: [ command ]
|
||||
choices: [ create, comment, edit, fetch, transition ]
|
||||
description:
|
||||
- The operation to perform.
|
||||
|
||||
username:
|
||||
required: true
|
||||
description:
|
||||
- The username to log-in with.
|
||||
|
||||
password:
|
||||
required: true
|
||||
description:
|
||||
- The password to log-in with.
|
||||
|
||||
project:
|
||||
aliases: [ prj ]
|
||||
required: false
|
||||
description:
|
||||
- The project for this operation. Required for issue creation.
|
||||
|
||||
summary:
|
||||
required: false
|
||||
description:
|
||||
- The issue summary, where appropriate.
|
||||
|
||||
description:
|
||||
required: false
|
||||
description:
|
||||
- The issue description, where appropriate.
|
||||
|
||||
issuetype:
|
||||
required: false
|
||||
description:
|
||||
- The issue type, for issue creation.
|
||||
|
||||
issue:
|
||||
required: false
|
||||
description:
|
||||
- An existing issue key to operate on.
|
||||
|
||||
comment:
|
||||
required: false
|
||||
description:
|
||||
- The comment text to add.
|
||||
|
||||
status:
|
||||
required: false
|
||||
description:
|
||||
- The desired status; only relevant for the transition operation.
|
||||
|
||||
assignee:
|
||||
required: false
|
||||
description:
|
||||
- Sets the assignee on create or transition operations. Note not all transitions will allow this.
|
||||
|
||||
fields:
|
||||
required: false
|
||||
description:
|
||||
- This is a free-form data structure that can contain arbitrary data. This is passed directly to the JIRA REST API (possibly after merging with other required data, as when passed to create). See examples for more information, and the JIRA REST API for the structure required for various fields.
|
||||
|
||||
notes:
|
||||
- "Currently this only works with basic-auth."
|
||||
|
||||
author: Steve Smith
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Create a new issue and add a comment to it:
|
||||
- name: Create an issue
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
project=ANS operation=create
|
||||
summary="Example Issue" description="Created using Ansible" issuetype=Task
|
||||
register: issue
|
||||
|
||||
- name: Comment on issue
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
issue={{issue.meta.key}} operation=comment
|
||||
comment="A comment added by Ansible"
|
||||
|
||||
# Assign an existing issue using edit
|
||||
- name: Assign an issue using free-form fields
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
issue={{issue.meta.key}} operation=edit
|
||||
assignee=ssmith
|
||||
|
||||
# Create an issue with an existing assignee
|
||||
- name: Create an assigned issue
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
project=ANS operation=create
|
||||
summary="Assigned issue" description="Created and assigned using Ansible"
|
||||
issuetype=Task assignee=ssmith
|
||||
|
||||
# Edit an issue using free-form fields
|
||||
- name: Set the labels on an issue using free-form fields
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
issue={{issue.meta.key}} operation=edit
|
||||
args: { fields: {labels: ["autocreated", "ansible"]}}
|
||||
|
||||
- name: Set the labels on an issue, YAML version
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
issue={{issue.meta.key}} operation=edit
|
||||
args:
|
||||
fields:
|
||||
labels:
|
||||
- "autocreated"
|
||||
- "ansible"
|
||||
- "yaml"
|
||||
|
||||
# Retrieve metadata for an issue and use it to create an account
|
||||
- name: Get an issue
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
project=ANS operation=fetch issue="ANS-63"
|
||||
register: issue
|
||||
|
||||
- name: Create a unix account for the reporter
|
||||
sudo: true
|
||||
user: name="{{issue.meta.fields.creator.name}}" comment="{{issue.meta.fields.creator.displayName}}"
|
||||
|
||||
# Transition an issue by target status
|
||||
- name: Close the issue
|
||||
jira: uri={{server}} username={{user}} password={{pass}}
|
||||
issue={{issue.meta.key}} operation=transition status="Done"
|
||||
"""
|
||||
|
||||
import json
|
||||
import base64
|
||||
|
||||
def request(url, user, passwd, data=None, method=None):
|
||||
if data:
|
||||
data = json.dumps(data)
|
||||
|
||||
# NOTE: fetch_url uses a password manager, which follows the
|
||||
# standard request-then-challenge basic-auth semantics. However as
|
||||
# JIRA allows some unauthorised operations it doesn't necessarily
|
||||
# send the challenge, so the request occurs as the anonymous user,
|
||||
# resulting in unexpected results. To work around this we manually
|
||||
# inject the basic-auth header up-front to ensure that JIRA treats
|
||||
# the requests as authorized for this user.
|
||||
auth = base64.encodestring('%s:%s' % (user, passwd)).replace('\n', '')
|
||||
response, info = fetch_url(module, url, data=data, method=method,
|
||||
headers={'Content-Type':'application/json',
|
||||
'Authorization':"Basic %s" % auth})
|
||||
|
||||
if info['status'] not in (200, 204):
|
||||
module.fail_json(msg=info['msg'])
|
||||
|
||||
body = response.read()
|
||||
|
||||
if body:
|
||||
return json.loads(body)
|
||||
else:
|
||||
return {}
|
||||
|
||||
def post(url, user, passwd, data):
|
||||
return request(url, user, passwd, data=data, method='POST')
|
||||
|
||||
def put(url, user, passwd, data):
|
||||
return request(url, user, passwd, data=data, method='PUT')
|
||||
|
||||
def get(url, user, passwd):
|
||||
return request(url, user, passwd)
|
||||
|
||||
|
||||
def create(restbase, user, passwd, params):
|
||||
createfields = {
|
||||
'project': { 'key': params['project'] },
|
||||
'summary': params['summary'],
|
||||
'description': params['description'],
|
||||
'issuetype': { 'name': params['issuetype'] }}
|
||||
|
||||
# Merge in any additional or overridden fields
|
||||
if params['fields']:
|
||||
createfields.update(params['fields'])
|
||||
|
||||
data = {'fields': createfields}
|
||||
|
||||
url = restbase + '/issue/'
|
||||
|
||||
ret = post(url, user, passwd, data)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def comment(restbase, user, passwd, params):
|
||||
data = {
|
||||
'body': params['comment']
|
||||
}
|
||||
|
||||
url = restbase + '/issue/' + params['issue'] + '/comment'
|
||||
|
||||
ret = post(url, user, passwd, data)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def edit(restbase, user, passwd, params):
|
||||
data = {
|
||||
'fields': params['fields']
|
||||
}
|
||||
|
||||
url = restbase + '/issue/' + params['issue']
|
||||
|
||||
ret = put(url, user, passwd, data)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def fetch(restbase, user, passwd, params):
|
||||
url = restbase + '/issue/' + params['issue']
|
||||
ret = get(url, user, passwd)
|
||||
return ret
|
||||
|
||||
|
||||
def transition(restbase, user, passwd, params):
|
||||
# Find the transition id
|
||||
turl = restbase + '/issue/' + params['issue'] + "/transitions"
|
||||
tmeta = get(turl, user, passwd)
|
||||
|
||||
target = params['status']
|
||||
tid = None
|
||||
for t in tmeta['transitions']:
|
||||
if t['name'] == target:
|
||||
tid = t['id']
|
||||
break
|
||||
|
||||
if not tid:
|
||||
raise ValueError("Failed find valid transition for '%s'" % target)
|
||||
|
||||
# Perform it
|
||||
url = restbase + '/issue/' + params['issue'] + "/transitions"
|
||||
data = { 'transition': { "id" : tid },
|
||||
'fields': params['fields']}
|
||||
|
||||
ret = post(url, user, passwd, data)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# Some parameters are required depending on the operation:
|
||||
OP_REQUIRED = dict(create=['project', 'issuetype', 'summary', 'description'],
|
||||
comment=['issue', 'comment'],
|
||||
edit=[],
|
||||
fetch=['issue'],
|
||||
transition=['status'])
|
||||
|
||||
def main():
|
||||
|
||||
global module
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
uri=dict(required=True),
|
||||
operation=dict(choices=['create', 'comment', 'edit', 'fetch', 'transition'],
|
||||
aliases=['command'], required=True),
|
||||
username=dict(required=True),
|
||||
password=dict(required=True),
|
||||
project=dict(),
|
||||
summary=dict(),
|
||||
description=dict(),
|
||||
issuetype=dict(),
|
||||
issue=dict(aliases=['ticket']),
|
||||
comment=dict(),
|
||||
status=dict(),
|
||||
assignee=dict(),
|
||||
fields=dict(default={})
|
||||
),
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
||||
op = module.params['operation']
|
||||
|
||||
# Check we have the necessary per-operation parameters
|
||||
missing = []
|
||||
for parm in OP_REQUIRED[op]:
|
||||
if not module.params[parm]:
|
||||
missing.append(parm)
|
||||
if missing:
|
||||
module.fail_json(msg="Operation %s require the following missing parameters: %s" % (op, ",".join(missing)))
|
||||
|
||||
# Handle rest of parameters
|
||||
uri = module.params['uri']
|
||||
user = module.params['username']
|
||||
passwd = module.params['password']
|
||||
if module.params['assignee']:
|
||||
module.params['fields']['assignee'] = { 'name': module.params['assignee'] }
|
||||
|
||||
if not uri.endswith('/'):
|
||||
uri = uri+'/'
|
||||
restbase = uri + 'rest/api/2'
|
||||
|
||||
# Dispatch
|
||||
try:
|
||||
|
||||
# Lookup the corresponding method for this operation. This is
|
||||
# safe as the AnsibleModule should remove any unknown operations.
|
||||
thismod = sys.modules[__name__]
|
||||
method = getattr(thismod, op)
|
||||
|
||||
ret = method(restbase, user, passwd, module.params)
|
||||
|
||||
except Exception as e:
|
||||
return module.fail_json(msg=e.message)
|
||||
|
||||
|
||||
module.exit_json(changed=True, meta=ret)
|
||||
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.urls import *
|
||||
main()
|
Loading…
Reference in New Issue