#!/usr/bin/python # Copyright 2021 Red Hat, Inc. # 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_config short_description: Update yum configuration files for deployments. version_added: "1.0.0" description: - Update specific options for different yum configuration files like yum repos, yum modules and yum global configuration. options: type: description: - The type of yum configuration to be changed. required: true type: str choices: [repo, module, global, 'enable-compose-repos'] name: description: - Name of the repo or module to be changed. This options is mandatory only for 'repo' when no 'down_url' is provided. This options is always mandatory for 'module' type. type: str enabled: description: - Change the yum repo or module to enabled or disabled. - This options is ignored for yum global configuration. type: bool default: true down_url: description: - URL of a downloadable repo file to be used as base to construct a new repo file. When used together with 'name', will update only the requested section, without a specific section 'name' will add or update all sections available in the downloaded file. type: str operation: description: - Operation to be execute within a dnf module. type: str choices: [install, remove, reset] stream: description: - Sets a module stream. This options is recommended when enabling a module that doesn't have a default stream. type: str profile: description: - Sets a module profile. This options is recommended when installing a module that doesn't have a default profile. type: str set_options: description: - Dictionary with options to be updated. All dictionary values must be string or list of strings. type: dict file_path: description: - Absolute path of the configuration file to be changed. type: path dir_path: description: - Absolute path of the directory that contains the configuration file to be changed. type: path default: /etc/yum.repos.d environment_file: description: - Absolute path to an environment file to be read before updating or creating yum config and repo files. type: path compose_url: description: - URL that contains CentOS compose repositories. type: str centos_release: description: - Target CentOS release. type: str choices: [centos-stream-8, centos-stream-9] arch: description: - System architecture which the repos will be configure. type: str choices: [aarch64, ppc64le, x86_64] default: x86_64 variants: description: - Repository variants that should be configured. If not provided, all available variants will be configured. type: list elements: str disable_conflicting_variants: description: - Disable all repos from the same directory that match variants' name. type: bool default: false disable_repos: description: - List with file path of repos that should be disabled after successfully enabling all compose repos. type: list elements: str author: - Douglas Viroel (@viroel) """ EXAMPLES = r""" # Set yum 'appstream' repo to enabled and exclude a list of packages - name: Enable appstream repo and exclude nodejs and mariadb packages become: true become_user: root repo_setup_yum_config: type: repo name: appstream enabled: true set_options: exclude: - nodejs* - mariadb* # Enable and install a yum/dnf module - name: Enable nginx module become: true become_user: root repo_setup_yum_config: type: module name: tomcat enabled: false stream: "1.18" - name: Enable nginx module become: true become_user: root repo_setup_yum_config: type: module name: nginx operation: install profile: common # Set yum global configuration options - name: Set yum global options become: true become_user: root repo_setup_yum_config: type: global file_path: /etc/dnf/dnf.conf set_options: skip_if_unavailable: "False" keepcache: "0" - name: Configure a set of repos based on latest CentOS Stream 8 compose become: true become_user: root repo_setup_yum_config: compose_url: https://composes.centos.org/latest-CentOS-Stream-8/compose/ centos_release: centos-stream-8 variants: - AppStream - BaseOS disable_conflicting_variants: true disable_repos: - /etc/yum.repos.d/CentOS-Linux-AppStream.repo - /etc/yum.repos.d/CentOS-Linux-BaseOS.repo """ RETURN = r""" # """ import os # noqa: E402 from ansible.module_utils import six # noqa: E402 from ansible.module_utils.basic import AnsibleModule # noqa: E402 def run_module(): try: import ansible_collections.repo_setup.repos.plugins.module_utils.repo_setup.yum_config.constants as const from ansible_collections.repo_setup.repos.plugins.module_utils.repo_setup.yum_config import ( utils, ) except ImportError: import repo_setup.yum_config.constants as const from repo_setup.yum_config import utils supported_config_types = ["repo", "global", "module", "enable-compose-repos"] supported_module_operations = ["install", "remove", "reset"] module_args = dict( type=dict(type="str", required=True, choices=supported_config_types), name=dict(type="str"), enabled=dict(type="bool", default=True), down_url=dict(type="str"), operation=dict(type="str", choices=supported_module_operations), stream=dict(type="str"), profile=dict(type="str"), set_options=dict(type="dict", default={}), file_path=dict(type="path"), dir_path=dict(type="path", default=const.YUM_REPO_DIR), environment_file=dict(type="path"), compose_url=dict(type="str"), centos_release=dict(type="str", choices=const.COMPOSE_REPOS_RELEASES), arch=dict( type="str", choices=const.COMPOSE_REPOS_SUPPORTED_ARCHS, default="x86_64" ), variants=dict(type="list", default=[], elements="str"), disable_conflicting_variants=dict(type="bool", default=False), disable_repos=dict(type="list", default=[], elements="str"), ) required_if_params = [ ["type", "module", ["name"]], ["type", "enable-compose-repos", ["compose_url"]], ] module = AnsibleModule( argument_spec=module_args, required_if=required_if_params, supports_check_mode=False, ) operations_not_supp_in_py2 = ["module", "enable-compose-repos"] if six.PY2 and module.params["type"] in operations_not_supp_in_py2: msg = ( "The configuration type '{0}' is not " "supported with python 2." ).format(module.params["type"]) module.fail_json(msg=msg) if ( module.params["type"] == "repo" and not module.params["name"] and not module.params["down_url"] ): msg = ( "When using configuration type '{0}' you must provide a repo " "'name' or a 'down_url'." ).format(module.params["type"]) module.fail_json(msg=msg) distro, major_version, __ = utils.get_distro_info() dnf_module_support = False for min_distro_ver in const.DNF_MODULE_MINIMAL_DISTRO_VERSIONS: if distro == min_distro_ver.get("distro") and int( major_version ) >= min_distro_ver.get("min_version"): dnf_module_support = True break if module.params["type"] == "module" and not dnf_module_support: msg = ( "The configuration type 'module' is not " "supported in this distro version " "({0}-{1}).".format(distro, major_version) ) module.fail_json(msg=msg) # 'set_options' expects a dict that can also contains a list of values. # List of elements will be converted to a comma-separated list m_set_opts = module.params.get("set_options") if m_set_opts: for k, v in m_set_opts.items(): if isinstance(v, list): m_set_opts[k] = ",".join([str(elem) for elem in v]) elif not isinstance(v, str): m_set_opts[k] = str(v) # Module execution try: try: import ansible_collections.repo_setup.repos.plugins.module_utils.repo_setup.yum_config.yum_config as cfg except ImportError: import repo_setup.yum_config.yum_config as cfg if module.params["type"] == "repo": config_obj = cfg.YumRepoConfig( dir_path=module.params["dir_path"], environment_file=module.params["environment_file"], ) if module.params["name"]: config_obj.add_or_update_section( module.params["name"], set_dict=m_set_opts, file_path=module.params["file_path"], enabled=module.params["enabled"], from_url=module.params["down_url"], ) else: config_obj.add_or_update_all_sections_from_url( module.params["down_url"], set_dict=m_set_opts, file_path=module.params["file_path"], enabled=module.params["enabled"], ) elif module.params["type"] == "global": config_obj = cfg.YumGlobalConfig( file_path=module.params["file_path"], environment_file=module.params["environment_file"], ) config_obj.update_section("main", m_set_opts) elif module.params["type"] == "enable-compose-repos": try: import ansible_collections.repo_setup.repos.plugins.module_utils.repo_setup.yum_config.compose_repos as repos except ImportError: import repo_setup.yum_config.compose_repos as repos # 1. Create compose repo config object repo_obj = repos.YumComposeRepoConfig( module.params["compose_url"], module.params["centos_release"], dir_path=module.params["dir_path"], arch=module.params["arch"], environment_file=module.params["environment_file"], ) # 2. enable CentOS compose repos repo_obj.enable_compose_repos( variants=module.params["variants"], override_repos=module.params["disable_conflicting_variants"], ) # 3. Disable all repos provided in disable_repos for file in module.params["disable_repos"]: valid_path = None rel_path = os.path.join(module.params["dir_path"], file) if cfg.validated_file_path(file): valid_path = file elif cfg.validated_file_path(rel_path): valid_path = rel_path if valid_path is not None: repo_obj.update_all_sections(valid_path, enabled=False) elif module.params["type"] == "module": try: import ansible_collections.repo_setup.repos.plugins.module_utils.repo_setup.yum_config.dnf_manager as dnf_mgr except ImportError: import repo_setup.yum_config.dnf_manager as dnf_mgr dnf_mod_mgr = dnf_mgr.DnfModuleManager() if module.params["enabled"]: dnf_mod_mgr.enable_module( module.params["name"], stream=module.params["stream"], profile=module.params["profile"], ) else: dnf_mod_mgr.disable_module( module.params["name"], stream=module.params["stream"], profile=module.params["profile"], ) if module.params["operation"]: dnf_method = getattr( dnf_mod_mgr, module.params["operation"] + "_module" ) dnf_method( module.params["name"], stream=module.params["stream"], profile=module.params["profile"], ) except Exception as exc: module.fail_json(msg=str(exc)) # Successful module execution result = { "changed": True, "msg": "Yum {0} configuration was successfully updated.".format( module.params["type"] ), } module.exit_json(**result) def main(): run_module() if __name__ == "__main__": main()