diff --git a/plugins/modules/packaging/os/yum_versionlock.py b/plugins/modules/packaging/os/yum_versionlock.py new file mode 100644 index 0000000000..0830b3a4ab --- /dev/null +++ b/plugins/modules/packaging/os/yum_versionlock.py @@ -0,0 +1,151 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: (c) 2018, Florian Paul Hoberg +# +# 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 + +DOCUMENTATION = r''' +--- +module: yum_versionlock +version_added: 2.0.0 +short_description: Locks / unlocks a installed package(s) from being updated by yum package manager +description: + - This module adds installed packages to yum versionlock to prevent the package from being updated. +options: + name: + description: + - Package name or a list of packages. + type: list + required: true + elements: str + state: + description: + - If state is C(present), package(s) will be added to yum versionlock list. + - If state is C(absent), package(s) will be removed from yum versionlock list. + choices: [ 'absent', 'present' ] + type: str + default: present +notes: + - Requires yum-plugin-versionlock package on the remote node. + - Supports C(check_mode). +requirements: +- yum +- yum-versionlock +author: + - Florian Paul Hoberg (@florianpaulhoberg) + - Amin Vakil (@aminvakil) +''' + +EXAMPLES = r''' +- name: Prevent Apache / httpd from being updated + community.general.yum_versionlock: + state: present + name: httpd + +- name: Prevent multiple packages from being updated + community.general.yum_versionlock: + state: present + name: + - httpd + - nginx + - haproxy + - curl + +- name: Remove lock from Apache / httpd to be updated again + community.general.yum_versionlock: + state: absent + package: httpd +''' + +RETURN = r''' +packages: + description: A list of package(s) in versionlock list. + returned: success + type: list + elements: str + sample: [ 'httpd' ] +state: + description: State of package(s). + returned: success + type: str + sample: present +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native + + +class YumVersionLock: + def __init__(self, module): + self.module = module + self.params = module.params + self.yum_bin = module.get_bin_path('yum', required=True) + + def get_versionlock_packages(self): + """ Get an overview of all packages on yum versionlock """ + rc, out, err = self.module.run_command([self.yum_bin, "versionlock", "list"]) + if rc == 0: + return out + elif rc == 1 and 'o such command:' in err: + self.module.fail_json(msg="Error: Please install rpm package yum-plugin-versionlock : " + to_native(err) + to_native(out)) + self.module.fail_json(msg="Error: " + to_native(err) + to_native(out)) + + def ensure_state(self, package, command): + """ Ensure package state """ + rc, out, err = self.module.run_command([self.yum_bin, "-q", "versionlock", command, package]) + if rc == 0: + return True + self.module.fail_json(msg="Error: " + to_native(err) + to_native(out)) + + +def main(): + """ start main program to add/remove a package to yum versionlock""" + module = AnsibleModule( + argument_spec=dict( + state=dict(default='present', choices=['present', 'absent']), + name=dict(required=True, type='list', elements='str'), + ), + supports_check_mode=True + ) + + state = module.params['state'] + packages = module.params['name'] + changed = False + + yum_v = YumVersionLock(module) + + # Get an overview of all packages that have a version lock + versionlock_packages = yum_v.get_versionlock_packages() + + # Ensure versionlock state of packages + if state in ('present'): + command = 'add' + for single_pkg in packages: + if single_pkg not in versionlock_packages: + if module.check_mode: + changed = True + continue + changed = yum_v.ensure_state(single_pkg, command) + elif state in ('absent'): + command = 'delete' + for single_pkg in packages: + if single_pkg in versionlock_packages: + if module.check_mode: + changed = True + continue + changed = yum_v.ensure_state(single_pkg, command) + + module.exit_json( + changed=changed, + meta={ + "packages": packages, + "state": state + } + ) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/yum_versionlock.py b/plugins/modules/yum_versionlock.py new file mode 120000 index 0000000000..a22c56195f --- /dev/null +++ b/plugins/modules/yum_versionlock.py @@ -0,0 +1 @@ +./packaging/os/yum_versionlock.py \ No newline at end of file diff --git a/tests/integration/targets/yum_versionlock/aliases b/tests/integration/targets/yum_versionlock/aliases new file mode 100644 index 0000000000..abe0a21e22 --- /dev/null +++ b/tests/integration/targets/yum_versionlock/aliases @@ -0,0 +1,5 @@ +shippable/posix/group1 +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/tests/integration/targets/yum_versionlock/tasks/main.yml b/tests/integration/targets/yum_versionlock/tasks/main.yml new file mode 100644 index 0000000000..dda5a11bf0 --- /dev/null +++ b/tests/integration/targets/yum_versionlock/tasks/main.yml @@ -0,0 +1,62 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +- block: + - name: Install necessary packages to test yum_versionlock + yum: + name: yum-plugin-versionlock + state: present + register: yum_versionlock_install + + - name: Yum checkupdate + yum: + list: updates + register: yum_updates + + - block: + - name: Lock all packages + community.general.yum_versionlock: + name: "{{ yum_updates.results | map(attribute='name') | list }}" + state: present + register: lock_all_packages + + - name: Update all packages + command: yum update --setopt=obsoletes=0 + register: update_all_locked_packages + changed_when: + - '"No packages marked for update" not in update_all_locked_packages.stdout' + - '"Nothing to do" not in update_all_locked_packages.stdout' + + - name: Unlock all packages + community.general.yum_versionlock: + name: "{{ yum_updates.results | map(attribute='name') | list }}" + state: absent + register: unlock_all_packages + + - name: Update all packages + yum: + name: '*' + state: latest + check_mode: yes + register: update_all_packages + when: yum_updates.results | length != 0 + + - name: Assert everything is fine + assert: + that: + - "{{ lock_all_packages.changed }}" + - "{{ not update_all_locked_packages.changed }}" + - "{{ unlock_all_packages.changed }}" + - "{{ update_all_packages.changed }}" + when: yum_updates.results | length != 0 + + - name: Remove installed packages in case it was not installed + yum: + name: yum-plugin-versionlock + state: absent + when: yum_versionlock_install is changed + when: (ansible_distribution in ['CentOS', 'RedHat'] and ansible_distribution_major_version is version('7', '>=')) or + (ansible_distribution == 'Fedora')