summaryrefslogtreecommitdiffstats
path: root/roles/lib_utils/library
diff options
context:
space:
mode:
authorThomas Wiest <twiest@redhat.com>2017-01-25 14:07:22 -0500
committerThomas Wiest <twiest@redhat.com>2017-01-31 08:15:37 -0500
commitb415e4855970131a77112940646a95641d3bd27b (patch)
tree951ce174c71c495db66b522fa0730816e4878f93 /roles/lib_utils/library
parentfca215887b2e4224779b58e8fd1b7662ec993f83 (diff)
downloadopenshift-b415e4855970131a77112940646a95641d3bd27b.tar.gz
openshift-b415e4855970131a77112940646a95641d3bd27b.tar.bz2
openshift-b415e4855970131a77112940646a95641d3bd27b.tar.xz
openshift-b415e4855970131a77112940646a95641d3bd27b.zip
Added repoquery to lib_utils.
Diffstat (limited to 'roles/lib_utils/library')
-rw-r--r--roles/lib_utils/library/repoquery.py607
-rw-r--r--roles/lib_utils/library/yedit.py19
2 files changed, 618 insertions, 8 deletions
diff --git a/roles/lib_utils/library/repoquery.py b/roles/lib_utils/library/repoquery.py
new file mode 100644
index 000000000..7f0105290
--- /dev/null
+++ b/roles/lib_utils/library/repoquery.py
@@ -0,0 +1,607 @@
+#!/usr/bin/env python
+# pylint: disable=missing-docstring
+# ___ ___ _ _ ___ ___ _ _____ ___ ___
+# / __| __| \| | __| _ \ /_\_ _| __| \
+# | (_ | _|| .` | _|| / / _ \| | | _|| |) |
+# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
+# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
+# | |) | (_) | | .` | (_) || | | _|| |) | | | |
+# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# -*- -*- -*- Begin included fragment: lib/import.py -*- -*- -*-
+
+# pylint: disable=wrong-import-order,wrong-import-position,unused-import
+
+from __future__ import print_function # noqa: F401
+import json # noqa: F401
+import os # noqa: F401
+import re # noqa: F401
+# pylint: disable=import-error
+import ruamel.yaml as yaml # noqa: F401
+import shutil # noqa: F401
+
+from ansible.module_utils.basic import AnsibleModule
+
+# -*- -*- -*- End included fragment: lib/import.py -*- -*- -*-
+
+# -*- -*- -*- Begin included fragment: doc/repoquery -*- -*- -*-
+
+DOCUMENTATION = '''
+---
+module: repoquery
+short_description: Query package information from Yum repositories
+description:
+ - Query package information from Yum repositories.
+options:
+ state:
+ description:
+ - The expected state. Currently only supports list.
+ required: false
+ default: list
+ choices: ["list"]
+ aliases: []
+ name:
+ description:
+ - The name of the package to query
+ required: true
+ default: None
+ aliases: []
+ query_type:
+ description:
+ - Narrows the packages queried based off of this value.
+ - If repos, it narrows the query to repositories defined on the machine.
+ - If installed, it narrows the query to only packages installed on the machine.
+ - If available, it narrows the query to packages that are available to be installed.
+ - If recent, it narrows the query to only recently edited packages.
+ - If updates, it narrows the query to only packages that are updates to existing installed packages.
+ - If extras, it narrows the query to packages that are not present in any of the available repositories.
+ - If all, it queries all of the above.
+ required: false
+ default: repos
+ aliases: []
+ verbose:
+ description:
+ - Shows more detail for the requested query.
+ required: false
+ default: false
+ aliases: []
+ show_duplicates:
+ description:
+ - Shows multiple versions of a package.
+ required: false
+ default: false
+ aliases: []
+ match_version:
+ description:
+ - Match the specific version given to the package.
+ required: false
+ default: None
+ aliases: []
+author:
+- "Matt Woodson <mwoodson@redhat.com>"
+extends_documentation_fragment: []
+'''
+
+EXAMPLES = '''
+# Example 1: Get bash versions
+ - name: Get bash version
+ repoquery:
+ name: bash
+ show_duplicates: True
+ register: bash_out
+
+# Results:
+# ok: [localhost] => {
+# "bash_out": {
+# "changed": false,
+# "results": {
+# "cmd": "/usr/bin/repoquery --quiet --pkgnarrow=repos --queryformat=%{version}|%{release}|%{arch}|%{repo}|%{version}-%{release} --show-duplicates bash",
+# "package_found": true,
+# "package_name": "bash",
+# "returncode": 0,
+# "versions": {
+# "available_versions": [
+# "4.2.45",
+# "4.2.45",
+# "4.2.45",
+# "4.2.46",
+# "4.2.46",
+# "4.2.46",
+# "4.2.46"
+# ],
+# "available_versions_full": [
+# "4.2.45-5.el7",
+# "4.2.45-5.el7_0.2",
+# "4.2.45-5.el7_0.4",
+# "4.2.46-12.el7",
+# "4.2.46-19.el7",
+# "4.2.46-20.el7_2",
+# "4.2.46-21.el7_3"
+# ],
+# "latest": "4.2.46",
+# "latest_full": "4.2.46-21.el7_3"
+# }
+# },
+# "state": "present"
+# }
+# }
+
+
+
+# Example 2: Get bash versions verbosely
+ - name: Get bash versions verbosely
+ repoquery:
+ name: bash
+ show_duplicates: True
+ verbose: True
+ register: bash_out
+
+# Results:
+# ok: [localhost] => {
+# "bash_out": {
+# "changed": false,
+# "results": {
+# "cmd": "/usr/bin/repoquery --quiet --pkgnarrow=repos --queryformat=%{version}|%{release}|%{arch}|%{repo}|%{version}-%{release} --show-duplicates bash",
+# "package_found": true,
+# "package_name": "bash",
+# "raw_versions": {
+# "4.2.45-5.el7": {
+# "arch": "x86_64",
+# "release": "5.el7",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.45",
+# "version_release": "4.2.45-5.el7"
+# },
+# "4.2.45-5.el7_0.2": {
+# "arch": "x86_64",
+# "release": "5.el7_0.2",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.45",
+# "version_release": "4.2.45-5.el7_0.2"
+# },
+# "4.2.45-5.el7_0.4": {
+# "arch": "x86_64",
+# "release": "5.el7_0.4",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.45",
+# "version_release": "4.2.45-5.el7_0.4"
+# },
+# "4.2.46-12.el7": {
+# "arch": "x86_64",
+# "release": "12.el7",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.46",
+# "version_release": "4.2.46-12.el7"
+# },
+# "4.2.46-19.el7": {
+# "arch": "x86_64",
+# "release": "19.el7",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.46",
+# "version_release": "4.2.46-19.el7"
+# },
+# "4.2.46-20.el7_2": {
+# "arch": "x86_64",
+# "release": "20.el7_2",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.46",
+# "version_release": "4.2.46-20.el7_2"
+# },
+# "4.2.46-21.el7_3": {
+# "arch": "x86_64",
+# "release": "21.el7_3",
+# "repo": "rhel-7-server-rpms",
+# "version": "4.2.46",
+# "version_release": "4.2.46-21.el7_3"
+# }
+# },
+# "results": "4.2.45|5.el7|x86_64|rhel-7-server-rpms|4.2.45-5.el7\n4.2.45|5.el7_0.2|x86_64|rhel-7-server-rpms|4.2.45-5.el7_0.2\n4.2.45|5.el7_0.4|x86_64|rhel-7-server-rpms|4.2.45-5.el7_0.4\n4.2.46|12.el7|x86_64|rhel-7-server-rpms|4.2.46-12.el7\n4.2.46|19.el7|x86_64|rhel-7-server-rpms|4.2.46-19.el7\n4.2.46|20.el7_2|x86_64|rhel-7-server-rpms|4.2.46-20.el7_2\n4.2.46|21.el7_3|x86_64|rhel-7-server-rpms|4.2.46-21.el7_3\n",
+# "returncode": 0,
+# "versions": {
+# "available_versions": [
+# "4.2.45",
+# "4.2.45",
+# "4.2.45",
+# "4.2.46",
+# "4.2.46",
+# "4.2.46",
+# "4.2.46"
+# ],
+# "available_versions_full": [
+# "4.2.45-5.el7",
+# "4.2.45-5.el7_0.2",
+# "4.2.45-5.el7_0.4",
+# "4.2.46-12.el7",
+# "4.2.46-19.el7",
+# "4.2.46-20.el7_2",
+# "4.2.46-21.el7_3"
+# ],
+# "latest": "4.2.46",
+# "latest_full": "4.2.46-21.el7_3"
+# }
+# },
+# "state": "present"
+# }
+# }
+
+# Example 3: Match a specific version
+ - name: matched versions repoquery test
+ repoquery:
+ name: atomic-openshift
+ show_duplicates: True
+ match_version: 3.3
+ register: openshift_out
+
+# Result:
+
+# ok: [localhost] => {
+# "openshift_out": {
+# "changed": false,
+# "results": {
+# "cmd": "/usr/bin/repoquery --quiet --pkgnarrow=repos --queryformat=%{version}|%{release}|%{arch}|%{repo}|%{version}-%{release} --show-duplicates atomic-openshift",
+# "package_found": true,
+# "package_name": "atomic-openshift",
+# "returncode": 0,
+# "versions": {
+# "available_versions": [
+# "3.2.0.43",
+# "3.2.1.23",
+# "3.3.0.32",
+# "3.3.0.34",
+# "3.3.0.35",
+# "3.3.1.3",
+# "3.3.1.4",
+# "3.3.1.5",
+# "3.3.1.7",
+# "3.4.0.39"
+# ],
+# "available_versions_full": [
+# "3.2.0.43-1.git.0.672599f.el7",
+# "3.2.1.23-1.git.0.88a7a1d.el7",
+# "3.3.0.32-1.git.0.37bd7ea.el7",
+# "3.3.0.34-1.git.0.83f306f.el7",
+# "3.3.0.35-1.git.0.d7bd9b6.el7",
+# "3.3.1.3-1.git.0.86dc49a.el7",
+# "3.3.1.4-1.git.0.7c8657c.el7",
+# "3.3.1.5-1.git.0.62700af.el7",
+# "3.3.1.7-1.git.0.0988966.el7",
+# "3.4.0.39-1.git.0.5f32f06.el7"
+# ],
+# "latest": "3.4.0.39",
+# "latest_full": "3.4.0.39-1.git.0.5f32f06.el7",
+# "matched_version_found": true,
+# "matched_version_full_latest": "3.3.1.7-1.git.0.0988966.el7",
+# "matched_version_latest": "3.3.1.7",
+# "matched_versions": [
+# "3.3.0.32",
+# "3.3.0.34",
+# "3.3.0.35",
+# "3.3.1.3",
+# "3.3.1.4",
+# "3.3.1.5",
+# "3.3.1.7"
+# ],
+# "matched_versions_full": [
+# "3.3.0.32-1.git.0.37bd7ea.el7",
+# "3.3.0.34-1.git.0.83f306f.el7",
+# "3.3.0.35-1.git.0.d7bd9b6.el7",
+# "3.3.1.3-1.git.0.86dc49a.el7",
+# "3.3.1.4-1.git.0.7c8657c.el7",
+# "3.3.1.5-1.git.0.62700af.el7",
+# "3.3.1.7-1.git.0.0988966.el7"
+# ],
+# "requested_match_version": "3.3"
+# }
+# },
+# "state": "present"
+# }
+# }
+
+'''
+
+# -*- -*- -*- End included fragment: doc/repoquery -*- -*- -*-
+
+# -*- -*- -*- Begin included fragment: lib/repoquery.py -*- -*- -*-
+
+'''
+ class that wraps the repoquery commands in a subprocess
+'''
+
+# pylint: disable=too-many-lines,wrong-import-position,wrong-import-order
+
+from collections import defaultdict # noqa: E402
+
+
+# pylint: disable=no-name-in-module,import-error
+# Reason: pylint errors with "No name 'version' in module 'distutils'".
+# This is a bug: https://github.com/PyCQA/pylint/issues/73
+from distutils.version import LooseVersion # noqa: E402
+
+import subprocess # noqa: E402
+
+
+class RepoqueryCLIError(Exception):
+ '''Exception class for repoquerycli'''
+ pass
+
+
+def _run(cmds):
+ ''' Actually executes the command. This makes mocking easier. '''
+ proc = subprocess.Popen(cmds,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ stdout, stderr = proc.communicate()
+
+ return proc.returncode, stdout, stderr
+
+
+# pylint: disable=too-few-public-methods
+class RepoqueryCLI(object):
+ ''' Class to wrap the command line tools '''
+ def __init__(self,
+ verbose=False):
+ ''' Constructor for RepoqueryCLI '''
+ self.verbose = verbose
+ self.verbose = True
+
+ def _repoquery_cmd(self, cmd, output=False, output_type='json'):
+ '''Base command for repoquery '''
+ cmds = ['/usr/bin/repoquery', '--plugins', '--quiet']
+
+ cmds.extend(cmd)
+
+ rval = {}
+ results = ''
+ err = None
+
+ if self.verbose:
+ print(' '.join(cmds))
+
+ returncode, stdout, stderr = _run(cmds)
+
+ rval = {
+ "returncode": returncode,
+ "results": results,
+ "cmd": ' '.join(cmds),
+ }
+
+ if returncode == 0:
+ if output:
+ if output_type == 'raw':
+ rval['results'] = stdout
+
+ if self.verbose:
+ print(stdout)
+ print(stderr)
+
+ if err:
+ rval.update({
+ "err": err,
+ "stderr": stderr,
+ "stdout": stdout,
+ "cmd": cmds
+ })
+
+ else:
+ rval.update({
+ "stderr": stderr,
+ "stdout": stdout,
+ "results": {},
+ })
+
+ return rval
+
+# -*- -*- -*- End included fragment: lib/repoquery.py -*- -*- -*-
+
+# -*- -*- -*- Begin included fragment: class/repoquery.py -*- -*- -*-
+
+
+class Repoquery(RepoqueryCLI):
+ ''' Class to wrap the repoquery
+ '''
+ # pylint: disable=too-many-arguments
+ def __init__(self, name, query_type, show_duplicates,
+ match_version, verbose):
+ ''' Constructor for YumList '''
+ super(Repoquery, self).__init__(None)
+ self.name = name
+ self.query_type = query_type
+ self.show_duplicates = show_duplicates
+ self.match_version = match_version
+ self.verbose = verbose
+
+ if self.match_version:
+ self.show_duplicates = True
+
+ self.query_format = "%{version}|%{release}|%{arch}|%{repo}|%{version}-%{release}"
+
+ def build_cmd(self):
+ ''' build the repoquery cmd options '''
+
+ repo_cmd = []
+
+ repo_cmd.append("--pkgnarrow=" + self.query_type)
+ repo_cmd.append("--queryformat=" + self.query_format)
+
+ if self.show_duplicates:
+ repo_cmd.append('--show-duplicates')
+
+ repo_cmd.append(self.name)
+
+ return repo_cmd
+
+ @staticmethod
+ def process_versions(query_output):
+ ''' format the package data into something that can be presented '''
+
+ version_dict = defaultdict(dict)
+
+ for version in query_output.split('\n'):
+ pkg_info = version.split("|")
+
+ pkg_version = {}
+ pkg_version['version'] = pkg_info[0]
+ pkg_version['release'] = pkg_info[1]
+ pkg_version['arch'] = pkg_info[2]
+ pkg_version['repo'] = pkg_info[3]
+ pkg_version['version_release'] = pkg_info[4]
+
+ version_dict[pkg_info[4]] = pkg_version
+
+ return version_dict
+
+ def format_versions(self, formatted_versions):
+ ''' Gather and present the versions of each package '''
+
+ versions_dict = {}
+ versions_dict['available_versions_full'] = formatted_versions.keys()
+
+ # set the match version, if called
+ if self.match_version:
+ versions_dict['matched_versions_full'] = []
+ versions_dict['requested_match_version'] = self.match_version
+ versions_dict['matched_versions'] = []
+
+ # get the "full version (version - release)
+ versions_dict['available_versions_full'].sort(key=LooseVersion)
+ versions_dict['latest_full'] = versions_dict['available_versions_full'][-1]
+
+ # get the "short version (version)
+ versions_dict['available_versions'] = []
+ for version in versions_dict['available_versions_full']:
+ versions_dict['available_versions'].append(formatted_versions[version]['version'])
+
+ if self.match_version:
+ if version.startswith(self.match_version):
+ versions_dict['matched_versions_full'].append(version)
+ versions_dict['matched_versions'].append(formatted_versions[version]['version'])
+
+ versions_dict['available_versions'].sort(key=LooseVersion)
+ versions_dict['latest'] = versions_dict['available_versions'][-1]
+
+ # finish up the matched version
+ if self.match_version:
+ if versions_dict['matched_versions_full']:
+ versions_dict['matched_version_found'] = True
+ versions_dict['matched_versions'].sort(key=LooseVersion)
+ versions_dict['matched_version_latest'] = versions_dict['matched_versions'][-1]
+ versions_dict['matched_version_full_latest'] = versions_dict['matched_versions_full'][-1]
+ else:
+ versions_dict['matched_version_found'] = False
+ versions_dict['matched_versions'] = []
+ versions_dict['matched_version_latest'] = ""
+ versions_dict['matched_version_full_latest'] = ""
+
+ return versions_dict
+
+ def repoquery(self):
+ '''perform a repoquery '''
+
+ repoquery_cmd = self.build_cmd()
+
+ rval = self._repoquery_cmd(repoquery_cmd, True, 'raw')
+
+ # check to see if there are actual results
+ if rval['results']:
+ processed_versions = Repoquery.process_versions(rval['results'].strip())
+ formatted_versions = self.format_versions(processed_versions)
+
+ rval['package_found'] = True
+ rval['versions'] = formatted_versions
+ rval['package_name'] = self.name
+
+ if self.verbose:
+ rval['raw_versions'] = processed_versions
+ else:
+ del rval['results']
+
+ # No packages found
+ else:
+ rval['package_found'] = False
+
+ return rval
+
+ @staticmethod
+ def run_ansible(params, check_mode):
+ '''run the ansible idempotent code'''
+
+ repoquery = Repoquery(
+ params['name'],
+ params['query_type'],
+ params['show_duplicates'],
+ params['match_version'],
+ params['verbose'],
+ )
+
+ state = params['state']
+
+ if state == 'list':
+ results = repoquery.repoquery()
+
+ if results['returncode'] != 0:
+ return {'failed': True,
+ 'msg': results}
+
+ return {'changed': False, 'results': results, 'state': 'list', 'check_mode': check_mode}
+
+ return {'failed': True,
+ 'changed': False,
+ 'msg': 'Unknown state passed. %s' % state,
+ 'state': 'unknown'}
+
+# -*- -*- -*- End included fragment: class/repoquery.py -*- -*- -*-
+
+# -*- -*- -*- Begin included fragment: ansible/repoquery.py -*- -*- -*-
+
+
+def main():
+ '''
+ ansible repoquery module
+ '''
+ module = AnsibleModule(
+ argument_spec=dict(
+ state=dict(default='list', type='str', choices=['list']),
+ name=dict(default=None, required=True, type='str'),
+ query_type=dict(default='repos', required=False, type='str',
+ choices=[
+ 'installed', 'available', 'recent',
+ 'updates', 'extras', 'all', 'repos'
+ ]),
+ verbose=dict(default=False, required=False, type='bool'),
+ show_duplicates=dict(default=False, required=False, type='bool'),
+ match_version=dict(default=None, required=False, type='str'),
+ ),
+ supports_check_mode=False,
+ required_if=[('show_duplicates', True, ['name'])],
+ )
+
+ rval = Repoquery.run_ansible(module.params, module.check_mode)
+
+ if 'failed' in rval:
+ module.fail_json(**rval)
+
+ module.exit_json(**rval)
+
+
+if __name__ == "__main__":
+ main()
+
+# -*- -*- -*- End included fragment: ansible/repoquery.py -*- -*- -*-
diff --git a/roles/lib_utils/library/yedit.py b/roles/lib_utils/library/yedit.py
index 8a2bd92f9..7ad2b7181 100644
--- a/roles/lib_utils/library/yedit.py
+++ b/roles/lib_utils/library/yedit.py
@@ -24,18 +24,21 @@
# limitations under the License.
#
-# -*- -*- -*- Begin included fragment: class/import.py -*- -*- -*-
+# -*- -*- -*- Begin included fragment: lib/import.py -*- -*- -*-
-# pylint: disable=wrong-import-order
-import json
-import os
-import re
+# pylint: disable=wrong-import-order,wrong-import-position,unused-import
+
+from __future__ import print_function # noqa: F401
+import json # noqa: F401
+import os # noqa: F401
+import re # noqa: F401
# pylint: disable=import-error
-import ruamel.yaml as yaml
-import shutil
+import ruamel.yaml as yaml # noqa: F401
+import shutil # noqa: F401
+
from ansible.module_utils.basic import AnsibleModule
-# -*- -*- -*- End included fragment: class/import.py -*- -*- -*-
+# -*- -*- -*- End included fragment: lib/import.py -*- -*- -*-
# -*- -*- -*- Begin included fragment: doc/yedit -*- -*- -*-