#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2023, Andrew Hyatt # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' --- module: dnf_config_manager short_description: Enable or disable dnf repositories using config-manager version_added: 8.2.0 description: - This module enables or disables repositories using the C(dnf config-manager) sub-command. author: Andrew Hyatt (@ahyattdev) requirements: - dnf - dnf-plugins-core extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: full diff_mode: support: none options: name: description: - Repository ID, for example V(crb). default: [] required: false type: list elements: str state: description: - Whether the repositories should be V(enabled) or V(disabled). default: enabled required: false type: str choices: [enabled, disabled] seealso: - module: ansible.builtin.dnf - module: ansible.builtin.yum_repository ''' EXAMPLES = r''' - name: Ensure the crb repository is enabled community.general.dnf_config_manager: name: crb state: enabled - name: Ensure the appstream and zfs repositories are disabled community.general.dnf_config_manager: name: - appstream - zfs state: disabled ''' RETURN = r''' repo_states_pre: description: Repo IDs before action taken. returned: success type: dict contains: enabled: description: Enabled repository IDs. returned: success type: list elements: str disabled: description: Disabled repository IDs. returned: success type: list elements: str sample: enabled: - appstream - baseos - crb disabled: - appstream-debuginfo - appstream-source - baseos-debuginfo - baseos-source - crb-debug - crb-source repo_states_post: description: Repository states after action taken. returned: success type: dict contains: enabled: description: Enabled repository IDs. returned: success type: list elements: str disabled: description: Disabled repository IDs. returned: success type: list elements: str sample: enabled: - appstream - baseos - crb disabled: - appstream-debuginfo - appstream-source - baseos-debuginfo - baseos-source - crb-debug - crb-source changed_repos: description: Repositories changed. returned: success type: list elements: str sample: [ 'crb' ] ''' from ansible.module_utils.basic import AnsibleModule import os import re DNF_BIN = "/usr/bin/dnf" REPO_ID_RE = re.compile(r'^Repo-id\s*:\s*(\S+)$') REPO_STATUS_RE = re.compile(r'^Repo-status\s*:\s*(disabled|enabled)$') def get_repo_states(module): rc, out, err = module.run_command([DNF_BIN, 'repolist', '--all', '--verbose'], check_rc=True) repos = dict() last_repo = '' for i, line in enumerate(out.split('\n')): m = REPO_ID_RE.match(line) if m: if len(last_repo) > 0: module.fail_json(msg='dnf repolist parse failure: parsed another repo id before next status') last_repo = m.group(1) continue m = REPO_STATUS_RE.match(line) if m: if len(last_repo) == 0: module.fail_json(msg='dnf repolist parse failure: parsed status before repo id') repos[last_repo] = m.group(1) last_repo = '' return repos def set_repo_states(module, repo_ids, state): module.run_command([DNF_BIN, 'config-manager', '--set-{0}'.format(state)] + repo_ids, check_rc=True) def pack_repo_states_for_return(states): enabled = [] disabled = [] for repo_id in states: if states[repo_id] == 'enabled': enabled.append(repo_id) else: disabled.append(repo_id) # Sort for consistent results enabled.sort() disabled.sort() return {'enabled': enabled, 'disabled': disabled} def main(): module_args = dict( name=dict(type='list', elements='str', required=False, default=[]), state=dict(type='str', required=False, choices=['enabled', 'disabled'], default='enabled') ) result = dict( changed=False ) module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) if not os.path.exists(DNF_BIN): module.fail_json(msg="%s was not found" % DNF_BIN) repo_states = get_repo_states(module) result['repo_states_pre'] = pack_repo_states_for_return(repo_states) desired_repo_state = module.params['state'] names = module.params['name'] to_change = [] for repo_id in names: if repo_id not in repo_states: module.fail_json(msg="did not find repo with ID '{0}' in dnf repolist --all --verbose".format(repo_id)) if repo_states[repo_id] != desired_repo_state: to_change.append(repo_id) result['changed'] = len(to_change) > 0 result['changed_repos'] = to_change if module.check_mode: module.exit_json(**result) if len(to_change) > 0: set_repo_states(module, to_change, desired_repo_state) repo_states_post = get_repo_states(module) result['repo_states_post'] = pack_repo_states_for_return(repo_states_post) for repo_id in to_change: if repo_states_post[repo_id] != desired_repo_state: module.fail_json(msg="dnf config-manager failed to make '{0}' {1}".format(repo_id, desired_repo_state)) module.exit_json(**result) if __name__ == "__main__": main()