|
|
@ -167,7 +167,7 @@ except ImportError:
|
|
|
|
HAS_PYTHON_APT = False
|
|
|
|
HAS_PYTHON_APT = False
|
|
|
|
|
|
|
|
|
|
|
|
def package_split(pkgspec):
|
|
|
|
def package_split(pkgspec):
|
|
|
|
parts = pkgspec.split('=')
|
|
|
|
parts = pkgspec.split('=', 1)
|
|
|
|
if len(parts) > 1:
|
|
|
|
if len(parts) > 1:
|
|
|
|
return parts[0], parts[1]
|
|
|
|
return parts[0], parts[1]
|
|
|
|
else:
|
|
|
|
else:
|
|
|
@ -205,19 +205,34 @@ def package_status(m, pkgname, version, cache, state):
|
|
|
|
# assume older version of python-apt is installed
|
|
|
|
# assume older version of python-apt is installed
|
|
|
|
package_is_installed = pkg.isInstalled
|
|
|
|
package_is_installed = pkg.isInstalled
|
|
|
|
|
|
|
|
|
|
|
|
if version and package_is_installed:
|
|
|
|
if version:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
installed_version = pkg.installed.version
|
|
|
|
installed_version = pkg.installed.version
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
installed_version = pkg.installedVersion
|
|
|
|
installed_version = pkg.installedVersion
|
|
|
|
return package_is_installed and fnmatch.fnmatch(installed_version, version), False, has_files
|
|
|
|
|
|
|
|
|
|
|
|
avail_upgrades = fnmatch.filter((p.version for p in pkg.versions), version)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if package_is_installed:
|
|
|
|
|
|
|
|
# Only claim the package is installed if the version is matched as well
|
|
|
|
|
|
|
|
package_is_installed = fnmatch.fnmatch(installed_version, version)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Only claim the package is upgradable if a candidate matches the version
|
|
|
|
|
|
|
|
package_is_upgradable = False
|
|
|
|
|
|
|
|
for candidate in avail_upgrades:
|
|
|
|
|
|
|
|
if pkg.versions[candidate] > p.installed:
|
|
|
|
|
|
|
|
package_is_upgradable = True
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
package_is_upgradable = bool(avail_upgrades)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
package_is_upgradable = pkg.is_upgradable
|
|
|
|
package_is_upgradable = pkg.is_upgradable
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
# assume older version of python-apt is installed
|
|
|
|
# assume older version of python-apt is installed
|
|
|
|
package_is_upgradable = pkg.isUpgradable
|
|
|
|
package_is_upgradable = pkg.isUpgradable
|
|
|
|
return package_is_installed, package_is_upgradable, has_files
|
|
|
|
|
|
|
|
|
|
|
|
return package_is_installed, package_is_upgradable, has_files
|
|
|
|
|
|
|
|
|
|
|
|
def expand_dpkg_options(dpkg_options_compressed):
|
|
|
|
def expand_dpkg_options(dpkg_options_compressed):
|
|
|
|
options_list = dpkg_options_compressed.split(',')
|
|
|
|
options_list = dpkg_options_compressed.split(',')
|
|
|
@ -229,39 +244,54 @@ def expand_dpkg_options(dpkg_options_compressed):
|
|
|
|
|
|
|
|
|
|
|
|
def expand_pkgspec_from_fnmatches(m, pkgspec, cache):
|
|
|
|
def expand_pkgspec_from_fnmatches(m, pkgspec, cache):
|
|
|
|
new_pkgspec = []
|
|
|
|
new_pkgspec = []
|
|
|
|
for pkgname_or_fnmatch_pattern in pkgspec:
|
|
|
|
for pkgspec_pattern in pkgspec:
|
|
|
|
# note that any of these chars is not allowed in a (debian) pkgname
|
|
|
|
pkgname_pattern, version = package_split(pkgspec_pattern)
|
|
|
|
if [c for c in pkgname_or_fnmatch_pattern if c in "*?[]!"]:
|
|
|
|
|
|
|
|
if "=" in pkgname_or_fnmatch_pattern:
|
|
|
|
# note that none of these chars is allowed in a (debian) pkgname
|
|
|
|
m.fail_json(msg="pkgname wildcard and version can not be mixed")
|
|
|
|
if frozenset('*?[]!').intersection(pkgname_pattern):
|
|
|
|
# handle multiarch pkgnames, the idea is that "apt*" should
|
|
|
|
# handle multiarch pkgnames, the idea is that "apt*" should
|
|
|
|
# only select native packages. But "apt*:i386" should still work
|
|
|
|
# only select native packages. But "apt*:i386" should still work
|
|
|
|
if not ":" in pkgname_or_fnmatch_pattern:
|
|
|
|
if not ":" in pkgname_pattern:
|
|
|
|
matches = fnmatch.filter(
|
|
|
|
try:
|
|
|
|
[pkg.name for pkg in cache
|
|
|
|
pkg_name_cache = _non_multiarch
|
|
|
|
if not ":" in pkg.name], pkgname_or_fnmatch_pattern)
|
|
|
|
except NameError:
|
|
|
|
|
|
|
|
pkg_name_cache = _non_multiarch = [pkg.name for pkg in cache if not ':' in pkg.name]
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
matches = fnmatch.filter(
|
|
|
|
try:
|
|
|
|
[pkg.name for pkg in cache], pkgname_or_fnmatch_pattern)
|
|
|
|
pkg_name_cache = _all_pkg_names
|
|
|
|
|
|
|
|
except NameError:
|
|
|
|
|
|
|
|
pkg_name_cache = _all_pkg_names = [pkg.name for pkg in cache]
|
|
|
|
|
|
|
|
matches = fnmatch.filter(pkg_name_cache, pkgname_pattern)
|
|
|
|
|
|
|
|
|
|
|
|
if len(matches) == 0:
|
|
|
|
if len(matches) == 0:
|
|
|
|
m.fail_json(msg="No package(s) matching '%s' available" % str(pkgname_or_fnmatch_pattern))
|
|
|
|
m.fail_json(msg="No package(s) matching '%s' available" % str(pkgname_pattern))
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
new_pkgspec.extend(matches)
|
|
|
|
new_pkgspec.extend(matches)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
new_pkgspec.append(pkgname_or_fnmatch_pattern)
|
|
|
|
# No wildcards in name
|
|
|
|
|
|
|
|
new_pkgspec.append(pkgspec_pattern)
|
|
|
|
return new_pkgspec
|
|
|
|
return new_pkgspec
|
|
|
|
|
|
|
|
|
|
|
|
def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
|
|
|
def install(m, pkgspec, cache, upgrade=False, default_release=None,
|
|
|
|
install_recommends=True, force=False,
|
|
|
|
install_recommends=True, force=False,
|
|
|
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
|
|
|
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
|
|
|
|
|
|
|
|
pkg_list = []
|
|
|
|
packages = ""
|
|
|
|
packages = ""
|
|
|
|
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
|
|
|
|
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
|
|
|
|
for package in pkgspec:
|
|
|
|
for package in pkgspec:
|
|
|
|
name, version = package_split(package)
|
|
|
|
name, version = package_split(package)
|
|
|
|
installed, upgradable, has_files = package_status(m, name, version, cache, state='install')
|
|
|
|
installed, upgradable, has_files = package_status(m, name, version, cache, state='install')
|
|
|
|
if not installed or (upgrade and upgradable):
|
|
|
|
if not installed or (upgrade and upgradable):
|
|
|
|
packages += "'%s' " % package
|
|
|
|
pkg_list.append("'%s'" % package)
|
|
|
|
|
|
|
|
if installed and upgradable and version:
|
|
|
|
|
|
|
|
# This happens when the package is installed, a newer version is
|
|
|
|
|
|
|
|
# available, and the version is a wildcard that matches both
|
|
|
|
|
|
|
|
#
|
|
|
|
|
|
|
|
# We do not apply the upgrade flag because we cannot specify both
|
|
|
|
|
|
|
|
# a version and state=latest. (This behaviour mirrors how apt
|
|
|
|
|
|
|
|
# treats a version with wildcard in the package)
|
|
|
|
|
|
|
|
pkg_list.append("'%s'" % package)
|
|
|
|
|
|
|
|
packages = ' '.join(pkg_list)
|
|
|
|
|
|
|
|
|
|
|
|
if len(packages) != 0:
|
|
|
|
if len(packages) != 0:
|
|
|
|
if force:
|
|
|
|
if force:
|
|
|
@ -350,13 +380,14 @@ def install_deb(m, debs, cache, force, install_recommends, dpkg_options):
|
|
|
|
|
|
|
|
|
|
|
|
def remove(m, pkgspec, cache, purge=False,
|
|
|
|
def remove(m, pkgspec, cache, purge=False,
|
|
|
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
|
|
|
|
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
|
|
|
|
packages = ""
|
|
|
|
pkg_list = []
|
|
|
|
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
|
|
|
|
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
|
|
|
|
for package in pkgspec:
|
|
|
|
for package in pkgspec:
|
|
|
|
name, version = package_split(package)
|
|
|
|
name, version = package_split(package)
|
|
|
|
installed, upgradable, has_files = package_status(m, name, version, cache, state='remove')
|
|
|
|
installed, upgradable, has_files = package_status(m, name, version, cache, state='remove')
|
|
|
|
if installed or (has_files and purge):
|
|
|
|
if installed or (has_files and purge):
|
|
|
|
packages += "'%s' " % package
|
|
|
|
pkg_list.append("'%s'" % package)
|
|
|
|
|
|
|
|
packages = ' '.join(pkg_list)
|
|
|
|
|
|
|
|
|
|
|
|
if len(packages) == 0:
|
|
|
|
if len(packages) == 0:
|
|
|
|
m.exit_json(changed=False)
|
|
|
|
m.exit_json(changed=False)
|
|
|
@ -567,4 +598,5 @@ def main():
|
|
|
|
# import module snippets
|
|
|
|
# import module snippets
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|
|
|
|
|
|
|
|
|
main()
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
|
|
main()
|
|
|
|