You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ansible/packaging/os/dnf.py

279 lines
8.6 KiB
Python

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# Copyright 2015 Cristian van Ee <cristian at cvee.org>
# Copyright 2015 Igor Gnatenko <i.gnatenko.brain@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 traceback
import os
import operator
import functools
import dnf
import dnf.cli
import dnf.util
DOCUMENTATION = '''
---
module: dnf
version_added: 1.9
short_description: Manages packages with the I(dnf) package manager
description:
- Installs, upgrade, removes, and lists packages and groups with the I(dnf) package manager.
options:
name:
description:
- "Package name, or package specifier with version, like C(name-1.0). When using state=latest, this can be '*' which means run: dnf -y update. You can also pass a url or a local path to a rpm file."
required: true
default: null
aliases: []
list:
description:
- Various (non-idempotent) commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples.
required: false
default: null
state:
description:
- Whether to install (C(present), C(latest)), or remove (C(absent)) a package.
required: false
choices: [ "present", "latest", "absent" ]
default: "present"
enablerepo:
description:
- I(Repoid) of repositories to enable for the install/update operation.
These repos will not persist beyond the transaction.
When specifying multiple repos, separate them with a ",".
required: false
default: null
aliases: []
disablerepo:
description:
- I(Repoid) of repositories to disable for the install/update operation.
These repos will not persist beyond the transaction.
When specifying multiple repos, separate them with a ",".
required: false
default: null
aliases: []
conf_file:
description:
- The remote dnf configuration file to use for the transaction.
required: false
default: null
aliases: []
disable_gpg_check:
description:
- Whether to disable the GPG checking of signatures of packages being
installed. Has an effect only if state is I(present) or I(latest).
required: false
default: "no"
choices: ["yes", "no"]
aliases: []
notes: []
# informational: requirements for nodes
requirements:
- "python >= 2.6"
- dnf
author:
- '"Igor Gnatenko (@ignatenkobrain) " <i.gnatenko.brain@gmail.com>'
- '"Cristian van Ee (@DJMuggs)" <cristian at cvee.org>'
'''
EXAMPLES = '''
- name: install the latest version of Apache
dnf: name=httpd state=latest
- name: remove the Apache package
dnf: name=httpd state=absent
- name: install the latest version of Apache from the testing repo
dnf: name=httpd enablerepo=testing state=present
- name: upgrade all packages
dnf: name=* state=latest
- name: install the nginx rpm from a remote repo
dnf: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
- name: install nginx rpm from a local file
dnf: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present
- name: install the 'Development tools' package group
dnf: name="@Development tools" state=present
'''
import syslog
def log(msg):
syslog.openlog('ansible-dnf', 0, syslog.LOG_USER)
syslog.syslog(syslog.LOG_NOTICE, msg)
def dnf_base(conf_file=None):
my = dnf.Base()
my.conf.debuglevel = 0
if conf_file and os.path.exists(conf_file):
my.conf.config_file_path = conf_file
my.conf.read()
my.read_all_repos()
my.fill_sack()
return my
def pkg_to_dict(pkg):
"""
Args:
pkg (hawkey.Package): The package
"""
d = {
'name': pkg.name,
'arch': pkg.arch,
'epoch': str(pkg.epoch),
'release': pkg.release,
'version': pkg.version,
'repo': pkg.repoid,
}
d['nevra'] = '{epoch}:{name}-{version}-{release}.{arch}'.format(**d)
if pkg.installed:
d['dnfstate'] = 'installed'
else:
d['dnfstate'] = 'available'
return d
def list_stuff(module, conf_file, stuff):
my = dnf_base(conf_file)
if stuff == 'installed':
return [pkg_to_dict(p) for p in my.sack.query().installed()]
elif stuff == 'updates':
return [pkg_to_dict(p) for p in my.sack.query().upgrades()]
elif stuff == 'available':
return [pkg_to_dict(p) for p in my.sack.query().available()]
elif stuff == 'repos':
return [dict(repoid=repo.id, state='enabled') for repo in my.repos.iter_enabled()]
else:
return [pkg_to_dict(p) for p in dnf.subject.Subject(stuff).get_best_query(my.sack)]
def ensure(module, state, pkgspec, conf_file, enablerepo, disablerepo, disable_gpg_check):
my = dnf_base(conf_file)
items = pkgspec.split(',')
if disablerepo:
for repo in disablerepo.split(','):
[r.disable() for r in b.repos.get_matching(repo)]
if enablerepo:
for repo in enablerepo.split(','):
[r.enable() for r in b.repos.get_matching(repo)]
my.conf.gpgcheck = disable_gpg_check
res = {}
res['results'] = []
res['msg'] = ''
res['rc'] = 0
res['changed'] = False
if not dnf.util.am_i_root():
res['msg'] = 'This command has to be run under the root user.'
res['rc'] = 1
pkg_specs, grp_specs, filenames = dnf.cli.commands.parse_spec_group_file(items)
if state in ['installed', 'present']:
# Install files.
local_pkgs = map(my.add_remote_rpm, filenames)
map(my.package_install, local_pkgs)
# Install groups.
if grp_specs:
my.read_comps()
my.env_group_install(grp_specs, dnf.const.GROUP_PACKAGE_TYPES)
# Install packages.
for pkg_spec in pkg_specs:
try:
my.install(pkg_spec)
except dnf.exceptions.MarkingError:
res['results'].append('No package %s available.' % pkg_spec)
res['rc'] = 1
if not my.resolve() and res['rc'] == 0:
res['msg'] += 'Nothing to do'
res['changed'] = False
else:
my.download_packages(my.transaction.install_set)
my.do_transaction()
[res['results'].append('Installed: %s' % pkg) for pkg in my.transaction.install_set)
[res['results'].append('Removed: %s' % pkg) for pkg in my.transaction.remove_set)
module.exit_json(**res)
def main():
# state=installed name=pkgspec
# state=removed name=pkgspec
# state=latest name=pkgspec
#
# informational commands:
# list=installed
# list=updates
# list=available
# list=repos
# list=pkgspec
module = AnsibleModule(
argument_spec = dict(
name=dict(aliases=['pkg']),
# removed==absent, installed==present, these are accepted as aliases
state=dict(default='installed', choices=['absent', 'present', 'installed', 'removed', 'latest']),
enablerepo=dict(),
disablerepo=dict(),
list=dict(),
conf_file=dict(default=None),
disable_gpg_check=dict(required=False, default="no", type='bool'),
),
required_one_of = [['name','list']],
mutually_exclusive = [['name','list']],
supports_check_mode = True
)
params = module.params
if not repoquery:
module.fail_json(msg="repoquery is required to use this module at this time. Please install the yum-utils package.")
if params['list']:
results = dict(results=list_stuff(module, params['conf_file'], params['list']))
module.exit_json(**results)
else:
pkg = params['name']
state = params['state']
enablerepo = params.get('enablerepo', '')
disablerepo = params.get('disablerepo', '')
disable_gpg_check = params['disable_gpg_check']
res = ensure(module, state, pkg, params['conf_file'], enablerepo,
disablerepo, disable_gpg_check)
module.fail_json(msg="we should never get here unless this all failed", **res)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()