From 6c9c4e7482a331c24fc23ef565e0c2c8139ab714 Mon Sep 17 00:00:00 2001 From: Robin Roth Date: Sun, 4 Sep 2016 08:20:10 +0200 Subject: [PATCH] Zypper repo autoimport keys (#2132) * zypper_repository add auto_import_keys options * also give more output on failure (rc, stdout, stderr) * be more specific in the doc for auto_import_keys * add runrefresh option to zypper_repository * this comes out of ansible/ansible-modules-extras#2411, where AnderEnder adds refresh to the zypper module * adds a way to force zypper to refresh a repository * can be used to refresh independently of auto_import_keys * add option to run name=* runrefresh=yes * name runrefresh to not break existing use to refresh (now alias to autorefresh) * add version_added flag to autorefresh * remove wrong version_added comment --- .../extras/packaging/os/zypper_repository.py | 117 +++++++++++++----- 1 file changed, 83 insertions(+), 34 deletions(-) diff --git a/lib/ansible/modules/extras/packaging/os/zypper_repository.py b/lib/ansible/modules/extras/packaging/os/zypper_repository.py index aac7d87096..bbed8143f3 100644 --- a/lib/ansible/modules/extras/packaging/os/zypper_repository.py +++ b/lib/ansible/modules/extras/packaging/os/zypper_repository.py @@ -59,12 +59,13 @@ options: required: false default: "no" choices: [ "yes", "no" ] - refresh: + autorefresh: description: - Enable autorefresh of the repository. required: false default: "yes" choices: [ "yes", "no" ] + aliases: [ "refresh" ] priority: description: - Set priority of repository. Packages will always be installed @@ -80,6 +81,26 @@ options: default: "no" choices: [ "yes", "no" ] version_added: "2.1" + auto_import_keys: + description: + - Automatically import the gpg signing key of the new or changed repository. + - Has an effect only if state is I(present). Has no effect on existing (unchanged) repositories or in combination with I(absent). + - Implies runrefresh. + required: false + default: "no" + choices: ["yes", "no"] + version_added: "2.2" + runrefresh: + description: + - Refresh the package list of the given repository. + - Can be used with repo=* to refresh all repositories. + required: false + default: "no" + choices: ["yes", "no"] + version_added: "2.2" + + + requirements: - "zypper >= 1.0 # included in openSuSE >= 11.1 or SuSE Linux Enterprise Server/Desktop >= 11.0" ''' @@ -93,6 +114,15 @@ EXAMPLES = ''' # Add python development repository - zypper_repository: repo=http://download.opensuse.org/repositories/devel:/languages:/python/SLE_11_SP3/devel:languages:python.repo + +# Refresh all repos +- zypper_repository: repo=* runrefresh=yes + +# Add a repo and add it's gpg key +- zypper_repository: repo=http://download.opensuse.org/repositories/systemsmanagement/openSUSE_Leap_42.1/ auto_import_keys=yes + +# Force refresh of a repository +- zypper_repository: repo=http://my_internal_ci_repo/repo name=my_ci_repo state=present runrefresh=yes ''' REPO_OPTS = ['alias', 'name', 'priority', 'enabled', 'autorefresh', 'gpgcheck'] @@ -121,14 +151,10 @@ def _parse_repos(module): elif rc == 6: return [] else: - d = { 'zypper_exit_code': rc } - if stderr: - d['stderr'] = stderr - if stdout: - d['stdout'] = stdout - module.fail_json(msg='Failed to execute "%s"' % " ".join(cmd), **d) + module.fail_json(msg='Failed to execute "%s"' % " ".join(cmd), rc=rc, stdout=stdout, stderr=stderr) def _repo_changes(realrepo, repocmp): + "Check whether the 2 given repos have different settings." for k in repocmp: if repocmp[k] and k not in realrepo: return True @@ -144,6 +170,13 @@ def _repo_changes(realrepo, repocmp): return False def repo_exists(module, repodata, overwrite_multiple): + """Check whether the repository already exists. + + returns (exists, mod, old_repos) + exists: whether a matching (name, URL) repo exists + mod: whether there are changes compared to the existing repo + old_repos: list of matching repos + """ existing_repos = _parse_repos(module) # look for repos that have matching alias or url to the one searched @@ -171,7 +204,8 @@ def repo_exists(module, repodata, overwrite_multiple): module.fail_json(msg=errmsg) -def modify_repo(module, repodata, old_repos, zypper_version, warnings): +def addmodify_repo(module, repodata, old_repos, zypper_version, warnings): + "Adds the repo, removes old repos before, that would conflict." repo = repodata['url'] cmd = ['/usr/bin/zypper', 'ar', '--check'] if repodata['name']: @@ -212,24 +246,15 @@ def modify_repo(module, repodata, old_repos, zypper_version, warnings): remove_repo(module, oldrepo['url']) rc, stdout, stderr = module.run_command(cmd, check_rc=False) - changed = rc == 0 - if rc == 0: - changed = True - else: - if stderr: - module.fail_json(msg=stderr) - else: - module.fail_json(msg=stdout) - - return changed + return rc, stdout, stderr def remove_repo(module, repo): + "Removes the repo." cmd = ['/usr/bin/zypper', 'rr', repo] rc, stdout, stderr = module.run_command(cmd, check_rc=True) - changed = rc == 0 - return changed + return rc, stdout, stderr def get_zypper_version(module): @@ -238,14 +263,16 @@ def get_zypper_version(module): return LooseVersion('1.0') return LooseVersion(stdout.split()[1]) +def runrefreshrepo(module, auto_import_keys=False, shortname=None): + "Forces zypper to refresh repo metadata." + cmd = ['/usr/bin/zypper', 'refresh', '--force'] + if auto_import_keys: + cmd.append('--gpg-auto-import-keys') + if shortname is not None: + cmd.extend(['-r', shortname]) -def fail_if_rc_is_null(module, rc, stdout, stderr): - if rc != 0: - #module.fail_json(msg=stderr if stderr else stdout) - if stderr: - module.fail_json(msg=stderr) - else: - module.fail_json(msg=stdout) + rc, stdout, stderr = module.run_command(cmd, check_rc=True) + return rc, stdout, stderr def main(): @@ -254,20 +281,25 @@ def main(): name=dict(required=False), repo=dict(required=False), state=dict(choices=['present', 'absent'], default='present'), + runrefresh=dict(required=False, default='no', type='bool'), description=dict(required=False), disable_gpg_check = dict(required=False, default=False, type='bool'), - refresh = dict(required=False, default=True, type='bool'), + autorefresh = dict(required=False, default=True, type='bool', aliases=['refresh']), priority = dict(required=False, type='int'), enabled = dict(required=False, default=True, type='bool'), overwrite_multiple = dict(required=False, default=False, type='bool'), + auto_import_keys = dict(required=False, default=False, type='bool'), ), supports_check_mode=False, + required_one_of = [['state','runrefresh']], ) repo = module.params['repo'] alias = module.params['name'] state = module.params['state'] overwrite_multiple = module.params['overwrite_multiple'] + auto_import_keys = module.params['auto_import_keys'] + runrefresh = module.params['runrefresh'] zypper_version = get_zypper_version(module) warnings = [] # collect warning messages for final output @@ -287,7 +319,7 @@ def main(): repodata['gpgcheck'] = '0' else: repodata['gpgcheck'] = '1' - if module.params['refresh']: + if module.params['autorefresh']: repodata['autorefresh'] = '1' else: repodata['autorefresh'] = '0' @@ -296,6 +328,13 @@ def main(): module.exit_json(changed=False, repodata=repodata, state=state) # Check run-time module parameters + if repo == '*' or alias == '*': + if runrefresh: + runrefreshrepo(module, auto_import_keys) + module.exit_json(changed=False, runrefresh=True) + else: + module.fail_json(msg='repo=* can only be used with the runrefresh option.') + if state == 'present' and not repo: module.fail_json(msg='Module option state=present requires repo') if state == 'absent' and not repo and not alias: @@ -310,18 +349,28 @@ def main(): exists, mod, old_repos = repo_exists(module, repodata, overwrite_multiple) + if repo: + shortname = repo + else: + shortname = alias + if state == 'present': if exists and not mod: + if runrefresh: + runrefreshrepo(module, auto_import_keys, shortname) exit_unchanged() - changed = modify_repo(module, repodata, old_repos, zypper_version, warnings) + rc, stdout, stderr = addmodify_repo(module, repodata, old_repos, zypper_version, warnings) + if rc == 0 and (runrefresh or auto_import_keys): + runrefreshrepo(module, auto_import_keys, shortname) elif state == 'absent': if not exists: exit_unchanged() - if not repo: - repo=alias - changed = remove_repo(module, repo) + rc, stdout, stderr = remove_repo(module, shortname) - module.exit_json(changed=changed, repodata=repodata, state=state, warnings=warnings) + if rc == 0: + module.exit_json(changed=True, repodata=repodata, state=state, warnings=warnings) + else: + module.fail_json(msg="Zypper failed with rc %s" % rc, rc=rc, stdout=stdout, stderr=stderr, repodata=repodata, state=state, warnings=warnings) # import module snippets from ansible.module_utils.basic import *