summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md13
-rw-r--r--callback_plugins/aa_version_requirement.py13
-rw-r--r--playbooks/byo/openshift-cluster/upgrades/README.md4
-rw-r--r--playbooks/byo/openshift-cluster/upgrades/v3_5/README.md18
l---------playbooks/byo/openshift-cluster/upgrades/v3_5/roles1
-rw-r--r--playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml99
-rw-r--r--playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml102
-rw-r--r--playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_nodes.yml100
-rw-r--r--playbooks/common/openshift-cluster/upgrades/init.yml14
-rw-r--r--playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml2
-rw-r--r--playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml15
-rw-r--r--roles/lib_openshift/src/sources.yml7
-rwxr-xr-xroles/lib_openshift/src/test/unit/oc_secret.py2
-rw-r--r--roles/lib_utils/library/repoquery.py607
-rw-r--r--roles/lib_utils/library/yedit.py19
-rw-r--r--roles/lib_utils/src/ansible/repoquery.py35
-rw-r--r--roles/lib_utils/src/class/import.py11
-rw-r--r--roles/lib_utils/src/class/repoquery.py156
-rw-r--r--roles/lib_utils/src/doc/repoquery275
-rw-r--r--roles/lib_utils/src/lib/import.py14
-rw-r--r--roles/lib_utils/src/lib/repoquery.py92
-rw-r--r--roles/lib_utils/src/sources.yml11
-rwxr-xr-xroles/lib_utils/src/test/integration/repoquery.yml136
-rwxr-xr-xroles/lib_utils/src/test/unit/repoquery.py87
-rwxr-xr-xroles/openshift_facts/library/openshift_facts.py17
-rw-r--r--roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py84
-rw-r--r--roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py61
-rw-r--r--roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py23
-rw-r--r--roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py16
-rw-r--r--utils/src/ooinstall/cli_installer.py30
30 files changed, 1961 insertions, 103 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 2a4f80a36..cdfd93725 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,3 +1,16 @@
+### <HTPASSWD_AUTH>
+
+We are aware of the current issues related to htpasswd_auth failures
+Please downgrade to ansible 2.2.0.0 until a fix is released.
+You can track the status of the bug fix in this issue:
+https://github.com/openshift/openshift-ansible/issues/3111
+Please erase this <HTPASSWD_AUTH> section if it does not apply to you.
+
+Thanks - 2017-01-31
+
+### </HTPASSWD_AUTH>
+
+
#### Description
Provide a brief description of your issue here. For example:
diff --git a/callback_plugins/aa_version_requirement.py b/callback_plugins/aa_version_requirement.py
index 1cca19a45..40affb18b 100644
--- a/callback_plugins/aa_version_requirement.py
+++ b/callback_plugins/aa_version_requirement.py
@@ -30,7 +30,12 @@ else:
# Set to minimum required Ansible version
REQUIRED_VERSION = '2.2.0.0'
-DESCRIPTION = "Supported versions: %s or newer" % REQUIRED_VERSION
+DESCRIPTION = "Supported versions: %s or newer (except 2.2.1.0)" % REQUIRED_VERSION
+FAIL_ON_2_2_1_0 = "There are known issues with Ansible version 2.2.1.0 which " \
+ "are impacting OpenShift-Ansible. Please use Ansible " \
+ "version 2.2.0.0 or a version greater than 2.2.1.0. " \
+ "See this issue for more details: " \
+ "https://github.com/openshift/openshift-ansible/issues/3111"
def version_requirement(version):
@@ -58,3 +63,9 @@ class CallbackModule(CallbackBase):
'FATAL: Current Ansible version (%s) is not supported. %s'
% (__version__, DESCRIPTION), color='red')
sys.exit(1)
+
+ if __version__ == '2.2.1.0':
+ display(
+ 'FATAL: Current Ansible version (%s) is not supported. %s'
+ % (__version__, FAIL_ON_2_2_1_0), color='red')
+ sys.exit(1)
diff --git a/playbooks/byo/openshift-cluster/upgrades/README.md b/playbooks/byo/openshift-cluster/upgrades/README.md
index de4e34e2d..e5b80a9b4 100644
--- a/playbooks/byo/openshift-cluster/upgrades/README.md
+++ b/playbooks/byo/openshift-cluster/upgrades/README.md
@@ -4,5 +4,5 @@ cluster. Additional notes for the associated upgrade playbooks are
provided in their respective directories.
# Upgrades available
-- [OpenShift Enterprise 3.2 to 3.3](v3_3/README.md)
-- [OpenShift Enterprise 3.1 to 3.2](v3_2/README.md)
+- [OpenShift Enterprise 3.4 to 3.5](v3_5/README.md)
+- [OpenShift Enterprise 3.3 to 3.4](v3_4/README.md)
diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/README.md b/playbooks/byo/openshift-cluster/upgrades/v3_5/README.md
new file mode 100644
index 000000000..53eebe65e
--- /dev/null
+++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/README.md
@@ -0,0 +1,18 @@
+# v3.5 Major and Minor Upgrade Playbook
+
+## Overview
+This playbook currently performs the
+following steps.
+
+ * Upgrade and restart master services
+ * Unschedule node.
+ * Upgrade and restart docker
+ * Upgrade and restart node services
+ * Modifies the subset of the configuration necessary
+ * Applies the latest cluster policies
+ * Updates the default router if one exists
+ * Updates the default registry if one exists
+ * Updates image streams and quickstarts
+
+## Usage
+ansible-playbook -i ~/ansible-inventory openshift-ansible/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml
diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/roles b/playbooks/byo/openshift-cluster/upgrades/v3_5/roles
new file mode 120000
index 000000000..6bc1a7aef
--- /dev/null
+++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/roles
@@ -0,0 +1 @@
+../../../../../roles \ No newline at end of file
diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml
new file mode 100644
index 000000000..bef15eaab
--- /dev/null
+++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade.yml
@@ -0,0 +1,99 @@
+---
+#
+# Full Control Plane + Nodes Upgrade
+#
+- include: ../../../../common/openshift-cluster/upgrades/init.yml
+ tags:
+ - pre_upgrade
+
+- name: Configure the upgrade target for the common upgrade tasks
+ hosts: l_oo_all_hosts
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_upgrade_target: "{{ '1.5' if deployment_type == 'origin' else '3.5' }}"
+ openshift_upgrade_min: "{{ '1.4' if deployment_type == 'origin' else '3.4' }}"
+
+# Pre-upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml
+ tags:
+ - pre_upgrade
+
+- name: Update repos and initialize facts on all hosts
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config:oo_lb_to_config
+ tags:
+ - pre_upgrade
+ roles:
+ - openshift_repos
+
+- name: Set openshift_no_proxy_internal_hostnames
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_no_proxy_internal_hostnames: "{{ hostvars | oo_select_keys(groups['oo_nodes_to_config']
+ | union(groups['oo_masters_to_config'])
+ | union(groups['oo_etcd_to_config'] | default([])))
+ | oo_collect('openshift.common.hostname') | default([]) | join (',')
+ }}"
+ when: "{{ (openshift_http_proxy is defined or openshift_https_proxy is defined) and
+ openshift_generate_no_proxy_hosts | default(True) | bool }}"
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_inventory_vars.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/initialize_openshift_version.yml
+ tags:
+ - pre_upgrade
+ vars:
+ # Request specific openshift_release and let the openshift_version role handle converting this
+ # to a more specific version, respecting openshift_image_tag and openshift_pkg_version if
+ # defined, and overriding the normal behavior of protecting the installed version
+ openshift_release: "{{ openshift_upgrade_target }}"
+ openshift_protect_installed_version: False
+
+ # We skip the docker role at this point in upgrade to prevent
+ # unintended package, container, or config upgrades which trigger
+ # docker restarts. At this early stage of upgrade we can assume
+ # docker is configured and running.
+ skip_docker_role: True
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_control_plane_running.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-master/validate_restart.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_docker_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/gate_checks.yml
+ tags:
+ - pre_upgrade
+
+# Pre-upgrade completed, nothing after this should be tagged pre_upgrade.
+
+# Separate step so we can execute in parallel and clear out anything unused
+# before we get into the serialized upgrade process which will then remove
+# remaining images if possible.
+- name: Cleanup unused Docker images
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config
+ tasks:
+ - include: ../../../../common/openshift-cluster/upgrades/cleanup_unused_images.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/upgrade_control_plane.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/upgrade_nodes.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/post_control_plane.yml
diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml
new file mode 100644
index 000000000..dd88dde5f
--- /dev/null
+++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_control_plane.yml
@@ -0,0 +1,102 @@
+---
+#
+# Control Plane Upgrade Playbook
+#
+# Upgrades masters and Docker (only on standalone etcd hosts)
+#
+# This upgrade does not include:
+# - node service running on masters
+# - docker running on masters
+# - node service running on dedicated nodes
+#
+# You can run the upgrade_nodes.yml playbook after this to upgrade these components separately.
+#
+- include: ../../../../common/openshift-cluster/upgrades/init.yml
+ tags:
+ - pre_upgrade
+
+# Configure the upgrade target for the common upgrade tasks:
+- hosts: l_oo_all_hosts
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_upgrade_target: "{{ '1.5' if deployment_type == 'origin' else '3.5' }}"
+ openshift_upgrade_min: "{{ '1.4' if deployment_type == 'origin' else '3.4' }}"
+
+# Pre-upgrade
+
+- name: Update repos on control plane hosts
+ hosts: oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config
+ tags:
+ - pre_upgrade
+ roles:
+ - openshift_repos
+
+- name: Set openshift_no_proxy_internal_hostnames
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_no_proxy_internal_hostnames: "{{ hostvars | oo_select_keys(groups['oo_nodes_to_config']
+ | union(groups['oo_masters_to_config'])
+ | union(groups['oo_etcd_to_config'] | default([])))
+ | oo_collect('openshift.common.hostname') | default([]) | join (',')
+ }}"
+ when: "{{ (openshift_http_proxy is defined or openshift_https_proxy is defined) and
+ openshift_generate_no_proxy_hosts | default(True) | bool }}"
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_inventory_vars.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/initialize_openshift_version.yml
+ tags:
+ - pre_upgrade
+ vars:
+ # Request specific openshift_release and let the openshift_version role handle converting this
+ # to a more specific version, respecting openshift_image_tag and openshift_pkg_version if
+ # defined, and overriding the normal behavior of protecting the installed version
+ openshift_release: "{{ openshift_upgrade_target }}"
+ openshift_protect_installed_version: False
+
+ # We skip the docker role at this point in upgrade to prevent
+ # unintended package, container, or config upgrades which trigger
+ # docker restarts. At this early stage of upgrade we can assume
+ # docker is configured and running.
+ skip_docker_role: True
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_control_plane_running.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-master/validate_restart.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_docker_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/gate_checks.yml
+ tags:
+ - pre_upgrade
+
+# Pre-upgrade completed, nothing after this should be tagged pre_upgrade.
+
+# Separate step so we can execute in parallel and clear out anything unused
+# before we get into the serialized upgrade process which will then remove
+# remaining images if possible.
+- name: Cleanup unused Docker images
+ hosts: oo_masters_to_config:oo_etcd_to_config
+ tasks:
+ - include: ../../../../common/openshift-cluster/upgrades/cleanup_unused_images.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/upgrade_control_plane.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/post_control_plane.yml
diff --git a/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_nodes.yml b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_nodes.yml
new file mode 100644
index 000000000..931a1bcd7
--- /dev/null
+++ b/playbooks/byo/openshift-cluster/upgrades/v3_5/upgrade_nodes.yml
@@ -0,0 +1,100 @@
+---
+#
+# Node Upgrade Playbook
+#
+# Upgrades nodes only, but requires the control plane to have already been upgraded.
+#
+- include: ../../../../common/openshift-cluster/upgrades/init.yml
+ tags:
+ - pre_upgrade
+
+# Configure the upgrade target for the common upgrade tasks:
+- hosts: l_oo_all_hosts
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_upgrade_target: "{{ '1.5' if deployment_type == 'origin' else '3.5' }}"
+ openshift_upgrade_min: "{{ '1.4' if deployment_type == 'origin' else '3.4' }}"
+
+# Pre-upgrade
+- include: ../../../../common/openshift-cluster/upgrades/initialize_nodes_to_upgrade.yml
+ tags:
+ - pre_upgrade
+
+- name: Update repos on nodes
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config:oo_lb_to_config
+ roles:
+ - openshift_repos
+ tags:
+ - pre_upgrade
+
+- name: Set openshift_no_proxy_internal_hostnames
+ hosts: oo_masters_to_config:oo_nodes_to_upgrade
+ tags:
+ - pre_upgrade
+ tasks:
+ - set_fact:
+ openshift_no_proxy_internal_hostnames: "{{ hostvars | oo_select_keys(groups['oo_nodes_to_upgrade']
+ | union(groups['oo_masters_to_config'])
+ | union(groups['oo_etcd_to_config'] | default([])))
+ | oo_collect('openshift.common.hostname') | default([]) | join (',')
+ }}"
+ when: "{{ (openshift_http_proxy is defined or openshift_https_proxy is defined) and
+ openshift_generate_no_proxy_hosts | default(True) | bool }}"
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_inventory_vars.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/initialize_openshift_version.yml
+ tags:
+ - pre_upgrade
+ vars:
+ # Request specific openshift_release and let the openshift_version role handle converting this
+ # to a more specific version, respecting openshift_image_tag and openshift_pkg_version if
+ # defined, and overriding the normal behavior of protecting the installed version
+ openshift_release: "{{ openshift_upgrade_target }}"
+ openshift_protect_installed_version: False
+
+ # We skip the docker role at this point in upgrade to prevent
+ # unintended package, container, or config upgrades which trigger
+ # docker restarts. At this early stage of upgrade we can assume
+ # docker is configured and running.
+ skip_docker_role: True
+
+- name: Verify masters are already upgraded
+ hosts: oo_masters_to_config
+ tags:
+ - pre_upgrade
+ tasks:
+ - fail: msg="Master running {{ openshift.common.version }} must be upgraded to {{ openshift_version }} before node upgrade can be run."
+ when: openshift.common.version != openshift_version
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_control_plane_running.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/verify_docker_upgrade_targets.yml
+ tags:
+ - pre_upgrade
+
+- include: ../../../../common/openshift-cluster/upgrades/pre/gate_checks.yml
+ tags:
+ - pre_upgrade
+
+# Pre-upgrade completed, nothing after this should be tagged pre_upgrade.
+
+# Separate step so we can execute in parallel and clear out anything unused
+# before we get into the serialized upgrade process which will then remove
+# remaining images if possible.
+- name: Cleanup unused Docker images
+ hosts: oo_nodes_to_upgrade
+ tasks:
+ - include: ../../../../common/openshift-cluster/upgrades/cleanup_unused_images.yml
+
+- include: ../../../../common/openshift-cluster/upgrades/upgrade_nodes.yml
diff --git a/playbooks/common/openshift-cluster/upgrades/init.yml b/playbooks/common/openshift-cluster/upgrades/init.yml
index b62557550..a3b8c489e 100644
--- a/playbooks/common/openshift-cluster/upgrades/init.yml
+++ b/playbooks/common/openshift-cluster/upgrades/init.yml
@@ -65,3 +65,17 @@
when: not openshift.common.is_atomic | bool
args:
warn: no
+
+- name: Ensure firewall is not switched during upgrade
+ hosts: oo_all_hosts
+ tasks:
+ - name: Check if iptables is running
+ command: systemctl status iptables
+ ignore_errors: true
+ changed_when: false
+ register: service_iptables_status
+
+ - name: Set fact os_firewall_use_firewalld FALSE for iptables
+ set_fact:
+ os_firewall_use_firewalld: false
+ when: "'Active: active' in service_iptables_status.stdout"
diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml
index 23b976192..9cad931af 100644
--- a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml
+++ b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml
@@ -32,7 +32,7 @@
include: ./etcd/main.yml
# Create service signer cert when missing. Service signer certificate
-# is added to master config in the master config hook for v3_3.
+# is added to master config in the master_config_upgrade hook.
- name: Determine if service signer cert must be created
hosts: oo_first_master
tasks:
diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml
index 5fa74898f..a6a49e5ff 100644
--- a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml
+++ b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml
@@ -81,6 +81,21 @@
failed_when: false
when: openshift.common.is_containerized | bool
+ - name: Upgrade openvswitch
+ package:
+ name: openvswitch
+ state: latest
+ register: ovs_pkg
+ when: inventory_hostname in groups.oo_nodes_to_upgrade and not openshift.common.is_containerized | bool
+
+ - name: Restart openvswitch
+ systemd:
+ name: openvswitch
+ state: restarted
+ when:
+ - inventory_hostname in groups.oo_nodes_to_upgrade and not openshift.common.is_containerized | bool
+ - ovs_pkg | changed
+
# Mandatory Docker restart, ensure all containerized services are running:
- include: docker/restart.yml
diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml
index 8a825a402..28929c02a 100644
--- a/roles/lib_openshift/src/sources.yml
+++ b/roles/lib_openshift/src/sources.yml
@@ -8,6 +8,7 @@ oadm_manage_node.py:
- lib/base.py
- class/oadm_manage_node.py
- ansible/oadm_manage_node.py
+
oc_edit.py:
- doc/generated
- doc/license
@@ -17,6 +18,7 @@ oc_edit.py:
- lib/base.py
- class/oc_edit.py
- ansible/oc_edit.py
+
oc_obj.py:
- doc/generated
- doc/license
@@ -26,6 +28,7 @@ oc_obj.py:
- lib/base.py
- class/oc_obj.py
- ansible/oc_obj.py
+
oc_route.py:
- doc/generated
- doc/license
@@ -36,6 +39,7 @@ oc_route.py:
- lib/route.py
- class/oc_route.py
- ansible/oc_route.py
+
oc_secret.py:
- doc/generated
- doc/license
@@ -46,6 +50,7 @@ oc_secret.py:
- lib/secret.py
- class/oc_secret.py
- ansible/oc_secret.py
+
oc_scale.py:
- doc/generated
- doc/license
@@ -57,6 +62,7 @@ oc_scale.py:
- lib/replicationcontroller.py
- class/oc_scale.py
- ansible/oc_scale.py
+
oc_version.py:
- doc/generated
- doc/license
@@ -66,6 +72,7 @@ oc_version.py:
- lib/base.py
- class/oc_version.py
- ansible/oc_version.py
+
oc_serviceaccount.py:
- doc/generated
- doc/license
diff --git a/roles/lib_openshift/src/test/unit/oc_secret.py b/roles/lib_openshift/src/test/unit/oc_secret.py
index 221f00ed6..835918b95 100755
--- a/roles/lib_openshift/src/test/unit/oc_secret.py
+++ b/roles/lib_openshift/src/test/unit/oc_secret.py
@@ -81,7 +81,7 @@ class OCSecretTest(unittest.TestCase):
# Making sure our mock was called as we expected
mock_openshift_cmd.assert_has_calls([
- mock.call(['get', 'secrets', '-o', 'json', 'secretname'], output=True),
+ mock.call(['get', 'secrets', 'secretname', '-o', 'json'], output=True),
mock.call(['secrets', 'new', 'secretname', 'somesecret.json=/tmp/somesecret.json']),
])
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 -*- -*- -*-
diff --git a/roles/lib_utils/src/ansible/repoquery.py b/roles/lib_utils/src/ansible/repoquery.py
new file mode 100644
index 000000000..cb4efa6c1
--- /dev/null
+++ b/roles/lib_utils/src/ansible/repoquery.py
@@ -0,0 +1,35 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+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()
diff --git a/roles/lib_utils/src/class/import.py b/roles/lib_utils/src/class/import.py
deleted file mode 100644
index 249e07228..000000000
--- a/roles/lib_utils/src/class/import.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# flake8: noqa
-# pylint: skip-file
-
-# pylint: disable=wrong-import-order
-import json
-import os
-import re
-# pylint: disable=import-error
-import ruamel.yaml as yaml
-import shutil
-from ansible.module_utils.basic import AnsibleModule
diff --git a/roles/lib_utils/src/class/repoquery.py b/roles/lib_utils/src/class/repoquery.py
new file mode 100644
index 000000000..2447719e2
--- /dev/null
+++ b/roles/lib_utils/src/class/repoquery.py
@@ -0,0 +1,156 @@
+# pylint: skip-file
+# flake8: noqa
+
+
+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'}
diff --git a/roles/lib_utils/src/doc/repoquery b/roles/lib_utils/src/doc/repoquery
new file mode 100644
index 000000000..82e273a42
--- /dev/null
+++ b/roles/lib_utils/src/doc/repoquery
@@ -0,0 +1,275 @@
+# flake8: noqa
+# pylint: skip-file
+
+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"
+# }
+# }
+
+'''
diff --git a/roles/lib_utils/src/lib/import.py b/roles/lib_utils/src/lib/import.py
new file mode 100644
index 000000000..d892353a1
--- /dev/null
+++ b/roles/lib_utils/src/lib/import.py
@@ -0,0 +1,14 @@
+# flake8: noqa
+# pylint: skip-file
+
+# 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
diff --git a/roles/lib_utils/src/lib/repoquery.py b/roles/lib_utils/src/lib/repoquery.py
new file mode 100644
index 000000000..91ccd9815
--- /dev/null
+++ b/roles/lib_utils/src/lib/repoquery.py
@@ -0,0 +1,92 @@
+# pylint: skip-file
+# flake8: noqa
+
+'''
+ 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
diff --git a/roles/lib_utils/src/sources.yml b/roles/lib_utils/src/sources.yml
index 9cf3a0981..053b59f77 100644
--- a/roles/lib_utils/src/sources.yml
+++ b/roles/lib_utils/src/sources.yml
@@ -2,7 +2,16 @@
yedit.py:
- doc/generated
- doc/license
-- class/import.py
+- lib/import.py
- doc/yedit
- class/yedit.py
- ansible/yedit.py
+
+repoquery.py:
+- doc/generated
+- doc/license
+- lib/import.py
+- doc/repoquery
+- lib/repoquery.py
+- class/repoquery.py
+- ansible/repoquery.py
diff --git a/roles/lib_utils/src/test/integration/repoquery.yml b/roles/lib_utils/src/test/integration/repoquery.yml
new file mode 100755
index 000000000..425324387
--- /dev/null
+++ b/roles/lib_utils/src/test/integration/repoquery.yml
@@ -0,0 +1,136 @@
+#!/usr/bin/ansible-playbook --module-path=../../../library/
+---
+- hosts: localhost
+ gather_facts: no
+
+ tasks:
+ - name: basic query test - Act
+ repoquery:
+ name: bash
+ register: rq_out
+
+ - name: Set a real package version to be used later
+ set_fact:
+ latest_available_bash_version: "{{ rq_out.results.versions.latest }}"
+ latest_available_full_bash_version: "{{ rq_out.results.versions.latest_full }}"
+
+ - name: basic query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == True"
+ - "rq_out.results.package_name == 'bash'"
+ - "rq_out.results.versions.available_versions | length == 1"
+ - "rq_out.results.versions.available_versions_full | length == 1"
+ - "rq_out.results.versions.latest is defined"
+ - "rq_out.results.versions.latest in rq_out.results.versions.available_versions"
+ - "rq_out.results.versions.latest_full is defined"
+ - "rq_out.results.versions.latest_full in rq_out.results.versions.available_versions_full"
+
+ - name: show_duplicates query test - Act
+ repoquery:
+ name: bash
+ show_duplicates: True
+ register: rq_out
+
+ - name: show_duplicates query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == True"
+ - "rq_out.results.package_name == 'bash'"
+ - "rq_out.results.versions.available_versions | length >= 1"
+ - "rq_out.results.versions.available_versions_full | length >= 1"
+ - "rq_out.results.versions.latest is defined"
+ - "rq_out.results.versions.latest in rq_out.results.versions.available_versions"
+ - "rq_out.results.versions.latest_full is defined"
+ - "rq_out.results.versions.latest_full in rq_out.results.versions.available_versions_full"
+
+ - name: show_duplicates verbose query test - Act
+ repoquery:
+ name: bash
+ show_duplicates: True
+ verbose: True
+ register: rq_out
+
+ - name: show_duplicates verbose query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == True"
+ - "rq_out.results.package_name == 'bash'"
+ - "rq_out.results.raw_versions | length > 0"
+ - "rq_out.results.versions.available_versions | length > 0"
+ - "rq_out.results.versions.available_versions_full | length > 0"
+ - "rq_out.results.versions.latest is defined"
+ - "rq_out.results.versions.latest in rq_out.results.versions.available_versions"
+ - "rq_out.results.versions.latest_full is defined"
+ - "rq_out.results.versions.latest_full in rq_out.results.versions.available_versions_full"
+
+ - name: query package does not exist query test - Act
+ repoquery:
+ name: somemadeuppackagenamethatwontmatch
+ show_duplicates: True
+ register: rq_out
+
+ - name: query package does not exist query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == False"
+ - "rq_out.results.results == ''"
+
+
+ - name: query match_version does not exist query test - Act
+ repoquery:
+ name: bash
+ show_duplicates: True
+ match_version: somemadeupversionnotexist
+ register: rq_out
+
+ - name: query match_version does not exist query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == True"
+ - "rq_out.results.package_name == 'bash'"
+ - "rq_out.results.versions.matched_version_found == False"
+ - "rq_out.results.versions.available_versions | length > 0"
+ - "rq_out.results.versions.available_versions_full | length > 0"
+ - "rq_out.results.versions.latest is defined"
+ - "rq_out.results.versions.latest in rq_out.results.versions.available_versions"
+ - "rq_out.results.versions.latest_full is defined"
+ - "rq_out.results.versions.latest_full in rq_out.results.versions.available_versions_full"
+
+ - name: query match_version exists query test - Act
+ repoquery:
+ name: bash
+ show_duplicates: True
+ match_version: "{{ latest_available_bash_version }}"
+ register: rq_out
+
+ - name: query match_version exists query test - Assert
+ assert:
+ that:
+ - "rq_out.state == 'list'"
+ - "rq_out.changed == False"
+ - "rq_out.results.returncode == 0"
+ - "rq_out.results.package_found == True"
+ - "rq_out.results.package_name == 'bash'"
+ - "rq_out.results.versions.matched_version_found == True"
+ - "rq_out.results.versions.available_versions | length > 0"
+ - "rq_out.results.versions.available_versions_full | length > 0"
+ - "rq_out.results.versions.latest is defined"
+ - "rq_out.results.versions.latest in rq_out.results.versions.available_versions"
+ - "rq_out.results.versions.latest_full is defined"
+ - "rq_out.results.versions.latest_full in rq_out.results.versions.available_versions_full"
diff --git a/roles/lib_utils/src/test/unit/repoquery.py b/roles/lib_utils/src/test/unit/repoquery.py
new file mode 100755
index 000000000..c487ab254
--- /dev/null
+++ b/roles/lib_utils/src/test/unit/repoquery.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python2
+'''
+ Unit tests for repoquery
+'''
+# To run:
+# ./repoquery.py
+#
+# .
+# Ran 1 test in 0.002s
+#
+# OK
+
+import os
+import sys
+import unittest
+import mock
+
+# Removing invalid variable names for tests so that I can
+# keep them brief
+# pylint: disable=invalid-name,no-name-in-module
+# Disable import-error b/c our libraries aren't loaded in jenkins
+# pylint: disable=import-error,wrong-import-position
+# place class in our python path
+module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501
+sys.path.insert(0, module_path)
+from repoquery import Repoquery # noqa: E402
+
+
+class RepoQueryTest(unittest.TestCase):
+ '''
+ Test class for RepoQuery
+ '''
+
+ def setUp(self):
+ ''' setup method for other tests '''
+ pass
+
+ @mock.patch('repoquery._run')
+ def test_querying_a_package(self, mock_cmd):
+ ''' Testing querying a package '''
+
+ # Arrange
+
+ # run_ansible input parameters
+ params = {
+ 'state': 'list',
+ 'name': 'bash',
+ 'query_type': 'repos',
+ 'verbose': False,
+ 'show_duplicates': False,
+ 'match_version': None,
+ }
+
+ valid_stderr = '''Repo rhel-7-server-extras-rpms forced skip_if_unavailable=True due to: /etc/pki/entitlement/3268107132875399464-key.pem
+ Repo rhel-7-server-rpms forced skip_if_unavailable=True due to: /etc/pki/entitlement/4128505182875899164-key.pem''' # not real
+
+ # Return values of our mocked function call. These get returned once per call.
+ mock_cmd.side_effect = [
+ (0, '4.2.46|21.el7_3|x86_64|rhel-7-server-rpms|4.2.46-21.el7_3', valid_stderr), # first call to the mock
+ ]
+
+ # Act
+ results = Repoquery.run_ansible(params, False)
+
+ # Assert
+ self.assertEqual(results['state'], 'list')
+ self.assertFalse(results['changed'])
+ self.assertTrue(results['results']['package_found'])
+ self.assertEqual(results['results']['returncode'], 0)
+ self.assertEqual(results['results']['package_name'], 'bash')
+ self.assertEqual(results['results']['versions'], {'latest_full': '4.2.46-21.el7_3',
+ 'available_versions': ['4.2.46'],
+ 'available_versions_full': ['4.2.46-21.el7_3'],
+ 'latest': '4.2.46'})
+
+ # Making sure our mock was called as we expected
+ mock_cmd.assert_has_calls([
+ mock.call(['/usr/bin/repoquery', '--plugins', '--quiet', '--pkgnarrow=repos', '--queryformat=%{version}|%{release}|%{arch}|%{repo}|%{version}-%{release}', 'bash']),
+ ])
+
+ def tearDown(self):
+ '''TearDown method'''
+ pass
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py
index 3c8e2ab9c..7c61da950 100755
--- a/roles/openshift_facts/library/openshift_facts.py
+++ b/roles/openshift_facts/library/openshift_facts.py
@@ -195,8 +195,7 @@ def hostname_valid(hostname):
if (not hostname or
hostname.startswith('localhost') or
hostname.endswith('localdomain') or
- hostname.endswith('novalocal') or
- len(hostname.split('.')) < 2):
+ hostname.endswith('novalocal')):
return False
return True
@@ -332,7 +331,8 @@ def normalize_gce_facts(metadata, facts):
facts['network']['ip'] = facts['network']['interfaces'][0]['ips'][0]
pub_ip = facts['network']['interfaces'][0]['public_ips'][0]
facts['network']['public_ip'] = pub_ip
- facts['network']['hostname'] = metadata['instance']['hostname']
+ # Split instance hostname from GCE metadata to use the short instance name
+ facts['network']['hostname'] = metadata['instance']['hostname'].split('.')[0]
# TODO: attempt to resolve public_hostname
facts['network']['public_hostname'] = facts['network']['public_ip']
@@ -867,6 +867,7 @@ def set_deployment_facts_if_unset(facts):
return facts
+# pylint: disable=too-many-statements
def set_version_facts_if_unset(facts):
""" Set version facts. This currently includes common.version and
common.version_gte_3_1_or_1_1.
@@ -904,8 +905,8 @@ def set_version_facts_if_unset(facts):
version_gte_3_1_1_or_1_1_1 = True
version_gte_3_2_or_1_2 = True
version_gte_3_3_or_1_3 = True
- version_gte_3_4_or_1_4 = False
- version_gte_3_5_or_1_5 = False
+ version_gte_3_4_or_1_4 = True
+ version_gte_3_5_or_1_5 = True
version_gte_3_6_or_1_6 = False
facts['common']['version_gte_3_1_or_1_1'] = version_gte_3_1_or_1_1
facts['common']['version_gte_3_1_1_or_1_1_1'] = version_gte_3_1_1_or_1_1_1
@@ -915,7 +916,9 @@ def set_version_facts_if_unset(facts):
facts['common']['version_gte_3_5_or_1_5'] = version_gte_3_5_or_1_5
facts['common']['version_gte_3_6_or_1_6'] = version_gte_3_6_or_1_6
- if version_gte_3_4_or_1_4:
+ if version_gte_3_5_or_1_5:
+ examples_content_version = 'v1.5'
+ elif version_gte_3_4_or_1_4:
examples_content_version = 'v1.4'
elif version_gte_3_3_or_1_3:
examples_content_version = 'v1.3'
@@ -1019,7 +1022,7 @@ def set_nodename(facts):
if 'cloudprovider' in facts and facts['cloudprovider']['kind'] == 'openstack':
facts['node']['nodename'] = facts['provider']['metadata']['hostname'].replace('.novalocal', '')
elif 'cloudprovider' in facts and facts['cloudprovider']['kind'] == 'gce':
- facts['node']['nodename'] = '.'.split(facts['provider']['metadata']['hostname'])[0]
+ facts['node']['nodename'] = facts['provider']['metadata']['instance']['hostname'].split('.')[0]
else:
facts['node']['nodename'] = facts['common']['hostname'].lower()
return facts
diff --git a/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py b/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py
index 29a59a0d3..0c94228c6 100644
--- a/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py
+++ b/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_predicates.py
@@ -52,32 +52,64 @@ class LookupModule(LookupBase):
# convert short_version to origin short_version
short_version = re.sub('^3.', '1.', short_version)
- if short_version in ['1.1', '1.2']:
- predicates.append({'name': 'PodFitsHostPorts'})
- predicates.append({'name': 'PodFitsResources'})
-
- # applies to all known versions
- predicates.append({'name': 'NoDiskConflict'})
-
- # only 1.1 didn't include NoVolumeZoneConflict
- if short_version != '1.1':
- predicates.append({'name': 'NoVolumeZoneConflict'})
-
- if short_version in ['1.1', '1.2']:
- predicates.append({'name': 'MatchNodeSelector'})
-
- if short_version != '1.1':
- predicates.append({'name': 'MaxEBSVolumeCount'})
- predicates.append({'name': 'MaxGCEPDVolumeCount'})
-
- if short_version not in ['1.1', '1.2']:
- predicates.append({'name': 'GeneralPredicates'})
- predicates.append({'name': 'PodToleratesNodeTaints'})
- predicates.append({'name': 'CheckNodeMemoryPressure'})
-
- if short_version not in ['1.1', '1.2', '1.3']:
- predicates.append({'name': 'CheckNodeDiskPressure'})
- predicates.append({'name': 'MatchInterPodAffinity'})
+ # Predicates ordered according to OpenShift Origin source:
+ # origin/vendor/k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go
+
+ if short_version == '1.1':
+ predicates.extend([
+ {'name': 'PodFitsHostPorts'},
+ {'name': 'PodFitsResources'},
+ {'name': 'NoDiskConflict'},
+ {'name': 'MatchNodeSelector'},
+ ])
+
+ if short_version == '1.2':
+ predicates.extend([
+ {'name': 'PodFitsHostPorts'},
+ {'name': 'PodFitsResources'},
+ {'name': 'NoDiskConflict'},
+ {'name': 'NoVolumeZoneConflict'},
+ {'name': 'MatchNodeSelector'},
+ {'name': 'MaxEBSVolumeCount'},
+ {'name': 'MaxGCEPDVolumeCount'}
+ ])
+
+ if short_version == '1.3':
+ predicates.extend([
+ {'name': 'NoDiskConflict'},
+ {'name': 'NoVolumeZoneConflict'},
+ {'name': 'MaxEBSVolumeCount'},
+ {'name': 'MaxGCEPDVolumeCount'},
+ {'name': 'GeneralPredicates'},
+ {'name': 'PodToleratesNodeTaints'},
+ {'name': 'CheckNodeMemoryPressure'}
+ ])
+
+ if short_version == '1.4':
+ predicates.extend([
+ {'name': 'NoDiskConflict'},
+ {'name': 'NoVolumeZoneConflict'},
+ {'name': 'MaxEBSVolumeCount'},
+ {'name': 'MaxGCEPDVolumeCount'},
+ {'name': 'GeneralPredicates'},
+ {'name': 'PodToleratesNodeTaints'},
+ {'name': 'CheckNodeMemoryPressure'},
+ {'name': 'CheckNodeDiskPressure'},
+ {'name': 'MatchInterPodAffinity'}
+ ])
+
+ if short_version in ['1.5', '1.6']:
+ predicates.extend([
+ {'name': 'NoVolumeZoneConflict'},
+ {'name': 'MaxEBSVolumeCount'},
+ {'name': 'MaxGCEPDVolumeCount'},
+ {'name': 'MatchInterPodAffinity'},
+ {'name': 'NoDiskConflict'},
+ {'name': 'GeneralPredicates'},
+ {'name': 'PodToleratesNodeTaints'},
+ {'name': 'CheckNodeMemoryPressure'},
+ {'name': 'CheckNodeDiskPressure'},
+ ])
if regions_enabled:
region_predicate = {
diff --git a/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py b/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py
index 36022597f..95ace7923 100644
--- a/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py
+++ b/roles/openshift_master_facts/lookup_plugins/openshift_master_facts_default_priorities.py
@@ -11,11 +11,7 @@ class LookupModule(LookupBase):
def run(self, terms, variables=None, zones_enabled=True, short_version=None,
deployment_type=None, **kwargs):
- priorities = [
- {'name': 'LeastRequestedPriority', 'weight': 1},
- {'name': 'BalancedResourceAllocation', 'weight': 1},
- {'name': 'SelectorSpreadPriority', 'weight': 1}
- ]
+ priorities = []
if short_version is None or deployment_type is None:
if 'openshift' not in variables:
@@ -57,18 +53,51 @@ class LookupModule(LookupBase):
# convert short_version to origin short_version
short_version = re.sub('^3.', '1.', short_version)
- if short_version == '1.4':
- priorities.append({'name': 'NodePreferAvoidPodsPriority', 'weight': 10000})
-
- # only 1.1 didn't include NodeAffinityPriority
- if short_version != '1.1':
- priorities.append({'name': 'NodeAffinityPriority', 'weight': 1})
+ if short_version == '1.1':
+ priorities.extend([
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'SelectorSpreadPriority', 'weight': 1}
+ ])
+
+ if short_version == '1.2':
+ priorities.extend([
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'SelectorSpreadPriority', 'weight': 1},
+ {'name': 'NodeAffinityPriority', 'weight': 1}
+ ])
+
+ if short_version == '1.3':
+ priorities.extend([
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'SelectorSpreadPriority', 'weight': 1},
+ {'name': 'NodeAffinityPriority', 'weight': 1},
+ {'name': 'TaintTolerationPriority', 'weight': 1}
+ ])
- if short_version not in ['1.1', '1.2']:
- priorities.append({'name': 'TaintTolerationPriority', 'weight': 1})
-
- if short_version not in ['1.1', '1.2', '1.3']:
- priorities.append({'name': 'InterPodAffinityPriority', 'weight': 1})
+ if short_version == '1.4':
+ priorities.extend([
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'SelectorSpreadPriority', 'weight': 1},
+ {'name': 'NodePreferAvoidPodsPriority', 'weight': 10000},
+ {'name': 'NodeAffinityPriority', 'weight': 1},
+ {'name': 'TaintTolerationPriority', 'weight': 1},
+ {'name': 'InterPodAffinityPriority', 'weight': 1}
+ ])
+
+ if short_version in ['1.5', '1.6']:
+ priorities.extend([
+ {'name': 'SelectorSpreadPriority', 'weight': 1},
+ {'name': 'InterPodAffinityPriority', 'weight': 1},
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'NodePreferAvoidPodsPriority', 'weight': 10000},
+ {'name': 'NodeAffinityPriority', 'weight': 1},
+ {'name': 'TaintTolerationPriority', 'weight': 1}
+ ])
if zones_enabled:
zone_priority = {
diff --git a/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py b/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py
index 07bac6826..68b6deb88 100644
--- a/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py
+++ b/roles/openshift_master_facts/test/openshift_master_facts_default_predicates_tests.py
@@ -9,6 +9,9 @@ sys.path = [os.path.abspath(os.path.dirname(__file__) + "/../lookup_plugins/")]
from openshift_master_facts_default_predicates import LookupModule # noqa: E402
+# Predicates ordered according to OpenShift Origin source:
+# origin/vendor/k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go
+
DEFAULT_PREDICATES_1_1 = [
{'name': 'PodFitsHostPorts'},
{'name': 'PodFitsResources'},
@@ -48,6 +51,18 @@ DEFAULT_PREDICATES_1_4 = [
{'name': 'MatchInterPodAffinity'}
]
+DEFAULT_PREDICATES_1_5 = [
+ {'name': 'NoVolumeZoneConflict'},
+ {'name': 'MaxEBSVolumeCount'},
+ {'name': 'MaxGCEPDVolumeCount'},
+ {'name': 'MatchInterPodAffinity'},
+ {'name': 'NoDiskConflict'},
+ {'name': 'GeneralPredicates'},
+ {'name': 'PodToleratesNodeTaints'},
+ {'name': 'CheckNodeMemoryPressure'},
+ {'name': 'CheckNodeDiskPressure'},
+]
+
REGION_PREDICATE = {
'name': 'Region',
'argument': {
@@ -66,10 +81,10 @@ TEST_VARS = [
('3.3', 'openshift-enterprise', DEFAULT_PREDICATES_1_3),
('1.4', 'origin', DEFAULT_PREDICATES_1_4),
('3.4', 'openshift-enterprise', DEFAULT_PREDICATES_1_4),
- ('1.5', 'origin', DEFAULT_PREDICATES_1_4),
- ('3.5', 'openshift-enterprise', DEFAULT_PREDICATES_1_4),
- ('1.6', 'origin', DEFAULT_PREDICATES_1_4),
- ('3.6', 'openshift-enterprise', DEFAULT_PREDICATES_1_4),
+ ('1.5', 'origin', DEFAULT_PREDICATES_1_5),
+ ('3.5', 'openshift-enterprise', DEFAULT_PREDICATES_1_5),
+ ('1.6', 'origin', DEFAULT_PREDICATES_1_5),
+ ('3.6', 'openshift-enterprise', DEFAULT_PREDICATES_1_5),
]
diff --git a/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py b/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py
index 5427a07a1..4e44a2b3d 100644
--- a/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py
+++ b/roles/openshift_master_facts/test/openshift_master_facts_default_priorities_tests.py
@@ -40,6 +40,16 @@ DEFAULT_PRIORITIES_1_4 = [
{'name': 'InterPodAffinityPriority', 'weight': 1}
]
+DEFAULT_PRIORITIES_1_5 = [
+ {'name': 'SelectorSpreadPriority', 'weight': 1},
+ {'name': 'InterPodAffinityPriority', 'weight': 1},
+ {'name': 'LeastRequestedPriority', 'weight': 1},
+ {'name': 'BalancedResourceAllocation', 'weight': 1},
+ {'name': 'NodePreferAvoidPodsPriority', 'weight': 10000},
+ {'name': 'NodeAffinityPriority', 'weight': 1},
+ {'name': 'TaintTolerationPriority', 'weight': 1}
+]
+
ZONE_PRIORITY = {
'name': 'Zone',
'argument': {
@@ -58,7 +68,11 @@ TEST_VARS = [
('1.3', 'origin', DEFAULT_PRIORITIES_1_3),
('3.3', 'openshift-enterprise', DEFAULT_PRIORITIES_1_3),
('1.4', 'origin', DEFAULT_PRIORITIES_1_4),
- ('3.4', 'openshift-enterprise', DEFAULT_PRIORITIES_1_4)
+ ('3.4', 'openshift-enterprise', DEFAULT_PRIORITIES_1_4),
+ ('1.5', 'origin', DEFAULT_PRIORITIES_1_5),
+ ('3.5', 'openshift-enterprise', DEFAULT_PRIORITIES_1_5),
+ ('1.6', 'origin', DEFAULT_PRIORITIES_1_5),
+ ('3.6', 'openshift-enterprise', DEFAULT_PRIORITIES_1_5),
]
diff --git a/utils/src/ooinstall/cli_installer.py b/utils/src/ooinstall/cli_installer.py
index 0bc9aa45e..b787741d7 100644
--- a/utils/src/ooinstall/cli_installer.py
+++ b/utils/src/ooinstall/cli_installer.py
@@ -25,33 +25,15 @@ QUIET_ANSIBLE_CONFIG = '/usr/share/atomic-openshift-utils/ansible-quiet.cfg'
DEFAULT_PLAYBOOK_DIR = '/usr/share/ansible/openshift-ansible/'
UPGRADE_MAPPINGS = {
- '3.0': {
- 'minor_version': '3.0',
- 'minor_playbook': 'v3_0_minor/upgrade.yml',
- 'major_version': '3.1',
- 'major_playbook': 'v3_0_to_v3_1/upgrade.yml',
- },
- '3.1': {
- 'minor_version': '3.1',
- 'minor_playbook': 'v3_1_minor/upgrade.yml',
- 'major_playbook': 'v3_1_to_v3_2/upgrade.yml',
- 'major_version': '3.2',
- },
- '3.2': {
- 'minor_version': '3.2',
- 'minor_playbook': 'v3_2/upgrade.yml',
- 'major_playbook': 'v3_3/upgrade.yml',
- 'major_version': '3.3',
- },
- '3.3': {
- 'minor_version': '3.3',
- 'minor_playbook': 'v3_3/upgrade.yml',
- 'major_playbook': 'v3_4/upgrade.yml',
- 'major_version': '3.4',
- },
'3.4': {
'minor_version': '3.4',
'minor_playbook': 'v3_4/upgrade.yml',
+ 'major_playbook': 'v3_5/upgrade.yml',
+ 'major_version': '3.5',
+ },
+ '3.5': {
+ 'minor_version': '3.5',
+ 'minor_playbook': 'v3_5/upgrade.yml',
},
}