소스 검색

Merge branch 'master' into vsphere-registry

Davis Phillips 7 년 전
부모
커밋
d9ef6abf1d
100개의 변경된 파일1712개의 추가작업 그리고 1124개의 파일을 삭제
  1. 1 1
      .tito/packages/openshift-ansible
  2. 5 0
      .tito/releasers.conf
  3. 1 0
      README.md
  4. 3 4
      images/installer/Dockerfile
  5. 24 11
      inventory/hosts.example
  6. 98 9
      openshift-ansible.spec
  7. 1 0
      playbooks/byo/openshift-cluster/upgrades/README.md
  8. 0 20
      playbooks/byo/openshift-cluster/upgrades/v3_8/README.md
  9. 0 5
      playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade.yml
  10. 0 14
      playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade_control_plane.yml
  11. 0 7
      playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade_nodes.yml
  12. 0 1
      playbooks/common/openshift-cluster/upgrades/atomic-openshift-master.j2
  13. 0 1
      playbooks/common/openshift-cluster/upgrades/docker-cluster
  14. 0 1
      playbooks/common/openshift-cluster/upgrades/native-cluster
  15. 0 1
      playbooks/common/openshift-cluster/upgrades/openshift.docker.node.dep.service
  16. 0 1
      playbooks/common/openshift-cluster/upgrades/openshift.docker.node.service
  17. 0 1
      playbooks/common/openshift-cluster/upgrades/openvswitch-avoid-oom.conf
  18. 0 1
      playbooks/common/openshift-cluster/upgrades/openvswitch.docker.service
  19. 0 1
      playbooks/common/openshift-cluster/upgrades/openvswitch.sysconfig.j2
  20. 2 7
      playbooks/common/openshift-cluster/upgrades/post_control_plane.yml
  21. 1 1
      playbooks/common/openshift-cluster/upgrades/pre/verify_upgrade_targets.yml
  22. 21 0
      playbooks/common/openshift-cluster/upgrades/upgrade_components.yml
  23. 3 320
      playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml
  24. 10 0
      playbooks/common/openshift-cluster/upgrades/v3_7/upgrade.yml
  25. 10 0
      playbooks/common/openshift-cluster/upgrades/v3_7/upgrade_control_plane.yml
  26. 0 20
      playbooks/common/openshift-cluster/upgrades/v3_8/master_config_upgrade.yml
  27. 0 1
      playbooks/common/openshift-cluster/upgrades/v3_8/roles
  28. 0 56
      playbooks/common/openshift-cluster/upgrades/v3_8/upgrade.yml
  29. 0 67
      playbooks/common/openshift-cluster/upgrades/v3_8/upgrade_control_plane.yml
  30. 0 38
      playbooks/common/openshift-cluster/upgrades/v3_8/upgrade_nodes.yml
  31. 9 0
      playbooks/common/openshift-cluster/upgrades/v3_9/upgrade_control_plane.yml
  32. 16 1
      playbooks/gcp/openshift-cluster/build_base_image.yml
  33. 10 0
      playbooks/gcp/openshift-cluster/build_image.yml
  34. 1 0
      playbooks/init/basic_facts.yml
  35. 4 2
      playbooks/openshift-etcd/private/scaleup.yml
  36. 27 4
      playbooks/openshift-grafana/private/config.yml
  37. 10 0
      playbooks/openshift-grafana/private/uninstall.yml
  38. 2 0
      playbooks/openshift-grafana/uninstall.yml
  39. 1 3
      playbooks/openshift-hosted/private/redeploy-router-certificates.yml
  40. 1 0
      playbooks/openshift-master/private/config.yml
  41. 0 0
      playbooks/openshift-master/private/create_service_signer_cert.yml
  42. 0 0
      playbooks/openshift-master/private/files/shared_resource_viewer_role.yaml
  43. 336 0
      playbooks/openshift-master/private/upgrade.yml
  44. 3 1
      playbooks/openshift-master/private/validate_restart.yml
  45. 1 0
      playbooks/openshift-node/private/configure_nodes.yml
  46. 1 0
      playbooks/openshift-node/private/containerized_nodes.yml
  47. 12 0
      playbooks/openstack/README.md
  48. 114 42
      playbooks/openstack/inventory.py
  49. 15 0
      playbooks/openstack/openshift-cluster/install.yml
  50. 3 23
      playbooks/openstack/openshift-cluster/provision.yml
  51. 44 0
      playbooks/openstack/sample-inventory/group_vars/all.yml
  52. 6 0
      roles/ansible_service_broker/tasks/install.yml
  53. 12 0
      roles/container_runtime/defaults/main.yml
  54. 2 0
      roles/container_runtime/tasks/systemcontainer_docker.yml
  55. 8 1
      roles/kuryr/defaults/main.yaml
  56. 1 1
      roles/kuryr/templates/cni-daemonset.yaml.j2
  57. 3 23
      roles/kuryr/templates/configmap.yaml.j2
  58. 1 1
      roles/kuryr/templates/controller-deployment.yaml.j2
  59. 25 8
      roles/lib_openshift/library/oc_adm_ca_server_cert.py
  60. 25 8
      roles/lib_openshift/library/oc_adm_csr.py
  61. 25 8
      roles/lib_openshift/library/oc_adm_manage_node.py
  62. 25 8
      roles/lib_openshift/library/oc_adm_policy_group.py
  63. 25 8
      roles/lib_openshift/library/oc_adm_policy_user.py
  64. 25 8
      roles/lib_openshift/library/oc_adm_registry.py
  65. 25 8
      roles/lib_openshift/library/oc_adm_router.py
  66. 25 8
      roles/lib_openshift/library/oc_clusterrole.py
  67. 25 8
      roles/lib_openshift/library/oc_configmap.py
  68. 52 16
      roles/lib_openshift/library/oc_edit.py
  69. 25 8
      roles/lib_openshift/library/oc_env.py
  70. 25 8
      roles/lib_openshift/library/oc_group.py
  71. 25 8
      roles/lib_openshift/library/oc_image.py
  72. 25 8
      roles/lib_openshift/library/oc_label.py
  73. 41 12
      roles/lib_openshift/library/oc_obj.py
  74. 25 8
      roles/lib_openshift/library/oc_objectvalidator.py
  75. 25 8
      roles/lib_openshift/library/oc_process.py
  76. 25 8
      roles/lib_openshift/library/oc_project.py
  77. 25 8
      roles/lib_openshift/library/oc_pvc.py
  78. 25 8
      roles/lib_openshift/library/oc_route.py
  79. 25 8
      roles/lib_openshift/library/oc_scale.py
  80. 25 8
      roles/lib_openshift/library/oc_secret.py
  81. 25 8
      roles/lib_openshift/library/oc_service.py
  82. 25 8
      roles/lib_openshift/library/oc_serviceaccount.py
  83. 25 8
      roles/lib_openshift/library/oc_serviceaccount_secret.py
  84. 25 8
      roles/lib_openshift/library/oc_storageclass.py
  85. 25 8
      roles/lib_openshift/library/oc_user.py
  86. 25 8
      roles/lib_openshift/library/oc_version.py
  87. 25 8
      roles/lib_openshift/library/oc_volume.py
  88. 4 1
      roles/lib_openshift/src/ansible/oc_edit.py
  89. 2 1
      roles/lib_openshift/src/ansible/oc_obj.py
  90. 17 7
      roles/lib_openshift/src/class/oc_edit.py
  91. 8 3
      roles/lib_openshift/src/class/oc_obj.py
  92. 6 0
      roles/lib_openshift/src/doc/edit
  93. 6 0
      roles/lib_openshift/src/doc/obj
  94. 25 8
      roles/lib_openshift/src/lib/base.py
  95. 18 1
      roles/lib_utils/action_plugins/sanity_checks.py
  96. 18 43
      roles/lib_utils/filter_plugins/oo_filters.py
  97. 1 1
      roles/lib_utils/library/repoquery.py
  98. 11 61
      roles/lib_utils/lookup_plugins/openshift_master_facts_default_predicates.py
  99. 5 63
      roles/lib_utils/lookup_plugins/openshift_master_facts_default_priorities.py
  100. 0 0
      roles/lib_utils/src/class/repoquery.py

+ 1 - 1
.tito/packages/openshift-ansible

@@ -1 +1 @@
-3.9.0-0.48.0 ./
+3.10.0-0.1.0 ./

+ 5 - 0
.tito/releasers.conf

@@ -52,6 +52,11 @@ releaser = tito.release.DistGitReleaser
 branches = rhaos-3.9-rhel-7
 srpm_disttag = .el7aos
 
+[aos-3.10]
+releaser = tito.release.DistGitReleaser
+branches = rhaos-3.10-rhel-7
+srpm_disttag = .el7aos
+
 [copr-openshift-ansible]
 releaser = tito.release.CoprReleaser
 project_name = @OpenShiftOnlineOps/openshift-ansible

+ 1 - 0
README.md

@@ -79,6 +79,7 @@ Additional requirements:
 Logging:
 
 - java-1.8.0-openjdk-headless
+- patch
 
 Metrics:
 

+ 3 - 4
images/installer/Dockerfile

@@ -10,12 +10,11 @@ COPY images/installer/origin-extra-root /
 # install ansible and deps
 RUN INSTALL_PKGS="python-lxml python-dns pyOpenSSL python2-cryptography openssl java-1.8.0-openjdk-headless python2-passlib httpd-tools openssh-clients origin-clients" \
  && yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS \
- && EPEL_PKGS="ansible python2-boto python2-boto3 python2-crypto google-cloud-sdk-183.0.0 which" \
+ && EPEL_PKGS="ansible python2-boto python2-boto3 python2-crypto google-cloud-sdk-183.0.0 which python2-pip.noarch" \
  && yum install -y epel-release \
  && yum install -y --setopt=tsflags=nodocs $EPEL_PKGS \
- && EPEL_TESTING_PKGS="python2-libcloud" \
- && yum install -y --enablerepo=epel-testing --setopt=tsflags=nodocs $EPEL_TESTING_PKGS \
- && rpm -V $INSTALL_PKGS $EPEL_PKGS $EPEL_TESTING_PKGS \
+ && rpm -V $INSTALL_PKGS $EPEL_PKGS \
+ && pip install apache-libcloud~=2.2.1 \
  && yum clean all
 
 LABEL name="openshift/origin-ansible" \

+ 24 - 11
inventory/hosts.example

@@ -51,7 +51,7 @@ openshift_deployment_type=origin
 # use this to lookup the latest exact version of the container images, which is the tag actually used to configure
 # the cluster. For RPM installations we just verify the version detected in your configured repos matches this
 # release.
-openshift_release=v3.7
+openshift_release=v3.9
 
 # default subdomain to use for exposed routes, you should have wildcard dns
 # for *.apps.test.example.com that points at your infra nodes which will run
@@ -73,12 +73,12 @@ debug_level=2
 # Specify an exact container image tag to install or configure.
 # WARNING: This value will be used for all hosts in containerized environments, even those that have another version installed.
 # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up.
-#openshift_image_tag=v3.7.0
+#openshift_image_tag=v3.9.0
 
 # Specify an exact rpm version to install or configure.
 # WARNING: This value will be used for all hosts in RPM based environments, even those that have another version installed.
 # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up.
-#openshift_pkg_version=-3.7.0
+#openshift_pkg_version=-3.9.0
 
 # This enables all the system containers except for docker:
 #openshift_use_system_containers=False
@@ -324,6 +324,13 @@ debug_level=2
 #
 # GCE
 #openshift_cloudprovider_kind=gce
+# Note: When using GCE, openshift_gcp_project and openshift_gcp_prefix must be
+# defined.
+# openshift_gcp_project is the project-id
+#openshift_gcp_project=
+# openshift_gcp_prefix is a unique string to identify each openshift cluster.
+#openshift_gcp_prefix=
+#openshift_gcp_multizone=False
 #
 # vSphere
 #openshift_cloudprovider_kind=vsphere
@@ -558,6 +565,12 @@ debug_level=2
 # openshift_hosted_registry_storage_access_modes=['ReadWriteOnce']
 # openshift_hosted_registry_storage_annotations=['volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/vsphere-volume']
 #
+# GCS Storage Bucket
+#openshift_hosted_registry_storage_provider=gcs
+#openshift_hosted_registry_storage_gcs_bucket=bucket01
+#openshift_hosted_registry_storage_gcs_keyfile=test.key
+#openshift_hosted_registry_storage_gcs_rootdirectory=/registry
+
 # Metrics deployment
 # See: https://docs.openshift.com/enterprise/latest/install_config/cluster_metrics.html
 #
@@ -613,10 +626,10 @@ debug_level=2
 #openshift_metrics_hawkular_hostname=hawkular-metrics.example.com
 # Configure the prefix and version for the component images
 #openshift_metrics_image_prefix=docker.io/openshift/origin-
-#openshift_metrics_image_version=v3.7
+#openshift_metrics_image_version=v3.9
 # when openshift_deployment_type=='openshift-enterprise'
 #openshift_metrics_image_prefix=registry.access.redhat.com/openshift3/
-#openshift_metrics_image_version=v3.7
+#openshift_metrics_image_version=v3.9
 #
 # StorageClass
 # openshift_storageclass_name=gp2
@@ -675,10 +688,10 @@ debug_level=2
 #openshift_logging_es_cluster_size=1
 # Configure the prefix and version for the component images
 #openshift_logging_image_prefix=docker.io/openshift/origin-
-#openshift_logging_image_version=v3.7.0
+#openshift_logging_image_version=v3.9.0
 # when openshift_deployment_type=='openshift-enterprise'
 #openshift_logging_image_prefix=registry.access.redhat.com/openshift3/
-#openshift_logging_image_version=3.7.0
+#openshift_logging_image_version=3.9.0
 
 # Prometheus deployment
 #
@@ -813,9 +826,9 @@ debug_level=2
 #openshift_master_console_port=8443
 
 # set exact RPM version (include - prefix)
-#openshift_pkg_version=-3.6.0
+#openshift_pkg_version=-3.9.0
 # you may also specify version and release, ie:
-#openshift_pkg_version=-3.7.0-0.126.0.git.0.9351aae.el7
+#openshift_pkg_version=-3.9.0-0.126.0.git.0.9351aae.el7
 
 # Configure custom ca certificate
 #openshift_master_ca_certificate={'certfile': '/path/to/ca.crt', 'keyfile': '/path/to/ca.key'}
@@ -972,10 +985,10 @@ debug_level=2
 #openshift_service_catalog_image_prefix=docker.io/openshift/origin-
 #openshift_service_catalog_image_prefix=registry.access.redhat.com/openshift3/ose-
 # Force a specific image version to use when pulling the service catalog image
-#openshift_service_catalog_image_version=v3.7
+#openshift_service_catalog_image_version=v3.9
 
 # TSB image tag
-#template_service_broker_version='v3.7'
+#template_service_broker_version='v3.9'
 
 # Configure one of more namespaces whose templates will be served by the TSB
 #openshift_template_service_broker_namespaces=['openshift']

+ 98 - 9
openshift-ansible.spec

