diff options
| author | Thomas Wiest <twiest@redhat.com> | 2017-01-25 14:07:22 -0500 | 
|---|---|---|
| committer | Thomas Wiest <twiest@redhat.com> | 2017-01-31 08:15:37 -0500 | 
| commit | b415e4855970131a77112940646a95641d3bd27b (patch) | |
| tree | 951ce174c71c495db66b522fa0730816e4878f93 /roles/lib_utils/library | |
| parent | fca215887b2e4224779b58e8fd1b7662ec993f83 (diff) | |
| download | openshift-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.py | 607 | ||||
| -rw-r--r-- | roles/lib_utils/library/yedit.py | 19 | 
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 -*- -*- -*- | 
