From: Marcin Krol Date: Fri, 21 Jun 2019 21:38:15 +0000 (+0200) Subject: - added support for poldek package manager X-Git-Url: https://git.tld-linux.org/?a=commitdiff_plain;h=870abe4e7fb61ded053d794a9f391727c4ae90ce;p=packages%2Fansible.git - added support for poldek package manager --- diff --git a/ansible.spec b/ansible.spec index 65796ee..74c5e3a 100644 --- a/ansible.spec +++ b/ansible.spec @@ -1,11 +1,13 @@ Summary: SSH-based configuration management, deployment, and task execution system Name: ansible Version: 2.8.0 -Release: 1 +Release: 2 License: GPL v3+ Group: Development/Libraries Source0: https://releases.ansible.com/ansible/%{name}-%{version}.tar.gz # Source0-md5: 9320cd9e26f929568038db49781df245 +Source1: poldek.py +Patch0: poldek.patch URL: http://ansible.github.com/ BuildRequires: python3-modules BuildRequires: rpm-pythonprov @@ -29,6 +31,7 @@ are transferred to managed machines automatically. %prep %setup -q +%patch0 -p1 %build %py3_build @@ -38,6 +41,8 @@ are transferred to managed machines automatically. rm -rf $RPM_BUILD_ROOT %py3_install +install -p %{SOURCE1} $RPM_BUILD_ROOT%{py3_sitescriptdir}/ansible/modules/packaging/os + install -d $RPM_BUILD_ROOT{%{_sysconfdir}/%{name},%{_mandir}} sed -re '/^#/ !s,[^#]+$,#&,' examples/hosts > $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/hosts cp -p examples/ansible.cfg $RPM_BUILD_ROOT%{_sysconfdir}/%{name} diff --git a/poldek.patch b/poldek.patch new file mode 100644 index 0000000..37d2a10 --- /dev/null +++ b/poldek.patch @@ -0,0 +1,13 @@ +diff -ur ansible-2.8.0.orig/lib/ansible/module_utils/facts/system/pkg_mgr.py ansible-2.8.0/lib/ansible/module_utils/facts/system/pkg_mgr.py +--- ansible-2.8.0.orig/lib/ansible/module_utils/facts/system/pkg_mgr.py 2019-06-21 22:50:15.462000000 +0200 ++++ ansible-2.8.0/lib/ansible/module_utils/facts/system/pkg_mgr.py 2019-06-21 22:51:42.117000000 +0200 +@@ -13,7 +13,8 @@ + # A list of dicts. If there is a platform with more than one + # package manager, put the preferred one last. If there is an + # ansible module, use that as the value for the 'name' key. +-PKG_MGRS = [{'path': '/usr/bin/yum', 'name': 'yum'}, ++PKG_MGRS = [{'path': '/usr/bin/poldek', 'name': 'poldek'}, ++ {'path': '/usr/bin/yum', 'name': 'yum'}, + {'path': '/usr/bin/dnf', 'name': 'dnf'}, + {'path': '/usr/bin/apt-get', 'name': 'apt'}, + {'path': '/usr/bin/zypper', 'name': 'zypper'}, diff --git a/poldek.py b/poldek.py new file mode 100644 index 0000000..74672f1 --- /dev/null +++ b/poldek.py @@ -0,0 +1,294 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Marcin Krol +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: poldek +short_description: Manage packages with I(poldek) +description: + - Manage packages with the I(poldek) package manager, which is used by TLD Linux and PLD Linux +version_added: "1.0" +author: + - Marcin Krol +options: + name: + description: + - Name or list of names of the package(s) or file(s) to install, upgrade, or remove. + Can't be used in combination with C(upgrade). + aliases: [ package, pkg ] + + state: + description: + - Desired state of the package. + default: present + choices: [ absent, latest, present ] + + extra_args: + description: + - Additional option to pass to poldek when enforcing C(state). + default: + + 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. + default: no + type: bool + aliases: [ update-cache ] + + update_cache_extra_args: + description: + - Additional option to pass to poldek when enforcing C(update_cache). + default: + + upgrade: + description: + - Whether or not to upgrade the whole system. + Can't be used in combination with C(name). + default: no + type: bool + + upgrade_extra_args: + description: + - Additional option to pass to poldek when enforcing C(upgrade). + default: +''' + +RETURN = ''' +packages: + description: a list of packages that have been changed + returned: when upgrade is set to yes + type: list + sample: [ package, other-package ] +''' + +EXAMPLES = ''' +- name: Install packages foo and bar + poldek: + name: + - foo + - bar + state: present + +- name: Update package cache and upgrade package foo + poldek: + name: foo + state: latest + update_cache: yes + +- name: Remove packages foo and bar + poldek: + name: + - foo + - bar + state: absent + +- name: Update package cache only + poldek: + update_cache: yes + +- name: Update package cache and upgrade all packages + poldek: + upgrade: yes + update_cache: yes +''' + +import re + +from ansible.module_utils.basic import AnsibleModule + + +def query_package(module, poldek_path, name): + """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. Note: indexes must be up to date to ensure that poldek knows latest version of package. + """ + lcmd = "%s -q --shcmd='ls -n --installed %s'" % (poldek_path, name) + lrc, lstdout, lstderr = module.run_command(lcmd, check_rc=False) + rcmd = "%s -q --shcmd='ls -n --installed --upgradeable %s'" % (poldek_path, name) + rrc, rstdout, rstderr = module.run_command(rcmd, check_rc=False) + if lrc != 0 or rrc != 0: + return False, False + + pkg_latest = False + if rstdout == "": + return True, True + else: + return True, False + + +def update_package_db(module, poldek_path): + if module.params['force']: + module.params["update_cache_extra_args"] += " --upa" + + cmd = "%s --noask --up %s" % (poldek_path, module.params["update_cache_extra_args"]) + rc, stdout, stderr = module.run_command(cmd, check_rc=False) + + if rc == 0: + return True + else: + module.fail_json(msg="error updating package database") + + +def upgrade(module, poldek_path): + cmdupgrade = "%s -v --noask --upgrade-dist %s" % (poldek_path, module.params["upgrade_extra_args"]) + cmdupgradeable = "%s -q --shcmd='ls -n --installed --upgradeable' %s" % (poldek_path, module.params["upgrade_extra_args"]) + rc, stdout, stderr = module.run_command(cmdupgradeable, check_rc=False) + + packages = stdout.split('\n')[:-1] + if stdout == "": + module.exit_json(changed=False, msg='Nothing to upgrade', packages=packages, stdout=stdout, stderr=stderr, rc=rc) + else: + if module.check_mode: + module.exit_json(changed=True, msg="%s package(s) would be upgraded" % (len(packages)), packages=packages, stdout=stdout, stderr=stderr, rc=rc) + rc, stdout, stderr = module.run_command(cmdupgrade, check_rc=False) + if rc == 0: + module.exit_json(changed=True, msg="%s package(s) upgraded" % (len(packages)), packages=packages, stdout=stdout, stderr=stderr, rc=rc) + else: + module.fail_json(msg="Error while upgrading packages", stdout=stdout, stderr=stderr, rc=rc) + + +def remove_packages(module, poldek_path, packages): + if module.params["force"]: + module.params["extra_args"] += " --force --nodeps" + + remove_c = 0 + out = "" + err = "" + + for package in packages: + installed, updated = query_package(module, poldek_path, package) + if not installed: + continue + + cmd = "%s -v --noask --erase %s %s" % (poldek_path, module.params["extra_args"], package) + rc, stdout, stderr = module.run_command(cmd, check_rc=False) + + out = out + stdout + err = err + stderr + + if rc != 0: + module.fail_json(msg="failed to remove %s" % (package), stdout=out, stderr=err, rc=rc) + + remove_c += 1 + + if remove_c > 0: + module.exit_json(changed=True, msg="removed %s package(s)" % (remove_c), packages=packages, stdout=out, stderr=err, rc=0) + + module.exit_json(changed=False, msg="package(s) already absent", packages=packages, stdout=out, stderr=err, rc=0) + + +def install_packages(module, poldek_path, state, packages): + if module.params["force"]: + module.params["extra_args"] += " --force --nodeps" + + install_c = 0 + out = "" + err = "" + + for package in packages: + installed, updated = query_package(module, poldek_path, package) + if installed and (state == 'present' or (state == 'latest' and updated)): + continue + + cmd = "%s -v --noask --upgrade %s %s" % (poldek_path, module.params["extra_args"], package) + rc, stdout, stderr = module.run_command(cmd, check_rc=False) + + out = out + stdout + err = err + stderr + + if rc != 0: + module.fail_json(msg="failed to install %s" % (package), stdout=out, stderr=err, rc=rc) + + install_c += 1 + + if install_c > 0: + module.exit_json(changed=True, msg="installed %s package(s)" % (install_c), packages=packages, stdout=out, stderr=err, rc=0) + + module.exit_json(changed=False, msg="package(s) already installed", packages=packages, stdout=out, stderr=err, rc=0) + + +def check_packages(module, poldek_path, packages, state): + would_be_changed = [] + + for package in packages: + installed, updated = query_package(module, poldek_path, package) + if ((state in ["present", "latest"] and not installed) or + (state == "absent" and installed) or + (state == "latest" and not updated)): + would_be_changed.append(package) + + if state == "absent": + state = "removed" + if state == "present" or state == "latest": + state = "installed" + + if would_be_changed: + module.exit_json(changed=True, msg="%s package(s) would be %s" % ( + len(would_be_changed), state)) + else: + module.exit_json(changed=False, msg="package(s) already %s" % state) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + name=dict(type='list', aliases=['pkg', 'package']), + state=dict(type='str', default='present', choices=['present', 'installed', 'latest', 'absent', 'removed']), + extra_args=dict(type='str', default=''), + upgrade=dict(type='bool', default=False), + upgrade_extra_args=dict(type='str', default=''), + update_cache=dict(type='bool', default=False, aliases=['update-cache']), + update_cache_extra_args=dict(type='str', default=''), + force=dict(type='bool', default=False), + ), + required_one_of=[['name', 'update_cache', 'upgrade']], + mutually_exclusive=[['name', 'upgrade']], + supports_check_mode=True, + ) + + poldek_path = module.get_bin_path('poldek', True) + + p = module.params + + 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: + update_package_db(module, poldek_path) + if not (p['name'] or p['upgrade']): + module.exit_json(changed=True, msg='Updated package database') + + if p['update_cache'] and module.check_mode and not (p['name'] or p['upgrade']): + module.exit_json(changed=True, msg='Would have updated package database') + + if p['upgrade']: + upgrade(module, poldek_path) + + if p['name']: + pkgs = p['name'] + + if module.check_mode: + check_packages(module, poldek_path, pkgs, p['state']) + + if p['state'] in ['present', 'latest']: + install_packages(module, poldek_path, p['state'], pkgs) + elif p['state'] == 'absent': + remove_packages(module, poldek_path, pkgs) + else: + module.exit_json(changed=False, msg="No package specified to work on") + + +if __name__ == "__main__": + main()