@@ -9,8 +9,8 @@
 %global __requires_exclude ^/usr/bin/ansible-playbook$
 
 Name:           openshift-ansible
-Version:        3.9.0
-Release:        0.48.0%{?dist}
+Version:        3.10.0
+Release:        0.1.0%{?dist}
 Summary:        Openshift and Atomic Enterprise Ansible
 License:        ASL 2.0
 URL:            https://github.com/openshift/openshift-ansible
@@ -29,6 +29,7 @@ Requires:      httpd-tools
 Requires:      libselinux-python
 Requires:      python-passlib
 Requires:      python2-crypto
+Requires:      patch
 
 %description
 Openshift and Atomic Enterprise Ansible
@@ -201,6 +202,94 @@ Atomic OpenShift Utilities includes
 
 
 %changelog
+* Wed Feb 28 2018 Scott Dodson <sdodson@redhat.com> 3.10.0-0.1.0
+- Adding 3.10 releaser (jupierce@redhat.com)
+- Add inventory docs for gcp variables (mgugino@redhat.com)
+- Add prometheus node-exporter (aweiteka@redhat.com)
+- hosts.example: use 3.9 versions in sample inventory file
+  (vrutkovs@redhat.com)
+- upgrade: skip restart during double upgrade (vrutkovs@redhat.com)
+- gcp: Move provisioning of SSH key into separate task
+  (chance.zibolski@coreos.com)
+- fix when logging metrics user is modified (jcantril@redhat.com)
+- bug 1537857. Additional logging proxy metrics fixes (jcantril@redhat.com)
+- changed logic due to failures in CI (davis.phillips@gmail.com)
+- ntpd/chronyd will now be started before node/master services
+  (fabian@fabianism.us)
+- Add service catalog components to upgrade (mgugino@redhat.com)
+- Add registry GCS storage to hosts.example (sdodson@redhat.com)
+- Remove no_log: True from openshift_version calls (sdodson@redhat.com)
+- docker: support ADDTL_MOUNTS (gscrivan@redhat.com)
+- refactor grafana role (m.judeikis@gmail.com)
+- Remove v3_8 upgrade playbooks (vrutkovs@redhat.com)
+- Dump verbose curl output and API logs when API doesn't become available.
+  (abutcher@redhat.com)
+- Start master API in parallel on all masters. (abutcher@redhat.com)
+- Update glusterfs-template:  - Add GB_LOGDIR  - failureThreshold as 50 secs
+  (sarumuga@redhat.com)
+- Don't upgrade master nodes during double upgrade (vrutkovs@redhat.com)
+- Don't upgrade nodes for OCP 3.8 (vrutkovs@redhat.com)
+- sanity_checks: warn that some OCP versions cannot be installed
+  (vrutkovs@redhat.com)
+- repo_query: always include package_name in results (vrutkovs@redhat.com)
+- Update upgrade README and add 3.7.x -> 3.9.x entry (vrutkovs@redhat.com)
+- Remove unused tasks upgrade_facts in openshift_master (mgugino@redhat.com)
+- Remove set_fact usage from web-console role (mgugino@redhat.com)
+- Retrieve node list from API when testing for nodes with selector.
+  (abutcher@redhat.com)
+- Update controller port to match containerPort (jpeeler@redhat.com)
+- Fix way openshift_openstack_nodes_to_remove parameter is parsed in template
+  (tzumainn@redhat.com)
+- logging: update README about cri-o (jwozniak@redhat.com)
+- Bug 1536651 - logging-mux not working in 3.7.z when logging installed with
+  openshift_logging_use_mux=true (nhosoi@redhat.com)
+- vsphere svc fix upgrade and datastore fix (davis.phillips@gmail.com)
+- logging: allow fluentd to determine cri-o (jwozniak@redhat.com)
+- add generic image-and-flavor check that verifies existence and compatibility
+  (tzumainn@redhat.com)
+
+* Sun Feb 25 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.53.0
+- 
+
+* Sun Feb 25 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.52.0
+- Move journald setup to node tasks from master (nakayamakenjiro@gmail.com)
+- [BZ 1497408] delete config map, dameon set, and cluster role
+  (john.sanda@gmail.com)
+- Fix aggregator relative paths (mgugino@redhat.com)
+- Fix package tasks ordering in OpenStack playbooks (tomas@sedovic.cz)
+- Change openshift_release to openshift_upgrade_target in upgrade
+  (mgugino@redhat.com)
+- Normalize times we wait on pods to 10s * 60retries (sdodson@redhat.com)
+- start_api_server: service catalog healthcheck doesn't require proxy
+  (vrutkovs@redhat.com)
+- Changing default of openshift_logging_public_master_url to use
+  openshift_master_cluster_public_hostname if available (ewolinet@redhat.com)
+- Sync v3.8 content (sdodson@redhat.com)
+- Sync v3.7 content (sdodson@redhat.com)
+- Sync v3.9 content (sdodson@redhat.com)
+- Allow branch specific pulls from origin (sdodson@redhat.com)
+- Fixing bz1540467 docker-registry env var migration. Adding ability to oc_edit
+  complex array style edits. (kwoodson@redhat.com)
+- [1537872] Adding seboolean for virt_use_samba (kwoodson@redhat.com)
+- Making patching a local_action and ensuring we become:false for local_actions
+  (ewolinet@redhat.com)
+- Cast string to dict in lib_utils_oo_dict_to_keqv_list (mgugino@redhat.com)
+- refine condition for doing ami fetching (jdiaz@redhat.com)
+- Add field_selector parameter to oc_obj. (abutcher@redhat.com)
+- GlusterFS: Check for groups in template file (jarrpa@redhat.com)
+- Updating AMI copying tags to no longer default to parent AMI.
+  (kwoodson@redhat.com)
+- Remove NoVolumeNodeConflict from 3.9+ (sdodson@redhat.com)
+
+* Fri Feb 23 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.51.0
+- 
+
+* Thu Feb 22 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.50.0
+- Fix upgrade verify_upgrade_targets (mgugino@redhat.com)
+- Ensure wire-aggregator run on 3.7 upgrades (mgugino@redhat.com)
+- Add no_log to prevent printing AWS creds (sedgar@redhat.com)
+- added ci inventory and groups for containerized (mgugino@redhat.com)
+
 * Thu Feb 22 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.48.0
 - Fix openshift_hosted_registry_storage_glusterfs_path (mgugino@redhat.com)
 - Revert openshift_portal_net (mgugino@redhat.com)
@@ -269,10 +358,10 @@ Atomic OpenShift Utilities includes
   (fabian@fabianism.us)
 
 * Thu Feb 15 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.45.0
-- 
+-
 
 * Thu Feb 15 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.44.0
-- 
+-
 
 * Thu Feb 15 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.43.0
 - Changing conditional_set_fact from module to action_plugin since it does not
@@ -497,7 +586,7 @@ Atomic OpenShift Utilities includes
   (rteague@redhat.com)
 
 * Tue Jan 30 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.33.0
-- 
+-
 
 * Tue Jan 30 2018 Justin Pierce <jupierce@redhat.com> 3.9.0-0.32.0
 - Revert "Revert "use non-deprecated REGISTRY_OPENSHIFT_SERVER_ADDR variable to
@@ -712,10 +801,10 @@ Atomic OpenShift Utilities includes
 - Clean up host-local IPAM data while nodes are drained (danw@redhat.com)
 
 * Fri Jan 12 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.19.0
-- 
+-
 
 * Fri Jan 12 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.18.0
-- 
+-
 
 * Fri Jan 12 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.17.0
 - Update latest image streams and templates (sdodson@redhat.com)
@@ -815,7 +904,7 @@ Atomic OpenShift Utilities includes
 - Add in-tree CI scripts (mgugino@redhat.com)
 
 * Wed Jan 03 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.15.0
-- 
+-
 
 * Wed Jan 03 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.14.0
 - Cast openshift_docker_use_system_container to bool (mgugino@redhat.com)
@@ -834,7 +923,7 @@ Atomic OpenShift Utilities includes
   (mgugino@redhat.com)
 
 * Tue Jan 02 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.12.0
-- 
+-
 
 * Mon Jan 01 2018 Jenkins CD Merge Bot <smunilla@redhat.com> 3.9.0-0.11.0
 - aws: Fix misnamed variable in provisioning_vars.yml.example

+ 1 - 0
playbooks/byo/openshift-cluster/upgrades/README.md

@@ -4,5 +4,6 @@ cluster. Additional notes for the associated upgrade playbooks are
 provided in their respective directories.
 
 # Upgrades available
+- [OpenShift Container Platform 3.7 to 3.9](v3_6/README.md) (works also to upgrade OpenShift Origin from 3.7.x to 3.9.x)
 - [OpenShift Container Platform 3.6 to 3.7](v3_7/README.md) (works also to upgrade OpenShift Origin from 3.6.x to 3.7.x)
 - [OpenShift Container Platform 3.5 to 3.6](v3_6/README.md) (works also to upgrade OpenShift Origin from 1.5.x to 3.6.x)

+ 0 - 20
playbooks/byo/openshift-cluster/upgrades/v3_8/README.md

