diff options
Diffstat (limited to 'lib/ansible/modules/yum_repository.py')
-rw-r--r-- | lib/ansible/modules/yum_repository.py | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/lib/ansible/modules/yum_repository.py b/lib/ansible/modules/yum_repository.py new file mode 100644 index 0000000..84a10b9 --- /dev/null +++ b/lib/ansible/modules/yum_repository.py @@ -0,0 +1,735 @@ +# encoding: utf-8 + +# (c) 2015-2016, Jiri Tyr <jiri.tyr@gmail.com> +# +# 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 = ''' +--- +module: yum_repository +author: Jiri Tyr (@jtyr) +version_added: '2.1' +short_description: Add or remove YUM repositories +description: + - Add or remove YUM repositories in RPM-based Linux distributions. + - If you wish to update an existing repository definition use M(community.general.ini_file) instead. + +options: + async: + description: + - If set to C(true) Yum will download packages and metadata from this + repo in parallel, if possible. + - In ansible-core 2.11, 2.12, and 2.13 the default value is C(true). + - This option has been deprecated in RHEL 8. If you're using one of the + versions listed above, you can set this option to None to avoid passing an + unknown configuration option. + type: bool + bandwidth: + description: + - Maximum available network bandwidth in bytes/second. Used with the + I(throttle) option. + - If I(throttle) is a percentage and bandwidth is C(0) then bandwidth + throttling will be disabled. If I(throttle) is expressed as a data rate + (bytes/sec) then this option is ignored. Default is C(0) (no bandwidth + throttling). + type: str + default: '0' + baseurl: + description: + - URL to the directory where the yum repository's 'repodata' directory + lives. + - It can also be a list of multiple URLs. + - This, the I(metalink) or I(mirrorlist) parameters are required if I(state) is set to + C(present). + type: list + elements: str + cost: + description: + - Relative cost of accessing this repository. Useful for weighing one + repo's packages as greater/less than any other. + type: str + default: '1000' + deltarpm_metadata_percentage: + description: + - When the relative size of deltarpm metadata vs pkgs is larger than + this, deltarpm metadata is not downloaded from the repo. Note that you + can give values over C(100), so C(200) means that the metadata is + required to be half the size of the packages. Use C(0) to turn off + this check, and always download metadata. + type: str + default: '100' + deltarpm_percentage: + description: + - When the relative size of delta vs pkg is larger than this, delta is + not used. Use C(0) to turn off delta rpm processing. Local repositories + (with file:// I(baseurl)) have delta rpms turned off by default. + type: str + default: '75' + description: + description: + - A human readable string describing the repository. This option corresponds to the "name" property in the repo file. + - This parameter is only required if I(state) is set to C(present). + type: str + enabled: + description: + - This tells yum whether or not use this repository. + - Yum default value is C(true). + type: bool + enablegroups: + description: + - Determines whether yum will allow the use of package groups for this + repository. + - Yum default value is C(true). + type: bool + exclude: + description: + - List of packages to exclude from updates or installs. This should be a + space separated list. Shell globs using wildcards (eg. C(*) and C(?)) + are allowed. + - The list can also be a regular YAML array. + type: list + elements: str + failovermethod: + choices: [roundrobin, priority] + default: roundrobin + description: + - C(roundrobin) randomly selects a URL out of the list of URLs to start + with and proceeds through each of them as it encounters a failure + contacting the host. + - C(priority) starts from the first I(baseurl) listed and reads through + them sequentially. + type: str + file: + description: + - File name without the C(.repo) extension to save the repo in. Defaults + to the value of I(name). + type: str + gpgcakey: + description: + - A URL pointing to the ASCII-armored CA key file for the repository. + type: str + gpgcheck: + description: + - Tells yum whether or not it should perform a GPG signature check on + packages. + - No default setting. If the value is not set, the system setting from + C(/etc/yum.conf) or system default of C(false) will be used. + type: bool + gpgkey: + description: + - A URL pointing to the ASCII-armored GPG key file for the repository. + - It can also be a list of multiple URLs. + type: list + elements: str + module_hotfixes: + description: + - Disable module RPM filtering and make all RPMs from the repository + available. The default is C(None). + version_added: '2.11' + type: bool + http_caching: + description: + - Determines how upstream HTTP caches are instructed to handle any HTTP + downloads that Yum does. + - C(all) means that all HTTP downloads should be cached. + - C(packages) means that only RPM package downloads should be cached (but + not repository metadata downloads). + - C(none) means that no HTTP downloads should be cached. + choices: [all, packages, none] + type: str + default: all + include: + description: + - Include external configuration file. Both, local path and URL is + supported. Configuration file will be inserted at the position of the + I(include=) line. Included files may contain further include lines. + Yum will abort with an error if an inclusion loop is detected. + type: str + includepkgs: + description: + - List of packages you want to only use from a repository. This should be + a space separated list. Shell globs using wildcards (eg. C(*) and C(?)) + are allowed. Substitution variables (e.g. C($releasever)) are honored + here. + - The list can also be a regular YAML array. + type: list + elements: str + ip_resolve: + description: + - Determines how yum resolves host names. + - C(4) or C(IPv4) - resolve to IPv4 addresses only. + - C(6) or C(IPv6) - resolve to IPv6 addresses only. + choices: ['4', '6', IPv4, IPv6, whatever] + type: str + default: whatever + keepalive: + description: + - This tells yum whether or not HTTP/1.1 keepalive should be used with + this repository. This can improve transfer speeds by using one + connection when downloading multiple files from a repository. + type: bool + default: 'no' + keepcache: + description: + - Either C(1) or C(0). Determines whether or not yum keeps the cache of + headers and packages after successful installation. + choices: ['0', '1'] + type: str + default: '1' + metadata_expire: + description: + - Time (in seconds) after which the metadata will expire. + - Default value is 6 hours. + type: str + default: '21600' + metadata_expire_filter: + description: + - Filter the I(metadata_expire) time, allowing a trade of speed for + accuracy if a command doesn't require it. Each yum command can specify + that it requires a certain level of timeliness quality from the remote + repos. from "I'm about to install/upgrade, so this better be current" + to "Anything that's available is good enough". + - C(never) - Nothing is filtered, always obey I(metadata_expire). + - C(read-only:past) - Commands that only care about past information are + filtered from metadata expiring. Eg. I(yum history) info (if history + needs to lookup anything about a previous transaction, then by + definition the remote package was available in the past). + - C(read-only:present) - Commands that are balanced between past and + future. Eg. I(yum list yum). + - C(read-only:future) - Commands that are likely to result in running + other commands which will require the latest metadata. Eg. + I(yum check-update). + - Note that this option does not override "yum clean expire-cache". + choices: [never, 'read-only:past', 'read-only:present', 'read-only:future'] + type: str + default: 'read-only:present' + metalink: + description: + - Specifies a URL to a metalink file for the repomd.xml, a list of + mirrors for the entire repository are generated by converting the + mirrors for the repomd.xml file to a I(baseurl). + - This, the I(baseurl) or I(mirrorlist) parameters are required if I(state) is set to + C(present). + type: str + mirrorlist: + description: + - Specifies a URL to a file containing a list of baseurls. + - This, the I(baseurl) or I(metalink) parameters are required if I(state) is set to + C(present). + type: str + mirrorlist_expire: + description: + - Time (in seconds) after which the mirrorlist locally cached will + expire. + - Default value is 6 hours. + type: str + default: '21600' + name: + description: + - Unique repository ID. This option builds the section name of the repository in the repo file. + - This parameter is only required if I(state) is set to C(present) or + C(absent). + type: str + required: true + password: + description: + - Password to use with the username for basic authentication. + type: str + priority: + description: + - Enforce ordered protection of repositories. The value is an integer + from 1 to 99. + - This option only works if the YUM Priorities plugin is installed. + type: str + default: '99' + protect: + description: + - Protect packages from updates from other repositories. + type: bool + default: 'no' + proxy: + description: + - URL to the proxy server that yum should use. Set to C(_none_) to + disable the global proxy setting. + type: str + proxy_password: + description: + - Password for this proxy. + type: str + proxy_username: + description: + - Username to use for proxy. + type: str + repo_gpgcheck: + description: + - This tells yum whether or not it should perform a GPG signature check + on the repodata from this repository. + type: bool + default: 'no' + reposdir: + description: + - Directory where the C(.repo) files will be stored. + type: path + default: /etc/yum.repos.d + retries: + description: + - Set the number of times any attempt to retrieve a file should retry + before returning an error. Setting this to C(0) makes yum try forever. + type: str + default: '10' + s3_enabled: + description: + - Enables support for S3 repositories. + - This option only works if the YUM S3 plugin is installed. + type: bool + default: 'no' + skip_if_unavailable: + description: + - If set to C(true) yum will continue running if this repository cannot be + contacted for any reason. This should be set carefully as all repos are + consulted for any given command. + type: bool + default: 'no' + ssl_check_cert_permissions: + description: + - Whether yum should check the permissions on the paths for the + certificates on the repository (both remote and local). + - If we can't read any of the files then yum will force + I(skip_if_unavailable) to be C(true). This is most useful for non-root + processes which use yum on repos that have client cert files which are + readable only by root. + type: bool + default: 'no' + sslcacert: + description: + - Path to the directory containing the databases of the certificate + authorities yum should use to verify SSL certificates. + type: str + aliases: [ ca_cert ] + sslclientcert: + description: + - Path to the SSL client certificate yum should use to connect to + repos/remote sites. + type: str + aliases: [ client_cert ] + sslclientkey: + description: + - Path to the SSL client key yum should use to connect to repos/remote + sites. + type: str + aliases: [ client_key ] + sslverify: + description: + - Defines whether yum should verify SSL certificates/hosts at all. + type: bool + default: 'yes' + aliases: [ validate_certs ] + state: + description: + - State of the repo file. + choices: [absent, present] + type: str + default: present + throttle: + description: + - Enable bandwidth throttling for downloads. + - This option can be expressed as a absolute data rate in bytes/sec. An + SI prefix (k, M or G) may be appended to the bandwidth value. + type: str + timeout: + description: + - Number of seconds to wait for a connection before timing out. + type: str + default: '30' + ui_repoid_vars: + description: + - When a repository id is displayed, append these yum variables to the + string if they are used in the I(baseurl)/etc. Variables are appended + in the order listed (and found). + type: str + default: releasever basearch + username: + description: + - Username to use for basic authentication to a repo or really any url. + type: str + +extends_documentation_fragment: + - action_common_attributes + - files +attributes: + check_mode: + support: full + diff_mode: + support: full + platform: + platforms: rhel +notes: + - All comments will be removed if modifying an existing repo file. + - Section order is preserved in an existing repo file. + - Parameters in a section are ordered alphabetically in an existing repo + file. + - The repo file will be automatically deleted if it contains no repository. + - When removing a repository, beware that the metadata cache may still remain + on disk until you run C(yum clean all). Use a notification handler for this. + - "The C(params) parameter was removed in Ansible 2.5 due to circumventing Ansible's parameter + handling" +''' + +EXAMPLES = ''' +- name: Add repository + ansible.builtin.yum_repository: + name: epel + description: EPEL YUM repo + baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/ + +- name: Add multiple repositories into the same file (1/2) + ansible.builtin.yum_repository: + name: epel + description: EPEL YUM repo + file: external_repos + baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/ + gpgcheck: no + +- name: Add multiple repositories into the same file (2/2) + ansible.builtin.yum_repository: + name: rpmforge + description: RPMforge YUM repo + file: external_repos + baseurl: http://apt.sw.be/redhat/el7/en/$basearch/rpmforge + mirrorlist: http://mirrorlist.repoforge.org/el7/mirrors-rpmforge + enabled: no + +# Handler showing how to clean yum metadata cache +- name: yum-clean-metadata + ansible.builtin.command: yum clean metadata + +# Example removing a repository and cleaning up metadata cache +- name: Remove repository (and clean up left-over metadata) + ansible.builtin.yum_repository: + name: epel + state: absent + notify: yum-clean-metadata + +- name: Remove repository from a specific repo file + ansible.builtin.yum_repository: + name: epel + file: external_repos + state: absent +''' + +RETURN = ''' +repo: + description: repository name + returned: success + type: str + sample: "epel" +state: + description: state of the target, after execution + returned: success + type: str + sample: "present" +''' + +import os + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six.moves import configparser +from ansible.module_utils._text import to_native + + +class YumRepo(object): + # Class global variables + module = None + params = None + section = None + repofile = configparser.RawConfigParser() + + # List of parameters which will be allowed in the repo file output + allowed_params = [ + 'async', + 'bandwidth', + 'baseurl', + 'cost', + 'deltarpm_metadata_percentage', + 'deltarpm_percentage', + 'enabled', + 'enablegroups', + 'exclude', + 'failovermethod', + 'gpgcakey', + 'gpgcheck', + 'gpgkey', + 'module_hotfixes', + 'http_caching', + 'include', + 'includepkgs', + 'ip_resolve', + 'keepalive', + 'keepcache', + 'metadata_expire', + 'metadata_expire_filter', + 'metalink', + 'mirrorlist', + 'mirrorlist_expire', + 'name', + 'password', + 'priority', + 'protect', + 'proxy', + 'proxy_password', + 'proxy_username', + 'repo_gpgcheck', + 'retries', + 's3_enabled', + 'skip_if_unavailable', + 'sslcacert', + 'ssl_check_cert_permissions', + 'sslclientcert', + 'sslclientkey', + 'sslverify', + 'throttle', + 'timeout', + 'ui_repoid_vars', + 'username'] + + # List of parameters which can be a list + list_params = ['exclude', 'includepkgs'] + + def __init__(self, module): + # To be able to use fail_json + self.module = module + # Shortcut for the params + self.params = self.module.params + # Section is always the repoid + self.section = self.params['repoid'] + + # Check if repo directory exists + repos_dir = self.params['reposdir'] + if not os.path.isdir(repos_dir): + self.module.fail_json( + msg="Repo directory '%s' does not exist." % repos_dir) + + # Set dest; also used to set dest parameter for the FS attributes + self.params['dest'] = os.path.join( + repos_dir, "%s.repo" % self.params['file']) + + # Read the repo file if it exists + if os.path.isfile(self.params['dest']): + self.repofile.read(self.params['dest']) + + def add(self): + # Remove already existing repo and create a new one + if self.repofile.has_section(self.section): + self.repofile.remove_section(self.section) + + # Add section + self.repofile.add_section(self.section) + + # Baseurl/mirrorlist is not required because for removal we need only + # the repo name. This is why we check if the baseurl/mirrorlist is + # defined. + req_params = (self.params['baseurl'], self.params['metalink'], self.params['mirrorlist']) + if req_params == (None, None, None): + self.module.fail_json( + msg="Parameter 'baseurl', 'metalink' or 'mirrorlist' is required for " + "adding a new repo.") + + # Set options + for key, value in sorted(self.params.items()): + if key in self.list_params and isinstance(value, list): + # Join items into one string for specific parameters + value = ' '.join(value) + elif isinstance(value, bool): + # Convert boolean value to integer + value = int(value) + + # Set the value only if it was defined (default is None) + if value is not None and key in self.allowed_params: + self.repofile.set(self.section, key, value) + + def save(self): + if len(self.repofile.sections()): + # Write data into the file + try: + with open(self.params['dest'], 'w') as fd: + self.repofile.write(fd) + except IOError as e: + self.module.fail_json( + msg="Problems handling file %s." % self.params['dest'], + details=to_native(e)) + else: + # Remove the file if there are not repos + try: + os.remove(self.params['dest']) + except OSError as e: + self.module.fail_json( + msg=( + "Cannot remove empty repo file %s." % + self.params['dest']), + details=to_native(e)) + + def remove(self): + # Remove section if exists + if self.repofile.has_section(self.section): + self.repofile.remove_section(self.section) + + def dump(self): + repo_string = "" + + # Compose the repo file + for section in sorted(self.repofile.sections()): + repo_string += "[%s]\n" % section + + for key, value in sorted(self.repofile.items(section)): + repo_string += "%s = %s\n" % (key, value) + + repo_string += "\n" + + return repo_string + + +def main(): + # Module settings + argument_spec = dict( + bandwidth=dict(), + baseurl=dict(type='list', elements='str'), + cost=dict(), + deltarpm_metadata_percentage=dict(), + deltarpm_percentage=dict(), + description=dict(), + enabled=dict(type='bool'), + enablegroups=dict(type='bool'), + exclude=dict(type='list', elements='str'), + failovermethod=dict(choices=['roundrobin', 'priority']), + file=dict(), + gpgcakey=dict(no_log=False), + gpgcheck=dict(type='bool'), + gpgkey=dict(type='list', elements='str', no_log=False), + module_hotfixes=dict(type='bool'), + http_caching=dict(choices=['all', 'packages', 'none']), + include=dict(), + includepkgs=dict(type='list', elements='str'), + ip_resolve=dict(choices=['4', '6', 'IPv4', 'IPv6', 'whatever']), + keepalive=dict(type='bool'), + keepcache=dict(choices=['0', '1']), + metadata_expire=dict(), + metadata_expire_filter=dict( + choices=[ + 'never', + 'read-only:past', + 'read-only:present', + 'read-only:future']), + metalink=dict(), + mirrorlist=dict(), + mirrorlist_expire=dict(), + name=dict(required=True), + params=dict(type='dict'), + password=dict(no_log=True), + priority=dict(), + protect=dict(type='bool'), + proxy=dict(), + proxy_password=dict(no_log=True), + proxy_username=dict(), + repo_gpgcheck=dict(type='bool'), + reposdir=dict(default='/etc/yum.repos.d', type='path'), + retries=dict(), + s3_enabled=dict(type='bool'), + skip_if_unavailable=dict(type='bool'), + sslcacert=dict(aliases=['ca_cert']), + ssl_check_cert_permissions=dict(type='bool'), + sslclientcert=dict(aliases=['client_cert']), + sslclientkey=dict(aliases=['client_key'], no_log=False), + sslverify=dict(type='bool', aliases=['validate_certs']), + state=dict(choices=['present', 'absent'], default='present'), + throttle=dict(), + timeout=dict(), + ui_repoid_vars=dict(), + username=dict(), + ) + + argument_spec['async'] = dict(type='bool') + + module = AnsibleModule( + argument_spec=argument_spec, + add_file_common_args=True, + supports_check_mode=True, + ) + + # Params was removed + # https://meetbot.fedoraproject.org/ansible-meeting/2017-09-28/ansible_dev_meeting.2017-09-28-15.00.log.html + if module.params['params']: + module.fail_json(msg="The params option to yum_repository was removed in Ansible 2.5 since it circumvents Ansible's option handling") + + name = module.params['name'] + state = module.params['state'] + + # Check if required parameters are present + if state == 'present': + if ( + module.params['baseurl'] is None and + module.params['metalink'] is None and + module.params['mirrorlist'] is None): + module.fail_json( + msg="Parameter 'baseurl', 'metalink' or 'mirrorlist' is required.") + if module.params['description'] is None: + module.fail_json( + msg="Parameter 'description' is required.") + + # Rename "name" and "description" to ensure correct key sorting + module.params['repoid'] = module.params['name'] + module.params['name'] = module.params['description'] + del module.params['description'] + + # Change list type to string for baseurl and gpgkey + for list_param in ['baseurl', 'gpgkey']: + if ( + list_param in module.params and + module.params[list_param] is not None): + module.params[list_param] = "\n".join(module.params[list_param]) + + # Define repo file name if it doesn't exist + if module.params['file'] is None: + module.params['file'] = module.params['repoid'] + + # Instantiate the YumRepo object + yumrepo = YumRepo(module) + + # Get repo status before change + diff = { + 'before_header': yumrepo.params['dest'], + 'before': yumrepo.dump(), + 'after_header': yumrepo.params['dest'], + 'after': '' + } + + # Perform action depending on the state + if state == 'present': + yumrepo.add() + elif state == 'absent': + yumrepo.remove() + + # Get repo status after change + diff['after'] = yumrepo.dump() + + # Compare repo states + changed = diff['before'] != diff['after'] + + # Save the file only if not in check mode and if there was a change + if not module.check_mode and changed: + yumrepo.save() + + # Change file attributes if needed + if os.path.isfile(module.params['dest']): + file_args = module.load_file_common_arguments(module.params) + changed = module.set_fs_attributes_if_different(file_args, changed) + + # Print status of the change + module.exit_json(changed=changed, repo=name, state=state, diff=diff) + + +if __name__ == '__main__': + main() |