2014-09-26 01:01:01 +00:00
|
|
|
#!/usr/bin/python -tt
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
# Copyright: (c) 2012, Afterburn <http://github.com/afterburn>
|
|
|
|
# Copyright: (c) 2013, Aaron Bull Schaefer <aaron@elasticdog.com>
|
|
|
|
# Copyright: (c) 2015, Indrajit Raychaudhuri <irc+code@indrajit.com>
|
2017-08-01 21:37:37 +00:00
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2017-08-16 03:16:38 +00:00
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
2017-03-14 16:07:22 +00:00
|
|
|
'status': ['preview'],
|
|
|
|
'supported_by': 'community'}
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
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"
|
2015-05-15 18:52:37 +00:00
|
|
|
author:
|
2017-09-27 07:13:18 +00:00
|
|
|
- Indrajit Raychaudhuri (@indrajitr)
|
|
|
|
- Aaron Bull Schaefer (@elasticdog) <aaron@elasticdog.com>
|
|
|
|
- Afterburn
|
2014-09-26 01:01:01 +00:00
|
|
|
options:
|
|
|
|
name:
|
|
|
|
description:
|
|
|
|
- Name of the package to install, upgrade, or remove.
|
2017-09-27 07:13:18 +00:00
|
|
|
aliases: [ package, pkg ]
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
state:
|
|
|
|
description:
|
|
|
|
- Desired state of the package.
|
2017-09-27 07:13:18 +00:00
|
|
|
default: present
|
|
|
|
choices: [ absent, latest, present ]
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
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.
|
2017-09-27 07:13:18 +00:00
|
|
|
type: bool
|
2015-08-04 00:10:50 +00:00
|
|
|
default: no
|
2014-09-26 01:01:01 +00:00
|
|
|
version_added: "1.3"
|
|
|
|
|
2015-03-31 08:28:35 +00:00
|
|
|
force:
|
|
|
|
description:
|
2016-02-11 12:34:44 +00:00
|
|
|
- When removing package - force remove package, without any
|
|
|
|
checks. When update_cache - force redownload repo
|
|
|
|
databases.
|
2017-09-27 07:13:18 +00:00
|
|
|
type: bool
|
2015-08-04 00:10:50 +00:00
|
|
|
default: no
|
2015-04-01 09:27:31 +00:00
|
|
|
version_added: "2.0"
|
2015-03-31 08:28:35 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
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.
|
2017-09-27 07:13:18 +00:00
|
|
|
type: bool
|
2015-08-04 00:10:50 +00:00
|
|
|
default: no
|
2017-09-27 07:13:18 +00:00
|
|
|
aliases: [ update-cache ]
|
2015-03-31 08:18:22 +00:00
|
|
|
|
2015-04-01 09:27:31 +00:00
|
|
|
upgrade:
|
2015-03-31 08:18:22 +00:00
|
|
|
description:
|
2017-09-27 07:13:18 +00:00
|
|
|
- Whether or not to upgrade whole system.
|
|
|
|
type: bool
|
2015-08-04 00:10:50 +00:00
|
|
|
default: no
|
2015-04-01 09:27:31 +00:00
|
|
|
version_added: "2.0"
|
2014-09-26 01:01:01 +00:00
|
|
|
'''
|
|
|
|
|
2017-03-08 15:32:02 +00:00
|
|
|
RETURN = '''
|
|
|
|
packages:
|
|
|
|
description: a list of packages that have been changed
|
|
|
|
returned: when upgrade is set to yes
|
2017-04-26 14:56:13 +00:00
|
|
|
type: list
|
2017-09-27 07:13:18 +00:00
|
|
|
sample: [ package, other-package ]
|
2017-03-08 15:32:02 +00:00
|
|
|
'''
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
EXAMPLES = '''
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Install package foo
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
name: foo
|
|
|
|
state: present
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Upgrade package foo
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
name: foo
|
|
|
|
state: latest
|
|
|
|
update_cache: yes
|
2015-01-10 15:40:03 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Remove packages foo and bar
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
name: foo,bar
|
|
|
|
state: absent
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Recursively remove package baz
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
name: baz
|
|
|
|
state: absent
|
|
|
|
recurse: yes
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Run the equivalent of "pacman -Sy" as a separate step
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
update_cache: yes
|
2015-03-31 08:18:22 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Run the equivalent of "pacman -Su" as a separate step
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
upgrade: yes
|
2015-03-31 08:28:35 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Run the equivalent of "pacman -Syu" as a separate step
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
update_cache: yes
|
|
|
|
upgrade: yes
|
2015-07-26 18:14:14 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
- name: Run the equivalent of "pacman -Rdd", force remove package baz
|
|
|
|
pacman:
|
2016-12-01 16:07:13 +00:00
|
|
|
name: baz
|
|
|
|
state: absent
|
|
|
|
force: yes
|
2014-09-26 01:01:01 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
|
|
|
|
|
2015-01-10 15:40:03 +00:00
|
|
|
def get_version(pacman_output):
|
|
|
|
"""Take pacman -Qi or pacman -Si output and get the Version"""
|
|
|
|
lines = pacman_output.split('\n')
|
|
|
|
for line in lines:
|
|
|
|
if 'Version' in line:
|
|
|
|
return line.split(':')[1].strip()
|
|
|
|
return None
|
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def query_package(module, pacman_path, name, state="present"):
|
2017-03-23 01:50:28 +00:00
|
|
|
"""Query the package status in both the local system and the repository. Returns a boolean to indicate if the package is installed, a second
|
|
|
|
boolean to indicate if the package is up-to-date and a third boolean to indicate whether online information were available
|
|
|
|
"""
|
2014-09-26 01:01:01 +00:00
|
|
|
if state == "present":
|
2015-07-25 04:07:20 +00:00
|
|
|
lcmd = "%s -Qi %s" % (pacman_path, name)
|
2015-01-10 15:40:03 +00:00
|
|
|
lrc, lstdout, lstderr = module.run_command(lcmd, check_rc=False)
|
|
|
|
if lrc != 0:
|
|
|
|
# package is not installed locally
|
2015-12-16 06:03:18 +00:00
|
|
|
return False, False, False
|
2015-07-25 04:07:20 +00:00
|
|
|
|
2015-01-10 15:40:03 +00:00
|
|
|
# get the version installed locally (if any)
|
|
|
|
lversion = get_version(lstdout)
|
2015-07-25 04:07:20 +00:00
|
|
|
|
|
|
|
rcmd = "%s -Si %s" % (pacman_path, name)
|
2015-01-10 15:40:03 +00:00
|
|
|
rrc, rstdout, rstderr = module.run_command(rcmd, check_rc=False)
|
|
|
|
# get the version in the repository
|
|
|
|
rversion = get_version(rstdout)
|
|
|
|
|
|
|
|
if rrc == 0:
|
|
|
|
# Return True to indicate that the package is installed locally, and the result of the version number comparison
|
|
|
|
# to determine if the package is up-to-date.
|
2015-12-16 06:03:18 +00:00
|
|
|
return True, (lversion == rversion), False
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2015-12-16 06:03:18 +00:00
|
|
|
# package is installed but cannot fetch remote Version. Last True stands for the error
|
|
|
|
return True, True, True
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def update_package_db(module, pacman_path):
|
2016-02-11 12:34:44 +00:00
|
|
|
if module.params["force"]:
|
|
|
|
args = "Syy"
|
|
|
|
else:
|
|
|
|
args = "Sy"
|
|
|
|
|
|
|
|
cmd = "%s -%s" % (pacman_path, args)
|
2014-09-26 01:01:01 +00:00
|
|
|
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")
|
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def upgrade(module, pacman_path):
|
|
|
|
cmdupgrade = "%s -Suq --noconfirm" % (pacman_path)
|
2017-03-08 15:32:02 +00:00
|
|
|
cmdneedrefresh = "%s -Qu" % (pacman_path)
|
2015-03-31 08:18:22 +00:00
|
|
|
rc, stdout, stderr = module.run_command(cmdneedrefresh, check_rc=False)
|
2017-03-08 15:32:02 +00:00
|
|
|
data = stdout.split('\n')
|
|
|
|
data.remove('')
|
|
|
|
packages = []
|
|
|
|
diff = {
|
|
|
|
'before': '',
|
|
|
|
'after': '',
|
|
|
|
}
|
2015-03-31 08:18:22 +00:00
|
|
|
|
|
|
|
if rc == 0:
|
2017-04-09 09:21:55 +00:00
|
|
|
regex = re.compile('([\w-]+) ((?:\S+)-(?:\S+)) -> ((?:\S+)-(?:\S+))')
|
2017-03-08 15:32:02 +00:00
|
|
|
for p in data:
|
|
|
|
m = regex.search(p)
|
|
|
|
packages.append(m.group(1))
|
|
|
|
if module._diff:
|
|
|
|
diff['before'] += "%s-%s\n" % (m.group(1), m.group(2))
|
|
|
|
diff['after'] += "%s-%s\n" % (m.group(1), m.group(3))
|
2015-07-26 18:14:14 +00:00
|
|
|
if module.check_mode:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=True, msg="%s package(s) would be upgraded" % (len(data)), packages=packages, diff=diff)
|
2015-07-25 04:07:20 +00:00
|
|
|
rc, stdout, stderr = module.run_command(cmdupgrade, check_rc=False)
|
|
|
|
if rc == 0:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=True, msg='System upgraded', packages=packages, diff=diff)
|
2015-03-31 08:18:22 +00:00
|
|
|
else:
|
2015-07-26 18:14:14 +00:00
|
|
|
module.fail_json(msg="Could not upgrade")
|
2015-03-31 08:18:22 +00:00
|
|
|
else:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=False, msg='Nothing to upgrade', packages=packages)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def remove_packages(module, pacman_path, packages):
|
2017-03-08 15:32:02 +00:00
|
|
|
data = []
|
|
|
|
diff = {
|
|
|
|
'before': '',
|
|
|
|
'after': '',
|
|
|
|
}
|
|
|
|
|
2016-02-11 12:34:44 +00:00
|
|
|
if module.params["recurse"] or module.params["force"]:
|
|
|
|
if module.params["recurse"]:
|
|
|
|
args = "Rs"
|
|
|
|
if module.params["force"]:
|
|
|
|
args = "Rdd"
|
|
|
|
if module.params["recurse"] and module.params["force"]:
|
|
|
|
args = "Rdds"
|
2015-03-31 08:28:35 +00:00
|
|
|
else:
|
|
|
|
args = "R"
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
remove_c = 0
|
2016-12-11 02:50:09 +00:00
|
|
|
# Using a for loop in case of error, we can report the package that failed
|
2014-09-26 01:01:01 +00:00
|
|
|
for package in packages:
|
|
|
|
# Query the package first, to see if we even need to remove
|
2015-12-16 06:03:18 +00:00
|
|
|
installed, updated, unknown = query_package(module, pacman_path, package)
|
2015-01-10 15:40:03 +00:00
|
|
|
if not installed:
|
2014-09-26 01:01:01 +00:00
|
|
|
continue
|
|
|
|
|
2016-12-20 03:17:47 +00:00
|
|
|
cmd = "%s -%s %s --noconfirm --noprogressbar" % (pacman_path, args, package)
|
2014-09-26 01:01:01 +00:00
|
|
|
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
|
|
|
|
2017-04-24 22:19:13 +00:00
|
|
|
if rc != 0:
|
|
|
|
module.fail_json(msg="failed to remove %s" % (package))
|
|
|
|
|
2017-03-08 15:32:02 +00:00
|
|
|
if module._diff:
|
|
|
|
d = stdout.split('\n')[2].split(' ')[2:]
|
|
|
|
for i, pkg in enumerate(d):
|
|
|
|
d[i] = re.sub('-[0-9].*$', '', d[i].split('/')[-1])
|
|
|
|
diff['before'] += "%s\n" % pkg
|
|
|
|
data.append('\n'.join(d))
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
remove_c += 1
|
|
|
|
|
|
|
|
if remove_c > 0:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=True, msg="removed %s package(s)" % remove_c, diff=diff)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
module.exit_json(changed=False, msg="package(s) already absent")
|
|
|
|
|
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def install_packages(module, pacman_path, state, packages, package_files):
|
2014-09-26 01:01:01 +00:00
|
|
|
install_c = 0
|
2015-12-16 06:03:18 +00:00
|
|
|
package_err = []
|
|
|
|
message = ""
|
2017-03-08 15:32:02 +00:00
|
|
|
data = []
|
|
|
|
diff = {
|
|
|
|
'before': '',
|
|
|
|
'after': '',
|
|
|
|
}
|
2014-09-26 01:01:01 +00:00
|
|
|
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
to_install_repos = []
|
|
|
|
to_install_files = []
|
2014-09-26 01:01:01 +00:00
|
|
|
for i, package in enumerate(packages):
|
2015-01-11 04:56:05 +00:00
|
|
|
# if the package is installed and state == present or state == latest and is up-to-date then skip
|
2015-12-16 06:03:18 +00:00
|
|
|
installed, updated, latestError = query_package(module, pacman_path, package)
|
|
|
|
if latestError and state == 'latest':
|
|
|
|
package_err.append(package)
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2015-01-11 04:56:05 +00:00
|
|
|
if installed and (state == 'present' or (state == 'latest' and updated)):
|
2014-09-26 01:01:01 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
if package_files[i]:
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
to_install_files.append(package_files[i])
|
2014-09-26 01:01:01 +00:00
|
|
|
else:
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
to_install_repos.append(package)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
if to_install_repos:
|
2016-12-20 03:17:47 +00:00
|
|
|
cmd = "%s -S %s --noconfirm --noprogressbar --needed" % (pacman_path, " ".join(to_install_repos))
|
2014-09-26 01:01:01 +00:00
|
|
|
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
2017-04-24 22:19:13 +00:00
|
|
|
|
|
|
|
if rc != 0:
|
|
|
|
module.fail_json(msg="failed to install %s: %s" % (" ".join(to_install_repos), stderr))
|
|
|
|
|
2017-03-08 15:32:02 +00:00
|
|
|
data = stdout.split('\n')[3].split(' ')[2:]
|
2017-09-27 07:13:18 +00:00
|
|
|
data = [i for i in data if i != '']
|
2017-03-08 15:32:02 +00:00
|
|
|
for i, pkg in enumerate(data):
|
|
|
|
data[i] = re.sub('-[0-9].*$', '', data[i].split('/')[-1])
|
|
|
|
if module._diff:
|
|
|
|
diff['after'] += "%s\n" % pkg
|
2014-09-26 01:01:01 +00:00
|
|
|
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
install_c += len(to_install_repos)
|
|
|
|
|
|
|
|
if to_install_files:
|
2016-12-20 03:17:47 +00:00
|
|
|
cmd = "%s -U %s --noconfirm --noprogressbar --needed" % (pacman_path, " ".join(to_install_files))
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
2017-04-24 22:19:13 +00:00
|
|
|
|
|
|
|
if rc != 0:
|
|
|
|
module.fail_json(msg="failed to install %s: %s" % (" ".join(to_install_files), stderr))
|
|
|
|
|
2017-03-08 15:32:02 +00:00
|
|
|
data = stdout.split('\n')[3].split(' ')[2:]
|
2017-09-27 07:13:18 +00:00
|
|
|
data = [i for i in data if i != '']
|
2017-03-08 15:32:02 +00:00
|
|
|
for i, pkg in enumerate(data):
|
|
|
|
data[i] = re.sub('-[0-9].*$', '', data[i].split('/')[-1])
|
|
|
|
if module._diff:
|
|
|
|
diff['after'] += "%s\n" % pkg
|
|
|
|
|
Improve and optimise pacman package installation
Previously, packages were installed one at a time in a loop. This caused
a couple of problems.
First, it was a performance issue - pacman would have to perform all of
its checks once per package. This is unnecessarily costly, especially
when you're trying to install several related packages at the same time.
Second, if a package you're trying to install depends on a virtual
package that is provided by several different packages (such as the
"libgl" package on Arch) and you aren't also installing something that
provides that virtual package at the same time, pacman will produce an
interactive prompt to allow the user to select a relevant package. This
is obviously incompatible with how ansible operates. Yes, this problem
could be avoided by installing packages in a different order, but the
order of installation shouldn't matter, and there may be situations
where it is not possible to control the order of installation.
With this refactoring, all of the above problems are avoided. The code
will now work out all of the packages that need to be installed from any
configured repositories and any packages that need to be installed from
local files, and then install all the repository packages in one go and
then all of the local file packages in one go.
2016-12-19 06:05:17 +00:00
|
|
|
install_c += len(to_install_files)
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2015-12-16 06:03:18 +00:00
|
|
|
if state == 'latest' and len(package_err) > 0:
|
|
|
|
message = "But could not ensure 'latest' state for %s package(s) as remote version could not be fetched." % (package_err)
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
if install_c > 0:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=True, msg="installed %s package(s). %s" % (install_c, message), diff=diff)
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=False, msg="package(s) already installed. %s" % (message), diff=diff)
|
2016-02-11 12:34:44 +00:00
|
|
|
|
2017-09-27 07:13:18 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
def check_packages(module, pacman_path, packages, state):
|
2014-09-26 01:01:01 +00:00
|
|
|
would_be_changed = []
|
2017-03-08 15:32:02 +00:00
|
|
|
diff = {
|
|
|
|
'before': '',
|
|
|
|
'after': '',
|
|
|
|
'before_header': '',
|
|
|
|
'after_header': ''
|
|
|
|
}
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
for package in packages:
|
2015-12-16 06:03:18 +00:00
|
|
|
installed, updated, unknown = query_package(module, pacman_path, package)
|
2015-01-11 04:56:05 +00:00
|
|
|
if ((state in ["present", "latest"] and not installed) or
|
|
|
|
(state == "absent" and installed) or
|
|
|
|
(state == "latest" and not updated)):
|
2014-09-26 01:01:01 +00:00
|
|
|
would_be_changed.append(package)
|
|
|
|
if would_be_changed:
|
|
|
|
if state == "absent":
|
|
|
|
state = "removed"
|
2017-03-08 15:32:02 +00:00
|
|
|
|
|
|
|
if module._diff and (state == 'removed'):
|
|
|
|
diff['before_header'] = 'removed'
|
|
|
|
diff['before'] = '\n'.join(would_be_changed) + '\n'
|
|
|
|
elif module._diff and ((state == 'present') or (state == 'latest')):
|
|
|
|
diff['after_header'] = 'installed'
|
|
|
|
diff['after'] = '\n'.join(would_be_changed) + '\n'
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
module.exit_json(changed=True, msg="%s package(s) would be %s" % (
|
2017-03-08 15:32:02 +00:00
|
|
|
len(would_be_changed), state), diff=diff)
|
2014-09-26 01:01:01 +00:00
|
|
|
else:
|
2017-03-08 15:32:02 +00:00
|
|
|
module.exit_json(changed=False, msg="package(s) already %s" % state, diff=diff)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
|
2016-02-11 20:44:30 +00:00
|
|
|
def expand_package_groups(module, pacman_path, pkgs):
|
|
|
|
expanded = []
|
|
|
|
|
|
|
|
for pkg in pkgs:
|
2017-09-27 07:13:18 +00:00
|
|
|
if pkg: # avoid empty strings
|
2017-06-12 15:10:16 +00:00
|
|
|
cmd = "%s -Sgq %s" % (pacman_path, pkg)
|
|
|
|
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
|
|
|
|
|
|
|
|
if rc == 0:
|
|
|
|
# A group was found matching the name, so expand it
|
|
|
|
for name in stdout.split('\n'):
|
|
|
|
name = name.strip()
|
|
|
|
if name:
|
|
|
|
expanded.append(name)
|
|
|
|
else:
|
|
|
|
expanded.append(pkg)
|
2016-02-11 20:44:30 +00:00
|
|
|
|
|
|
|
return expanded
|
|
|
|
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
2017-09-27 07:13:18 +00:00
|
|
|
argument_spec=dict(
|
|
|
|
name=dict(type='list', aliases=['package', 'pkg']),
|
|
|
|
state=dict(type='str', default='present', choices=['absent', 'installed', 'latest', 'present', 'removed']),
|
|
|
|
recurse=dict(type='bool', default=False),
|
|
|
|
force=dict(type='bool', default=False),
|
|
|
|
upgrade=dict(type='bool', default=False),
|
|
|
|
update_cache=dict(type='bool', default=False, aliases=['update-cache']),
|
2015-09-28 22:11:10 +00:00
|
|
|
),
|
2017-09-27 07:13:18 +00:00
|
|
|
required_one_of=[['name', 'update_cache', 'upgrade']],
|
2017-11-10 14:52:40 +00:00
|
|
|
supports_check_mode=True,
|
|
|
|
)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
pacman_path = module.get_bin_path('pacman', True)
|
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
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:
|
2015-07-25 04:07:20 +00:00
|
|
|
update_package_db(module, pacman_path)
|
2015-07-26 18:14:14 +00:00
|
|
|
if not (p['name'] or p['upgrade']):
|
|
|
|
module.exit_json(changed=True, msg='Updated the package master lists')
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-07-26 18:14:14 +00:00
|
|
|
if p['update_cache'] and module.check_mode and not (p['name'] or p['upgrade']):
|
2014-09-26 01:01:01 +00:00
|
|
|
module.exit_json(changed=True, msg='Would have updated the package cache')
|
|
|
|
|
2015-03-31 08:18:22 +00:00
|
|
|
if p['upgrade']:
|
2015-07-25 04:07:20 +00:00
|
|
|
upgrade(module, pacman_path)
|
2015-03-31 08:18:22 +00:00
|
|
|
|
2014-09-26 01:01:01 +00:00
|
|
|
if p['name']:
|
2016-02-11 20:44:30 +00:00
|
|
|
pkgs = expand_package_groups(module, pacman_path, p['name'])
|
2014-09-26 01:01:01 +00:00
|
|
|
|
|
|
|
pkg_files = []
|
|
|
|
for i, pkg in enumerate(pkgs):
|
2017-09-27 07:13:18 +00:00
|
|
|
if not pkg: # avoid empty strings
|
2017-06-12 15:10:16 +00:00
|
|
|
continue
|
|
|
|
elif re.match(".*\.pkg\.tar(\.(gz|bz2|xz|lrz|lzo|Z))?$", pkg):
|
2014-09-26 01:01:01 +00:00
|
|
|
# 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:
|
2015-07-25 04:07:20 +00:00
|
|
|
check_packages(module, pacman_path, pkgs, p['state'])
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-01-11 04:56:05 +00:00
|
|
|
if p['state'] in ['present', 'latest']:
|
2015-07-25 04:07:20 +00:00
|
|
|
install_packages(module, pacman_path, p['state'], pkgs, pkg_files)
|
2014-09-26 01:01:01 +00:00
|
|
|
elif p['state'] == 'absent':
|
2015-07-25 04:07:20 +00:00
|
|
|
remove_packages(module, pacman_path, pkgs)
|
2014-09-26 01:01:01 +00:00
|
|
|
|
2015-07-25 04:07:20 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|