@@ -1,20 +0,0 @@
-# v3.8 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_8/upgrade.yml
-```

+ 0 - 5
playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade.yml

@@ -1,5 +0,0 @@
----
-#
-# Full Control Plane + Nodes Upgrade
-#
-- import_playbook: ../../../../common/openshift-cluster/upgrades/v3_8/upgrade.yml

+ 0 - 14
playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade_control_plane.yml

@@ -1,14 +0,0 @@
----
-#
-# 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.
-#
-- import_playbook: ../../../../common/openshift-cluster/upgrades/v3_8/upgrade_control_plane.yml

+ 0 - 7
playbooks/byo/openshift-cluster/upgrades/v3_8/upgrade_nodes.yml

@@ -1,7 +0,0 @@
----
-#
-# Node Upgrade Playbook
-#
-# Upgrades nodes only, but requires the control plane to have already been upgraded.
-#
-- import_playbook: ../../../../common/openshift-cluster/upgrades/v3_8/upgrade_nodes.yml

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/atomic-openshift-master.j2

@@ -1 +0,0 @@
-../../../../roles/openshift_master/templates/atomic-openshift-master.j2

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/docker-cluster

@@ -1 +0,0 @@
-../../../../roles/openshift_master/templates/docker-cluster

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/native-cluster

@@ -1 +0,0 @@
-../../../../roles/openshift_master/templates/native-cluster

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/openshift.docker.node.dep.service

@@ -1 +0,0 @@
-../../../../roles/openshift_node/templates/openshift.docker.node.dep.service

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/openshift.docker.node.service

@@ -1 +0,0 @@
-../../../../roles/openshift_node/templates/openshift.docker.node.service

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/openvswitch-avoid-oom.conf

@@ -1 +0,0 @@
-../../../../roles/openshift_node/templates/openvswitch-avoid-oom.conf

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/openvswitch.docker.service

@@ -1 +0,0 @@
-../../../../roles/openshift_node/templates/openvswitch.docker.service

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/openvswitch.sysconfig.j2

@@ -1 +0,0 @@
-../../../../roles/openshift_node/templates/openvswitch.sysconfig.j2

+ 2 - 7
playbooks/common/openshift-cluster/upgrades/post_control_plane.yml

@@ -115,7 +115,7 @@
 
   post_tasks:
   # Do not perform these tasks when the registry is insecure.  The default registry is insecure in openshift_hosted/defaults/main.yml
-  - when: not (openshift_docker_hosted_registry_insecure | default(True))
+  - when: not (openshift_docker_hosted_registry_insecure | default(False))
     block:
     # we need to migrate customers to the new pattern of pushing to the registry via dns
     # Step 1: verify the certificates have the docker registry service name
@@ -166,9 +166,4 @@
     when:
     - __shared_resource_viewer_protected | default(false)
 
-- name: Upgrade Service Catalog
-  hosts: oo_first_master
-  roles:
-  - role: openshift_service_catalog
-    when:
-    - openshift_enable_service_catalog | default(true) | bool
+- import_playbook: upgrade_components.yml

+ 1 - 1
playbooks/common/openshift-cluster/upgrades/pre/verify_upgrade_targets.yml

@@ -43,7 +43,7 @@
     fail:
       msg: "OpenShift {{ avail_openshift_version }} is available, but {{ openshift_upgrade_target }} or greater is required"
     when:
-    - (openshift_pkg_version | default('-0.0', True)).split('-')[1] is version_compare(openshift_release, '<')
+    - (openshift_pkg_version | default('-0.0', True)).split('-')[1] is version_compare(openshift_upgrade_target, '<')
 
 - name: Fail when openshift version does not meet minium requirement for Origin upgrade
   fail:

+ 21 - 0
playbooks/common/openshift-cluster/upgrades/upgrade_components.yml

@@ -0,0 +1,21 @@
+---
+- name: Upgrade Service Catalog
+  hosts: oo_first_master
+  vars:
+    first_master: "{{ groups.oo_first_master[0] }}"
+  tasks:
+  - import_role:
+      name: openshift_service_catalog
+      tasks_from: install.yml
+    when:
+    - openshift_enable_service_catalog | default(true) | bool
+  - import_role:
+      name: ansible_service_broker
+      tasks_from: install.yml
+    when:
+    - openshift_enable_service_catalog | default(true) | bool
+  - import_role:
+      name: template_service_broker
+      tasks_from: upgrade.yml
+    when:
+    - openshift_enable_service_catalog | default(true) | bool

+ 3 - 320
playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml

@@ -1,327 +1,10 @@
 ---
 ###############################################################################
-# Upgrade Masters
+# Upgrade Control Plane
 ###############################################################################
 
 - name: Backup and upgrade etcd
   import_playbook: ../../../openshift-etcd/private/upgrade_main.yml
 
-# Create service signer cert when missing. Service signer certificate
-# 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:
-  - name: Determine if service signer certificate must be created
-    stat:
-      path: "{{ openshift.common.config_base }}/master/service-signer.crt"
-    register: service_signer_cert_stat
-    changed_when: false
-
-- import_playbook: create_service_signer_cert.yml
-
-# oc adm migrate storage should be run prior to etcd v3 upgrade
-# See: https://github.com/openshift/origin/pull/14625#issuecomment-308467060
-- name: Pre master upgrade - Upgrade all storage
-  hosts: oo_first_master
-  roles:
-  - openshift_facts
-  tasks:
-  - name: Upgrade all storage
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      migrate storage --include=* --confirm
-    register: l_pb_upgrade_control_plane_pre_upgrade_storage
-    when: openshift_upgrade_pre_storage_migration_enabled | default(true) | bool
-    failed_when:
-    - l_pb_upgrade_control_plane_pre_upgrade_storage.rc != 0
-    - openshift_upgrade_pre_storage_migration_fatal | default(true) | bool
-
-# Set openshift_master_facts separately. In order to reconcile
-# admission_config's, we currently must run openshift_master_facts and
-# then run openshift_facts.
-- name: Set OpenShift master facts
-  hosts: oo_masters_to_config
-  roles:
-  - openshift_master_facts
-
-# The main master upgrade play. Should handle all changes to the system in one pass, with
-# support for optional hooks to be defined.
-- name: Upgrade master
-  hosts: oo_masters_to_config
-  serial: 1
-  roles:
-  - openshift_facts
-  tasks:
-  # Run the pre-upgrade hook if defined:
-  - debug: msg="Running master pre-upgrade hook {{ openshift_master_upgrade_pre_hook }}"
-    when: openshift_master_upgrade_pre_hook is defined
-
-  - include_tasks: "{{ openshift_master_upgrade_pre_hook }}"
-    when: openshift_master_upgrade_pre_hook is defined
-
-  - import_role:
-      name: openshift_master
-      tasks_from: upgrade.yml
-
-  - name: Setup and enable bootstrapping options
-    include_tasks: ../../../openshift-master/private/tasks/enable_bootstrap.yml
-    when: openshift_master_bootstrap_enabled | default(false) | bool
-
-  # Run the upgrade hook prior to restarting services/system if defined:
-  - debug: msg="Running master upgrade hook {{ openshift_master_upgrade_hook }}"
-    when: openshift_master_upgrade_hook is defined
-
-  - include_tasks: "{{ openshift_master_upgrade_hook }}"
-    when: openshift_master_upgrade_hook is defined
-
-  - name: Disable master controller
-    service:
-      name: "{{ openshift_service_type }}-master-controllers"
-      enabled: false
-    when: openshift.common.rolling_restart_mode == 'system'
-
-  - include_tasks: ../../../openshift-master/private/tasks/restart_hosts.yml
-    when: openshift.common.rolling_restart_mode == 'system'
-
-  - include_tasks: ../../../openshift-master/private/tasks/restart_services.yml
-    when: openshift.common.rolling_restart_mode == 'services'
-
-  # Run the post-upgrade hook if defined:
-  - debug: msg="Running master post-upgrade hook {{ openshift_master_upgrade_post_hook }}"
-    when: openshift_master_upgrade_post_hook is defined
-
-  - include_tasks: "{{ openshift_master_upgrade_post_hook }}"
-    when: openshift_master_upgrade_post_hook is defined
-
-  - name: Post master upgrade - Upgrade clusterpolicies storage
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      migrate storage --include=clusterpolicies --confirm
-    register: l_pb_upgrade_control_plane_post_upgrade_storage
-    when:
-    - openshift_upgrade_post_storage_migration_enabled | default(true) | bool
-    - openshift_version is version_compare('3.7','<')
-    failed_when:
-    - l_pb_upgrade_control_plane_post_upgrade_storage.rc != 0
-    - openshift_upgrade_post_storage_migration_fatal | default(false) | bool
-    run_once: true
-    delegate_to: "{{ groups.oo_first_master.0 }}"
-
-  - set_fact:
-      master_update_complete: True
-
-##############################################################################
-# Gate on master update complete
-##############################################################################
-- name: Gate on master update
-  hosts: localhost
-  connection: local
-  tasks:
-  - set_fact:
-      master_update_completed: "{{ hostvars
-                                 | lib_utils_oo_select_keys(groups.oo_masters_to_config)
-                                 | lib_utils_oo_collect('inventory_hostname', {'master_update_complete': true}) }}"
-  - set_fact:
-      master_update_failed: "{{ groups.oo_masters_to_config | difference(master_update_completed) | list }}"
-  - fail:
-      msg: "Upgrade cannot continue. The following masters did not finish updating: {{ master_update_failed | join(',') }}"
-    when: master_update_failed | length > 0
-
-###############################################################################
-# Reconcile Cluster Roles, Cluster Role Bindings and Security Context Constraints
-###############################################################################
-
-- name: Reconcile Cluster Roles and Cluster Role Bindings and Security Context Constraints
-  hosts: oo_masters_to_config
-  roles:
-  - { role: openshift_cli }
-  - { role: openshift_facts }
-  vars:
-    __master_shared_resource_viewer_file: "shared_resource_viewer_role.yaml"
-  tasks:
-  - name: Reconcile Cluster Roles
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      policy reconcile-cluster-roles --additive-only=true --confirm -o name
-    register: reconcile_cluster_role_result
-    when: openshift_version is version_compare('3.7','<')
-    changed_when:
-    - reconcile_cluster_role_result.stdout != ''
-    - reconcile_cluster_role_result.rc == 0
-    run_once: true
-
-  - name: Reconcile Cluster Role Bindings
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      policy reconcile-cluster-role-bindings
-      --exclude-groups=system:authenticated
-      --exclude-groups=system:authenticated:oauth
-      --exclude-groups=system:unauthenticated
-      --exclude-users=system:anonymous
-      --additive-only=true --confirm -o name
-    when: openshift_version is version_compare('3.7','<')
-    register: reconcile_bindings_result
-    changed_when:
-    - reconcile_bindings_result.stdout != ''
-    - reconcile_bindings_result.rc == 0
-    run_once: true
-
-  - name: Reconcile Jenkins Pipeline Role Bindings
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig policy reconcile-cluster-role-bindings system:build-strategy-jenkinspipeline --confirm -o name
-    run_once: true
-    register: reconcile_jenkins_role_binding_result
-    changed_when:
-    - reconcile_jenkins_role_binding_result.stdout != ''
-    - reconcile_jenkins_role_binding_result.rc == 0
-    when:
-    - openshift_version is version_compare('3.7','<')
-
-  - when: openshift_upgrade_target is version_compare('3.7','<')
-    block:
-    - name: Retrieve shared-resource-viewer
-      oc_obj:
-        state: list
-        kind: role
-        name: "shared-resource-viewer"
-        namespace: "openshift"
-      register: objout
-
-    - name: Determine if shared-resource-viewer is protected
-      set_fact:
-        __shared_resource_viewer_protected: true
-      when:
-      - "'results' in objout"
-      - "'results' in objout['results']"
-      - "'annotations' in objout['results']['results'][0]['metadata']"
-      - "'openshift.io/reconcile-protect' in objout['results']['results'][0]['metadata']['annotations']"
-      - "objout['results']['results'][0]['metadata']['annotations']['openshift.io/reconcile-protect'] == 'true'"
-    - copy:
-        src: "{{ item }}"
-        dest: "/tmp/{{ item }}"
-      with_items:
-      - "{{ __master_shared_resource_viewer_file }}"
-      when: __shared_resource_viewer_protected is not defined
-
-    - name: Fixup shared-resource-viewer role
-      oc_obj:
-        state: present
-        kind: role
-        name: "shared-resource-viewer"
-        namespace: "openshift"
-        files:
-        - "/tmp/{{ __master_shared_resource_viewer_file }}"
-        delete_after: true
-      when: __shared_resource_viewer_protected is not defined
-      register: result
-      retries: 3
-      delay: 5
-      until: result.rc == 0
-      ignore_errors: true
-
-
-  - name: Reconcile Security Context Constraints
-    command: >
-      {{ openshift_client_binary }} adm policy --config={{ openshift.common.config_base }}/master/admin.kubeconfig reconcile-sccs --confirm --additive-only=true -o name
-    register: reconcile_scc_result
-    changed_when:
-    - reconcile_scc_result.stdout != ''
-    - reconcile_scc_result.rc == 0
-    run_once: true
-
-  - name: Migrate storage post policy reconciliation
-    command: >
-      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      migrate storage --include=* --confirm
-    run_once: true
-    register: l_pb_upgrade_control_plane_post_upgrade_storage
-    when: openshift_upgrade_post_storage_migration_enabled | default(true) | bool
-    failed_when:
-    - l_pb_upgrade_control_plane_post_upgrade_storage.rc != 0
-    - openshift_upgrade_post_storage_migration_fatal | default(false) | bool
-
-  - set_fact:
-      reconcile_complete: True
-
-##############################################################################
-# Gate on reconcile
-##############################################################################
-- name: Gate on reconcile
-  hosts: localhost
-  connection: local
-  tasks:
-  - set_fact:
-      reconcile_completed: "{{ hostvars
-                                 | lib_utils_oo_select_keys(groups.oo_masters_to_config)
-                                 | lib_utils_oo_collect('inventory_hostname', {'reconcile_complete': true}) }}"
-  - set_fact:
-      reconcile_failed: "{{ groups.oo_masters_to_config | difference(reconcile_completed) | list }}"
-  - fail:
-      msg: "Upgrade cannot continue. The following masters did not finish reconciling: {{ reconcile_failed | join(',') }}"
-    when: reconcile_failed | length > 0
-
-- name: Upgrade Docker on dedicated containerized etcd hosts
-  hosts: oo_etcd_to_config:!oo_nodes_to_upgrade
-  serial: 1
-  any_errors_fatal: true
-  roles:
-  - openshift_facts
-  tasks:
-  - include_tasks: docker/tasks/upgrade.yml
-    when: l_docker_upgrade is defined and l_docker_upgrade | bool and not openshift_is_atomic | bool
-
-- name: Drain and upgrade master nodes
-  hosts: oo_masters_to_config:&oo_nodes_to_upgrade
-  # This var must be set with -e on invocation, as it is not a per-host inventory var
-  # and is evaluated early. Values such as "20%" can also be used.
-  serial: "{{ openshift_upgrade_control_plane_nodes_serial | default(1) }}"
-  max_fail_percentage: "{{ openshift_upgrade_control_plane_nodes_max_fail_percentage | default(0) }}"
-
-  pre_tasks:
-  - name: Load lib_openshift modules
-    import_role:
-      name: lib_openshift
-
-  # TODO: To better handle re-trying failed upgrades, it would be nice to check if the node
-  # or docker actually needs an upgrade before proceeding. Perhaps best to save this until
-  # we merge upgrade functionality into the base roles and a normal config.yml playbook run.
-  - name: Mark node unschedulable
-    oc_adm_manage_node:
-      node: "{{ openshift.node.nodename | lower }}"
-      schedulable: False
-    delegate_to: "{{ groups.oo_first_master.0 }}"
-    retries: 10
-    delay: 5
-    register: node_unschedulable
-    until: node_unschedulable is succeeded
-
-  - name: Drain Node for Kubelet upgrade
-    command: >
-      {{ hostvars[groups.oo_first_master.0]['first_master_client_binary'] }} adm drain {{ openshift.node.nodename | lower }}
-      --config={{ openshift.common.config_base }}/master/admin.kubeconfig
-      --force --delete-local-data --ignore-daemonsets
-      --timeout={{ openshift_upgrade_nodes_drain_timeout | default(0) }}s
-    delegate_to: "{{ groups.oo_first_master.0 }}"
-    register: l_upgrade_control_plane_drain_result
-    until: not (l_upgrade_control_plane_drain_result is failed)
-    retries: "{{ 1 if ( openshift_upgrade_nodes_drain_timeout | default(0) | int ) == 0 else 0 }}"
-    delay: 5
-    failed_when:
-    - l_upgrade_control_plane_drain_result is failed
-    - openshift_upgrade_nodes_drain_timeout | default(0) | int == 0
-
-  roles:
-  - openshift_facts
-  post_tasks:
-  - import_role:
-      name: openshift_node
-      tasks_from: upgrade_pre.yml
-  - import_role:
-      name: openshift_node
-      tasks_from: upgrade.yml
-  - import_role:
-      name: openshift_manage_node
-      tasks_from: config.yml
-    vars:
-      openshift_master_host: "{{ groups.oo_first_master.0 }}"
-      openshift_manage_node_is_master: true
+- name: Upgrade Masters
+  import_playbook: ../../../openshift-master/private/upgrade.yml

+ 10 - 0
playbooks/common/openshift-cluster/upgrades/v3_7/upgrade.yml

@@ -39,6 +39,16 @@
   vars:
     master_config_hook: "v3_7/master_config_upgrade.yml"
 
+# this must occur after the control plane is upgraded because systemd service
+# names are changed
+- name: Configure API aggregation on masters
+  hosts: oo_masters_to_config
+  serial: 1
+  roles:
+  - role: openshift_facts
+  tasks:
+  - include_tasks: ../../../../openshift-master/private/tasks/wire_aggregator.yml
+
 # All controllers must be stopped at the same time then restarted
 - name: Cycle all controller services to force new leader election mode
   hosts: oo_masters_to_config

+ 10 - 0
playbooks/common/openshift-cluster/upgrades/v3_7/upgrade_control_plane.yml

@@ -51,6 +51,16 @@
   vars:
     master_config_hook: "v3_7/master_config_upgrade.yml"
 
+# this must occur after the control plane is upgraded because systemd service
+# names are changed
+- name: Configure API aggregation on masters
+  hosts: oo_masters_to_config
+  serial: 1
+  roles:
+  - role: openshift_facts
+  tasks:
+  - include_tasks: ../../../../openshift-master/private/tasks/wire_aggregator.yml
+
 # All controllers must be stopped at the same time then restarted
 - name: Cycle all controller services to force new leader election mode
   hosts: oo_masters_to_config

+ 0 - 20
playbooks/common/openshift-cluster/upgrades/v3_8/master_config_upgrade.yml

@@ -1,20 +0,0 @@
----
-- modify_yaml:
-    dest: "{{ openshift.common.config_base}}/master/master-config.yaml"
-    yaml_key: 'controllerConfig.election.lockName'
-    yaml_value: 'openshift-master-controllers'
-
-- modify_yaml:
-    dest: "{{ openshift.common.config_base}}/master/master-config.yaml"
-    yaml_key: 'controllerConfig.serviceServingCert.signer.certFile'
-    yaml_value: service-signer.crt
-
-- modify_yaml:
-    dest: "{{ openshift.common.config_base}}/master/master-config.yaml"
-    yaml_key: 'controllerConfig.serviceServingCert.signer.keyFile'
-    yaml_value: service-signer.key
-
-- modify_yaml:
-    dest: "{{ openshift.common.config_base }}/master/master-config.yaml"
-    yaml_key: servingInfo.clientCA
-    yaml_value: ca.crt

+ 0 - 1
playbooks/common/openshift-cluster/upgrades/v3_8/roles

@@ -1 +0,0 @@
-../../../../../roles/

+ 0 - 56
playbooks/common/openshift-cluster/upgrades/v3_8/upgrade.yml

@@ -1,56 +0,0 @@
----
-#
-# Full Control Plane + Nodes Upgrade
-#
-- import_playbook: ../init.yml
-  tags:
-  - pre_upgrade
-
-- name: Configure the upgrade target for the common upgrade tasks
-  hosts: oo_all_hosts
-  tags:
-  - pre_upgrade
-  tasks:
-  - set_fact:
-      openshift_upgrade_target: '3.8'
-      openshift_upgrade_min: '3.7'
-
-- import_playbook: ../pre/config.yml
-  vars:
-    l_upgrade_repo_hosts: "oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config:oo_lb_to_config"
-    l_upgrade_no_proxy_hosts: "oo_masters_to_config:oo_nodes_to_upgrade"
-    l_upgrade_health_check_hosts: "oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config"
-    l_upgrade_verify_targets_hosts: "oo_masters_to_config:oo_nodes_to_upgrade"
-    l_upgrade_docker_target_hosts: "oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config"
-    l_upgrade_excluder_hosts: "oo_nodes_to_config:oo_masters_to_config"
-    openshift_protect_installed_version: False
-
-- name: Flag pre-upgrade checks complete for hosts without errors
-  hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config
-  tasks:
-  - set_fact:
-      pre_upgrade_complete: True
-
-# Pre-upgrade completed
-
-- import_playbook: ../upgrade_control_plane.yml
-
-# All controllers must be stopped at the same time then restarted
-- name: Cycle all controller services to force new leader election mode
-  hosts: oo_masters_to_config
-  gather_facts: no
-  roles:
-  - role: openshift_facts
-  tasks:
-  - name: Stop {{ openshift_service_type }}-master-controllers
-    systemd:
-      name: "{{ openshift_service_type }}-master-controllers"
-      state: stopped
-  - name: Start {{ openshift_service_type }}-master-controllers
-    systemd:
-      name: "{{ openshift_service_type }}-master-controllers"
-      state: started
-
-- import_playbook: ../upgrade_nodes.yml
-
-- import_playbook: ../post_control_plane.yml

+ 0 - 67
playbooks/common/openshift-cluster/upgrades/v3_8/upgrade_control_plane.yml

@@ -1,67 +0,0 @@
----
-#
-# 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.
-#
-- import_playbook: ../init.yml
-  vars:
-    l_upgrade_no_switch_firewall_hosts: "oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config"
-    l_init_fact_hosts: "oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config"
-  when: not skip_version_info | default(false)
-
-- name: Configure the upgrade target for the common upgrade tasks
-  hosts: oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config
-  tasks:
-  - set_fact:
-      openshift_upgrade_target: '3.8'
-      openshift_upgrade_min: '3.7'
-
-- import_playbook: ../pre/config.yml
-  # These vars a meant to exclude oo_nodes from plays that would otherwise include
-  # them by default.
-  vars:
-    l_openshift_version_set_hosts: "oo_etcd_to_config:oo_masters_to_config:!oo_first_master"
-    l_openshift_version_check_hosts: "oo_masters_to_config:!oo_first_master"
-    l_upgrade_repo_hosts: "oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config"
-    l_upgrade_no_proxy_hosts: "oo_masters_to_config"
-    l_upgrade_health_check_hosts: "oo_masters_to_config:oo_etcd_to_config:oo_lb_to_config"
-    l_upgrade_verify_targets_hosts: "oo_masters_to_config"
-    l_upgrade_docker_target_hosts: "oo_masters_to_config:oo_etcd_to_config"
-    l_upgrade_excluder_hosts: "oo_masters_to_config"
-    openshift_protect_installed_version: False
-
-- name: Flag pre-upgrade checks complete for hosts without errors
-  hosts: oo_masters_to_config:oo_etcd_to_config
-  tasks:
-  - set_fact:
-      pre_upgrade_complete: True
-
-# Pre-upgrade completed
-
-- import_playbook: ../upgrade_control_plane.yml
-
-# All controllers must be stopped at the same time then restarted
-- name: Cycle all controller services to force new leader election mode
-  hosts: oo_masters_to_config
-  gather_facts: no
-  roles:
-  - role: openshift_facts
-  tasks:
-  - name: Stop {{ openshift_service_type }}-master-controllers
-    systemd:
-      name: "{{ openshift_service_type }}-master-controllers"
-      state: stopped
-  - name: Start {{ openshift_service_type }}-master-controllers
-    systemd:
-      name: "{{ openshift_service_type }}-master-controllers"
-      state: started
-
-- import_playbook: ../post_control_plane.yml

+ 0 - 38
playbooks/common/openshift-cluster/upgrades/v3_8/upgrade_nodes.yml

@@ -1,38 +0,0 @@
----
-#
-# Node Upgrade Playbook
-#
-# Upgrades nodes only, but requires the control plane to have already been upgraded.
-#
-- import_playbook: ../init.yml
-  tags:
-  - pre_upgrade
-
-- name: Configure the upgrade target for the common upgrade tasks
-  hosts: oo_all_hosts
-  tags:
-  - pre_upgrade
-  tasks:
-  - set_fact:
-      openshift_upgrade_target: '3.8'
-      openshift_upgrade_min: '3.7'
-
-- import_playbook: ../pre/config.yml
-  vars:
-    l_upgrade_repo_hosts: "oo_nodes_to_config"
-    l_upgrade_no_proxy_hosts: "oo_all_hosts"
-    l_upgrade_health_check_hosts: "oo_nodes_to_config"
-    l_upgrade_verify_targets_hosts: "oo_nodes_to_config"
-    l_upgrade_docker_target_hosts: "oo_nodes_to_config"
-    l_upgrade_excluder_hosts: "oo_nodes_to_config:!oo_masters_to_config"
-    l_upgrade_nodes_only: True
-
-- name: Flag pre-upgrade checks complete for hosts without errors
-  hosts: oo_masters_to_config:oo_nodes_to_upgrade:oo_etcd_to_config
-  tasks:
-  - set_fact:
-      pre_upgrade_complete: True
-
-# Pre-upgrade completed
-
-- import_playbook: ../upgrade_nodes.yml

+ 9 - 0
playbooks/common/openshift-cluster/upgrades/v3_9/upgrade_control_plane.yml

@@ -142,3 +142,12 @@
   - import_role:
       name: openshift_web_console
       tasks_from: remove_old_asset_config
+
+# This is for a migration for a dc/docker-registry env var
+# https://bugzilla.redhat.com/show_bug.cgi?id=1540467
+# OPENSHIFT_DEFAULT_REGISTRY to REGISTRY_OPENSHIFT_SERVER_ADDR
+- hosts: oo_first_master
+  tasks:
+  - import_role:
+      name: openshift_hosted
+      tasks_from: migrate_default_registry_var.yml

+ 16 - 1
playbooks/gcp/openshift-cluster/build_base_image.yml

@@ -1,6 +1,6 @@
 ---
 # This playbook ensures that a base image is up to date with all of the required settings
-- name: Launch image build instance
+- name: Verify prerequisites for image build
   hosts: localhost
   connection: local
   gather_facts: no
@@ -10,6 +10,21 @@
       msg: "A root OS image name or family is required for base image building.  Please ensure `openshift_gcp_root_image` is defined."
     when: openshift_gcp_root_image is undefined
 
+- name: Provision ssh key
+  hosts: localhost
+  connection: local
+  gather_facts: no
+  tasks:
+  - name: Set up core host GCP configuration
+    import_role:
+      name: openshift_gcp
+      tasks_from: provision_ssh_keys.yml
+
+- name: Launch image build instance
+  hosts: localhost
+  connection: local
+  gather_facts: no
+  tasks:
   - name: Create the image instance disk
     gce_pd:
       service_account_email: "{{ (lookup('file', openshift_gcp_iam_service_account_keyfile ) | from_json ).client_email }}"

+ 10 - 0
playbooks/gcp/openshift-cluster/build_image.yml

@@ -9,6 +9,16 @@
       msg: "A base image name or family is required for image building.  Please ensure `openshift_gcp_base_image` is defined."
     when: openshift_gcp_base_image is undefined
 
+- name: Provision ssh key
+  hosts: localhost
+  connection: local
+  gather_facts: no
+  tasks:
+  - name: Set up core host GCP configuration
+    import_role:
+      name: openshift_gcp
+      tasks_from: provision_ssh_keys.yml
+
 - name: Launch image build instance
   hosts: localhost
   connection: local

+ 1 - 0
playbooks/init/basic_facts.yml

@@ -15,6 +15,7 @@
   - name: Run openshift_sanitize_inventory to set variables
     import_role:
       name: openshift_sanitize_inventory
+    no_log: True
 
   - name: Detecting Operating System from ostree_booted
     stat:

+ 4 - 2
playbooks/openshift-etcd/private/scaleup.yml

@@ -15,7 +15,8 @@
   pre_tasks:
   - name: Add new etcd members to cluster
     command: >
-      /usr/bin/etcdctl --cert-file {{ etcd_peer_cert_file }}
+      {{ r_etcd_common_etcdctl_command }}
+                       --cert-file {{ etcd_peer_cert_file }}
                        --key-file {{ etcd_peer_key_file }}
                        --ca-file {{ etcd_peer_ca_file }}
                        -C {{ etcd_peer_url_scheme }}://{{ hostvars[etcd_ca_host].etcd_ip }}:{{ etcd_client_port }}
@@ -49,7 +50,8 @@
   post_tasks:
   - name: Verify cluster is stable
     command: >
-      /usr/bin/etcdctl --cert-file {{ etcd_peer_cert_file }}
+      {{ r_etcd_common_etcdctl_command }}
+                       --cert-file {{ etcd_peer_cert_file }}
                        --key-file {{ etcd_peer_key_file }}
                        --ca-file {{ etcd_peer_ca_file }}
                        -C {{ etcd_peer_url_scheme }}://{{ hostvars[etcd_ca_host].etcd_hostname }}:{{ etcd_client_port }}

+ 27 - 4
playbooks/openshift-grafana/private/config.yml

@@ -1,6 +1,29 @@
 ---
-- name: Deploy grafana server
-  hosts: masters
+- name: Grafana Install Checkpoint Start
+  hosts: all
+  gather_facts: false
   tasks:
-  - include_role:
-      name: openshift_grafana
+  - name: Set Grafana install 'In Progress'
+    run_once: true
+    set_stats:
+      data:
+        installer_phase_grafana:
+          status: "In Progress"
+          start: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}"
+
+- name: OpenShift Grafana
+  hosts: oo_first_master
+  roles:
+  - role: openshift_grafana
+
+- name: Grafana Install Checkpoint End
+  hosts: all
+  gather_facts: false
+  tasks:
+  - name: Set Grafana install 'Complete'
+    run_once: true
+    set_stats:
+      data:
+        installer_phase_grafana:
+          status: "Complete"
+          end: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}"

+ 10 - 0
playbooks/openshift-grafana/private/uninstall.yml

@@ -0,0 +1,10 @@
+---
+- name: Uninstall Grafana
+  hosts: masters[0]
+  vars:
+    openshift_grafana_state: absent
+  tasks:
+  - name: Run the Grafana Uninstall Role Tasks
+    include_role:
+      name: openshift_grafana
+      tasks_from: uninstall_grafana

+ 2 - 0
playbooks/openshift-grafana/uninstall.yml

@@ -0,0 +1,2 @@
+---
+- import_playbook: private/uninstall.yml

+ 1 - 3
playbooks/openshift-hosted/private/redeploy-router-certificates.yml

@@ -117,9 +117,7 @@
 
   - import_role:
       name: openshift_hosted
-      tasks_from: main
-    vars:
-      openshift_hosted_manage_registry: false
+      tasks_from: router.yml
     when:
     - l_router_dc.rc == 0
     - l_router_svc.rc == 0

+ 1 - 0
playbooks/openshift-master/private/config.yml

@@ -180,6 +180,7 @@
   - role: openshift_master_facts
   - role: openshift_clock
   - role: openshift_cloud_provider
+    when: openshift_cloudprovider_kind is defined
   - role: openshift_builddefaults
   - role: openshift_buildoverrides
   - role: nickhammond.logrotate

playbooks/common/openshift-cluster/upgrades/create_service_signer_cert.yml → playbooks/openshift-master/private/create_service_signer_cert.yml


playbooks/common/openshift-cluster/upgrades/files/shared_resource_viewer_role.yaml → playbooks/openshift-master/private/files/shared_resource_viewer_role.yaml


+ 336 - 0
playbooks/openshift-master/private/upgrade.yml

@@ -0,0 +1,336 @@
+---
+###############################################################################
+# Upgrade Masters
+###############################################################################
+
+# Create service signer cert when missing. Service signer certificate
+# 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:
+  - name: Determine if service signer certificate must be created
+    stat:
+      path: "{{ openshift.common.config_base }}/master/service-signer.crt"
+    register: service_signer_cert_stat
+    changed_when: false
+
+- import_playbook: create_service_signer_cert.yml
+
+# oc adm migrate storage should be run prior to etcd v3 upgrade
+# See: https://github.com/openshift/origin/pull/14625#issuecomment-308467060
+- name: Pre master upgrade - Upgrade all storage
+  hosts: oo_first_master
+  roles:
+  - openshift_facts
+  tasks:
+  - name: Upgrade all storage
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      migrate storage --include=* --confirm
+    register: l_pb_upgrade_control_plane_pre_upgrade_storage
+    when: openshift_upgrade_pre_storage_migration_enabled | default(true) | bool
+    failed_when:
+    - l_pb_upgrade_control_plane_pre_upgrade_storage.rc != 0
+    - openshift_upgrade_pre_storage_migration_fatal | default(true) | bool
+
+# Set openshift_master_facts separately. In order to reconcile
+# admission_config's, we currently must run openshift_master_facts and
+# then run openshift_facts.
+- name: Set OpenShift master facts
+  hosts: oo_masters_to_config
+  roles:
+  - openshift_master_facts
+
+- name: configure vsphere svc account
+  hosts: oo_first_master
+  tasks:
+  - include_role:
+      name: openshift_cloud_provider
+      tasks_from: vsphere-svc
+    when:
+    - openshift_cloudprovider_kind is defined
+    - openshift_cloudprovider_kind == 'vsphere'
+    - openshift_version | version_compare('3.9', '>=')
+
+# The main master upgrade play. Should handle all changes to the system in one pass, with
+# support for optional hooks to be defined.
+- name: Upgrade master
+  hosts: oo_masters_to_config
+  serial: 1
+  roles:
+  - openshift_facts
+  tasks:
+  # Run the pre-upgrade hook if defined:
+  - debug: msg="Running master pre-upgrade hook {{ openshift_master_upgrade_pre_hook }}"
+    when: openshift_master_upgrade_pre_hook is defined
+
+  - include_tasks: "{{ openshift_master_upgrade_pre_hook }}"
+    when: openshift_master_upgrade_pre_hook is defined
+
+  - import_role:
+      name: openshift_master
+      tasks_from: upgrade.yml
+
+  - name: update vsphere provider master config
+    include_role:
+      name: openshift_master
+      tasks_from: update-vsphere
+    when:
+    - openshift_cloudprovider_kind is defined
+    - openshift_cloudprovider_kind == 'vsphere'
+    - openshift_version | version_compare('3.9', '>=')
+
+  - name: Setup and enable bootstrapping options
+    include_tasks: tasks/enable_bootstrap.yml
+    when: openshift_master_bootstrap_enabled | default(false) | bool
+
+  # Run the upgrade hook prior to restarting services/system if defined:
+  - debug: msg="Running master upgrade hook {{ openshift_master_upgrade_hook }}"
+    when: openshift_master_upgrade_hook is defined
+
+  - include_tasks: "{{ openshift_master_upgrade_hook }}"
+    when: openshift_master_upgrade_hook is defined
+
+  - name: Disable master controller
+    service:
+      name: "{{ openshift_service_type }}-master-controllers"
+      enabled: false
+    when: openshift.common.rolling_restart_mode == 'system'
+
+  - include_tasks: tasks/restart_hosts.yml
+    when: openshift.common.rolling_restart_mode == 'system'
+
+  - include_tasks: tasks/restart_services.yml
+    when: openshift.common.rolling_restart_mode == 'services'
+
+  # Run the post-upgrade hook if defined:
+  - debug: msg="Running master post-upgrade hook {{ openshift_master_upgrade_post_hook }}"
+    when: openshift_master_upgrade_post_hook is defined
+
+  - include_tasks: "{{ openshift_master_upgrade_post_hook }}"
+    when: openshift_master_upgrade_post_hook is defined
+
+  - name: Post master upgrade - Upgrade clusterpolicies storage
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      migrate storage --include=clusterpolicies --confirm
+    register: l_pb_upgrade_control_plane_post_upgrade_storage
+    when:
+    - openshift_upgrade_post_storage_migration_enabled | default(true) | bool
+    - openshift_version is version_compare('3.7','<')
+    failed_when:
+    - l_pb_upgrade_control_plane_post_upgrade_storage.rc != 0
+    - openshift_upgrade_post_storage_migration_fatal | default(false) | bool
+    run_once: true
+    delegate_to: "{{ groups.oo_first_master.0 }}"
+
+  - set_fact:
+      master_update_complete: True
+
+##############################################################################
+# Gate on master update complete
+##############################################################################
+- name: Gate on master update
+  hosts: localhost
+  connection: local
+  tasks:
+  - set_fact:
+      master_update_completed: "{{ hostvars
+                                 | lib_utils_oo_select_keys(groups.oo_masters_to_config)
+                                 | lib_utils_oo_collect('inventory_hostname', {'master_update_complete': true}) }}"
+  - set_fact:
+      master_update_failed: "{{ groups.oo_masters_to_config | difference(master_update_completed) | list }}"
+  - fail:
+      msg: "Upgrade cannot continue. The following masters did not finish updating: {{ master_update_failed | join(',') }}"
+    when: master_update_failed | length > 0
+
+###############################################################################
+# Reconcile Cluster Roles, Cluster Role Bindings and Security Context Constraints
+###############################################################################
+
+- name: Reconcile Cluster Roles and Cluster Role Bindings and Security Context Constraints
+  hosts: oo_masters_to_config
+  roles:
+  - { role: openshift_cli }
+  - { role: openshift_facts }
+  vars:
+    __master_shared_resource_viewer_file: "shared_resource_viewer_role.yaml"
+  tasks:
+  - name: Reconcile Cluster Roles
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      policy reconcile-cluster-roles --additive-only=true --confirm -o name
+    register: reconcile_cluster_role_result
+    when: openshift_version is version_compare('3.7','<')
+    changed_when:
+    - reconcile_cluster_role_result.stdout != ''
+    - reconcile_cluster_role_result.rc == 0
+    run_once: true
+
+  - name: Reconcile Cluster Role Bindings
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      policy reconcile-cluster-role-bindings
+      --exclude-groups=system:authenticated
+      --exclude-groups=system:authenticated:oauth
+      --exclude-groups=system:unauthenticated
+      --exclude-users=system:anonymous
+      --additive-only=true --confirm -o name
+    when: openshift_version is version_compare('3.7','<')
+    register: reconcile_bindings_result
+    changed_when:
+    - reconcile_bindings_result.stdout != ''
+    - reconcile_bindings_result.rc == 0
+    run_once: true
+
+  - name: Reconcile Jenkins Pipeline Role Bindings
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig policy reconcile-cluster-role-bindings system:build-strategy-jenkinspipeline --confirm -o name
+    run_once: true
+    register: reconcile_jenkins_role_binding_result
+    changed_when:
+    - reconcile_jenkins_role_binding_result.stdout != ''
+    - reconcile_jenkins_role_binding_result.rc == 0
+    when:
+    - openshift_version is version_compare('3.7','<')
+
+  - when: openshift_upgrade_target is version_compare('3.7','<')
+    block:
+    - name: Retrieve shared-resource-viewer
+      oc_obj:
+        state: list
+        kind: role
+        name: "shared-resource-viewer"
+        namespace: "openshift"
+      register: objout
+
+    - name: Determine if shared-resource-viewer is protected
+      set_fact:
+        __shared_resource_viewer_protected: true
+      when:
+      - "'results' in objout"
+      - "'results' in objout['results']"
+      - "'annotations' in objout['results']['results'][0]['metadata']"
+      - "'openshift.io/reconcile-protect' in objout['results']['results'][0]['metadata']['annotations']"
+      - "objout['results']['results'][0]['metadata']['annotations']['openshift.io/reconcile-protect'] == 'true'"
+    - copy:
+        src: "{{ item }}"
+        dest: "/tmp/{{ item }}"
+      with_items:
+      - "{{ __master_shared_resource_viewer_file }}"
+      when: __shared_resource_viewer_protected is not defined
+
+    - name: Fixup shared-resource-viewer role
+      oc_obj:
+        state: present
+        kind: role
+        name: "shared-resource-viewer"
+        namespace: "openshift"
+        files:
+        - "/tmp/{{ __master_shared_resource_viewer_file }}"
+        delete_after: true
+      when: __shared_resource_viewer_protected is not defined
+      register: result
+      retries: 3
+      delay: 5
+      until: result.rc == 0
+      ignore_errors: true
+
+
+  - name: Reconcile Security Context Constraints
+    command: >
+      {{ openshift_client_binary }} adm policy --config={{ openshift.common.config_base }}/master/admin.kubeconfig reconcile-sccs --confirm --additive-only=true -o name
+    register: reconcile_scc_result
+    changed_when:
+    - reconcile_scc_result.stdout != ''
+    - reconcile_scc_result.rc == 0
+    run_once: true
+
+  - name: Migrate storage post policy reconciliation
+    command: >
+      {{ openshift_client_binary }} adm --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      migrate storage --include=* --confirm
+    run_once: true
+    register: l_pb_upgrade_control_plane_post_upgrade_storage
+    when: openshift_upgrade_post_storage_migration_enabled | default(true) | bool
+    failed_when:
+    - l_pb_upgrade_control_plane_post_upgrade_storage.rc != 0
+    - openshift_upgrade_post_storage_migration_fatal | default(false) | bool
+
+  - set_fact:
+      reconcile_complete: True
+
+##############################################################################
+# Gate on reconcile
+##############################################################################
+- name: Gate on reconcile
+  hosts: localhost
+  connection: local
+  tasks:
+  - set_fact:
+      reconcile_completed: "{{ hostvars
+                                 | lib_utils_oo_select_keys(groups.oo_masters_to_config)
+                                 | lib_utils_oo_collect('inventory_hostname', {'reconcile_complete': true}) }}"
+  - set_fact:
+      reconcile_failed: "{{ groups.oo_masters_to_config | difference(reconcile_completed) | list }}"
+  - fail:
+      msg: "Upgrade cannot continue. The following masters did not finish reconciling: {{ reconcile_failed | join(',') }}"
+    when: reconcile_failed | length > 0
+
+- name: Drain and upgrade master nodes
+  # There is no need to update nodes in the middle of double upgrade
+  # This would skip node update to 3.8 during 3.7->3.9 upgrade
+  hosts: "{{ l_double_upgrade_cp | default(False) | ternary('all:!all', 'oo_masters_to_config:&oo_nodes_to_upgrade') }}"
+  # This var must be set with -e on invocation, as it is not a per-host inventory var
+  # and is evaluated early. Values such as "20%" can also be used.
+  serial: "{{ openshift_upgrade_control_plane_nodes_serial | default(1) }}"
+  max_fail_percentage: "{{ openshift_upgrade_control_plane_nodes_max_fail_percentage | default(0) }}"
+
+  pre_tasks:
+  - name: Load lib_openshift modules
+    import_role:
+      name: lib_openshift
+
+  # TODO: To better handle re-trying failed upgrades, it would be nice to check if the node
+  # or docker actually needs an upgrade before proceeding. Perhaps best to save this until
+  # we merge upgrade functionality into the base roles and a normal config.yml playbook run.
+  - name: Mark node unschedulable
+    oc_adm_manage_node:
+      node: "{{ openshift.node.nodename | lower }}"
+      schedulable: False
+    delegate_to: "{{ groups.oo_first_master.0 }}"
+    retries: 10
+    delay: 5
+    register: node_unschedulable
+    until: node_unschedulable is succeeded
+
+  - name: Drain Node for Kubelet upgrade
+    command: >
+      {{ hostvars[groups.oo_first_master.0]['first_master_client_binary'] }} adm drain {{ openshift.node.nodename | lower }}
+      --config={{ openshift.common.config_base }}/master/admin.kubeconfig
+      --force --delete-local-data --ignore-daemonsets
+      --timeout={{ openshift_upgrade_nodes_drain_timeout | default(0) }}s
+    delegate_to: "{{ groups.oo_first_master.0 }}"
+    register: l_upgrade_control_plane_drain_result
+    until: not (l_upgrade_control_plane_drain_result is failed)
+    retries: "{{ 1 if ( openshift_upgrade_nodes_drain_timeout | default(0) | int ) == 0 else 0 }}"
+    delay: 5
+    failed_when:
+    - l_upgrade_control_plane_drain_result is failed
+    - openshift_upgrade_nodes_drain_timeout | default(0) | int == 0
+
+  roles:
+  - openshift_facts
+  post_tasks:
+  - import_role:
+      name: openshift_node
+      tasks_from: upgrade_pre.yml
+  - import_role:
+      name: openshift_node
+      tasks_from: upgrade.yml
+  - import_role:
+      name: openshift_manage_node
+      tasks_from: config.yml
+    vars:
+      openshift_master_host: "{{ groups.oo_first_master.0 }}"
+      openshift_manage_node_is_master: true

+ 3 - 1
playbooks/openshift-master/private/validate_restart.yml

@@ -61,4 +61,6 @@
     - exists.stat.exists and openshift.common.rolling_restart_mode == 'system'
   - set_fact:
       current_host: "{{ exists.stat.exists }}"
-    when: openshift.common.rolling_restart_mode == 'system'
+    when:
+    - "'stat' in exists"
+    - openshift.common.rolling_restart_mode == 'system'

+ 1 - 0
playbooks/openshift-node/private/configure_nodes.yml

@@ -12,6 +12,7 @@
   roles:
   - role: openshift_clock
   - role: openshift_cloud_provider
+    when: openshift_cloudprovider_kind is defined
   - role: openshift_node
   - role: tuned
   - role: nickhammond.logrotate

+ 1 - 0
playbooks/openshift-node/private/containerized_nodes.yml

@@ -14,6 +14,7 @@
   roles:
   - role: openshift_clock
   - role: openshift_cloud_provider
+    when: openshift_cloudprovider_kind is defined
   - role: openshift_node
     openshift_ca_host: "{{ groups.oo_first_master.0 }}"
   - role: nickhammond.logrotate

+ 12 - 0
playbooks/openstack/README.md

@@ -144,7 +144,19 @@ $ vi inventory/group_vars/all.yml
 4. Set the `openshift_openstack_default_flavor` to the flavor you want your
    OpenShift VMs to use.
    - See `openstack flavor list` for the list of available flavors.
+5. If you opt to use Kuryr for the networking, make sure that you review all
+   the kuryr options in the file. At the very least, if you use Kuryr, you
+   should uncomment:
 
+```bash
+#openshift_use_kuryr: True
+#use_trunk_ports: True
+#openshift_use_openshift_sdn: False
+#os_sdn_network_plugin_name: cni
+#openshift_node_proxy_mode: userspace
+#openshift_hosted_manage_registry: false
+#kuryr_openstack_public_subnet_id: uuid of my public subnet
+```
 
 
 #### OpenShift configuration

+ 114 - 42
playbooks/openstack/inventory.py

@@ -11,6 +11,7 @@ from __future__ import print_function
 
 from collections import Mapping
 import json
+import os
 
 import shade
 
@@ -58,6 +59,7 @@ def base_openshift_inventory(cluster_hosts):
     inventory['glusterfs'] = {'hosts': cns}
     inventory['dns'] = {'hosts': dns}
     inventory['lb'] = {'hosts': load_balancers}
+    inventory['localhost'] = {'ansible_connection': 'local'}
 
     return inventory
 
@@ -75,6 +77,49 @@ def get_docker_storage_mountpoints(volumes):
     return docker_storage_mountpoints
 
 
+def _get_hostvars(server, docker_storage_mountpoints):
+    ssh_ip_address = server.public_v4 or server.private_v4
+    hostvars = {
+        'ansible_host': ssh_ip_address
+    }
+
+    public_v4 = server.public_v4 or server.private_v4
+    if public_v4:
+        hostvars['public_v4'] = server.public_v4
+        hostvars['openshift_public_ip'] = server.public_v4
+    # TODO(shadower): what about multiple networks?
+    if server.private_v4:
+        hostvars['private_v4'] = server.private_v4
+        hostvars['openshift_ip'] = server.private_v4
+
+        # NOTE(shadower): Yes, we set both hostname and IP to the private
+        # IP address for each node. OpenStack doesn't resolve nodes by
+        # name at all, so using a hostname here would require an internal
+        # DNS which would complicate the setup and potentially introduce
+        # performance issues.
+        hostvars['openshift_hostname'] = server.metadata.get(
+            'openshift_hostname', server.private_v4)
+    hostvars['openshift_public_hostname'] = server.name
+
+    if server.metadata['host-type'] == 'cns':
+        hostvars['glusterfs_devices'] = ['/dev/nvme0n1']
+
+    node_labels = server.metadata.get('node_labels')
+    # NOTE(shadower): the node_labels value must be a dict not string
+    if not isinstance(node_labels, Mapping):
+        node_labels = json.loads(node_labels)
+
+    if node_labels:
+        hostvars['openshift_node_labels'] = node_labels
+
+    # check for attached docker storage volumes
+    if 'os-extended-volumes:volumes_attached' in server:
+        if server.id in docker_storage_mountpoints:
+            hostvars['docker_storage_mountpoints'] = ' '.join(
+                docker_storage_mountpoints[server.id])
+    return hostvars
+
+
 def build_inventory():
     '''Build the dynamic inventory.'''
     cloud = shade.openstack_cloud()
@@ -97,51 +142,78 @@ def build_inventory():
     inventory['_meta'] = {'hostvars': {}}
 
     # cinder volumes used for docker storage
-    docker_storage_mountpoints = get_docker_storage_mountpoints(cloud.list_volumes())
-
+    docker_storage_mountpoints = get_docker_storage_mountpoints(
+        cloud.list_volumes())
     for server in cluster_hosts:
-        ssh_ip_address = server.public_v4 or server.private_v4
-        hostvars = {
-            'ansible_host': ssh_ip_address
-        }
-
-        public_v4 = server.public_v4 or server.private_v4
-        if public_v4:
-            hostvars['public_v4'] = server.public_v4
-            hostvars['openshift_public_ip'] = server.public_v4
-        # TODO(shadower): what about multiple networks?
-        if server.private_v4:
-            hostvars['private_v4'] = server.private_v4
-            hostvars['openshift_ip'] = server.private_v4
-
-            # NOTE(shadower): Yes, we set both hostname and IP to the private
-            # IP address for each node. OpenStack doesn't resolve nodes by
-            # name at all, so using a hostname here would require an internal
-            # DNS which would complicate the setup and potentially introduce
-            # performance issues.
-            hostvars['openshift_hostname'] = server.metadata.get(
-                'openshift_hostname', server.private_v4)
-        hostvars['openshift_public_hostname'] = server.name
-
-        if server.metadata['host-type'] == 'cns':
-            hostvars['glusterfs_devices'] = ['/dev/nvme0n1']
-
-        node_labels = server.metadata.get('node_labels')
-        # NOTE(shadower): the node_labels value must be a dict not string
-        if not isinstance(node_labels, Mapping):
-            node_labels = json.loads(node_labels)
-
-        if node_labels:
-            hostvars['openshift_node_labels'] = node_labels
-
-        # check for attached docker storage volumes
-        if 'os-extended-volumes:volumes_attached' in server:
-            if server.id in docker_storage_mountpoints:
-                hostvars['docker_storage_mountpoints'] = ' '.join(docker_storage_mountpoints[server.id])
-
-        inventory['_meta']['hostvars'][server.name] = hostvars
+        inventory['_meta']['hostvars'][server.name] = _get_hostvars(
+            server,
+            docker_storage_mountpoints)
+
+    stout = _get_stack_outputs(cloud)
+    if stout is not None:
+        try:
+            inventory['localhost'].update({
+                'openshift_openstack_api_lb_provider':
+                stout['api_lb_provider'],
+                'openshift_openstack_api_lb_port_id':
+                stout['api_lb_vip_port_id'],
+                'openshift_openstack_api_lb_sg_id':
+                stout['api_lb_sg_id']})
+        except KeyError:
+            pass  # Not an API load balanced deployment
+
+        try:
+            inventory['OSEv3']['vars'] = _get_kuryr_vars(cloud, stout)
+        except KeyError:
+            pass  # Not a kuryr deployment
     return inventory
 
 
+def _get_stack_outputs(cloud_client):
+    """Returns a dictionary with the stack outputs"""
+    cluster_name = os.getenv('OPENSHIFT_CLUSTER', 'openshift-cluster')
+
+    stack = cloud_client.get_stack(cluster_name)
+    if stack is None or stack['stack_status'] not in (
+            'CREATE_COMPLETE', 'UPDATE_COMPLETE'):
+        return None
+
+    data = {}
+    for output in stack['outputs']:
+        data[output['output_key']] = output['output_value']
+    return data
+
+
+def _get_kuryr_vars(cloud_client, data):
+    """Returns a dictionary of Kuryr variables resulting of heat stacking"""
+    settings = {}
+    settings['kuryr_openstack_pod_subnet_id'] = data['pod_subnet']
+    settings['kuryr_openstack_worker_nodes_subnet_id'] = data['vm_subnet']
+    settings['kuryr_openstack_service_subnet_id'] = data['service_subnet']
+    settings['kuryr_openstack_pod_sg_id'] = data['pod_access_sg_id']
+    settings['kuryr_openstack_pod_project_id'] = (
+        cloud_client.current_project_id)
+
+    settings['kuryr_openstack_auth_url'] = cloud_client.auth['auth_url']
+    settings['kuryr_openstack_username'] = cloud_client.auth['username']
+    settings['kuryr_openstack_password'] = cloud_client.auth['password']
+    if 'user_domain_id' in cloud_client.auth:
+        settings['kuryr_openstack_user_domain_name'] = (
+            cloud_client.auth['user_domain_id'])
+    else:
+        settings['kuryr_openstack_user_domain_name'] = (
+            cloud_client.auth['user_domain_name'])
+    # FIXME(apuimedo): consolidate kuryr controller credentials into the same
+    #                  vars the openstack playbook uses.
+    settings['kuryr_openstack_project_id'] = cloud_client.current_project_id
+    if 'project_domain_id' in cloud_client.auth:
+        settings['kuryr_openstack_project_domain_name'] = (
+            cloud_client.auth['project_domain_id'])
+    else:
+        settings['kuryr_openstack_project_domain_name'] = (
+            cloud_client.auth['project_domain_name'])
+    return settings
+
+
 if __name__ == '__main__':
     print(json.dumps(build_inventory(), indent=4, sort_keys=True))

+ 15 - 0
playbooks/openstack/openshift-cluster/install.yml

@@ -10,5 +10,20 @@
 
 - import_playbook: ../../prerequisites.yml
 
+- name: Prepare the Nodes in the cluster for installation
+  hosts: oo_all_hosts
+  become: yes
+  gather_facts: yes
+  tasks:
+  - name: Install dependencies
+    import_role:
+      name: openshift_openstack
+      tasks_from: node-packages.yml
+
+  - name: Configure Node
+    import_role:
+      name: openshift_openstack
+      tasks_from: node-configuration.yml
+
 - name: run the cluster deploy
   import_playbook: ../../deploy_cluster.yml

+ 3 - 23
playbooks/openstack/openshift-cluster/provision.yml

@@ -2,6 +2,9 @@
 - name: Create the OpenStack resources for cluster installation
   hosts: localhost
   tasks:
+  - name: retrieve cluster name from the environment if present
+    set_fact:
+      openshift_openstack_stack_name: "{{ lookup('env', 'OPENSHIFT_CLUSTER') | ternary (lookup('env', 'OPENSHIFT_CLUSTER'), omit) }}"
   - name: provision cluster
     import_role:
       name: openshift_openstack
@@ -55,26 +58,3 @@
     - ansible_distribution == "RedHat"
     - rhsub_user is defined
     - rhsub_pass is defined
-
-  - name: Enable required YUM repositories
-    import_role:
-      name: openshift_repos
-    when:
-    - ansible_distribution == "RedHat"
-    - rh_subscribed is defined
-
-
-- name: Prepare the Nodes in the cluster for installation
-  hosts: oo_all_hosts
-  become: yes
-  gather_facts: yes
-  tasks:
-  - name: Install dependencies
-    import_role:
-      name: openshift_openstack
-      tasks_from: node-packages.yml
-
-  - name: Configure Node
-    import_role:
-      name: openshift_openstack
-      tasks_from: node-configuration.yml

+ 44 - 0
playbooks/openstack/sample-inventory/group_vars/all.yml

@@ -20,6 +20,50 @@ openshift_openstack_external_network_name: "public"
 # # NOTE: this is only supported with Flannel SDN yet
 #openstack_private_data_network_name: "openshift-ansible-{{ openshift_openstack_stack_name }}-data-net"
 
+## Kuryr networking
+# TODO: Allow the user to specify pre-existing subnets for pod and services
+#openshift_openstack_kuryr_service_subnet_cidr: "172.30.0.0/16"
+
+#
+## You can alter the port pooling defaults here
+#kuryr_openstack_enable_pools: True
+#kuryr_openstack_pool_max: 0
+#kuryr_openstack_pool_min: 1
+#kuryr_openstack_pool_batch: 5
+#kuryr_openstack_pool_update_frequency: 20
+#
+## You should set the following if you want to use Kuryr/Neutron as your SDN
+#openshift_use_kuryr: True
+#openshift_use_openshift_sdn: False
+
+# NOTE: you must uncomment these for Kuryr to work properly as well:
+# openshift_master_open_ports:
+# - service: dns tcp
+#   port: 53/tcp
+# - service: dns udp
+#   port: 53/udp
+# openshift_node_open_ports:
+# - service: dns tcp
+#   port: 53/tcp
+# - service: dns udp
+#   port: 53/udp
+
+#use_trunk_ports: True
+#os_sdn_network_plugin_name: cni
+#openshift_node_proxy_mode: userspace
+# # Kuryr needs to have the pod based registry (if desired in the cluster)
+# deployed after kuryr is up and running. This can be done with oadm
+# #Disable management of the OpenShift Registry
+#openshift_hosted_manage_registry: false
+# # Kuryr needs to know the subnet you will be taking Floating IPs for the
+# loadbalancer services from.
+# kuryr_openstack_public_subnet_id: uuid_of_my_fip_subnet
+
+# If you VM images will name the ethernet device different than 'eth0',
+# override this
+#kuryr_cni_link_interface: eth0
+
+
 ## If you want to use a provider network, set its name here.
 ## NOTE: the `openshift_openstack_external_network_name` and
 ## `openshift_openstack_private_network_name` options will be ignored when using a

+ 6 - 0
roles/ansible_service_broker/tasks/install.yml

@@ -268,6 +268,12 @@
                   env:
                     - name: BROKER_CONFIG
                       value: /etc/ansible-service-broker/config.yaml
+                    - name: HTTP_PROXY
+                      value: "{{ openshift.common.http_proxy  | default('') }}"
+                    - name: HTTPS_PROXY
+                      value: "{{ openshift.common.https_proxy  | default('') }}"
+                    - name: NO_PROXY
+                      value: "{{ openshift.common.no_proxy  | default('') }}"
                   resources: {}
                   terminationMessagePath: /tmp/termination-log
                   readinessProbe:

+ 12 - 0
roles/container_runtime/defaults/main.yml

@@ -107,6 +107,18 @@ container_runtime_crio_additional_mounts: []
 
 l_crio_additional_mounts: "{{ ',' + (container_runtime_crio_additional_mounts | lib_utils_oo_l_of_d_to_csv) if container_runtime_crio_additional_mounts != [] else '' }}"
 
+# this is a list of dictionaries of mounts
+# container_runtime_docker_additional_mounts:
+# - destination: /test
+#   source: /var/test
+#   options:
+#   - rw
+#   - mode=755
+#   type: bind
+container_runtime_docker_additional_mounts: []
+
+l_docker_additional_mounts: "{{ ',' + (container_runtime_docker_additional_mounts | lib_utils_oo_l_of_d_to_csv) if container_runtime_docker_additional_mounts != [] else '' }}"
+
 openshift_crio_image_tag_default: "latest"
 
 l_crt_crio_image_tag_dict:

+ 2 - 0
roles/container_runtime/tasks/systemcontainer_docker.yml

@@ -71,6 +71,8 @@
     name: "{{ openshift_docker_service_name }}"
     image: "{{ l_docker_image }}"
     state: latest
+    values:
+      - "ADDTL_MOUNTS={{ l_docker_additional_mounts }}"
 
 - name: Configure Container Engine Service File
   template:

+ 8 - 1
roles/kuryr/defaults/main.yaml

@@ -12,17 +12,24 @@ kuryr_openstack_user_domain_name: default
 kuryr_openstack_project_domain_name: default
 
 # Kuryr OpenShift namespace
-kuryr_namespace: kube-system
+kuryr_namespace: openshift-infra
 
 # Whether to run the cni plugin in debug mode
 kuryr_cni_debug: "false"
 
+# Default pod-in-VM link interface
+kuryr_cni_link_interface: eth0
+
 # The version of cni binaries
 cni_version: v0.5.2
 
 # Path to bin dir (where kuryr execs get installed)
 bin_dir: /usr/bin
 
+# Default controller and CNI images
+openshift_openstack_kuryr_controller_image: kuryr/controller:latest
+openshift_openstack_kuryr_cni_image: kuryr/cni:latest
+
 # Path to the cni binaries
 cni_bin_dir: /opt/cni/bin
 

+ 1 - 1
roles/kuryr/templates/cni-daemonset.yaml.j2

@@ -23,7 +23,7 @@ spec:
       serviceAccountName: kuryr-controller
       containers:
       - name: kuryr-cni
-        image: kuryr/cni:latest
+        image: {{ openshift_openstack_kuryr_cni_image }}
         imagePullPolicy: IfNotPresent
         command: [ "cni_ds_init" ]
         env:

+ 3 - 23
roles/kuryr/templates/configmap.yaml.j2

@@ -141,15 +141,7 @@ data:
     # Driver to use for binding and unbinding ports. (string value)
     # Deprecated group/name - [binding]/driver
     #default_driver = kuryr.lib.binding.drivers.veth
-
-    # Drivers to use for binding and unbinding ports. (list value)
-    #enabled_drivers = kuryr.lib.binding.drivers.veth
-
-    # Specifies the name of the Nova instance interface to link the virtual devices
-    # to (only applicable to some binding drivers. (string value)
-    link_iface = eth0
-
-    driver = kuryr.lib.binding.drivers.vlan
+    default_driver = kuryr.lib.binding.drivers.vlan
 
 
     [cni_daemon]
@@ -301,7 +293,7 @@ data:
     # TODO (apuimedo): Remove the duplicated line just after this one once the
     # RDO packaging contains the upstream patch
     worker_nodes_subnet = {{ kuryr_openstack_worker_nodes_subnet_id }}
-    external_svc_subnet = {{ kuryr_openstack_external_svc_subnet_id }}
+    external_svc_subnet = {{ kuryr_openstack_public_subnet_id }}
 
     [pod_vif_nested]
 
@@ -466,21 +458,9 @@ data:
     # From kuryr_kubernetes
     #
 
-    # The name prefix of the veth endpoint put inside the container. (string value)
-    #veth_dst_prefix = eth
-
-    # Driver to use for binding and unbinding ports. (string value)
-    # Deprecated group/name - [binding]/driver
-    #default_driver = kuryr.lib.binding.drivers.veth
-
-    # Drivers to use for binding and unbinding ports. (list value)
-    #enabled_drivers = kuryr.lib.binding.drivers.veth
-
     # Specifies the name of the Nova instance interface to link the virtual devices
     # to (only applicable to some binding drivers. (string value)
-    link_iface = eth0
-
-    driver = kuryr.lib.binding.drivers.vlan
+    link_iface = {{ kuryr_cni_link_interface }}
 
 
     [cni_daemon]

+ 1 - 1
roles/kuryr/templates/controller-deployment.yaml.j2

@@ -19,7 +19,7 @@ spec:
       automountServiceAccountToken: true
       hostNetwork: true
       containers:
-      - image: kuryr/controller:latest
+      - image: {{ openshift_openstack_kuryr_controller_image }}
         imagePullPolicy: IfNotPresent
         name: controller
 {% if kuryr_openstack_enable_pools | default(false) %}

+ 25 - 8
roles/lib_openshift/library/oc_adm_ca_server_cert.py

@@ -912,7 +912,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -921,13 +921,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1005,12 +1016,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_csr.py

@@ -890,7 +890,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -899,13 +899,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -983,12 +994,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_manage_node.py

@@ -898,7 +898,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -907,13 +907,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -991,12 +1002,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_policy_group.py

@@ -884,7 +884,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -893,13 +893,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -977,12 +988,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_policy_user.py

@@ -898,7 +898,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -907,13 +907,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -991,12 +1002,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_registry.py

@@ -1002,7 +1002,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -1011,13 +1011,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1095,12 +1106,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_adm_router.py

@@ -1027,7 +1027,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -1036,13 +1036,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1120,12 +1131,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_clusterrole.py

@@ -876,7 +876,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -885,13 +885,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -969,12 +980,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_configmap.py

@@ -882,7 +882,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -891,13 +891,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -975,12 +986,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 52 - 16
roles/lib_openshift/library/oc_edit.py

@@ -140,6 +140,12 @@ options:
     required: false
     default: None
     aliases: []
+  edits:
+    description:
+    - a list of dictionaries with a yedit format for edits
+    required: false
+    default: None
+    aliases: []
   force:
     description:
     - Whether or not to force the operation
@@ -926,7 +932,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -935,13 +941,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
+
+            if results['changed']:
+                updated = True
 
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1019,12 +1036,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])
@@ -1506,7 +1529,7 @@ class Edit(OpenShiftCLI):
         '''return a secret by name '''
         return self._get(self.kind, self.name)
 
-    def update(self, file_name, content, force=False, content_type='yaml'):
+    def update(self, file_name, content, edits, force=False, content_type='yaml'):
         '''run update '''
         if file_name:
             if content_type == 'yaml':
@@ -1514,13 +1537,22 @@ class Edit(OpenShiftCLI):
             elif content_type == 'json':
                 data = json.loads(open(file_name).read())
 
-            changes = []
             yed = Yedit(filename=file_name, content=data, separator=self.separator)
-            for key, value in content.items():
-                changes.append(yed.put(key, value))
+            # Keep this for compatibility
+            if content is not None:
+                changes = []
+
+                for key, value in content.items():
+                    changes.append(yed.put(key, value))
+
+                if any([not change[0] for change in changes]):
+                    return {'returncode': 0, 'updated': False}
+
+            elif edits is not None:
+                results = Yedit.process_edits(edits, yed)
 
-            if any([not change[0] for change in changes]):
-                return {'returncode': 0, 'updated': False}
+                if not results['changed']:
+                    return results
 
             yed.write()
 
@@ -1528,7 +1560,7 @@ class Edit(OpenShiftCLI):
 
             return self._replace(file_name, force=force)
 
-        return self._replace_content(self.kind, self.name, content, force=force, sep=self.separator)
+        return self._replace_content(self.kind, self.name, content, edits, force=force, sep=self.separator)
 
     @staticmethod
     def run_ansible(params, check_mode):
@@ -1557,6 +1589,7 @@ class Edit(OpenShiftCLI):
 
         api_rval = ocedit.update(params['file_name'],
                                  params['content'],
+                                 params['edits'],
                                  params['force'],
                                  params['file_format'])
 
@@ -1595,11 +1628,14 @@ def main():
             kind=dict(required=True, type='str'),
             file_name=dict(default=None, type='str'),
             file_format=dict(default='yaml', type='str'),
-            content=dict(default=None, required=True, type='dict'),
+            content=dict(default=None, type='dict'),
             force=dict(default=False, type='bool'),
             separator=dict(default='.', type='str'),
+            edits=dict(default=None, type='list'),
         ),
         supports_check_mode=True,
+        mutually_exclusive=[['content', 'edits']],
+        required_one_of=[['content', 'edits']],
     )
 
     rval = Edit.run_ansible(module.params, module.check_mode)

+ 25 - 8
roles/lib_openshift/library/oc_env.py

@@ -893,7 +893,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -902,13 +902,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -986,12 +997,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_group.py

@@ -866,7 +866,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -875,13 +875,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -959,12 +970,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_image.py

@@ -885,7 +885,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -894,13 +894,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -978,12 +989,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_label.py

@@ -902,7 +902,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -911,13 +911,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -995,12 +1006,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 41 - 12
roles/lib_openshift/library/oc_obj.py

@@ -133,6 +133,12 @@ options:
     required: false
     default: None
     aliases: []
+  field_selector:
+    description:
+    - Field selector that gets added to the query.
+    required: false
+    default: None
+    aliases: []
 author:
 - "Kenny Woodson <kwoodson@redhat.com>"
 extends_documentation_fragment: []
@@ -905,7 +911,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -914,13 +920,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
+
+            if results['changed']:
+                updated = True
 
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -998,12 +1015,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])
@@ -1477,17 +1500,19 @@ class OCObject(OpenShiftCLI):
                  selector=None,
                  kubeconfig='/etc/origin/master/admin.kubeconfig',
                  verbose=False,
-                 all_namespaces=False):
+                 all_namespaces=False,
+                 field_selector=None):
         ''' Constructor for OpenshiftOC '''
         super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose,
                                        all_namespaces=all_namespaces)
         self.kind = kind
         self.name = name
         self.selector = selector
+        self.field_selector = field_selector
 
     def get(self):
         '''return a kind by name '''
-        results = self._get(self.kind, name=self.name, selector=self.selector)
+        results = self._get(self.kind, name=self.name, selector=self.selector, field_selector=self.field_selector)
         if (results['returncode'] != 0 and 'stderr' in results and
                 '\"{}\" not found'.format(self.name) in results['stderr']):
             results['returncode'] = 0
@@ -1576,7 +1601,8 @@ class OCObject(OpenShiftCLI):
                          params['selector'],
                          kubeconfig=params['kubeconfig'],
                          verbose=params['debug'],
-                         all_namespaces=params['all_namespaces'])
+                         all_namespaces=params['all_namespaces'],
+                         field_selector=params['field_selector'])
 
         state = params['state']
 
@@ -1586,6 +1612,8 @@ class OCObject(OpenShiftCLI):
         # Get
         #####
         if state == 'list':
+            if api_rval['returncode'] != 0:
+                return {'changed': False, 'failed': True, 'msg': api_rval}
             return {'changed': False, 'results': api_rval, 'state': state}
 
         ########
@@ -1697,8 +1725,9 @@ def main():
             content=dict(default=None, type='dict'),
             force=dict(default=False, type='bool'),
             selector=dict(default=None, type='str'),
+            field_selector=dict(default=None, type='str'),
         ),
-        mutually_exclusive=[["content", "files"], ["selector", "name"]],
+        mutually_exclusive=[["content", "files"], ["selector", "name"], ["field_selector", "name"]],
 
         supports_check_mode=True,
     )

+ 25 - 8
roles/lib_openshift/library/oc_objectvalidator.py

@@ -837,7 +837,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -846,13 +846,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -930,12 +941,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_process.py

@@ -894,7 +894,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -903,13 +903,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -987,12 +998,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_project.py

@@ -891,7 +891,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -900,13 +900,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
+
+            if results['changed']:
+                updated = True
 
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -984,12 +995,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_pvc.py

@@ -898,7 +898,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -907,13 +907,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -991,12 +1002,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_route.py

@@ -942,7 +942,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -951,13 +951,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1035,12 +1046,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_scale.py

@@ -880,7 +880,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -889,13 +889,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -973,12 +984,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_secret.py

@@ -938,7 +938,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -947,13 +947,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1031,12 +1042,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_service.py

@@ -945,7 +945,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -954,13 +954,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1038,12 +1049,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_serviceaccount.py

@@ -878,7 +878,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -887,13 +887,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -971,12 +982,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_serviceaccount_secret.py

@@ -878,7 +878,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -887,13 +887,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -971,12 +982,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_storageclass.py

@@ -896,7 +896,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -905,13 +905,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -989,12 +1000,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_user.py

@@ -938,7 +938,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -947,13 +947,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1031,12 +1042,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_version.py

@@ -850,7 +850,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -859,13 +859,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -943,12 +954,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 25 - 8
roles/lib_openshift/library/oc_volume.py

@@ -927,7 +927,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -936,13 +936,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -1020,12 +1031,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 4 - 1
roles/lib_openshift/src/ansible/oc_edit.py

@@ -18,11 +18,14 @@ def main():
             kind=dict(required=True, type='str'),
             file_name=dict(default=None, type='str'),
             file_format=dict(default='yaml', type='str'),
-            content=dict(default=None, required=True, type='dict'),
+            content=dict(default=None, type='dict'),
             force=dict(default=False, type='bool'),
             separator=dict(default='.', type='str'),
+            edits=dict(default=None, type='list'),
         ),
         supports_check_mode=True,
+        mutually_exclusive=[['content', 'edits']],
+        required_one_of=[['content', 'edits']],
     )
 
     rval = Edit.run_ansible(module.params, module.check_mode)

+ 2 - 1
roles/lib_openshift/src/ansible/oc_obj.py

@@ -22,8 +22,9 @@ def main():
             content=dict(default=None, type='dict'),
             force=dict(default=False, type='bool'),
             selector=dict(default=None, type='str'),
+            field_selector=dict(default=None, type='str'),
         ),
-        mutually_exclusive=[["content", "files"], ["selector", "name"]],
+        mutually_exclusive=[["content", "files"], ["selector", "name"], ["field_selector", "name"]],
 
         supports_check_mode=True,
     )

+ 17 - 7
roles/lib_openshift/src/class/oc_edit.py

@@ -22,7 +22,7 @@ class Edit(OpenShiftCLI):
         '''return a secret by name '''
         return self._get(self.kind, self.name)
 
-    def update(self, file_name, content, force=False, content_type='yaml'):
+    def update(self, file_name, content, edits, force=False, content_type='yaml'):
         '''run update '''
         if file_name:
             if content_type == 'yaml':
@@ -30,13 +30,22 @@ class Edit(OpenShiftCLI):
             elif content_type == 'json':
                 data = json.loads(open(file_name).read())
 
-            changes = []
             yed = Yedit(filename=file_name, content=data, separator=self.separator)
-            for key, value in content.items():
-                changes.append(yed.put(key, value))
+            # Keep this for compatibility
+            if content is not None:
+                changes = []
 
-            if any([not change[0] for change in changes]):
-                return {'returncode': 0, 'updated': False}
+                for key, value in content.items():
+                    changes.append(yed.put(key, value))
+
+                if any([not change[0] for change in changes]):
+                    return {'returncode': 0, 'updated': False}
+
+            elif edits is not None:
+                results = Yedit.process_edits(edits, yed)
+
+                if not results['changed']:
+                    return results
 
             yed.write()
 
@@ -44,7 +53,7 @@ class Edit(OpenShiftCLI):
 
             return self._replace(file_name, force=force)
 
-        return self._replace_content(self.kind, self.name, content, force=force, sep=self.separator)
+        return self._replace_content(self.kind, self.name, content, edits, force=force, sep=self.separator)
 
     @staticmethod
     def run_ansible(params, check_mode):
@@ -73,6 +82,7 @@ class Edit(OpenShiftCLI):
 
         api_rval = ocedit.update(params['file_name'],
                                  params['content'],
+                                 params['edits'],
                                  params['force'],
                                  params['file_format'])
 

+ 8 - 3
roles/lib_openshift/src/class/oc_obj.py

@@ -14,17 +14,19 @@ class OCObject(OpenShiftCLI):
                  selector=None,
                  kubeconfig='/etc/origin/master/admin.kubeconfig',
                  verbose=False,
-                 all_namespaces=False):
+                 all_namespaces=False,
+                 field_selector=None):
         ''' Constructor for OpenshiftOC '''
         super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose,
                                        all_namespaces=all_namespaces)
         self.kind = kind
         self.name = name
         self.selector = selector
+        self.field_selector = field_selector
 
     def get(self):
         '''return a kind by name '''
-        results = self._get(self.kind, name=self.name, selector=self.selector)
+        results = self._get(self.kind, name=self.name, selector=self.selector, field_selector=self.field_selector)
         if (results['returncode'] != 0 and 'stderr' in results and
                 '\"{}\" not found'.format(self.name) in results['stderr']):
             results['returncode'] = 0
@@ -113,7 +115,8 @@ class OCObject(OpenShiftCLI):
                          params['selector'],
                          kubeconfig=params['kubeconfig'],
                          verbose=params['debug'],
-                         all_namespaces=params['all_namespaces'])
+                         all_namespaces=params['all_namespaces'],
+                         field_selector=params['field_selector'])
 
         state = params['state']
 
@@ -123,6 +126,8 @@ class OCObject(OpenShiftCLI):
         # Get
         #####
         if state == 'list':
+            if api_rval['returncode'] != 0:
+                return {'changed': False, 'failed': True, 'msg': api_rval}
             return {'changed': False, 'results': api_rval, 'state': state}
 
         ########

+ 6 - 0
roles/lib_openshift/src/doc/edit

@@ -88,6 +88,12 @@ options:
     required: false
     default: None
     aliases: []
+  edits:
+    description:
+    - a list of dictionaries with a yedit format for edits
+    required: false
+    default: None
+    aliases: []
   force:
     description:
     - Whether or not to force the operation

+ 6 - 0
roles/lib_openshift/src/doc/obj

@@ -81,6 +81,12 @@ options:
     required: false
     default: None
     aliases: []
+  field_selector:
+    description:
+    - Field selector that gets added to the query.
+    required: false
+    default: None
+    aliases: []
 author:
 - "Kenny Woodson <kwoodson@redhat.com>"
 extends_documentation_fragment: []

+ 25 - 8
roles/lib_openshift/src/lib/base.py

@@ -52,7 +52,7 @@ class OpenShiftCLI(object):
 
     # Pylint allows only 5 arguments to be passed.
     # pylint: disable=too-many-arguments
-    def _replace_content(self, resource, rname, content, force=False, sep='.'):
+    def _replace_content(self, resource, rname, content, edits=None, force=False, sep='.'):
         ''' replace the current object with the content '''
         res = self._get(resource, rname)
         if not res['results']:
@@ -61,13 +61,24 @@ class OpenShiftCLI(object):
         fname = Utils.create_tmpfile(rname + '-')
 
         yed = Yedit(fname, res['results'][0], separator=sep)
-        changes = []
-        for key, value in content.items():
-            changes.append(yed.put(key, value))
+        updated = False
 
-        if any([change[0] for change in changes]):
-            yed.write()
+        if content is not None:
+            changes = []
+            for key, value in content.items():
+                changes.append(yed.put(key, value))
+
+            if any([change[0] for change in changes]):
+                updated = True
+
+        elif edits is not None:
+            results = Yedit.process_edits(edits, yed)
 
+            if results['changed']:
+                updated = True
+
+        if updated:
+            yed.write()
             atexit.register(Utils.cleanup, [fname])
 
             return self._replace(fname, force)
@@ -145,12 +156,18 @@ class OpenShiftCLI(object):
 
         return self.openshift_cmd(['create', '-f', fname])
 
-    def _get(self, resource, name=None, selector=None):
+    def _get(self, resource, name=None, selector=None, field_selector=None):
         '''return a resource by name '''
         cmd = ['get', resource]
+
         if selector is not None:
             cmd.append('--selector={}'.format(selector))
-        elif name is not None:
+
+        if field_selector is not None:
+            cmd.append('--field-selector={}'.format(field_selector))
+
+        # Name cannot be used with selector or field_selector.
+        if selector is None and field_selector is None and name is not None:
             cmd.append(name)
 
         cmd.extend(['-o', 'json'])

+ 18 - 1
roles/lib_utils/action_plugins/sanity_checks.py

@@ -15,7 +15,8 @@ NET_PLUGIN_LIST = (('openshift_use_openshift_sdn', True),
                    ('openshift_use_flannel', False),
                    ('openshift_use_nuage', False),
                    ('openshift_use_contiv', False),
-                   ('openshift_use_calico', False))
+                   ('openshift_use_calico', False),
+                   ('openshift_use_kuryr', False))
 
 ENTERPRISE_TAG_REGEX_ERROR = """openshift_image_tag must be in the format
 v#.#[.#[.#]]. Examples: v1.2, v3.4.1, v3.5.1.3,
@@ -33,6 +34,10 @@ ENTERPRISE_TAG_REGEX = {'re': '(^v\\d+\\.\\d+(\\.\\d+)*(-\\d+(\\.\\d+)*)?$)',
 IMAGE_TAG_REGEX = {'origin': ORIGIN_TAG_REGEX,
                    'openshift-enterprise': ENTERPRISE_TAG_REGEX}
 
+UNSUPPORTED_OCP_VERSIONS = {
+    '^3.8.*$': 'OCP 3.8 is not supported and cannot be installed'
+}
+
 CONTAINERIZED_NO_TAG_ERROR_MSG = """To install a containerized Origin release,
 you must set openshift_release or openshift_image_tag in your inventory to
 specify which version of the OpenShift component images to use.
@@ -144,6 +149,17 @@ class ActionModule(ActionBase):
                 msg = '{} must be 63 characters or less'.format(varname)
                 raise errors.AnsibleModuleError(msg)
 
+    def check_supported_ocp_version(self, hostvars, host, openshift_deployment_type):
+        """Checks that the OCP version supported"""
+        if openshift_deployment_type == 'origin':
+            return None
+        openshift_version = self.template_var(hostvars, host, 'openshift_version')
+        for regex_to_match, error_msg in UNSUPPORTED_OCP_VERSIONS.items():
+            res = re.match(regex_to_match, str(openshift_version))
+            if res is not None:
+                raise errors.AnsibleModuleError(error_msg)
+        return None
+
     def run_checks(self, hostvars, host):
         """Execute the hostvars validations against host"""
         distro = self.template_var(hostvars, host, 'ansible_distribution')
@@ -153,6 +169,7 @@ class ActionModule(ActionBase):
         self.no_origin_image_version(hostvars, host, odt)
         self.network_plugin_check(hostvars, host)
         self.check_hostname_vars(hostvars, host)
+        self.check_supported_ocp_version(hostvars, host, odt)
 
     def run(self, tmp=None, task_vars=None):
         result = super(ActionModule, self).run(tmp, task_vars)

+ 18 - 43
roles/lib_utils/filter_plugins/oo_filters.py

@@ -4,6 +4,7 @@
 """
 Custom filters for use in openshift-ansible
 """
+import ast
 import json
 import os
 import pdb
@@ -23,7 +24,7 @@ from ansible import errors
 from ansible.parsing.yaml.dumper import AnsibleDumper
 
 # pylint: disable=import-error,no-name-in-module
-from ansible.module_utils.six import string_types, u
+from ansible.module_utils.six import iteritems, string_types, u
 # pylint: disable=import-error,no-name-in-module
 from ansible.module_utils.six.moves.urllib.parse import urlparse
 
@@ -248,6 +249,15 @@ def lib_utils_oo_dict_to_keqv_list(data):
         Return data:
         ['a=1', 'b=2']
     """
+    if not isinstance(data, dict):
+        try:
+            # This will attempt to convert something that looks like a string
+            # representation of a dictionary (including json) into a dictionary.
+            data = ast.literal_eval(data)
+        except ValueError:
+            msg = "|failed expects first param is a dict. Got {}. Type: {}"
+            msg = msg.format(str(data), str(type(data)))
+            raise errors.AnsibleFilterError(msg)
     return ['='.join(str(e) for e in x) for x in data.items()]
 
 
@@ -660,48 +670,14 @@ def map_from_pairs(source, delim="="):
     return dict(item.split(delim) for item in source.split(","))
 
 
-def lib_utils_oo_get_node_labels(source, hostvars=None):
-    ''' Return a list of labels assigned to schedulable nodes '''
-    labels = list()
-
-    # Filter out the unschedulable nodes
-    for host in source:
-        if host not in hostvars:
-            return
-        node_vars = hostvars[host]
-
-        # All nodes are considered schedulable,
-        # unless explicitly marked so
-        schedulable = node_vars.get('openshift_schedulable')
-        if schedulable is None:
-            schedulable = True
-        try:
-            if not strtobool(str(schedulable)):
-                # explicitly marked as unschedulable
-                continue
-        except ValueError:
-            # Incorrect value in openshift_schedulable, skip node
-            continue
-
-        # Get a list of labels from the node
-        node_labels = node_vars.get('openshift_node_labels')
-        if node_labels:
-            labels.append(node_labels)
-
-    return labels
+def map_to_pairs(source, delim="="):
+    ''' Returns a comma separated str given the source as a dict '''
 
+    # Some default selectors are empty strings.
+    if source == {} or source == '':
+        return str()
 
-def lib_utils_oo_has_no_matching_selector(source, selector=None):
-    ''' Return True when selector cannot be placed
-        on nodes with labels from source '''
-    # Empty selector means any node
-    if not selector:
-        return False
-    for item in source:
-        if set(selector.items()).issubset(set(item.items())):
-            # Matching selector found
-            return False
-    return True
+    return ','.join(["{}{}{}".format(key, delim, value) for key, value in iteritems(source)])
 
 
 class FilterModule(object):
@@ -735,7 +711,6 @@ class FilterModule(object):
             "lib_utils_oo_selector_to_string_list": lib_utils_oo_selector_to_string_list,
             "lib_utils_oo_filter_sa_secrets": lib_utils_oo_filter_sa_secrets,
             "lib_utils_oo_l_of_d_to_csv": lib_utils_oo_l_of_d_to_csv,
-            "lib_utils_oo_has_no_matching_selector": lib_utils_oo_has_no_matching_selector,
-            "lib_utils_oo_get_node_labels": lib_utils_oo_get_node_labels,
             "map_from_pairs": map_from_pairs,
+            "map_to_pairs": map_to_pairs,
         }

+ 1 - 1
roles/lib_utils/library/repoquery.py

@@ -547,13 +547,13 @@ class Repoquery(RepoqueryCLI):
         rval = self._repoquery_cmd(repoquery_cmd, True, 'raw')
 
         # check to see if there are actual results
+        rval['package_name'] = self.name
         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

+ 11 - 61
roles/lib_utils/lookup_plugins/openshift_master_facts_default_predicates.py

@@ -1,6 +1,5 @@
 # pylint: disable=missing-docstring
 
-import re
 from ansible.errors import AnsibleError
 from ansible.plugins.lookup import LookupBase
 
@@ -9,24 +8,12 @@ class LookupModule(LookupBase):
     # pylint: disable=too-many-branches,too-many-statements,too-many-arguments
 
     def run(self, terms, variables=None, regions_enabled=True, short_version=None,
-            deployment_type=None, **kwargs):
+            **kwargs):
 
         predicates = []
 
-        if short_version is None or deployment_type is None:
-            if 'openshift' not in variables:
-                raise AnsibleError("This lookup module requires openshift_facts to be run prior to use")
-
-        if deployment_type is None:
-            if 'common' not in variables['openshift'] or 'deployment_type' not in variables['openshift']['common']:
-                raise AnsibleError("This lookup module requires that the deployment_type be set")
-
-            deployment_type = variables['openshift']['common']['deployment_type']
-
         if short_version is None:
-            if 'short_version' in variables['openshift']['common']:
-                short_version = variables['openshift']['common']['short_version']
-            elif 'openshift_release' in variables:
+            if 'openshift_release' in variables:
                 release = variables['openshift_release']
                 if release.startswith('v'):
                     short_version = release[1:]
@@ -39,18 +26,9 @@ class LookupModule(LookupBase):
             else:
                 # pylint: disable=line-too-long
                 raise AnsibleError("Either OpenShift needs to be installed or openshift_release needs to be specified")
-        if deployment_type == 'origin':
-            if short_version not in ['1.1', '1.2', '1.3', '1.4', '1.5', '3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
-                raise AnsibleError("Unknown short_version %s" % short_version)
-        elif deployment_type == 'openshift-enterprise':
-            if short_version not in ['3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
-                raise AnsibleError("Unknown short_version %s" % short_version)
-        else:
-            raise AnsibleError("Unknown deployment_type %s" % deployment_type)
 
-        if deployment_type == 'origin':
-            # convert short_version to enterprise short_version
-            short_version = re.sub('^1.', '3.', short_version)
+        if short_version not in ['3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
+            raise AnsibleError("Unknown short_version %s" % short_version)
 
         if short_version == 'latest':
             short_version = '3.10'
@@ -58,63 +36,35 @@ class LookupModule(LookupBase):
         # Predicates ordered according to OpenShift Origin source:
         # origin/vendor/k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go
 
-        if short_version == '3.1':
-            predicates.extend([
-                {'name': 'PodFitsHostPorts'},
-                {'name': 'PodFitsResources'},
-                {'name': 'NoDiskConflict'},
-                {'name': 'MatchNodeSelector'},
-            ])
-
-        if short_version == '3.2':
-            predicates.extend([
-                {'name': 'PodFitsHostPorts'},
-                {'name': 'PodFitsResources'},
-                {'name': 'NoDiskConflict'},
-                {'name': 'NoVolumeZoneConflict'},
-                {'name': 'MatchNodeSelector'},
-                {'name': 'MaxEBSVolumeCount'},
-                {'name': 'MaxGCEPDVolumeCount'}
-            ])
-
-        if short_version == '3.3':
+        if short_version in ['3.6']:
             predicates.extend([
-                {'name': 'NoDiskConflict'},
                 {'name': 'NoVolumeZoneConflict'},
                 {'name': 'MaxEBSVolumeCount'},
                 {'name': 'MaxGCEPDVolumeCount'},
-                {'name': 'GeneralPredicates'},
-                {'name': 'PodToleratesNodeTaints'},
-                {'name': 'CheckNodeMemoryPressure'}
-            ])
-
-        if short_version == '3.4':
-            predicates.extend([
+                {'name': 'MatchInterPodAffinity'},
                 {'name': 'NoDiskConflict'},
-                {'name': 'NoVolumeZoneConflict'},
-                {'name': 'MaxEBSVolumeCount'},
-                {'name': 'MaxGCEPDVolumeCount'},
                 {'name': 'GeneralPredicates'},
                 {'name': 'PodToleratesNodeTaints'},
                 {'name': 'CheckNodeMemoryPressure'},
                 {'name': 'CheckNodeDiskPressure'},
-                {'name': 'MatchInterPodAffinity'}
             ])
 
-        if short_version in ['3.5', '3.6']:
+        if short_version in ['3.7', '3.8']:
             predicates.extend([
                 {'name': 'NoVolumeZoneConflict'},
                 {'name': 'MaxEBSVolumeCount'},
                 {'name': 'MaxGCEPDVolumeCount'},
+                {'name': 'MaxAzureDiskVolumeCount'},
                 {'name': 'MatchInterPodAffinity'},
                 {'name': 'NoDiskConflict'},
                 {'name': 'GeneralPredicates'},
                 {'name': 'PodToleratesNodeTaints'},
                 {'name': 'CheckNodeMemoryPressure'},
                 {'name': 'CheckNodeDiskPressure'},
+                {'name': 'NoVolumeNodeConflict'},
             ])
 
-        if short_version in ['3.7', '3.8', '3.9', '3.10']:
+        if short_version in ['3.9', '3.10']:
             predicates.extend([
                 {'name': 'NoVolumeZoneConflict'},
                 {'name': 'MaxEBSVolumeCount'},
@@ -126,7 +76,7 @@ class LookupModule(LookupBase):
                 {'name': 'PodToleratesNodeTaints'},
                 {'name': 'CheckNodeMemoryPressure'},
                 {'name': 'CheckNodeDiskPressure'},
-                {'name': 'NoVolumeNodeConflict'},
+                {'name': 'CheckVolumeBinding'},
             ])
 
         if regions_enabled:

+ 5 - 63
roles/lib_utils/lookup_plugins/openshift_master_facts_default_priorities.py

@@ -1,6 +1,5 @@
 # pylint: disable=missing-docstring
 
-import re
 from ansible.errors import AnsibleError
 from ansible.plugins.lookup import LookupBase
 
@@ -9,24 +8,12 @@ class LookupModule(LookupBase):
     # pylint: disable=too-many-branches,too-many-statements,too-many-arguments
 
     def run(self, terms, variables=None, zones_enabled=True, short_version=None,
-            deployment_type=None, **kwargs):
+            **kwargs):
 
         priorities = []
 
-        if short_version is None or deployment_type is None:
-            if 'openshift' not in variables:
-                raise AnsibleError("This lookup module requires openshift_facts to be run prior to use")
-
-        if deployment_type is None:
-            if 'common' not in variables['openshift'] or 'deployment_type' not in variables['openshift']['common']:
-                raise AnsibleError("This lookup module requires that the deployment_type be set")
-
-            deployment_type = variables['openshift']['common']['deployment_type']
-
         if short_version is None:
-            if 'short_version' in variables['openshift']['common']:
-                short_version = variables['openshift']['common']['short_version']
-            elif 'openshift_release' in variables:
+            if 'openshift_release' in variables:
                 release = variables['openshift_release']
                 if release.startswith('v'):
                     short_version = release[1:]
@@ -40,58 +27,13 @@ class LookupModule(LookupBase):
                 # pylint: disable=line-too-long
                 raise AnsibleError("Either OpenShift needs to be installed or openshift_release needs to be specified")
 
-        if deployment_type == 'origin':
-            if short_version not in ['1.1', '1.2', '1.3', '1.4', '1.5', '3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
-                raise AnsibleError("Unknown short_version %s" % short_version)
-        elif deployment_type == 'openshift-enterprise':
-            if short_version not in ['3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
-                raise AnsibleError("Unknown short_version %s" % short_version)
-        else:
-            raise AnsibleError("Unknown deployment_type %s" % deployment_type)
-
-        if deployment_type == 'origin':
-            # convert short_version to origin short_version
-            short_version = re.sub('^1.', '3.', short_version)
+        if short_version not in ['3.6', '3.7', '3.8', '3.9', '3.10', 'latest']:
+            raise AnsibleError("Unknown short_version %s" % short_version)
 
         if short_version == 'latest':
             short_version = '3.10'
 
-        if short_version == '3.1':
-            priorities.extend([
-                {'name': 'LeastRequestedPriority', 'weight': 1},
-                {'name': 'BalancedResourceAllocation', 'weight': 1},
-                {'name': 'SelectorSpreadPriority', 'weight': 1}
-            ])
-
-        if short_version == '3.2':
-            priorities.extend([
-                {'name': 'LeastRequestedPriority', 'weight': 1},
-                {'name': 'BalancedResourceAllocation', 'weight': 1},
-                {'name': 'SelectorSpreadPriority', 'weight': 1},
-                {'name': 'NodeAffinityPriority', 'weight': 1}
-            ])
-
-        if short_version == '3.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 == '3.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 ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10']:
+        if short_version in ['3.6', '3.7', '3.8', '3.9', '3.10']:
             priorities.extend([
                 {'name': 'SelectorSpreadPriority', 'weight': 1},
                 {'name': 'InterPodAffinityPriority', 'weight': 1},

+ 0 - 0
roles/lib_utils/src/class/repoquery.py


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.