From 72856455dc1d7234ec6fa5b8a817ebadf69537fe Mon Sep 17 00:00:00 2001 From: Marcin Krol Date: Sun, 7 Jan 2024 23:42:49 +0100 Subject: [PATCH] - ansible-core based on ansible.spec --- ansible-core.spec | 77 ++++++++++++ poldek.patch | 13 ++ poldek.py | 293 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 ansible-core.spec create mode 100644 poldek.patch create mode 100644 poldek.py diff --git a/ansible-core.spec b/ansible-core.spec new file mode 100644 index 0000000..05da20d --- /dev/null +++ b/ansible-core.spec @@ -0,0 +1,77 @@ +Summary: SSH-based configuration management, deployment, and task execution system +Name: ansible-core +Version: 2.16.2 +Release: 1 +License: GPL v3+ +Group: Development/Libraries +Source0: https://github.com/ansible/ansible/archive/refs/tags/v%{version}.tar.gz +# Source0-md5: 91855c745f4304113e5f3bc8df7525cf +Source2: poldek.py +Patch0: poldek.patch +URL: https://ansible.com +BuildRequires: python3-PyYAML +BuildRequires: python3-modules +BuildRequires: rpm-pythonprov +BuildRequires: rpmbuild(macros) >= 1.710 +BuildArch: noarch +BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) + +%description +Ansible is a radically simple model-driven configuration management, +multi-node deployment, and remote task execution system. Ansible works +over SSH and does not require any software or daemons to be installed +on remote nodes. Extension modules can be written in any language and +are transferred to managed machines automatically. + +%prep +%setup -q -n ansible-%{version} +%patch0 -p1 + +%{__sed} -i '1s,/usr/bin/env python,%{__python3},' \ + bin/ansible-test \ + test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py + +%build +%py3_build + +mkdir -p docs/man/man1 +LC_ALL=en_US.UTF-8 %{__python3} packaging/cli-doc/build.py man --output-dir docs/man/man1 + +%install +rm -rf $RPM_BUILD_ROOT +%py3_install + +install -p %{SOURCE2} $RPM_BUILD_ROOT%{py3_sitescriptdir}/ansible/modules/ + +install -d $RPM_BUILD_ROOT%{_mandir} +cp -a docs/man/* $RPM_BUILD_ROOT%{_mandir} + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(644,root,root,755) +%doc README.md +%attr(755,root,root) %{_bindir}/ansible +%attr(755,root,root) %{_bindir}/ansible-connection +%attr(755,root,root) %{_bindir}/ansible-test +%{_bindir}/ansible-config +%{_bindir}/ansible-console +%{_bindir}/ansible-doc +%{_bindir}/ansible-galaxy +%{_bindir}/ansible-inventory +%{_bindir}/ansible-playbook +%{_bindir}/ansible-pull +%{_bindir}/ansible-vault +%{_mandir}/man1/ansible-config.1* +%{_mandir}/man1/ansible-console.1* +%{_mandir}/man1/ansible-galaxy.1* +%{_mandir}/man1/ansible-inventory.1* +%{_mandir}/man1/ansible-vault.1* +%{_mandir}/man1/ansible.1* +%{_mandir}/man1/ansible-doc.1* +%{_mandir}/man1/ansible-playbook.1* +%{_mandir}/man1/ansible-pull.1* +%{py3_sitescriptdir}/ansible +%{py3_sitescriptdir}/ansible_core-%{version}-*.egg-info +%{py3_sitescriptdir}/ansible_test diff --git a/poldek.patch b/poldek.patch new file mode 100644 index 0000000..9777eb3 --- /dev/null +++ b/poldek.patch @@ -0,0 +1,13 @@ +-diff -ur ansible-2.16.2.orig/lib/ansible/module_utils/facts/system/pkg_mgr.py ansible-2.16.2/lib/ansible/module_utils/facts/system/pkg_mgr.py +--- ansible-2.16.2.orig/lib/ansible/module_utils/facts/system/pkg_mgr.py 2023-12-11 21:48:58.000000000 +0100 ++++ ansible-2.16.2/lib/ansible/module_utils/facts/system/pkg_mgr.py 2024-01-07 20:19:31.342969058 +0100 +@@ -15,7 +15,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/rpm-ostree', 'name': 'atomic_container'}, ++PKG_MGRS = [{'path': '/usr/bin/poldek', 'name': 'poldek'}, ++ {'path': '/usr/bin/rpm-ostree', 'name': 'atomic_container'}, + {'path': '/usr/bin/yum', 'name': 'yum'}, + + # NOTE the `path` key for dnf/dnf5 is effectively discarded when matched for Red Hat OS family, diff --git a/poldek.py b/poldek.py new file mode 100644 index 0000000..9b44bd5 --- /dev/null +++ b/poldek.py @@ -0,0 +1,293 @@ +# -*- 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 --upgradeable --qf=\"%%{NAME}-%%{VERSION}-%%{RELEASE}\n\"' %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() -- 2.46.0