mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
Reorganization.
This commit is contained in:
parent
ee87304fb8
commit
7d6ceb4f06
23 changed files with 0 additions and 0 deletions
0
lib/ansible/modules/extras/packaging/os/__init__.py
Normal file
0
lib/ansible/modules/extras/packaging/os/__init__.py
Normal file
835
lib/ansible/modules/extras/packaging/os/homebrew.py
Normal file
835
lib/ansible/modules/extras/packaging/os/homebrew.py
Normal file
|
@ -0,0 +1,835 @@
|
|||
#!/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()
|
513
lib/ansible/modules/extras/packaging/os/homebrew_cask.py
Normal file
513
lib/ansible/modules/extras/packaging/os/homebrew_cask.py
Normal file
|
@ -0,0 +1,513 @@
|
|||
#!/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()
|
215
lib/ansible/modules/extras/packaging/os/homebrew_tap.py
Normal file
215
lib/ansible/modules/extras/packaging/os/homebrew_tap.py
Normal file
|
@ -0,0 +1,215 @@
|
|||
#!/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()
|
236
lib/ansible/modules/extras/packaging/os/layman.py
Normal file
236
lib/ansible/modules/extras/packaging/os/layman.py
Normal file
|
@ -0,0 +1,236 @@
|
|||
#!/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()
|
217
lib/ansible/modules/extras/packaging/os/macports.py
Normal file
217
lib/ansible/modules/extras/packaging/os/macports.py
Normal file
|
@ -0,0 +1,217 @@
|
|||
#!/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()
|
414
lib/ansible/modules/extras/packaging/os/openbsd_pkg.py
Normal file
414
lib/ansible/modules/extras/packaging/os/openbsd_pkg.py
Normal file
|
@ -0,0 +1,414 @@
|
|||
#!/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
|
||||
|
||||
# Update all packages on the system
|
||||
- openbsd_pkg: name=* state=latest
|
||||
'''
|
||||
|
||||
# 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'])
|
||||
|
||||
# Function used for upgrading all installed packages.
|
||||
def upgrade_packages(module):
|
||||
if module.check_mode:
|
||||
upgrade_cmd = 'pkg_add -Imnu'
|
||||
else:
|
||||
upgrade_cmd = 'pkg_add -Imu'
|
||||
|
||||
# Attempt to upgrade all packages.
|
||||
rc, stdout, stderr = execute_command("%s" % upgrade_cmd, module)
|
||||
|
||||
# Try to find any occurance of a package changing version like:
|
||||
# "bzip2-1.0.6->1.0.6p0: ok".
|
||||
match = re.search("\W\w.+->.+: ok\W", stdout)
|
||||
if match:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
||||
changed=True
|
||||
|
||||
else:
|
||||
changed=False
|
||||
|
||||
# It seems we can not trust the return value, so depend on the presence of
|
||||
# stderr to know if something failed.
|
||||
if stderr:
|
||||
rc = 1
|
||||
else:
|
||||
rc = 0
|
||||
|
||||
return (rc, stdout, stderr, changed)
|
||||
|
||||
# ===========================================
|
||||
# Main control flow.
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True),
|
||||
state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']),
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
|
||||
rc = 0
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
result = {}
|
||||
result['name'] = name
|
||||
result['state'] = state
|
||||
|
||||
if name == '*':
|
||||
if state != 'latest':
|
||||
module.fail_json(msg="the package name '*' is only valid when using state=latest")
|
||||
else:
|
||||
# Perform an upgrade of all installed packages.
|
||||
(rc, stdout, stderr, changed) = upgrade_packages(module)
|
||||
else:
|
||||
# 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()
|
150
lib/ansible/modules/extras/packaging/os/opkg.py
Normal file
150
lib/ansible/modules/extras/packaging/os/opkg.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
#!/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()
|
234
lib/ansible/modules/extras/packaging/os/pacman.py
Normal file
234
lib/ansible/modules/extras/packaging/os/pacman.py
Normal file
|
@ -0,0 +1,234 @@
|
|||
#!/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()
|
168
lib/ansible/modules/extras/packaging/os/pkgin.py
Executable file
168
lib/ansible/modules/extras/packaging/os/pkgin.py
Executable file
|
@ -0,0 +1,168 @@
|
|||
#!/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()
|
301
lib/ansible/modules/extras/packaging/os/pkgng.py
Normal file
301
lib/ansible/modules/extras/packaging/os/pkgng.py
Normal file
|
@ -0,0 +1,301 @@
|
|||
#!/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()
|
179
lib/ansible/modules/extras/packaging/os/pkgutil.py
Normal file
179
lib/ansible/modules/extras/packaging/os/pkgutil.py
Normal file
|
@ -0,0 +1,179 @@
|
|||
#!/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()
|
405
lib/ansible/modules/extras/packaging/os/portage.py
Normal file
405
lib/ansible/modules/extras/packaging/os/portage.py
Normal file
|
@ -0,0 +1,405 @@
|
|||
#!/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()
|
207
lib/ansible/modules/extras/packaging/os/portinstall.py
Normal file
207
lib/ansible/modules/extras/packaging/os/portinstall.py
Normal file
|
@ -0,0 +1,207 @@
|
|||
#!/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()
|
234
lib/ansible/modules/extras/packaging/os/svr4pkg.py
Normal file
234
lib/ansible/modules/extras/packaging/os/svr4pkg.py
Normal file
|
@ -0,0 +1,234 @@
|
|||
#!/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()
|
196
lib/ansible/modules/extras/packaging/os/swdepot.py
Normal file
196
lib/ansible/modules/extras/packaging/os/swdepot.py
Normal file
|
@ -0,0 +1,196 @@
|
|||
#!/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()
|
||||
|
200
lib/ansible/modules/extras/packaging/os/urpmi.py
Normal file
200
lib/ansible/modules/extras/packaging/os/urpmi.py
Normal file
|
@ -0,0 +1,200 @@
|
|||
#!/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()
|
282
lib/ansible/modules/extras/packaging/os/zypper.py
Normal file
282
lib/ansible/modules/extras/packaging/os/zypper.py
Normal file
|
@ -0,0 +1,282 @@
|
|||
#!/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 zypper version
|
||||
def zypper_version(module):
|
||||
"""Return (rc, message) tuple"""
|
||||
cmd = ['/usr/bin/zypper', '-V']
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
if rc == 0:
|
||||
return rc, stdout
|
||||
else:
|
||||
return rc, stderr
|
||||
|
||||
# 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, old_zypper):
|
||||
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 and not old_zypper:
|
||||
cmd.append('--no-gpg-check')
|
||||
else:
|
||||
cmd.append('--no-gpg-checks')
|
||||
|
||||
cmd.extend(['install', '--auto-agree-with-licenses'])
|
||||
# add install parameter
|
||||
if disable_recommends and not old_zypper:
|
||||
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, old_zypper):
|
||||
|
||||
# first of all, make sure all the packages are installed
|
||||
(rc, stdout, stderr, changed) = package_present(m, name, installed_state, disable_gpg_check, disable_recommends, old_zypper)
|
||||
|
||||
# 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)
|
||||
|
||||
if old_zypper:
|
||||
cmd = ['/usr/bin/zypper', '--non-interactive', 'install', '--auto-agree-with-licenses']
|
||||
else:
|
||||
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, old_zypper):
|
||||
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
|
||||
|
||||
rc, out = zypper_version(module)
|
||||
match = re.match(r'zypper\s+(\d+)\.(\d+)\.(\d+)', out)
|
||||
if not match or int(match.group(1)) > 0:
|
||||
old_zypper = False
|
||||
else:
|
||||
old_zypper = True
|
||||
|
||||
# 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, old_zypper)
|
||||
elif state in ['absent', 'removed']:
|
||||
(rc, stdout, stderr, changed) = package_absent(module, name, installed_state, old_zypper)
|
||||
elif state == 'latest':
|
||||
(rc, stdout, stderr, changed) = package_latest(module, name, installed_state, disable_gpg_check, disable_recommends, old_zypper)
|
||||
|
||||
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()
|
275
lib/ansible/modules/extras/packaging/os/zypper_repository.py
Normal file
275
lib/ansible/modules/extras/packaging/os/zypper_repository.py
Normal file
|
@ -0,0 +1,275 @@
|
|||
#!/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
|
||||
'''
|
||||
|
||||
REPO_OPTS = ['alias', 'name', 'priority', 'enabled', 'autorefresh', 'gpgcheck']
|
||||
|
||||
def zypper_version(module):
|
||||
"""Return (rc, message) tuple"""
|
||||
cmd = ['/usr/bin/zypper', '-V']
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
||||
if rc == 0:
|
||||
return rc, stdout
|
||||
else:
|
||||
return rc, stderr
|
||||
|
||||
def _parse_repos(module):
|
||||
"""parses the output of zypper -x lr and returns a parse repo dictionary"""
|
||||
cmd = ['/usr/bin/zypper', '-x', 'lr']
|
||||
repos = []
|
||||
|
||||
from xml.dom.minidom import parseString as parseXML
|
||||
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 _parse_repos_old(module):
|
||||
"""parses the output of zypper sl and returns a parse repo dictionary"""
|
||||
cmd = ['/usr/bin/zypper', 'sl']
|
||||
repos = []
|
||||
rc, stdout, stderr = module.run_command(cmd, check_rc=True)
|
||||
for line in stdout.split('\n'):
|
||||
matched = re.search(r'\d+\s+\|\s+(?P<enabled>\w+)\s+\|\s+(?P<autorefresh>\w+)\s+\|\s+(?P<type>\w+)\s+\|\s+(?P<name>\w+)\s+\|\s+(?P<url>.*)', line)
|
||||
if matched == None:
|
||||
continue
|
||||
|
||||
m = matched.groupdict()
|
||||
m['alias']= m['name']
|
||||
m['priority'] = 100
|
||||
m['gpgcheck'] = 1
|
||||
repos.append(m)
|
||||
|
||||
return repos
|
||||
|
||||
def repo_exists(module, old_zypper, **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
|
||||
|
||||
if old_zypper:
|
||||
repos = _parse_repos_old(module)
|
||||
else:
|
||||
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, old_zypper):
|
||||
if old_zypper:
|
||||
cmd = ['/usr/bin/zypper', 'sa']
|
||||
else:
|
||||
cmd = ['/usr/bin/zypper', 'ar', '--check', '--refresh']
|
||||
|
||||
if repo.startswith("file:/") and old_zypper:
|
||||
cmd.extend(['-t', 'Plaindir'])
|
||||
else:
|
||||
cmd.extend(['-t', 'plaindir'])
|
||||
|
||||
if description:
|
||||
cmd.extend(['--name', description])
|
||||
|
||||
if disable_gpg_check and not old_zypper:
|
||||
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)
|
||||
if stderr:
|
||||
module.fail_json(msg=stderr)
|
||||
else:
|
||||
module.fail_json(msg=stdout)
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
def remove_repo(module, repo, alias, old_zypper):
|
||||
|
||||
if old_zypper:
|
||||
cmd = ['/usr/bin/zypper', 'sd']
|
||||
else:
|
||||
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)
|
||||
if stderr:
|
||||
module.fail_json(msg=stderr)
|
||||
else:
|
||||
module.fail_json(msg=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)
|
||||
|
||||
rc, out = zypper_version(module)
|
||||
match = re.match(r'zypper\s+(\d+)\.(\d+)\.(\d+)', out)
|
||||
if not match or int(match.group(1)) > 0:
|
||||
old_zypper = False
|
||||
else:
|
||||
old_zypper = True
|
||||
|
||||
# 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, old_zypper, url=repo, alias=name)
|
||||
elif repo:
|
||||
exists = repo_exists(module, old_zypper, url=repo)
|
||||
else:
|
||||
exists = repo_exists(module, old_zypper, alias=name)
|
||||
|
||||
if state == 'present':
|
||||
if exists:
|
||||
exit_unchanged()
|
||||
|
||||
changed = add_repo(module, repo, name, description, disable_gpg_check, old_zypper)
|
||||
elif state == 'absent':
|
||||
if not exists:
|
||||
exit_unchanged()
|
||||
|
||||
changed = remove_repo(module, repo, name, old_zypper)
|
||||
|
||||
module.exit_json(changed=changed, repo=repo, state=state)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue