Skip to content


add shell (fnmatch) style pattern support for the apt module #3569

wants to merge 3 commits into from

3 participants


This branch adds support to install multiple packages via fnmatch style patterns in the "apt" module.

Our use-case is that for a university lab we want to install all r-cran-* packages and this seems to be the simplest way of doing it. The alternative would be to have a "apt_facts" module that exposes the apt cache information maybe? I'm happy to explore that option if you prefer it, but the approach in this branch looks simpler to understand in a playbook.

Feedback welcome!



Happy to test this, though may I request list comprehensions rather than lambda/filter?

Makes things a bit more readable IMHO and also will help us out when these are later deprecated, should we want to make Python 3 versions.

I'd like to avoid apt_facts if possible.


Thanks, I updated the code now to avoid the lambda/filter.

To test you can e.g. run
$ ansible -m apt -a pkg='apt*' test-host
and it should try to install a bunch of apt releated pkgs

With "pkg='apt*:i386'" on a modern amd64 ubuntu you should get a bunch of i386 packages that match the pattern.


Looks like this needs to be rebased due to changes in the apt module, can you take care of that?



I update the branch now. Please let me know if more is needed.


Hm, I did a git merge instead of a git rebase, let me see if I can fix this, otherwise I will push a new branch. Sorry for that.


Yeah you may be able to fix with a force push, but I'm not sure.

I will let you resubmit this one. Thanks!

@mpdehaan mpdehaan closed this

This commit breaks the ability to install the latest version of package using :
apt: pkg=foo=*
which is valid apt-get action:
apt-get install foo=*


@uvizhe apt: pkg=foo will install the latest already, no? unless you use a special /etc/apt/preferences policy of course.


@mvo5 sure pkg=foo installs the latest. But when you specify package version in variable and you don't know which is the latest this (foo=*) is the only possibility AFAIK. You can't leave variable empty since "apt: pkg=foo=" is illegal in Ansible.


@mvo5 thanks! This magically works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 17, 2013
  1. @mvo5
Commits on Jul 18, 2013
  1. @mvo5
Commits on Aug 8, 2013
  1. @mvo5

    Merge remote-tracking branch 'upstream/devel' into feature/apt-fnmatch

    mvo5 committed
Showing with 23 additions and 1 deletion.
  1. +23 −1 library/packaging/apt
24 library/packaging/apt
@@ -29,7 +29,7 @@ version_added: "0.0.2"
- - A package name or package specifier with version, like C(foo) or C(foo=1.0)
+ - A package name or package specifier with version, like C(foo) or C(foo=1.0). Shell like wildcards (fnmatch) like apt* are also supported.
required: false
default: null
@@ -169,8 +169,30 @@ def package_status(m, pkgname, version, cache, state):
#assume older version of python-apt is installed
return pkg.isInstalled, pkg.isUpgradable, has_files
+def expand_pkgspec_from_fnmatches(m, pkgspec, cache):
+ new_pkgspec = []
+ for pkgname_or_fnmatch_pattern in pkgspec:
+ # note that any of these chars is not allowed in a (debian) pkgname
+ if [c for c in pkgname_or_fnmatch_pattern if c in "*?[]!"]:
+ if "=" in pkgname_or_fnmatch_pattern:
+ m.fail_json(msg="pkgname wildcard and version can not be mixed")
+ # handle multiarch pkgnames, the idea is that "apt*" should
+ # only select native packages. But "apt*:i386" should still work
+ if not ":" in pkgname_or_fnmatch_pattern:
+ matches = fnmatch.filter(
+ [ for pkg in cache
+ if not ":" in], pkgname_or_fnmatch_pattern)
+ else:
+ matches = fnmatch.filter(
+ [ for pkg in cache], pkgname_or_fnmatch_pattern)
+ new_pkgspec.extend(matches)
+ else:
+ new_pkgspec.append(pkgname_or_fnmatch_pattern)
+ return new_pkgspec
def install(m, pkgspec, cache, upgrade=False, default_release=None, install_recommends=True, force=False):
packages = ""
+ pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
for package in pkgspec:
name, version = package_split(package)
installed, upgradable, has_files = package_status(m, name, version, cache, state='install')
Something went wrong with that request. Please try again.