New Module: package_facts (#33195)

* new package_facts module

Signed-off-by: Adam Miller <maxamillion@fedoraproject.org>

* remove package_facts pkg manager aliases, they are misleading

Signed-off-by: Adam Miller <maxamillion@fedoraproject.org>

* fix package_facts tests now that aliases are dropped

Signed-off-by: Adam Miller <maxamillion@fedoraproject.org>
This commit is contained in:
Adam Miller 2017-12-14 12:47:47 -06:00 committed by Brian Coca
parent d1cf9cfeb6
commit 6cb388a98a
3 changed files with 315 additions and 0 deletions

View file

@ -0,0 +1,237 @@
#!/usr/bin/python
# (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# most of it copied from AWX's scan_packages module
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: package_facts
short_description: package information as facts
description:
- Return information about installed packages as facts
options:
manager:
description:
- The package manager used by the system so we can query the package information
default: auto
choices: ["auto", "rpm", "apt"]
required: False
version_added: "2.5"
author:
- Matthew Jones
- Brian Coca
- Adam Miller
'''
EXAMPLES = '''
- name: get the rpm package facts
package_facts:
manager: "auto"
- name: show them
debug: var=ansible_facts.packages
'''
RETURN = '''
ansible_facts:
description: facts to add to ansible_facts
returned: always
type: complex
contains:
packages:
description: list of dicts with package information
returned: when operating system level package manager is specified or auto detected manager
type: dict
sample_rpm:
{
"packages": {
"kernel": [
{
"arch": "x86_64",
"epoch": null,
"name": "kernel",
"release": "514.26.2.el7",
"source": "rpm",
"version": "3.10.0"
},
{
"arch": "x86_64",
"epoch": null,
"name": "kernel",
"release": "514.16.1.el7",
"source": "rpm",
"version": "3.10.0"
},
{
"arch": "x86_64",
"epoch": null,
"name": "kernel",
"release": "514.10.2.el7",
"source": "rpm",
"version": "3.10.0"
},
{
"arch": "x86_64",
"epoch": null,
"name": "kernel",
"release": "514.21.1.el7",
"source": "rpm",
"version": "3.10.0"
},
{
"arch": "x86_64",
"epoch": null,
"name": "kernel",
"release": "693.2.2.el7",
"source": "rpm",
"version": "3.10.0"
}
],
"kernel-tools": [
{
"arch": "x86_64",
"epoch": null,
"name": "kernel-tools",
"release": "693.2.2.el7",
"source": "rpm",
"version": "3.10.0"
}
],
"kernel-tools-libs": [
{
"arch": "x86_64",
"epoch": null,
"name": "kernel-tools-libs",
"release": "693.2.2.el7",
"source": "rpm",
"version": "3.10.0"
}
],
}
}
sample_deb:
{
"packages": {
"libbz2-1.0": [
{
"version": "1.0.6-5",
"source": "apt",
"arch": "amd64",
"name": "libbz2-1.0"
}
],
"patch": [
{
"version": "2.7.1-4ubuntu1",
"source": "apt",
"arch": "amd64",
"name": "patch"
}
],
}
}
'''
import sys
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
def rpm_package_list():
try:
import rpm
except ImportError:
module.fail_json('Unable to use the rpm python bindings, please ensure they are installed under the python the module runs under')
trans_set = rpm.TransactionSet()
installed_packages = {}
for package in trans_set.dbMatch():
package_details = dict(name=package[rpm.RPMTAG_NAME],
version=package[rpm.RPMTAG_VERSION],
release=package[rpm.RPMTAG_RELEASE],
epoch=package[rpm.RPMTAG_EPOCH],
arch=package[rpm.RPMTAG_ARCH],
source='rpm')
if package_details['name'] not in installed_packages:
installed_packages[package_details['name']] = [package_details]
else:
installed_packages[package_details['name']].append(package_details)
return installed_packages
def apt_package_list():
try:
import apt
except ImportError:
module.fail_json('Unable to use the apt python bindings, please ensure they are installed under the python the module runs under')
apt_cache = apt.Cache()
installed_packages = {}
apt_installed_packages = [pk for pk in apt_cache.keys() if apt_cache[pk].is_installed]
for package in apt_installed_packages:
ac_pkg = apt_cache[package].installed
package_details = dict(name=package, version=ac_pkg.version, arch=ac_pkg.architecture, source='apt')
if package_details['name'] not in installed_packages:
installed_packages[package_details['name']] = [package_details]
else:
installed_packages[package_details['name']].append(package_details)
return installed_packages
# FIXME: add more listing methods
def main():
global module
module = AnsibleModule(argument_spec=dict(manager=dict()))
manager = module.params['manager']
packages = {}
results = {}
if manager is None or manager == 'auto':
# detect!
for manager_lib in ('rpm', 'apt'):
try:
dummy = __import__(manager_lib)
manager = manager_lib
break
except ImportError:
pass
# FIXME: add more detection methods
try:
if manager == "rpm":
packages = rpm_package_list()
elif manager == "apt":
packages = apt_package_list()
else:
if manager:
results['msg'] = 'Unsupported package manager: %s' % manager
results['skipped'] = True
else:
module.fail_json(msg='Could not detect supported package manager')
except Exception as e:
from traceback import format_tb
module.fail_json(msg='Failed to retrieve packages: %s' % to_text(e), exception=format_tb(sys.exc_info()[2]))
results['ansible_facts'] = {}
# Set the facts, this will override the facts in ansible_facts that might
# exist from previous runs when using operating system level or distribution
# package managers
results['ansible_facts']['packages'] = packages
module.exit_json(**results)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,3 @@
posix/ci/group3
skip/freebsd
skip/osx

View file

@ -0,0 +1,75 @@
# Test playbook for the package_facts module
# (c) 2017, Adam Miller <admiller@redhat.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/>.
- name: Prep package_fact tests - Debian Family
block:
- name: install python apt bindings - python2
package: name="python-apt" state=installed
when: ansible_python.version.major|int == 2
- name: install python apt bindings - python3
package: name="python3-apt" state=installed
when: ansible_python.version.major|int == 3
- name: Gather package facts
package_facts:
manager: apt
- name: check for ansible_facts.packages exists
assert:
that: ansible_facts.packages is defined
when: ansible_os_family == "Debian"
- name: Run package_fact tests - Red Hat Family
block:
- name: Gather package facts
package_facts:
manager: rpm
- name: check for ansible_facts.packages exists
assert:
that: ansible_facts.packages is defined
when: (ansible_os_family == "RedHat")
- name: Run package_fact tests - SUSE/OpenSUSE Family
block:
- name: install python rpm bindings - python2
package: name="rpm-python" state=installed
when: ansible_python.version.major|int == 2
- name: install python rpm bindings - python3
package: name="python3-rpm" state=installed
when: ansible_python.version.major|int == 3
- name: Gather package facts
package_facts:
manager: rpm
- name: check for ansible_facts.packages exists
assert:
that: ansible_facts.packages is defined
when: (ansible_os_family == "openSUSE Leap") or (ansible_os_family == "Suse")
# Check that auto detection works also
- name: Gather package facts
package_facts:
manager: auto
- name: check for ansible_facts.packages exists
assert:
that: ansible_facts.packages is defined